feat(paywalls): isolate web_view content with a fixed CSP (PWENG-98)#3652
feat(paywalls): isolate web_view content with a fixed CSP (PWENG-98)#3652alexrepty wants to merge 1 commit into
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
66c0ccb to
a3e9e1a
Compare
61039df to
38f5302
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## alexrepty/paywalls-web-view-rendering #3652 +/- ##
======================================================================
Coverage 80.48% 80.48%
======================================================================
Files 404 404
Lines 16728 16728
Branches 2389 2389
======================================================================
Hits 13464 13464
Misses 2322 2322
Partials 942 942 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
38f5302 to
6c73e7c
Compare
a3e9e1a to
0dffacc
Compare
6c73e7c to
71e76e1
Compare
0dffacc to
353f584
Compare
353f584 to
df0ae01
Compare
88cb259 to
fd0371b
Compare
df0ae01 to
22be911
Compare
22be911 to
9cff885
Compare
fd0371b to
bc0f4e0
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9cff885. Configure here.
| meta.setAttribute('http-equiv', 'Content-Security-Policy'); | ||
| meta.setAttribute('content', "${policy.escapeForMetaContent()}"); | ||
| var head = document.head || document.getElementsByTagName('head')[0] || document.documentElement; | ||
| if (head.firstChild) { head.insertBefore(meta, head.firstChild); } else { head.appendChild(meta); } |
There was a problem hiding this comment.
CSP meta placed outside head
High Severity
When document.head is not yet available, the injection script falls back to document.documentElement and inserts the CSP meta as a direct child of html. CSP meta tags are only applied when they live inside head, so this placement is ignored and isolation does not take effect. Setting window.__revenueCatCspInstalled on that failed attempt can also block later injections on the same navigation.
Reviewed by Cursor Bugbot for commit 9cff885. Configure here.
9cff885 to
9349754
Compare
📸 Snapshot Test3 modified, 590 unchanged
🛸 Powered by Emerge Tools |
9349754 to
428d1c2
Compare
bc0f4e0 to
2634d3b
Compare
When a `web_view` declares a `protocol_version`, the renderer injects a fixed Content-Security-Policy meta tag at document start to isolate the content from external sources: only same-origin images/scripts/fonts are allowed, `data:` references for images/fonts are blocked, and XHR/fetch/WebSocket are disallowed (`connect-src 'none'`). Geolocation is disabled. The bundle must be fully self-contained. Recommended labels: pr:other, pr:RevenueCatUI, feat:Paywalls_V2
428d1c2 to
ef64abf
Compare
2634d3b to
4fb9844
Compare



Checklist
purchases-iosand hybridsMotivation
A hosted web bundle rendered inside the paywall must be isolated from external sources. For v1 the bundle is required to be fully self-contained, so the SDK enforces this rather than relying on the bundle to behave.
Part of PWENG-98.
Description
web_viewdeclares aprotocol_version, injects a fixed Content-Security-Policy<meta>at document start: same-origin scripts/images/fonts only, nodata:for images/fonts, and no XHR/fetch/WebSocket (connect-src 'none'). Geolocation is disabled.WebViewContentSecurityPolicyisinternalto:ui:revenuecatui(its only consumer is the renderer).WebViewContentSecurityPolicyTestandStyleFactoryTests.iOS parity: mirrors
purchases-ios(web-view-bridge-wiring).Stack (merge bottom-up): schema → rendering → CSP → value types → message model → variables provider → JS bridge → wiring.
Note
Medium Risk
Security-sensitive WebView behavior changes for protocol_version paywalls; mis-timed CSP injection or overly strict connect-src could break self-contained bundles that rely on same-origin fetches.
Overview
Paywalls V2
web_viewcomponents that declareprotocol_versionare now treated as v1 isolated bundles: the SDK enforces a fixed Content-Security-Policy instead of trusting the hosted page alone.protocol_versionflows from the paywall schema intoWebViewComponentStyleviaStyleFactory. Legacy configs without it keepprotocolVersion == nulland no CSP (unchanged behavior).The renderer turns on isolation when
protocolVersion != null: geolocation is disabled on theWebView, andonPageStartedinjects an idempotent script that inserts a<meta http-equiv="Content-Security-Policy">at the front of<head>before page scripts run. Policy string lives inWebViewContentSecurityPolicy(same-origindefault-src, scripts/styles with inline allowances for self-contained bundles,img-src/font-srcwithdata:,connect-src 'self'only).Unit tests cover policy shape, meta injection/escaping, and
protocolVersionmapping inStyleFactoryTests.Reviewed by Cursor Bugbot for commit ef64abf. Bugbot is set up for automated code reviews on this repo. Configure here.