fix(csp): close report-only allow-list gaps for the SW, RPC tail and Google Ads - #2564
Open
innolope-dev wants to merge 1 commit into
Open
fix(csp): close report-only allow-list gaps for the SW, RPC tail and Google Ads#2564innolope-dev wants to merge 1 commit into
innolope-dev wants to merge 1 commit into
Conversation
…Google Ads Five gaps, all of which would break something the moment the report-only policy is promoted to enforcing. None break anything today. Google Ads remarketing. GA4 sends its audience beacon (/ads/ga-audiences) to the user's OWN country Google domain — www.google.com.ar in Argentina, www.google.it in Italy — chosen by Google at runtime from the user's locale. CSP host-sources allow * only as the leftmost label, so no wildcard spans TLDs: *.google.com does not match google.com.ar, and www.google.* is not valid grammar. All 187 domains are therefore enumerated, taken verbatim from Google's own https://www.google.com/supported_domains, in their own module so next.config stays readable. This supersedes the lone www.google.com entry, which is the first element of that list. Header grows ~2.6KB -> ~6.5KB; that is the unavoidable cost of keeping remarketing alive under enforcement. Service worker. pbs.twimg.com serves the landing page's testimonial avatars. They render as <img>, so img-src would normally cover them — but serwist's runtime cache re-fetches them from inside sw.js, and in a service worker every fetch() is connect-src whatever the response is used for. The reports arrive with document-uri https://peanut.me/sw.js for exactly this reason; sw.js is served by the same /:path* header rule, so it inherits this policy. RPC tail. getPublicClient hands any chain absent from rpcUrls a bare http(), and viem then uses that chain's own default endpoint. That path is reachable from client code via fetchTokenSymbol on the token selector, whose chain list is API-driven, so it covers the whole chainRegistry, not just the ten chains wired into wagmi.config. Ten hosts added, one per uncovered EVM chain, each read out of viem/chains rather than guessed. Silent in the reports because these chains are rarely picked — the same reason the wss:// gap stayed invisible until #2519 went looking for it. Typeform and jsdelivr. The feedback Widget needs form.typeform.com (iframe), embed.typeform.com (injected stylesheet) and api.typeform.com (single-embed fetch). It is currently unreachable — its Modal is never opened — so it produces no violations, but re-arming it is a one-line change. jsdelivr serves the mermaid bundle on /dev/kyc-flows, a real violation on a dev-only route. Deliberately NOT added: www.googleadservices.com and googleads.g.doubleclick.net. They are the other two standard Google Ads hosts but appear nowhere in 14 days of violation data, so adding them would be speculative widening. Add them if a Google Ads remarketing tag is ever added to GTM.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Comment |
Contributor
🧪 UI test report — ✅ all greenSuites
📊 Coverage (unit)
⏱ 10 slowest test cases
|
Contributor
Code-analysis diffPainscore total: 6656.05 → 6656.05 (0) |
innolope-dev
added a commit
that referenced
this pull request
Jul 29, 2026
…s, PIX exit, verification tasks Cherry-picks the open native-facing work onto the release branch: - KYC now routes every native entry point through the Sumsub Cordova SDK instead of the WebSDK (peanut-ui #2562) — the one change here that genuinely requires a new binary rather than an OTA. - Deferred deep linking survives the store install, so a link followed before install lands on its destination on first launch (#2560, TASK-20772). - PIX deposit "Done" exits to home from both the completed and processing states instead of dropping the user into a new deposit (#2548). - Pending Bridge verification tasks card on home, dismissible and resurfaced under Unlocked regions (#2549). - Native demo/passkey follow-ups: the awaited token clear in clearAuthState plus the two callers it changed (#2517 — the rest shipped in 1.0.40). - Documents left running on a superseded deployment now reload (#2563). - Android safe zone sized from natively measured insets. Not carried: the CSP allow-list gaps (#2564). It builds on the #2519 collector rework — a /api/csp-report route and ten commits that are not on this branch — and the policy is report-only, so the gaps cost report fidelity, not behaviour.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Five gaps in the report-only
connect-src/script-src/style-src/frame-srcallow-list. Nothing breaks today — the enforcing header is still onlyframe-ancestors/object-src/base-uri, and everything here isdisposition: report. Each of these would break something the moment the policy is promoted to enforcing.Companion to #2563, which fixes why the report-only signal is currently unreadable (stale documents replaying superseded policies). Independent of it — different files, no conflict.
Every host below was read out of a source, not guessed: Google's own published domain list,
viem/chains, and the Typeform SDK bundle.1. Google Ads remarketing — 187 domains, and why it can't be a wildcard
GA4 sends its audience/remarketing beacon (
/ads/ga-audiences) to the user's own country Google domain —www.google.com.arfor a user in Argentina,www.google.itin Italy — picked by Google at runtime from the user's locale, not by us.CSP host-sources allow
*only as the leftmost label. So there is no wildcard that spans TLDs:https://*.google.comdoes not matchgoogle.com.ar— different registrable domain.https://www.google.*is not valid CSP grammar.Enumeration is the only option that keeps remarketing working under enforcement. All 187 domains live in
csp-google-domains.js, taken verbatim from Google's own list athttps://www.google.com/supported_domains, with the fetch date and refresh command recorded in the file. Only thewww.host is allowed — the only form present in the violation data.Observed live under the current policy (so these are real gaps, not stale-client noise):
PEANUT-UI-R8F(.com.ar, 54 users),PEANUT-UI-R8T(.com.bd, 35 users),PEANUT-UI-R8Z(.it, 8 users) — all still firing.This supersedes the former lone
https://www.google.comentry, which is the first element of the generated list.2. Service worker —
pbs.twimg.comThe landing page's testimonial avatars. They render as
<img>, soimg-src https:would normally cover them — but serwist's runtime cache re-fetches them from insidesw.js, and in a service worker everyfetch()isconnect-srcregardless of what the response is used for. The reports carrydocument-uri: https://peanut.me/sw.jsfor exactly this reason;/sw.jsis served by the same/:path*header rule, so it inherits this policy.Same mechanism behind the existing
www.googletagmanager.comandapi.onesignal.comviolations reported fromsw.js/OneSignalSDKWorker.js— those hosts were already allow-listed, so they needed no change.3. RPC tail — 10 hosts
getPublicClient(src/app/actions/clients.ts) hands any chain absent fromrpcUrlsa barehttp(), and viem then uses that chain's own default endpoint. That path is reachable from client code viafetchTokenSymbolon the token selector, whose chain list is API-driven — so it covers the whole ofchainRegistry.consts.ts, not just the ten chains wired intowagmi.config.Taking the registry as the boundary rather than picking "popular" by feel: it is exactly the set a user can select. Gnosis, Celo and Linea were already covered; the remaining ten, each resolved from
viem/chains:api.avax.networkrpc.mantle.xyzrpc.hyperliquid.xyzpublic-en.node.kaia.iorpc-gel.inkonchain.com,rpc-qnd.inkonchain.comrpc.katana.networkrpc.plasma.torpc.stable.xyzrpc.presto.tempo.xyzSilent in the report-only data because these chains are rarely picked — not because they are unreachable. Same shape as the
wss://gap, which stayed invisible until #2519 went looking for it.4. Typeform —
frame-src/style-src/connect-srcThe feedback
WidgetinGlobal/Layoutneedsform.typeform.com(the SDK'sDEFAULT_DOMAIN, i.e. the iframe),embed.typeform.com(injected stylesheet from/next/css/) andapi.typeform.com(single-embedfetch). Directives confirmed by reading the SDK bundle.Currently unreachable — its
Modalis never opened, so it produces zero violations — but re-arming it is a one-line change, and the failure mode under enforcement is a blank iframe.5. jsdelivr —
script-srccdn.jsdelivr.netserves the mermaid bundle for/dev/kyc-flows. Dev-only route, but a real violation on a real deployment.Deliberately NOT added
www.googleadservices.comandgoogleads.g.doubleclick.net— the other two standard Google Ads remarketing hosts. Neither appears anywhere in 14 days of violation data; the current setup beacons via the GA4ga-audiencespath only. Adding them would be speculative widening, against the precedent #2519 set. Add them if a Google Ads remarketing tag is ever added to GTM.Verification
pnpm buildsucceeds.pbs.twimg.com,api.typeform.com,api.avax.network,rpc.mantle.xyz,public-en.node.kaia.io,cdn.jsdelivr.net,embed.typeform.com,form.typeform.comappears exactly once.connect-srchas 241 sources with zero duplicates.prettier --checkclean on both files.Risks