security: add Content-Security-Policy header (audit O5)#64
Conversation
Ships a CSP in Report-Only mode by default so it cannot break the live
site; browsers evaluate the policy and surface violations without
blocking. Set CSP_ENFORCE=true in the deploy environment to promote it
to an enforcing Content-Security-Policy header after staging confirms
zero violations — no code change required.
Allow-list rationale is documented inline in next.config.ts:
- script-src/style-src allow 'unsafe-inline' (Next App Router inline
hydration scripts + inline style={{…}} in 19 components); 'unsafe-eval'
is dev-only for React Fast Refresh.
- img-src is permissive (https:) because lesson MDX renders via
dangerouslySetInnerHTML and may reference arbitrary image hosts.
- connect-src covers Sentry ingest and Vercel Blob.
- frame-ancestors 'none' mirrors the existing X-Frame-Options: DENY.
Documents the CSP_ENFORCE flag in .env.example and updates the audit +
handover docs to reflect O5 as Report-Only shipped, enforce pending.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018nEP4xYTgNh2JdLjTRNnYZ
📝 WalkthroughWalkthroughAdds an environment-controlled Content-Security-Policy generator to Next.js, applies the selected header to all path responses, and documents report-only defaults, enforcement instructions, and rollout status. ChangesCSP rollout
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant CSP_ENFORCE environment
participant next.config.ts
participant HTTP response
CSP_ENFORCE environment->>next.config.ts: Select CSP enforcement mode
next.config.ts->>HTTP response: Add Content-Security-Policy or Report-Only header
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
next.config.ts (1)
34-44: 🔒 Security & Privacy | 🔵 TrivialNo
report-to/report-urisink configured for the Report-Only phase.The rollout comment says violations get "reported without blocking the site," but without a
report-to/report-uridirective there's no systematic collection channel — violations only surface in an individual browser's devtools console. That makes "confirm zero violations in staging" (the documented gate for flippingCSP_ENFORCE) hard to verify reliably across pages/browsers. This is already tracked as a follow-up in the rollout docs, so flagging for visibility rather than as a blocker.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@next.config.ts` around lines 34 - 44, Update the CSP directives configuration to add a reporting sink for report-only violations, using the project’s documented report-to or report-uri endpoint/configuration. Keep the existing source and enforcement directives unchanged, and ensure the sink is available during the staging rollout used to verify violations before enabling CSP_ENFORCE.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@next.config.ts`:
- Around line 23-51: Add unit tests for contentSecurityPolicy() covering
production versus non-production behavior for 'unsafe-eval' and
upgrade-insecure-requests, plus CSP_ENFORCE-driven selection of the response
header key. Ensure tests isolate and restore NODE_ENV and CSP_ENFORCE so they do
not affect other configuration tests.
- Around line 10-12: Update the comment in the CSP configuration guidance to
replace the em-dash between “header” and “no code change required” with
punctuation permitted by the coding guidelines, preserving the original meaning.
---
Nitpick comments:
In `@next.config.ts`:
- Around line 34-44: Update the CSP directives configuration to add a reporting
sink for report-only violations, using the project’s documented report-to or
report-uri endpoint/configuration. Keep the existing source and enforcement
directives unchanged, and ensure the sink is available during the staging
rollout used to verify violations before enabling CSP_ENFORCE.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 0c2d866e-1470-428a-92fc-7915ea2dbd53
📒 Files selected for processing (4)
.env.exampleSESSION-HANDOVER.mddocs/security/code-audit-2026-07-15.mdnext.config.ts
| // everything. Once staging has been observed to produce zero violations, set | ||
| // `CSP_ENFORCE=true` in the deploy environment to promote it to an enforcing | ||
| // `Content-Security-Policy` header — no code change required. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove the em-dash.
Line 12 uses an em-dash ("header — no code change required"). As per coding guidelines, **/*.{ts,tsx,js,jsx}: "do not use em-dashes. Use periods, commas, or parentheses instead."
Proposed fix
-// `Content-Security-Policy` header — no code change required.
+// `Content-Security-Policy` header (no code change required).📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // everything. Once staging has been observed to produce zero violations, set | |
| // `CSP_ENFORCE=true` in the deploy environment to promote it to an enforcing | |
| // `Content-Security-Policy` header — no code change required. | |
| // everything. Once staging has been observed to produce zero violations, set | |
| // `CSP_ENFORCE=true` in the deploy environment to promote it to an enforcing | |
| // `Content-Security-Policy` header (no code change required). |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@next.config.ts` around lines 10 - 12, Update the comment in the CSP
configuration guidance to replace the em-dash between “header” and “no code
change required” with punctuation permitted by the coding guidelines, preserving
the original meaning.
Source: Coding guidelines
| function contentSecurityPolicy(): string { | ||
| const isProd = process.env.NODE_ENV === 'production'; | ||
| const scriptSrc = ["'self'", "'unsafe-inline'"]; | ||
| if (!isProd) scriptSrc.push("'unsafe-eval'"); | ||
|
|
||
| const directives: Record<string, string[]> = { | ||
| 'default-src': ["'self'"], | ||
| 'script-src': scriptSrc, | ||
| 'style-src': ["'self'", "'unsafe-inline'"], | ||
| 'img-src': ["'self'", 'data:', 'blob:', 'https:'], | ||
| 'font-src': ["'self'", 'data:'], | ||
| 'connect-src': [ | ||
| "'self'", | ||
| 'https://*.sentry.io', | ||
| 'https://*.ingest.sentry.io', | ||
| 'https://*.blob.vercel-storage.com', | ||
| ], | ||
| 'frame-ancestors': ["'none'"], | ||
| 'base-uri': ["'self'"], | ||
| 'form-action': ["'self'"], | ||
| 'object-src': ["'none'"], | ||
| }; | ||
|
|
||
| const parts = Object.entries(directives).map( | ||
| ([name, values]) => `${name} ${values.join(' ')}`, | ||
| ); | ||
| if (isProd) parts.push('upgrade-insecure-requests'); | ||
| return parts.join('; '); | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
No tests added for the new contentSecurityPolicy() logic.
This is a new feature (env-sensitive directive builder with prod/dev and CSP_ENFORCE branching). As per coding guidelines, **/*.{ts,tsx,md,mdx}: "New features must include tests." A small unit test asserting 'unsafe-eval'/upgrade-insecure-requests toggle correctly with NODE_ENV, and that the header key switches on CSP_ENFORCE, would catch regressions cheaply given this is a security-relevant config.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@next.config.ts` around lines 23 - 51, Add unit tests for
contentSecurityPolicy() covering production versus non-production behavior for
'unsafe-eval' and upgrade-insecure-requests, plus CSP_ENFORCE-driven selection
of the response header key. Ensure tests isolate and restore NODE_ENV and
CSP_ENFORCE so they do not affect other configuration tests.
Source: Coding guidelines
Summary
docs/security/code-audit-2026-07-15.md).Content-Security-Policy-Report-Only) so it can never break the live site — browsers evaluate and report violations without blocking anything.CSP_ENFORCE=truein the deploy environment to promote to enforcingContent-Security-Policy, no code change required.Test plan
pnpm typecheckcleanCSP_ENFORCE=trueSummary by CodeRabbit
New Features
CSP_ENFORCE=true.Documentation