fix(server): add security response headers #23

Merged
nalum merged 1 commit from fix/security-headers into main 2026-08-13 11:38:59 +00:00
Owner

Adds a WithSecurityHeaders middleware around the root mux, so the SPA, /docs, and the RPC surface all carry:

  • Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self' — no unsafe-inline anywhere: the built bundle has no inline styles or scripts (verified against a real npm run build output). data: for fonts/images is required because Vite inlines small @fontsource subsets.
  • Strict-Transport-Security: max-age=31536000 (no includeSubDomains — sibling subdomains exist on the household domain)
  • X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: no-referrer

Unit test asserts every header and that no unsafe-* expression appears in the CSP.

Fixes #10

🤖 Generated with Claude Code

Adds a `WithSecurityHeaders` middleware around the root mux, so the SPA, `/docs`, and the RPC surface all carry: - `Content-Security-Policy: default-src 'self'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'` — no `unsafe-inline` anywhere: the built bundle has no inline styles or scripts (verified against a real `npm run build` output). `data:` for fonts/images is required because Vite inlines small @fontsource subsets. - `Strict-Transport-Security: max-age=31536000` (no `includeSubDomains` — sibling subdomains exist on the household domain) - `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`, `Referrer-Policy: no-referrer` Unit test asserts every header and that no `unsafe-*` expression appears in the CSP. Fixes #10 🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix(server): add security response headers
All checks were successful
check / go (push) Successful in 1m42s
check / web (push) Successful in 1m28s
5bf7ddefa8
The binary served the SPA, the docs site and the RPC surface with no
browser security headers at all: no CSP, no HSTS, no nosniff, nothing
stopping another origin from framing the app. One middleware around the
root mux now stamps every response.

The CSP is strict because the app is self-contained by design: scripts,
styles, fonts and the worker all ship in the binary, so default-src
'self' holds with no 'unsafe-inline' anywhere — the SPA and docs chrome
touch styles only through the CSSOM, which CSP does not restrict, and
no served page carries a <style> block or style attribute. img-src and
font-src add data: because Vite inlines assets under its size threshold
(the built stylesheet carries the smallest @fontsource subsets as
data:font/woff2 URIs — verified against a real build). object-src
'none', frame-ancestors 'none', base-uri and form-action 'self' close
the directives that never fall back to default-src.

HSTS is one year without includeSubDomains: other services live on
sibling subdomains and are not this app's to commit.

Fixes #10
nalum force-pushed fix/security-headers from 5bf7ddefa8
All checks were successful
check / go (push) Successful in 1m42s
check / web (push) Successful in 1m28s
to 2f8f843ab2
Some checks failed
tag / tag (push) Has been cancelled
check / go (push) Successful in 1m48s
check / web (push) Successful in 1m37s
2026-08-13 11:33:34 +00:00
Compare
nalum scheduled this pull request to auto merge when all checks succeed 2026-08-13 11:34:01 +00:00
nalum merged commit 2f8f843ab2 into main 2026-08-13 11:38:59 +00:00
nalum deleted branch fix/security-headers 2026-08-13 11:38:59 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eagraiclainne/app!23
No description provided.