package web import ( "net/http" "net/http/httptest" "testing" ) // #59: SecurityHeaders must add the CSP and hardening headers to rendered // HTML responses only; JSON and /raw responses pass through untouched. func TestSecurityHeaders(t *testing.T) { pages := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte("ok")) }) h := SecurityHeaders(pages) rec := httptest.NewRecorder() h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil)) wantCSP := "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; frame-ancestors 'none'" if got := rec.Header().Get("Content-Security-Policy"); got != wantCSP { t.Errorf("CSP = %q, want %q", got, wantCSP) } if got := rec.Header().Get("Referrer-Policy"); got != "no-referrer" { t.Errorf("Referrer-Policy = %q, want no-referrer", got) } if got := rec.Header().Get("X-Content-Type-Options"); got != "nosniff" { t.Errorf("X-Content-Type-Options = %q, want nosniff", got) } // JSON/raw responses: headers are now set unconditionally BEFORE the handler // runs. The previous post-handler approach was silently dropped once a page // handler flushed its template output (headers must be set before WriteHeader). // CSP/nosniff/referrer on non-HTML bodies is harmless and desirable. jsonh := SecurityHeaders(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{"ok":true}`)) })) rec = httptest.NewRecorder() jsonh.ServeHTTP(rec, httptest.NewRequest("GET", "/api/x", nil)) if got := rec.Header().Get("Content-Security-Policy"); got != wantCSP { t.Errorf("CSP missing on JSON response: got %q", got) } if got := rec.Header().Get("Referrer-Policy"); got != "no-referrer" { t.Errorf("Referrer-Policy missing on JSON response: got %q", got) } }