Skip to content

fix(console-ui): allow full Privy/WalletConnect/Turnstile stack in CSP#456

Merged
Gajesh2007 merged 3 commits into
masterfrom
fix/csp-walletconnect-connect-src
Jun 23, 2026
Merged

fix(console-ui): allow full Privy/WalletConnect/Turnstile stack in CSP#456
Gajesh2007 merged 3 commits into
masterfrom
fix/csp-walletconnect-connect-src

Conversation

@Gajesh2007

@Gajesh2007 Gajesh2007 commented Jun 23, 2026

Copy link
Copy Markdown
Member

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-src WalletConnect-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 — the cspDirectives array (single source of the deployed CSP header) + its explanatory comment block. admin-ui is intentionally untouched (no Privy/WalletConnect there).

Final CSP

default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://js.stripe.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob: https:; font-src 'self' data:; connect-src 'self' https://api.darkbloom.dev https://*.privy.io wss://*.privy.io https://*.rpc.privy.systems https://www.google-analytics.com https://api.stripe.com https://*.walletconnect.com wss://*.walletconnect.com https://*.walletconnect.org wss://*.walletconnect.org wss://www.walletlink.org; frame-src 'self' https://auth.privy.io https://js.stripe.com https://challenges.cloudflare.com https://verify.walletconnect.com https://verify.walletconnect.org; child-src 'self' https://auth.privy.io https://verify.walletconnect.com https://verify.walletconnect.org; worker-src 'self' blob:; frame-ancestors 'none'; base-uri 'self'; form-action 'self'

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"]
  end
Loading

Before / After — code (cspDirectives in next.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 --> W2
Loading

Verification

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 with curl)

  • Page renders fully and is interactive — clicking the theme toggle flipped light→dark and re-rendered (React event handlers fire); live coordinator data ("269 providers online") loaded.
  • Zero CSP violations captured (via a securitypolicyviolation collector installed pre-navigation through CDP addScriptToEvaluateOnNewDocument) — both on initial load and after interaction.
  • Live Privy wallet-login flow not exercised locally: NEXT_PUBLIC_PRIVY_APP_ID is unset in this environment, so PrivyClientProvider short-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/.org https+wss wildcards
  • frame-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 for frame-src)
  • worker-src: new — '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.

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>
@vercel

vercel Bot commented Jun 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
d-inference Ready Ready Preview Jun 23, 2026 10:51pm
d-inference-console-ui-dev Ready Ready Preview Jun 23, 2026 10:51pm
d-inference-landing Ready Ready Preview Jun 23, 2026 10:51pm

Request Review

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

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

  • TB-004 — Browser → Coordinator (console-ui surface, security headers)

Threat Assessment

T-019 — Missing security headers increase XSS blast radius

Strengthens mitigationframe-ancestors 'none', base-uri 'self', and form-action 'self' are now present and provide meaningful clickjacking and injection-pivot protection. This is the first application-layer CSP configuration for the console-ui, directly addressing SEC-014.

Caveats that prevent marking this fully closed:

  1. 'unsafe-inline' + 'unsafe-eval' on script-src (line "script-src 'self' 'unsafe-inline' 'unsafe-eval' ..."): these directives negate the XSS-blocking benefit of script-src entirely. The comment acknowledges this and defers nonce-based CSP as a follow-up — that deferral is reasonable, but SEC-014 should remain open until it lands.
  2. 'unsafe-inline' on style-src: lower risk than script, but CSS injection via style-src 'unsafe-inline' can still be used for data exfiltration (e.g. CSS attribute selectors). Low priority but worth noting.
  3. No X-Content-Type-Options, Referrer-Policy, or Permissions-Policy headers added — these were also called out in T-019 / SEC-014 and are still absent from next.config.ts.
  4. X-Frame-Options is not needed because frame-ancestors 'none' is the CSP equivalent and takes precedence in modern browsers, but legacy browser coverage may warrant keeping both.

New Attack Surface — WalletConnect / WalletLink Origins

The connect-src and frame-src additions include several wildcard and third-party origins that were not present before. These warrant review:

Added origin Directive Concern
https://*.walletconnect.com, wss://*.walletconnect.com connect-src Wildcard subdomain — any compromised or newly created *.walletconnect.com subdomain can receive authenticated API calls and API keys forwarded by BFF routes (see T-017 / SEC-001).
https://*.walletconnect.org, wss://*.walletconnect.org connect-src Same concern as above for the .org TLD.
wss://www.walletlink.org connect-src Coinbase WalletLink relay — adds a third-party WebSocket endpoint.
https://verify.walletconnect.com, https://verify.walletconnect.org frame-src + child-src Allows iframes from WalletConnect verify domains. If these domains are compromised, injected iframes run in the console-ui context.
https://challenges.cloudflare.com script-src + frame-src Turnstile — executes scripts from Cloudflare. Cloudflare is a large, low-risk origin, but it is a new script-src entry.
https://*.rpc.privy.systems connect-src Wildcard Privy RPC subdomain — same wildcard concern as WalletConnect.

Specific risk not covered by an existing threat: The wildcard connect-src entries for *.walletconnect.com and *.walletconnect.org mean that an XSS payload (which unsafe-inline+unsafe-eval does not prevent) could exfiltrate darkbloom_api_key or Privy JWTs to any subdomain of those domains. This is a new cross-origin credential-leakage vector that sits between T-017 (SSRF) and T-001 (API key theft) but is not precisely described by either. It is not a regression relative to having no CSP, but it is an incremental surface worth tracking.

Recommendation: Prefer explicit subdomains over wildcards where the WalletConnect SDK documentation allows it. If wildcard is unavoidable, document the accepted risk.


Open Findings Affected

  • SEC-014 — Partially addressed (frame-ancestors, base-uri, form-action are now in place; unsafe-inline/unsafe-eval on script-src keeps the finding open). Suggest narrowing the finding scope to the nonce-CSP follow-up.

Not Addressed (still open)

  • X-Content-Type-Options: nosniff
  • Referrer-Policy
  • Permissions-Policy
  • Zustand localStorage not cleared on logout (T-018 / SEC-024) — unrelated to this PR, no change.

🔐 Threat model: docs/threat-model.yaml · Updates on each push to this PR

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread console-ui/next.config.ts Outdated
"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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread console-ui/next.config.ts Outdated
"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",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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>
@Gajesh2007 Gajesh2007 changed the title fix(console-ui): allow WalletConnect domains in CSP connect-src fix(console-ui): allow full Privy/WalletConnect/Turnstile stack in CSP Jun 23, 2026

@ethenotethan ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread console-ui/next.config.ts
"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'",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [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 ethenotethan left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment thread console-ui/next.config.ts
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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [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

Comment thread console-ui/next.config.ts
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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [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

@Gajesh2007 Gajesh2007 merged commit c19302d into master Jun 23, 2026
22 of 23 checks passed
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.

2 participants