From a652692ba44e78c12786ecd1ffd0408020c22c83 Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Thu, 18 Jun 2026 10:50:51 +0900 Subject: [PATCH] checker: align X-XSS-Protection severities with OWASP Absent is now OK (OWASP recommends leaving it unset or set to 0), and filtering mode (bare 1 or 1; report=...) is Warn since selective script rewriting can itself introduce XSS. 1; mode=block stays Info. --- README.md | 2 +- checker/rules_security_headers.go | 8 ++++---- checker/rules_security_headers_test.go | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ca92a97..98bd6aa 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ relies on TLS for transport. | `http.csp` | Verifies the presence and quality of the Content-Security-Policy header on HTTPS responses. | WARNING | | `http.x_frame_options` | Verifies that responses set X-Frame-Options or a CSP frame-ancestors directive. | WARNING | | `http.x_content_type_options` | Verifies that responses set X-Content-Type-Options: nosniff. | WARNING | -| `http.x_xss_protection` | Reports the value of the legacy X-XSS-Protection header (disabled is preferred; CSP is the proper replacement). | INFO | +| `http.x_xss_protection` | Reports the legacy X-XSS-Protection header; warns on filtering mode (can introduce XSS), absent/`0` are fine, CSP is the real defense. | WARNING | | `http.referrer_policy` | Verifies that responses set a Referrer-Policy header with a privacy-preserving value. | WARNING | | `http.permissions_policy` | Verifies that the Permissions-Policy header restricts powerful APIs (camera, microphone, geolocation, …). | WARNING | | `http.coop` | Verifies the Cross-Origin-Opener-Policy (COOP) header for cross-origin process isolation. | WARNING | diff --git a/checker/rules_security_headers.go b/checker/rules_security_headers.go index 0d1a877..d5bed10 100644 --- a/checker/rules_security_headers.go +++ b/checker/rules_security_headers.go @@ -63,9 +63,9 @@ func init() { Inspect: inspectXXSSProtection, OnMissing: func(_ HTTPProbe, _ sdk.CheckerOptions) []HeaderResult { return []HeaderResult{{ - Status: sdk.StatusInfo, + Status: sdk.StatusOK, Suffix: "absent", - Message: "X-XSS-Protection is not set; CSP is the recommended replacement.", + Message: "X-XSS-Protection is not set, which is acceptable (OWASP recommends leaving it unset or setting `0`); CSP is the proper protection.", }} }, })) @@ -235,8 +235,8 @@ func inspectXXSSProtection(value string, _ HTTPProbe, _ sdk.CheckerOptions) []He }} default: return []HeaderResult{{ - Status: sdk.StatusInfo, Suffix: "enabled", - Message: "X-XSS-Protection is enabled. Modern browsers ignore this header; CSP is the proper replacement.", + Status: sdk.StatusWarn, Suffix: "filtering", + Message: "X-XSS-Protection is in filtering mode (e.g. `1` or `1; report=...`). Selective script rewriting can itself introduce XSS in otherwise-safe pages. Set `0` or use `1; mode=block`, and rely on CSP instead.", }} } } diff --git a/checker/rules_security_headers_test.go b/checker/rules_security_headers_test.go index 00c6c27..a6587a4 100644 --- a/checker/rules_security_headers_test.go +++ b/checker/rules_security_headers_test.go @@ -260,9 +260,11 @@ func TestXXSSProtectionRule(t *testing.T) { want sdk.Status code string }{ - {"", sdk.StatusInfo, "http.x_xss_protection.absent"}, + {"", sdk.StatusOK, "http.x_xss_protection.absent"}, {"0", sdk.StatusOK, "http.x_xss_protection.disabled"}, {"1; mode=block", sdk.StatusInfo, "http.x_xss_protection.enabled"}, + {"1", sdk.StatusWarn, "http.x_xss_protection.filtering"}, + {"1; report=https://example.com/r", sdk.StatusWarn, "http.x_xss_protection.filtering"}, } for _, c := range cases { p := httpsProbe("a:443")