Skip to content

feat(security): step 6 — headers, proxy matching, server-only, prerender check + build in CI - #53

Merged
chriskehayias merged 3 commits into
devfrom
feat/step6-hardening
Oct 10, 2026
Merged

chriskehayias merged 3 commits into
devfrom
feat/step6-hardening

Conversation

@chriskehayias

Copy link
Copy Markdown
Contributor

Closes the last two open TODOs: step 6 of the 2026-09-30 playbook audit and the held CSP notes for customer clones.

Headers and proxy

  • next.config.ts: COOP/CORP same-origin, poweredByHeader: false, images.unoptimized (nothing uses next/image, so the optimizer was only SSRF surface), logging.serverFunctions: false (stops next dev printing server-action arguments such as household records).
  • proxy.ts: /api/auth is public only as a whole path segment (/api/authz now gets the session check). Matcher exclusions are anchored, so look-alike paths like /favicon.ico-admin are still gated.
  • CSP: MP origins are accepted only over https (http to localhost only in dev). data:, javascript:, plain-http and credentialed URLs are dropped. base-uri 'none'.
  • Opt-in CSP_REPORT_URI: adds report-uri + report-to and a Reporting-Endpoints header. Unset means no reporting, and the repo ships no collector.

Build guards

  • import 'server-only' in every service, auth.ts, env.ts, tool-params.server.ts and the MP client/credentials modules. Tested by importing domainTimezoneService from a client component, which made next build fail with the server-only trace.
  • Vitest aliases server-only to its no-op entry. The mp:generate* scripts now run tsx --conditions=react-server.
  • scripts/check-prerender.mjs (npm run check:prerender) fails when any route other than /_not-found or /_global-error is statically prerendered. Tested with a planted static page.
  • CI test job: new Build step (placeholder env, no MP call) and Check prerendered routes step.

Verification

  • lint, typecheck and test:coverage pass: 2191 tests, 99.13% statements.
  • A build with CI placeholder env and no .env.local passes.
  • Enforced-CSP walk against a production build (2026-10-10): sign-in, every tool, Radix select/popover/command and dialog (scroll-lock <style> applied), contact search and household load. Zero CSP violations. Headers checked with curl on the real server.
  • Not walked: sign-out. UserMenu isn't mounted anywhere, so a production build has no sign-out control. This was already the case before this PR.

Docs

Security reference (headers, matching, reporting, customer-clone roll-out), GOTCHA-046/047, CLAUDE.md (CI builds now, server-only practice). Both TODOs deleted; the index shows 0 open.

🤖 Generated with Claude Code

chriskehayias and others added 3 commits October 10, 2026 13:36
- next.config: COOP/CORP same-origin, poweredByHeader off, images.unoptimized
  (no next/image in use), logging.serverFunctions off so `next dev` stops
  printing server-action arguments.
- proxy: /api/auth public check matches the segment, not the prefix; matcher
  exclusions anchored so look-alike paths stay gated.
- CSP: MP origins accepted only over https (loopback http in dev), base-uri
  'none', opt-in CSP_REPORT_URI emitting report-uri/report-to plus
  Reporting-Endpoints.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- `server-only` on every service, auth, env, tool-params.server and the MP
  client/credentials modules; a client import now fails `next build`
  (verified by planting one). Vitest aliases it to the no-op entry; the
  codegen CLIs run tsx with --conditions=react-server.
- scripts/check-prerender.mjs fails when any route besides Next's built-in
  /_not-found and /_global-error is statically prerendered (no CSP nonce).
- CI `test` job now runs `npm run build` (placeholder env) and the
  prerender check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… close TODOs

- security reference: new headers, proxy path matching, CSP source
  validation, CSP_REPORT_URI, customer-clone notes, prerender check, and the
  enforced-CSP walk result.
- GOTCHA-046 (server-only) and GOTCHA-047 (prerender check).
- CLAUDE.md: CI now builds; server-only practice; codegen --conditions flag.
- Delete the step6 and held-CSP TODOs; index at 0 open.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chriskehayias
chriskehayias merged commit 13f0885 into dev Oct 10, 2026
1 check passed
@chriskehayias
chriskehayias deleted the feat/step6-hardening branch October 10, 2026 18:06
@codecov

codecov Bot commented Oct 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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.

1 participant