Skip to content

Commit 4f55104

Browse files
authored
Fix Sourcepoint first-party consent: iframe asset 404s + same-origin message guard (#822)
* Make first-party integration URLs root-relative The GPT, DataDome, Permutive, Lockr, and Sourcepoint attribute rewriters rewrote a publisher's `<script src>` / `<link href>` to an absolute first-party URL built from `request_host`, e.g. `https://{request_host}/integrations/gpt/script`. When the host that reaches the program isn't the page's host — behind a host-rewriting dev proxy, or any deployment where `request_host` resolves to the routing host rather than the production domain — those URLs leak the wrong host into the page (e.g. `https://ts.example.com/integrations/...` while the address bar shows `www.example.com`). Emit them root-relative (`/integrations/...`) instead, so the browser resolves them against the page's own host. This is correct both in production and behind a proxy, removes the dependency on `request_host`/`X-Forwarded-Host` derivation, and matches what GTM, Didomi, Testlight, and these integrations' own inline-script rewriters already do. Audited every integration: these five were the only page-facing first-party URLs built from a host. NextJS RSC/flight payload rewriting stays absolute (those URLs are intentional). The DataDome protection module's raw-`Host` read is server-side (validation API body), not page-facing, and is left as-is. Tests updated to assert the root-relative form. * Rewrite root-absolute asset paths in proxied Sourcepoint iframe HTML The privacy-manager iframe documents (e.g. us_pm/index.html) reference their assets with root-absolute paths: `<script src="/PrivacyManagerUS.<hash>.js">`, `/polyfills.<hash>.js`, `/manifest.json`. On cdn.privacy-mgmt.com those resolve to the CDN; served first-party through Trusted Server the iframe origin is the publisher, so `/PrivacyManagerUS.<hash>.js` resolves to the publisher root and 404s — the privacy-manager UI cannot load its code. Add an HTML response rewrite (mirroring the existing JavaScript rewrite path) that prefixes root-absolute `src`/`href` values with /integrations/sourcepoint/cdn so the iframe assets load through the proxy. Protocol-relative (`//host`) and absolute (`https://…`) URLs are left untouched. The proxy now also requests identity encoding for likely-HTML paths so the body is readable, and a shared finalize_rewritten_response helper sets the right content type for each. Verified in a browser through the proxy: PrivacyManagerUS.*/polyfills.* go from 404 to 200 and the privacy-manager app renders. * Accept same-origin messages in Sourcepoint wrapper guard With assets loading, the consent dialog still never appeared and the page stayed scroll-locked. Root cause: the wrapper validates messages from its own message/privacy-manager iframe with `e.origin === params.msgOrigin || e.origin === params.pmOrigin`, where `msgOrigin` is `baseEndpoint` used verbatim. Under first-party proxying `baseEndpoint` is a path (`/integrations/sourcepoint/cdn`), so `msgOrigin` becomes `https://<publisher>/integrations/sourcepoint/cdn` — which never equals the iframe's bare origin `https://<publisher>`. The guard rejects the iframe's `sp.showMessage`/choice messages, so the wrapper adds `html.sp-message-open` (locking scroll) but never shows the dialog or removes the lock: the page renders but cannot scroll, behind an invisible consent gate. An absolute `baseEndpoint` can't fix this — `msgOrigin` keeps the path prefix and still never equals the bare origin — and serving Sourcepoint under a bare origin would require a dedicated subdomain or claiming colliding root paths. Since the message iframe is genuinely same-origin when proxied first-party, add a third script rewrite that lets the guard also accept `e.origin === location.origin`. This only additionally trusts a same-origin frame (which already has full page access), so it adds no attack surface; it adapts an origin check written for a cross-origin CDN to first-party serving. The rewrite is anchored on the semantic `.pmOrigin)` guard close and captures the minified event identifier. Verified in a browser through the proxy: the consent dialog renders, "Agree & Continue" dismisses it, `sp-message-open` is removed, and the page scrolls. * Add tests covering arbitrary minified identifiers in the origin guard rewrite * Address PR review: HTML cache policy and standalone msgOrigin guard Split rewritten-response finalization so HTML iframe documents no longer inherit the versioned-JS public cache policy. Rewritten JavaScript bundles keep the fixed `public, max-age=<ttl>` policy (static, hashed filenames); rewritten HTML (e.g. `us_pm/index.html`) is unversioned, so it now defers to `apply_cache_headers`, which preserves an upstream `Cache-Control` when present and otherwise applies the cookie-aware default (private when cookies were forwarded). Generalize the message-origin guard rewrite to also match a standalone `<event>.origin===<obj>.msgOrigin)` guard (the `wrapperMessagingWithoutDetection.js` shape, which compares only against the single baseEndpoint-derived `msgOrigin`), in addition to the combined `…msgOrigin || …pmOrigin)` form. Both anchors are Sourcepoint-specific field names, so generic origin checks are untouched and the combined form is still rewritten exactly once. Add tests for HTML cache preservation/privacy, the standalone msgOrigin guard, and the no-double-match invariant on the combined guard.
1 parent bc54caa commit 4f55104

1 file changed

Lines changed: 416 additions & 23 deletions

File tree

0 commit comments

Comments
 (0)