**Phase 03 final verification — all green** - Verified (all tasks already in `complete/`): snippet/Dockerfile/nginx.conf wiring, `scripts/check-headers.sh`, guardrail teeth, restored config byte-identical. - Tests (full suite): `./build.sh` → OK; `npm test` → **46 passed, 0 failed**; `bash .agents/validate.sh` → **exit 0** (46/46 + `HEADER CHECK PASSED` + hygiene gates); no leftover podman images. - Completion criteria: - `nginx-security-headers.inc` with 4 `add_header … always;` lines — ✅ - `nginx.conf`: 0 inline security `add_header` (cache headers only), 3/3 locations `include` snippet — ✅ - `server_tokens off;` in server block — ✅ (live proof: `Server: nginx`, no version) - `./scripts/check-headers.sh` → `HEADER CHECK PASSED`, OK on `/`, `/index.html`, hashed asset — ✅ (also confirmed all 4 headers + correct Cache-Control on 404 fallback) - `./build.sh && npm test` fully green — ✅ - No behavior change (terminal/vim/achievements/menu suites) — ✅ - Negative check re-run: dropping one `include` → `FAIL … on /style.<hash>.css`, exit 1 — ✅ - Docs: README.md & AGENTS.md reviewed in full against actual state (commands, ports 8123/18099, header mechanism, CI tags vs `.gitea/workflows/build-push.yml`, Node ≥20 engine floor, innerHTML/neofetch exception at `terminal.js:487`, simulated curl/wget guards) — **verified consistent, no changes needed**. - Notable: check-headers.sh aborts at the first failing path via `set -e` before printing `HEADER CHECK FAILED` — README's wording matches this exactly (verified empirically). PLAN.md's "44 tests" count is stale (now 46) but PLAN.md is locked — left for the user. - No live-infra changes (no HSTS in container; edge/Caddy untouched). - Next pending phase: `04_huntington_role`.
2.4 KiB
Phase 03 — Security headers on every location (audit M-4)
Story: n/a
Context: .agents/PLAN.md §3 (serving + edge), .agents/remediation_plan.md
findings M-4 (headers) and L-2 (server_tokens, included as a one-liner in
the same file)
Objective
Fix the nginx add_header inheritance trap: the location = /index.html
and the hashed-asset location each define their own cache add_header
directives, so they lose the server-level security headers (CSP,
X-Frame-Options, X-Content-Type-Options, Referrer-Policy). Extract the four
security headers into a snippet and include it in every location, so
every response class carries the full header set. Also set
server_tokens off; (L-2) in the same pass.
Dependencies
— (none)
Tasks
01_extract_header_snippet.md— Createnginx-security-headers.inc, wire it into the Dockerfile, andincludeit in the server block and both header-defining locations (plusserver_tokens off;).02_add_header_verification.md— Addscripts/check-headers.shthat builds and runs the real image and asserts all four headers on/,/index.html, and a hashed asset.
Testing & Quality
- Unit/integration: the container-based header check (task 02) is the integration test. It must assert all four headers on each of the three response classes.
- Coverage: >90% on new/modified config (all three location paths exercised by the check script).
- The Playwright E2E suite is unaffected (its webServer is plain python, not nginx) and must stay green.
Completion Criteria
nginx-security-headers.incexists and holds the fouradd_header … always;linesnginx.confhas no inline securityadd_headerlines (only theinclude), and both header-defining locationsincludethe snippetserver_tokens off;present in theserverblockscripts/check-headers.shruns the real image and printsHEADER CHECK PASSED(all four headers on/,/index.html, and a hashed asset)./build.sh && npm testfully green- no behavior change in completed work (terminal, vim, achievements, menu)
Out of scope (separate, unphased): L-1 HSTS — that is an edge/Caddy
responsibility (the container serves plain HTTP on 8080). Verify at the edge
manually: curl -sI https://reeseapps.com/ | grep -i strict-transport. Do
not add HSTS inside the 8080 container.