#6 — defense-in-depth security headers:
* New _SecurityHeadersMiddleware emits five headers on every response:
- Content-Security-Policy: tight default-src 'self', allow-list the
three CDNs we actively load (unpkg for HTMX, cdnjs for QR codes,
jsdelivr for xterm.js), plus 'unsafe-inline' for the inline script
in settings.html and inline style in job_print.html. Tighten via
nonces later if you want true CSP-level XSS protection.
- X-Content-Type-Options: nosniff
- Referrer-Policy: same-origin
- X-Frame-Options: DENY (no clickjacking)
- Permissions-Policy: camera/microphone/geolocation/interest-cohort
all blocked
* Middleware ordering: SecurityHeaders -> AuthGate -> Session, so
headers go on EVERY response including 401/403/redirects.
#7 — session-fixation defense:
* request.session.clear() now runs BEFORE setting user_id/username on
successful /login AND /api/v1/auth/setup. Discards any pre-login
payload an attacker might have seeded the cookie with. Combined
with SameSite=strict + the HMAC-signed Starlette session cookie,
this closes the residual fixation surface.
Verified: curl -sSI /login returns all five headers; container boots
clean; /health 200; existing session for the operator continues to
work because we only clear on the LOGIN flow itself.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>