| 1 | package homeconnect |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "html/template" |
| 6 | "strings" |
| 7 | "testing" |
| 8 | ) |
| 9 | |
| 10 | func render(t *testing.T, name string, data any) string { |
| 11 | t.Helper() |
| 12 | view, err := template.New("view").ParseFS(Templates(), "templates/*.html") |
| 13 | if err != nil { |
| 14 | t.Fatal(err) |
| 15 | } |
| 16 | var out bytes.Buffer |
| 17 | if err := view.ExecuteTemplate(&out, name, data); err != nil { |
| 18 | t.Fatal(err) |
| 19 | } |
| 20 | return out.String() |
| 21 | } |
| 22 | |
| 23 | func TestCallerTranslationsAndScriptlessActions(t *testing.T) { |
| 24 | labels := map[string]string{"en": "Continue", "de": "Weiter", "es": "Continuar", "fr": "Continuer", "it": "Continua", "pt": "Continuar", "nl": "Doorgaan", "pl": "Kontynuuj", "ar": "متابعة", "ja": "続行", "zh": "继续", "ko": "계속"} |
| 25 | for locale, label := range labels { |
| 26 | t.Run(locale, func(t *testing.T) { |
| 27 | data := PageData{Title: label, Body: label, Address: "person@example.test", Action: "/account/home", ActionLabel: label, CancelLabel: label, PostAction: true} |
| 28 | html := render(t, "home-connect", data) |
| 29 | for _, want := range []string{label, `method="post" action="/account/home"`, `<bdi>person@example.test</bdi>`, `rst-btn="ghost"`} { |
| 30 | if !strings.Contains(html, want) { |
| 31 | t.Fatalf("missing %q", want) |
| 32 | } |
| 33 | } |
| 34 | if strings.Contains(html, "<script") { |
| 35 | t.Fatal("connection action requires script") |
| 36 | } |
| 37 | data.PostAction = false |
| 38 | if html = render(t, "home-connect", data); strings.Contains(html, "<form") || !strings.Contains(html, `href="/account/home"`) { |
| 39 | t.Fatal("navigation action is not a link") |
| 40 | } |
| 41 | }) |
| 42 | } |
| 43 | } |
| 44 | func TestUntrustedTextAndDestinationAreEscaped(t *testing.T) { |
| 45 | html := render(t, "home-connect", PageData{Title: `<script>bad</script>`, Address: `<img src=x onerror=bad>`, Action: "javascript:bad", ActionLabel: "Go"}) |
| 46 | if strings.Contains(html, "<script>") || strings.Contains(html, "<img") || !strings.Contains(html, "#ZgotmplZ") { |
| 47 | t.Fatal("unsafe connection markup", html) |
| 48 | } |
| 49 | } |
| 50 | func TestOAuthHeadAndBodyRetainValidatedDestination(t *testing.T) { |
| 51 | d := OAuthData{URL: "https://keymail.example.test/oauth/authorize?state=test&client_id=app", Title: "Continue", ActionLabel: "Continue"} |
| 52 | head, body := render(t, "home-oauth-head", d), render(t, "home-oauth", d) |
| 53 | if !strings.Contains(head, `http-equiv="refresh"`) || !strings.Contains(head, "keymail.example.test/oauth/authorize") || !strings.Contains(body, `href="https://keymail.example.test/oauth/authorize?state=test&client_id=app"`) { |
| 54 | t.Fatal("scriptless OAuth continuation missing", head, body) |
| 55 | } |
| 56 | } |
| 57 | |