feat: add security response headers middleware (closes #98)
All checks were successful
check / check (push) Successful in 35s
All checks were successful
check / check (push) Successful in 35s
Add SecurityHeaders() to internal/middleware and register it in the
global middleware stack so every response - dashboard, embedded static
assets, healthchecks, JSON API, and metrics - carries the six response
headers required by REPO_POLICIES.md before tagging 1.0:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; script-src 'none';
style-src 'self'; img-src 'self';
font-src 'none'; connect-src 'none';
object-src 'none'; base-uri 'none';
form-action 'none'; frame-ancestors 'none'
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Permissions-Policy: unused browser features denied
The dashboard template ships no JavaScript, no inline styles, no inline
event handlers and no images, and its only subresource is the embedded
stylesheet at /s/css/tailwind.min.css, so the policy needs neither
unsafe-inline nor unsafe-eval. frame-ancestors 'none' is the primary
anti-framing control with X-Frame-Options as the legacy fallback.
HSTS is emitted unconditionally rather than gated on r.TLS, because the
service runs behind a TLS-terminating proxy and the browser must still
enforce HTTPS end to end.
The headers are set before the request reaches the next handler, so
they are present on error responses too, including recovered panics and
request timeouts.
Tests cover each header's exact value, the CSP's required and forbidden
directives, presence on a 500 response, and a render of the real
dashboard through the middleware confirming the page still references
its stylesheet.
This commit is contained in:
10
TODO.md
10
TODO.md
@@ -25,6 +25,16 @@ confirm make check still passes.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-09: security response headers middleware
|
||||
(`SecurityHeaders()` in `internal/middleware/middleware.go`)
|
||||
registered globally in `internal/server/routes.go`, so HSTS, CSP,
|
||||
`X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, and
|
||||
`Permissions-Policy` are set on every response including `/s/...` and
|
||||
`/metrics`; the CSP needs no `unsafe-inline`/`unsafe-eval` because the
|
||||
dashboard ships no JavaScript and no inline styles; HSTS is emitted
|
||||
unconditionally per policy (TLS-terminating proxy in front). Remaining
|
||||
1.0 hardening items — `http.Server` timeouts, request body limits,
|
||||
rate limiting, CORS scoping — are tracked separately
|
||||
- 2026-08-07: golangci-lint bumped to v2.12.2 (commit-pinned installs
|
||||
in `Dockerfile` and `script/bootstrap`); `.golangci.yml` set to the
|
||||
org-standard v2-schema config used across the org's repos
|
||||
|
||||
Reference in New Issue
Block a user