Skip to content

fix(server): add app-wide browser security headers (#1541) - #2026

Draft
bestony wants to merge 1 commit into
mainfrom
fix/1541-app-wide-security-headers
Draft

fix(server): add app-wide browser security headers (#1541)#2026
bestony wants to merge 1 commit into
mainfrom
fix/1541-app-wide-security-headers

Conversation

@bestony

@bestony bestony commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds an app-wide browser security header layer for the Fastify server (packages/server/src/security-headers.ts), wired as a root-level onSend hook in buildApp so every response — SPA shell, static assets, API JSON, error and 404 responses — carries the set.
  • Headers: Content-Security-Policy (incl. frame-ancestors 'none'), Strict-Transport-Security, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, X-Frame-Options: DENY, Permissions-Policy.
  • Why it matters: issue Add app-wide browser security headers for Fastify/SPA #1541 / audit SEC-041 found no in-repo, testable security-header contract — the authenticated dashboard could be framed and inline/remote scripts ran with full origin authority unless edge infra invisibly compensated. The guarantee now lives in code and is covered by tests.

Closes #1541

CSP notes

  • script-src 'self' 'unsafe-inline' https://www.googletagmanager.com https://www.clarity.mspackages/web/index.html ships three inline bootstrap scripts (GA4 loader, Clarity loader, theme init) which inject the GA4/Clarity tags from those hosts. Nonce-based hardening means rewriting index.html per response and is left as follow-up work.
  • connect-src allows same-origin REST + WebSocket (ws:/wss:), GA4 collect, Clarity collect, and optional Sentry ingest (VITE_SENTRY_DSN).
  • img-src 'self' data: blob: https: keeps user-pasted external images in chat markdown rendering.
  • The hook only fills headers a route has not set itself, so route-local policies (e.g. attachments) keep winning.

Validation

  • pnpm check (biome — changed files clean)
  • pnpm --filter @first-tree/server typecheck
  • pnpm --filter @first-tree/server test — new security-headers.test.ts covers the policy shape (framing denied, object-src 'none', analytics/Sentry allowlist), header presence on SPA shell / API 404 / /healthz via buildApp + inject, and route-override preservation; full server suite green

Change Surface

  • apps/cli public CLI or help output
  • tree onboarding / binding / inspection behavior
  • shipped or planned skill topology
  • docs or contributor-facing repository metadata
  • CI / packaging / release plumbing

Notes

  • package or install behavior changes: none — no new dependencies; used the "or equivalent hooks" option from the issue instead of @fastify/helmet to keep the policy explicit and the diff dependency-free
  • docs or tests updated to match: new test file packages/server/src/__tests__/security-headers.test.ts
  • follow-up work: nonce-based CSP for the inline scripts in packages/web/index.html; post-deploy verification per the issue — curl -sI https://<prod-host>/ | grep -iE 'content-security-policy|strict-transport-security|x-content-type-options|referrer-policy|x-frame-options'

@github-actions

github-actions Bot commented Jul 27, 2026

Copy link
Copy Markdown

All contributors are covered by the First Tree CLA.
Posted by the CLA Assistant Lite bot.

@bestony

bestony commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add app-wide browser security headers for Fastify/SPA

1 participant