feat(security): report-only CSP with a script-src allow-list - #2462
Conversation
The app ships no script-src today, so an XSS anywhere runs unconstrained and can exfiltrate to any origin. Enforcing a guessed allow-list would blank the app, so this ships REPORT-ONLY: it collects violations from real traffic until the list is known complete, then gets promoted to the enforcing header. Reports go to Sentry via a report-uri derived from the browser DSN; the policy still ships (without reporting) when the DSN is absent. object-src 'none' and base-uri 'self' are added to the ENFORCING policy now — the app embeds no plugins and sets no <base>, so neither can break a working page. Known-loose and to be tightened before promotion: 'unsafe-inline' and 'unsafe-eval' in script-src (Next's bootstrap and the wallet SDKs), and a connect-src that cannot enumerate every env-driven chain RPC.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthrough
ChangesCSP header configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed: dependency version conflict. Check your lock file or package.json. Comment |
Code-analysis diffPainscore total: 6212.38 → 6212.38 (0) |
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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.js`:
- Around line 18-21: Update the DSN-to-CSP report URI construction around the
URL parsing logic to preserve the parsed protocol and complete pathname, rather
than stripping the path and hardcoding HTTPS. Use Sentry’s parsed DSN fields to
build the endpoint while retaining the existing validation of host, username,
and project information.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6d23caa4-b046-48af-af7f-a2d0f9d6c635
📒 Files selected for processing (1)
next.config.js
The report URI hardcoded https and dropped everything but the last path segment, so a path-prefixed DSN (self-hosted Sentry under a sub-path) posted reports to an endpoint that doesn't exist, and an http DSN was silently upgraded. Verified against a standard ingest DSN, a path-prefixed one, and a plain http localhost DSN.
kushagrasarathe
left a comment
There was a problem hiding this comment.
Requesting changes — report-only measurement is fine; two things before it's useful/enforceable
Confirmed Content-Security-Policy-Report-Only — can't break the app, and provides no protection today (measurement only). The Sentry report-URI derivation is correct. No new dep, no attacker-controlled origin, no backdoor.
Required:
- Add
report-to+ aReporting-Endpointsheader alongside the deprecatedreport-uri.report-uriis being phased out; withoutreport-tothe violation stream this PR exists to collect will be partial/empty in non-Chromium browsers, and you could promote on false confidence. - Before any promotion to enforcing (don't let this slip): remove
'unsafe-inline'and'unsafe-eval'fromscript-src(nonce/hash +strict-dynamic). As written, enforcing this policy would not stop inline-script XSS — the dominant vector — so it would be theater. - Confirm Sentry PII scrubbing covers CSP report bodies — Peanut puts
amount/stepin URLs, which land indocument-uri.
report-uri alone is deprecated and Chromium is where most violations will come from once report-to is the only mechanism; shipping both (plus the Reporting-Endpoints header the report-to group resolves against) keeps the violation stream complete across engines, so the eventual promotion to enforcing isn't decided on partial data.
|
Point by point:
Sentry PII scrubbing of CSP report bodies ( |
kushagrasarathe
left a comment
There was a problem hiding this comment.
Re-reviewed — report-to condition RESOLVED. Policy now emits report-to csp-endpoint paired with a consistent Reporting-Endpoints: csp-endpoint="…" header (same group, both gated on a non-null DSN). Still Report-Only; enforced header stays minimal/safe (frame-ancestors/object-src/base-uri). unsafe-inline/unsafe-eval correctly remain flagged for the enforcement phase. SAFE. Advisory (not gating): confirm Sentry server-side PII scrubbing of CSP report bodies before ramp.
Why
peanut-uihas noscript-srctoday — the only CSP anywhere isframe-ancestors. That means an XSS anywhere in the app runs unconstrained and can exfiltrate to any origin it likes. Combined with a JS-readable session token, one injection is a full account takeover.This is item E of the session-security series. Companion PRs: the native app lock (#2461) and, on the API, session lifetime bounds and step-up auth.
What
Content-Security-Policy-Report-Onlywith a full first-draft allow-list —script-src,connect-src,style-src,img-src,font-src,frame-src,worker-src,form-action.report-uriderived fromNEXT_PUBLIC_SENTRY_DSN. No DSN → the policy still ships, just without reporting (better than a malformed directive).object-src 'none'andbase-uri 'self'. The app embeds no plugins and sets no<base>, so neither can break a working page.Nothing in the report-only policy is enforced, so this PR cannot break the app. That's the point — enforcing a guessed allow-list would blank it.
Rollout
Known-loose, to tighten before promotion
'unsafe-inline'+'unsafe-eval'inscript-src— Next's inline bootstrap and the wallet SDKs need them today. Moving to nonces is its own change, and until it happensscript-srclimits where script can come from but not inline injection.connect-srccan't enumerate every chain RPC (env-driven, varies by network). The report stream is what completes that list.Testing
Rendered both headers out of the real config and asserted the Sentry
report-uriderivation, plus the DSN-absent fallback. Not covered by an automated test —next.config.jsisn't in the jest project.Follow-up not in this PR
Strict-Transport-Securityis still missing and was part of the same lost audit branch. Left out to keep this reviewable; worth a one-line PR.Summary by CodeRabbit