Every template and handler message in internal/web goes through a JSON
catalogue under internal/web/locales/<code>/, with English as the fallback.
The language is the person's choice (a cookie set by /_lang/{code}), else
Accept-Language, else landing.language. Tests refuse a key some language
lacks, a translation with other arguments than English, and template text
left in English.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
73 lines
2.3 KiB
Go
73 lines
2.3 KiB
Go
package web
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"io/fs"
|
|
"net/http/httptest"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAPageNamesItsInlineScriptsAndNothingElse(t *testing.T) {
|
|
page := []byte(`<script src="/_static/x.js"></script><script>alert(1)</script>` +
|
|
`<script type="application/json" id="d">{"a":1}</script><script>alert(1)</script>`)
|
|
sum := sha256.Sum256([]byte("alert(1)"))
|
|
csp := contentSecurityPolicy(page)
|
|
want := "script-src 'self' 'sha256-" + base64.StdEncoding.EncodeToString(sum[:]) + "';"
|
|
if !strings.Contains(csp, want) {
|
|
t.Errorf("policy %q lacks %q", csp, want)
|
|
}
|
|
scripts := strings.Split(strings.Split(csp, "script-src")[1], ";")[0]
|
|
if strings.Contains(scripts, "unsafe") || strings.Count(scripts, "sha256-") != 1 {
|
|
t.Errorf("scripts are this server's and the one inline script, nothing unsafe: %q", scripts)
|
|
}
|
|
}
|
|
|
|
func TestRenderedPagesCarryThePolicy(t *testing.T) {
|
|
rec := httptest.NewRecorder()
|
|
renderStatus(rec, nil, 401, "login", loginData{Error: "no"})
|
|
if rec.Code != 401 {
|
|
t.Errorf("status %d", rec.Code)
|
|
}
|
|
csp := rec.Header().Get("Content-Security-Policy")
|
|
if !strings.Contains(csp, "frame-ancestors 'none'") {
|
|
t.Errorf("policy %q", csp)
|
|
}
|
|
// Every inline script on the page is in it.
|
|
for _, m := range inlineScript.FindAllSubmatch(rec.Body.Bytes(), -1) {
|
|
if bytes.Contains(m[1], []byte("src=")) {
|
|
continue
|
|
}
|
|
sum := sha256.Sum256(m[2])
|
|
if !strings.Contains(csp, base64.StdEncoding.EncodeToString(sum[:])) {
|
|
t.Errorf("an inline script is not in the policy: %.60s", m[2])
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNoTemplateUsesWhatThePolicyRefuses(t *testing.T) {
|
|
handler := regexp.MustCompile(`\son[a-z]+\s*=\s*["']`)
|
|
err := fs.WalkDir(files, "templates", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil || d.IsDir() {
|
|
return err
|
|
}
|
|
b, _ := fs.ReadFile(files, path)
|
|
if loc := handler.FindIndex(b); loc != nil {
|
|
t.Errorf("%s has an inline event handler: %.60s", path, b[loc[0]:])
|
|
}
|
|
if bytes.Contains(b, []byte("javascript:")) {
|
|
t.Errorf("%s has a javascript: URL", path)
|
|
}
|
|
if bytes.Contains(b, []byte(`hx-trigger="`)) && regexp.MustCompile(`hx-trigger="[^"]*\[`).Match(b) {
|
|
t.Errorf("%s has an htmx trigger filter, which is evaluated JavaScript", path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|