fix(sw): pin CSP/initiator handlers to sender origin (RQ-3050) - #98
Open
dinex-dev wants to merge 1 commit into
Open
fix(sw): pin CSP/initiator handlers to sender origin (RQ-3050)#98dinex-dev wants to merge 1 commit into
dinex-dev wants to merge 1 commit into
Conversation
The onErrorOccurred and onBeforeAjaxRequest message handlers derived the target origin from the caller-supplied requestDetails.initiator. That message is relayed from the page MAIN world over the forgeable "requestly:client" channel, so any web page could set initiator to an arbitrary origin. - onErrorOccurred -> handleCSPError removes a site's Content-Security-Policy header for requestDetails.initiator (RQ-3050). A forged initiator let a page strip a third-party origin's CSP (tab-scoped). - onBeforeAjaxRequest -> handleInitiatorDomainFunction stamps requestDetails.initiator into headers for rules using rq_request_initiator_origin(). A forged initiator let a page spoof that origin value (conditional on a matching enabled rule). Both now pin initiator to the browser-provided sender origin (sender.origin / sender.url), which a page cannot forge, via a shared getTrustedSenderOrigin() helper (URL parsing guarded). The legitimate emitter already sends initiator === location.origin === sender origin, so this is a no-op for real traffic; forged cross-origin values are discarded. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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
Two message handlers in the MV3 service worker derived a target origin from the caller-supplied
requestDetails.initiator. That message is relayed from the page's MAIN world over the forgeablesource: "requestly:client"channel, so any web page could setinitiatorto an arbitrary origin. Both are now pinned to the browser-provided sender origin, which a page cannot forge.What was vulnerable
onErrorOccurred→handleCSPError(RQ-3050): removes a site'sContent-Security-Policyresponse header forrequestDetails.initiator. A forged initiator let a page arm a tab-scoped rule that strips a third-party origin's CSP, weakening its XSS/clickjacking defenses.onBeforeAjaxRequest→handleInitiatorDomainFunction(sibling, found in review): stampsrequestDetails.initiatorinto headers for rules usingrq_request_initiator_origin(). A forged initiator let a page spoof the origin value that feature is meant to certify (conditional on the victim having a matching enabled Headers rule). Lower severity — no CSP removal, rule-gated, tab-scoped — but the same root cause.The fix
getTrustedSenderOrigin(sender)derives the origin fromsender.origin(falling back to a guardednew URL(sender.url).origin) — browser-populated, unforgeable — returningundefined/"null"for indeterminate/opaque frames.onErrorOccurred: pins the CSP target to the sender origin; skips when there is no trusted origin (fail-closed).onBeforeAjaxRequest: overwritesinitiatorwith the sender origin before processing.url/requestHeadersremain caller-supplied (the AJAX target is legitimately cross-origin).Why this is safe (no behavior change for real traffic)
The legitimate emitter (
ajaxRequestInterceptor) always sendsinitiator === location.originand posts same-frame, sosender.originequals the value it already sends — the pin is a no-op for genuine traffic and only discards forged cross-origin values. A per-frame comment documents the "same-frame emitter" invariant.Testing
npm run build:current, clean) and loaded unpacked; both pins verified indist/serviceWorker.js.github.com) and produces a CSP-removal rule for its own origin; a forgedonErrorOccurredwith a cross-origininitiatorno longer creates a rule for that origin.rq_request_initiator_origin()still stamps the true page origin (incl. iframe / cross-origin XHR variants); a forgedinitiatoris overridden with the real sender origin.Notes / follow-ups (not in this PR)
cacheSharedState, same file) is a related, still-open issue — will be handled separately after its own review.requestly-automationpackage vendors a built copy of this extension; it will be re-vendored after this PR merges.masterthat is a few commits behindorigin/master; rebase before merge if needed.🤖 Generated with Claude Code