fabro/lib
Bryan Helmkamp 13f612b111
feat(server): emit baseline HTTP security headers on every response
fabro-server previously sent no security headers beyond content-type
and cache-control. Add a tower middleware that fills in a conservative
default set on every response, preserving any header the handler
already set so routes can still override.

Always applied:
  - X-Content-Type-Options: nosniff
  - X-Frame-Options: DENY
  - Referrer-Policy: strict-origin-when-cross-origin
  - Cross-Origin-Opener-Policy: same-origin
  - Cross-Origin-Resource-Policy: same-origin
  - Permissions-Policy: (deny sensor/payment/xr APIs)
  - X-Download-Options: noopen
  - X-Permitted-Cross-Domain-Policies: none
  - X-XSS-Protection: 0  (current OWASP guidance — the legacy filter
    has known bypasses; CSP is the proper replacement)
  - Cache-Control: no-store  (default; asset routes keep their own)
  - Pragma: no-cache
  - Vary: Accept-Encoding

Applied only when the request reached an HTTPS edge (direct TLS or
X-Forwarded-Proto: https from a reverse proxy):
  - Strict-Transport-Security: max-age=63072000; includeSubDomains

CSP is deliberately not included — it needs a dedicated audit of the
SPA's script/style/font/connect sources and isn't a drop-in header.
Filed as a separate follow-up.

Tests cover each applied header, non-override behavior against the
static-file cache-control, HSTS gating on X-Forwarded-Proto (including
the chained "https, http" leftmost-wins case), and an integration test
against a live router confirming both API and SPA responses carry the
headers.
2026-04-18 15:59:32 -04:00
..
crates feat(server): emit baseline HTTP security headers on every response 2026-04-18 15:59:32 -04:00
packages/fabro-api-client feat(cli): surface debug build profile in version output 2026-04-18 09:44:54 -04:00