fix(console-ui): allow full Privy/WalletConnect/Turnstile stack in CSP#456
Conversation
Privy's wallet-login (WalletConnect) flow was blocked by the console-ui Content-Security-Policy. The connect-src directive omitted the WalletConnect domains, so the browser refused to: - fetch the WalletConnect wallet registry from explorer-api.walletconnect.com - open the relay websocket at wss://relay.walletconnect.com / .org - post analytics to pulse.walletconnect.org This surfaced as "Refused to connect ... violates the document's Content Security Policy" and "TypeError: Failed to fetch", leaving the wallet list empty and wallet login broken. Add https/wss wildcards for *.walletconnect.com and *.walletconnect.org to connect-src so the registry fetch, relay socket, and pulse analytics are permitted. CSP host wildcards match a single subdomain label, which covers explorer-api / relay / pulse. No other directives change. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
ethenotethan
left a comment
There was a problem hiding this comment.
Automated Code Review — Layr-Labs/d-inference#
Verdict: COMMENT
Security — ✅ No issues found
Performance — ✅ No issues found
Type_diligence — ✅ No issues found
Additive_complexity — ✅ No issues found
✅ All four passes clean. No issues found.
🤖 Automated review by Centaur · DAR-186
|
This PR meaningfully strengthens the console-ui security posture by shipping a real CSP (T-019 partially mitigated), but the newly added origins deserve scrutiny and a few gaps remain open. Trust Boundaries Touched
Threat AssessmentT-019 — Missing security headers increase XSS blast radius✅ Strengthens mitigation — Caveats that prevent marking this fully closed:
New Attack Surface — WalletConnect / WalletLink OriginsThe
Specific risk not covered by an existing threat: The wildcard Recommendation: Prefer explicit subdomains over wildcards where the WalletConnect SDK documentation allows it. If wildcard is unavoidable, document the accepted risk. Open Findings Affected
Not Addressed (still open)
🔐 Threat model: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c6062cdbb4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| "img-src 'self' data: blob: https:", | ||
| "font-src 'self' data:", | ||
| "connect-src 'self' https://api.darkbloom.dev https://*.privy.io wss://*.privy.io https://www.google-analytics.com https://api.stripe.com", | ||
| "connect-src 'self' https://api.darkbloom.dev https://*.privy.io wss://*.privy.io https://www.google-analytics.com https://api.stripe.com https://*.walletconnect.com wss://*.walletconnect.com https://*.walletconnect.org wss://*.walletconnect.org", |
There was a problem hiding this comment.
Add WalletConnect verify domains to frame-src
When Privy's wallet login opens the WalletConnect verification/fallback iframe, this patch still leaves frame-src limited to auth.privy.io and Stripe, so the browser will block https://verify.walletconnect.com / .org even though the registry and relay connections are now allowed. Privy's CSP guidance lists those as required frame-src/child-src domains; include them with this CSP change or the wallet flow remains partially broken.
Useful? React with 👍 / 👎.
| "img-src 'self' data: blob: https:", | ||
| "font-src 'self' data:", | ||
| "connect-src 'self' https://api.darkbloom.dev https://*.privy.io wss://*.privy.io https://www.google-analytics.com https://api.stripe.com", | ||
| "connect-src 'self' https://api.darkbloom.dev https://*.privy.io wss://*.privy.io https://www.google-analytics.com https://api.stripe.com https://*.walletconnect.com wss://*.walletconnect.com https://*.walletconnect.org wss://*.walletconnect.org", |
There was a problem hiding this comment.
Add WalletLink and Privy RPC to connect-src
For wallet-login paths that use Coinbase Wallet or Privy's RPC-backed wallet features, Privy's required connect-src entries also include wss://www.walletlink.org and https://*.rpc.privy.systems; this change only allows WalletConnect domains, so those wallet choices can still fail with CSP connection errors after the modal gets past the WalletConnect registry/relay step. Add the rest of the required Privy wallet endpoints alongside these WalletConnect hosts.
Useful? React with 👍 / 👎.
The deployed console-ui was fully non-interactive ("can't click
anywhere"). Root cause: Privy's React provider stack throws during
initialization when its WalletConnect / Privy-RPC / captcha network
calls are blocked by the Content-Security-Policy. The uncaught throw
aborts React hydration, so the entire SPA never becomes interactive.
The earlier connect-src WalletConnect-only patch was necessary but not
sufficient — the policy still omitted several domains the Privy +
WalletConnect + Cloudflare-Turnstile stack requires. This applies the
full policy from Privy's official CSP guide plus Cloudflare Turnstile's
requirements:
- script-src: add https://challenges.cloudflare.com (Turnstile = Privy
captcha).
- connect-src: add https://*.rpc.privy.systems (Privy RPC),
https://*.walletconnect.com/.org + wss://*.walletconnect.com/.org
(explorer-api, relay sockets, pulse), and wss://www.walletlink.org
(Coinbase Wallet).
- frame-src: add https://challenges.cloudflare.com and
https://verify.walletconnect.com/.org (WalletConnect verify iframes);
add 'self'.
- child-src: new directive for the Privy auth iframe + WalletConnect
verify iframes (older browsers fall back to child-src).
- worker-src: 'self' blob: (Privy/wagmi may spawn blob: workers).
Existing https://*.privy.io / wss://*.privy.io (broader than
auth.privy.io) and the Stripe + Google Analytics entries are retained.
admin-ui CSP is intentionally untouched (no Privy/WalletConnect there).
Co-authored-by: Cursor <cursoragent@cursor.com>
ethenotethan
left a comment
There was a problem hiding this comment.
Automated Code Review — Layr-Labs/d-inference#
Verdict: COMMENT
Security — ✅ No issues found
Performance — ✅ No issues found
Type_diligence — ✅ No issues found
Additive_complexity — 1 finding(s)
- 🔵 [INFO]
console-ui/next.config.ts:16— CSP connect-src line becoming unwieldy with many domain wildcards- Suggestion: Consider extracting CSP domains to a separate constant or config object to improve readability and maintainability
1 finding(s) total, 0 blocking. Verdict: COMMENT.
🤖 Automated review by Centaur · DAR-186
| "default-src 'self'", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com https://challenges.cloudflare.com", | ||
| "style-src 'self' 'unsafe-inline'", |
There was a problem hiding this comment.
🔵 [INFO] 🧩 CSP connect-src line becoming unwieldy with many domain wildcards
💡 Suggestion: Consider extracting CSP domains to a separate constant or config object to improve readability and maintainability
📊 Score: 2×3 = 6 · Category: over-configuration
ethenotethan
left a comment
There was a problem hiding this comment.
Automated Code Review — Layr-Labs/d-inference#
Verdict: REQUEST_CHANGES
Security — 2 finding(s) (2 blocking)
- 🟡 [MEDIUM]
console-ui/next.config.ts:15— CSP allows 'unsafe-eval' in script-src which enables code injection- Suggestion: Replace 'unsafe-eval' with nonce-based CSP or strict-dynamic when feasible. Consider if Privy SDK truly requires eval() or if alternatives exist.
- 🟡 [MEDIUM]
console-ui/next.config.ts:15— CSP allows 'unsafe-inline' in script-src which enables XSS- Suggestion: Replace 'unsafe-inline' with nonce-based CSP or strict-dynamic. Use CSP nonces for legitimate inline scripts.
Performance — ✅ No issues found
Type_diligence — ✅ No issues found
Additive_complexity — ✅ No issues found
2 finding(s) total, 2 blocking. Verdict: REQUEST_CHANGES.
🤖 Automated review by Centaur · DAR-186
| const cspDirectives = [ | ||
| "default-src 'self'", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com https://challenges.cloudflare.com", |
There was a problem hiding this comment.
🟡 [MEDIUM] 🔒 CSP allows 'unsafe-eval' in script-src which enables code injection
💡 Suggestion: Replace 'unsafe-eval' with nonce-based CSP or strict-dynamic when feasible. Consider if Privy SDK truly requires eval() or if alternatives exist.
📊 Score: 3×4 = 12 · Category: unsafe-eval
| const cspDirectives = [ | ||
| "default-src 'self'", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com", | ||
| "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com https://challenges.cloudflare.com", |
There was a problem hiding this comment.
🟡 [MEDIUM] 🔒 CSP allows 'unsafe-inline' in script-src which enables XSS
💡 Suggestion: Replace 'unsafe-inline' with nonce-based CSP or strict-dynamic. Use CSP nonces for legitimate inline scripts.
📊 Score: 3×4 = 12 · Category: unsafe-inline
Summary
The deployed console-ui was fully non-interactive ("can't click anywhere"). Root cause: Privy's React provider stack throws during initialization when its WalletConnect / Privy-RPC / Cloudflare-Turnstile network calls are blocked by the Content-Security-Policy. The uncaught throw aborts React hydration, so the entire SPA never becomes interactive — and wallet login would break next even if interactivity returned.
The earlier
connect-srcWalletConnect-only patch was necessary but not sufficient. This PR applies the complete policy from Privy's official CSP guide plus Cloudflare Turnstile's CSP requirements (Turnstile is Privy's captcha).File / line:
console-ui/next.config.ts— thecspDirectivesarray (single source of the deployed CSP header) + its explanatory comment block.admin-uiis intentionally untouched (no Privy/WalletConnect there).Final CSP
Before / After — behavior
flowchart TD subgraph Before [Before - deployed] A1["Load console-ui"] --> B1["Privy provider init"] B1 --> C1["fetch WalletConnect / privy-rpc / Turnstile"] C1 --> D1{"connect/script/frame-src allow?"} D1 -->|no| E1["CSP blocks -> Privy init throws (TypeError: Failed to fetch)"] E1 --> F1["React hydration aborts"] F1 --> G1["Entire SPA non-interactive — cannot click anywhere"] end subgraph After [After - this PR] A2["Load console-ui"] --> B2["Privy provider init"] B2 --> C2["fetch WalletConnect / privy-rpc / Turnstile"] C2 --> D2{"connect/script/frame-src allow?"} D2 -->|yes| E2["requests permitted: privy / walletconnect / walletlink / rpc / turnstile"] E2 --> F2["Privy init succeeds; hydration completes"] F2 --> G2["SPA interactive; wallet login works"] endBefore / After — code (
cspDirectivesinnext.config.ts)flowchart LR subgraph BeforeCode [Before] S1["script-src: GA, Stripe"] N1["connect-src: privy.io, GA, Stripe"] R1["frame-src: auth.privy.io, Stripe"] X1["(no child-src)"] W1["(no worker-src)"] end subgraph AfterCode [After] S2["script-src: + challenges.cloudflare.com"] N2["connect-src: + *.rpc.privy.systems, + *.walletconnect.com/.org (https+wss), + wss://www.walletlink.org"] R2["frame-src: + challenges.cloudflare.com, + verify.walletconnect.com/.org, + 'self'"] X2["child-src: auth.privy.io, verify.walletconnect.com/.org"] W2["worker-src: 'self' blob:"] end S1 --> S2 N1 --> N2 R1 --> R2 X1 --> X2 W1 --> W2Verification
Build gate
npm run build— succeeds (✓ Compiled successfully, all 39 routes generated)npx eslint src/— 0 errors (only pre-existing warnings in unrelated files)Browser (local production build via
next start, the new CSP header verified on the wire withcurl)securitypolicyviolationcollector installed pre-navigation through CDPaddScriptToEvaluateOnNewDocument) — both on initial load and after interaction.NEXT_PUBLIC_PRIVY_APP_IDis unset in this environment, soPrivyClientProvidershort-circuits to a mock auth context and never mounts the real Privy SDK (forcing it would require disrupting the running dev server / shared build). The Privy/WalletConnect/Turnstile entries are validated by exact match to Privy's official CSP guide + Cloudflare Turnstile docs and the verified response header — CSP allowlists are declarative, so the listed origins are permitted for whichever page triggers them.Directives added vs. the prior (WalletConnect-only) state
script-src:https://challenges.cloudflare.com(Turnstile / Privy captcha)connect-src:https://*.rpc.privy.systems,wss://www.walletlink.org(Coinbase Wallet) — on top of the WalletConnect*.walletconnect.com/.orghttps+wss wildcardsframe-src:https://challenges.cloudflare.com,https://verify.walletconnect.com,https://verify.walletconnect.org, and'self'child-src: new —auth.privy.io+ WalletConnect verify iframes (older-browser fallback forframe-src)worker-src: new —'self' blob:(Privy/wagmi may spawn blob: workers)Existing
https://*.privy.io/wss://*.privy.io(broader thanauth.privy.io) and the Stripe + Google Analytics entries are retained.