test(19.3-06): unit tests for the review fixes

- TestCORSCredentialedOriginRules, TestCORSAnchoredPatternNoSuffixMatch (D-04)
- TestAPIDocsCSPNoHostSource (D-17)
- TestLoadCookieConfigProductionInferredSecure, TestZeroCookieConfigDefaults (D-03)
- TestSiteCachePurgeWaitAndClose, TestSiteCacheBulkCommitCoalesced (D-10)
This commit is contained in:
Jakub Zych
2026-10-15 22:45:16 +02:00
parent 4d4e37932c
commit 8d80580083
4 changed files with 409 additions and 0 deletions

View File

@@ -305,3 +305,67 @@ func TestAPIDocsTitleEscaped(t *testing.T) {
t.Fatal("default title missing")
}
}
// cspDirectives splits a Content-Security-Policy value into its directives.
func cspDirectives(csp string) map[string][]string {
out := map[string][]string{}
for _, part := range strings.Split(csp, ";") {
fields := strings.Fields(part)
if len(fields) == 0 {
continue
}
out[fields[0]] = fields[1:]
}
return out
}
func TestAPIDocsCSPNoHostSource(t *testing.T) {
h, err := Assemble(apiDocsApp(t, "api_docs:\n enabled: true\n"), []party.Plugin{specPlugin{id: "acme.zoo", spec: orderedSpecYAML}})
if err != nil {
t.Fatal(err)
}
nonces := map[string]bool{}
for range 2 {
rec := apiGet(h, "/api-docs")
if rec.Code != http.StatusOK {
t.Fatalf("GET /api-docs = %d", rec.Code)
}
dirs := cspDirectives(rec.Header().Get("Content-Security-Policy"))
script := dirs["script-src"]
if len(script) != 1 || !strings.HasPrefix(script[0], "'nonce-") || !strings.HasSuffix(script[0], "'") {
t.Fatalf("script-src = %q, want exactly one nonce source", script)
}
nonce := strings.TrimSuffix(strings.TrimPrefix(script[0], "'nonce-"), "'")
if nonce == "" {
t.Fatal("empty nonce")
}
for _, d := range []string{"object-src", "base-uri", "frame-ancestors"} {
if got := dirs[d]; len(got) != 1 || got[0] != "'none'" {
t.Errorf("%s = %q, want 'none'", d, got)
}
}
page := rec.Body.String()
tags := regexp.MustCompile(`<script[^>]*>`).FindAllString(page, -1)
if len(tags) != 3 {
t.Fatalf("script tags = %q, want 3", tags)
}
for _, tag := range tags {
if !strings.Contains(tag, ` nonce="`+nonce+`"`) {
t.Errorf("script tag %q lacks nonce %q", tag, nonce)
}
}
for _, sri := range []string{
"sha384-Ov4/wv3j2bmct8cDc5X4ngJZohVPzEmc6uDPH8WeljUxO5vtoykvMEfbu9Vh6RaW",
"sha384-ZPehFMQommnnuaZ4rpxgkgTT2DKFVp4hZC/7pLit+9Lek9T1YGSo23eHFbvNkXkw",
"sha384-My2aDM4r2Mbm3ybHcubKm9O9U8FEjvF/O5nGvE9YK5dzqOTbWEKa79RPJ1krdMaF",
} {
if !strings.Contains(page, `integrity="`+sri+`"`) {
t.Errorf("page lacks integrity %s", sri)
}
}
nonces[nonce] = true
}
if len(nonces) != 2 {
t.Fatal("two requests got the same nonce")
}
}

View File

@@ -718,3 +718,103 @@ func TestTurnstileEmptySecretWarnsAtBoot(t *testing.T) {
t.Fatalf("warnings without turnstile routes = %v", got)
}
}
func TestCORSCredentialedOriginRules(t *testing.T) {
// Each block lists a good entry first, so a refusal names index [1].
blocks := []struct{ name, prefix, yamlHead, indent string }{
{"global", "http.cors", "cors:\n paths: [\"api/*\"]\n", " "},
{"profile", "http.cors_profiles.spa", "cors_profiles:\n spa:\n", " "},
}
load := func(t *testing.T, block int, body string) error {
t.Helper()
b := blocks[block]
cfg := writeHTTPConfig(t, b.yamlHead+body)
if b.name == "global" {
_, err := LoadCORSConfig(cfg)
return err
}
_, err := LoadCORSProfiles(cfg)
return err
}
entry := func(indent, key, good, value string, credentials bool) string {
out := indent + key + ": ['" + good + "', '" + value + "']\n"
if credentials {
out += indent + "supports_credentials: true\n"
}
return out
}
const goodOrigin, goodPattern = "https://www.example.com", `^https://www\.example\.com$`
accepted := []struct{ key, value string }{
{"allowed_origins", "https://www.example.com"},
{"allowed_origins", "https://*.example.com"},
{"allowed_origins", "https://*.api.example.com:8443"},
{"allowed_origins_patterns", `^https://[a-z]+\.example\.org$`},
}
refused := []struct{ key, value string }{
{"allowed_origins", "https://*"},
{"allowed_origins", "*://*"},
{"allowed_origins", "https://*example.com"},
{"allowed_origins", "https://*.com"},
{"allowed_origins", "http*://www.example.com"},
{"allowed_origins_patterns", `https://.*\.example\.com`},
{"allowed_origins_patterns", `^https://.*\.example\.com`},
{"allowed_origins_patterns", `^https?://.*$`},
{"allowed_origins_patterns", `^.*$`},
}
for i, b := range blocks {
for _, tc := range accepted {
good := goodOrigin
if tc.key == "allowed_origins_patterns" {
good = goodPattern
}
if err := load(t, i, entry(b.indent, tc.key, good, tc.value, true)); err != nil {
t.Errorf("%s %s %q with credentials: %v", b.name, tc.key, tc.value, err)
}
}
for _, tc := range refused {
good := goodOrigin
if tc.key == "allowed_origins_patterns" {
good = goodPattern
}
want := b.prefix + "." + tc.key + "[1]"
err := load(t, i, entry(b.indent, tc.key, good, tc.value, true))
if err == nil || !strings.Contains(err.Error(), want) {
t.Errorf("%s %q with credentials: error = %v, want one naming %s", b.name, tc.value, err, want)
}
if err := load(t, i, entry(b.indent, tc.key, good, tc.value, false)); err != nil {
t.Errorf("%s %q without credentials: %v", b.name, tc.value, err)
}
}
}
}
func TestCORSAnchoredPatternNoSuffixMatch(t *testing.T) {
plugin := routesPluginFunc{id: "acme.spa", routes: func(r pact.Router) {
r.Get("/x/me", okHandler, "cors:spa")
}}
app := transportApp(t, "development", map[string]string{"http.yaml": transportBodyLimits + `cors_profiles:
spa:
allowed_origins_patterns: ['^https://[a-z]+\.example\.com$']
allowed_methods: ["GET"]
supports_credentials: true
`})
h, err := Assemble(app, []party.Plugin{plugin})
if err != nil {
t.Fatal(err)
}
rec := serveCORS(h, corsRequest(http.MethodGet, "/x/me", "https://www.example.com", false))
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "https://www.example.com" || rec.Header().Get("Access-Control-Allow-Credentials") != "true" {
t.Fatalf("allowed origin: Allow-Origin %q Allow-Credentials %q", got, rec.Header().Get("Access-Control-Allow-Credentials"))
}
rec = serveCORS(h, corsRequest(http.MethodGet, "/x/me", "https://www.example.com.attacker.net", false))
if got := rec.Header().Get("Access-Control-Allow-Origin"); got != "" {
t.Fatalf("suffixed origin: Allow-Origin = %q, want none", got)
}
if rec.Header().Get("Access-Control-Allow-Credentials") != "" {
t.Fatal("suffixed origin: Allow-Credentials set")
}
if !varyHas(rec.Header(), "Origin") {
t.Fatalf("suffixed origin: Vary = %q, want Origin", rec.Header().Values("Vary"))
}
}