Medium CVSS 6.2 webkit Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
6.2
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionA download's origin may be incorrectly associated
ComponentWebKit UIProcess
Bug ClassCross Origin
Tracker293994
Fix commit0578185b8a74 (WebKit/WebKit) +138/-3
CWECWE-703
CVSS vectorCVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedSyarif Muhammad Sajjad
Disclosed2025-07-29

Background

DownloadProxy
The UIProcess object representing an in-flight download, whose originating frame info is surfaced to clients (e.g. WKDownload.originatingFrame).
originatingFrameInfo
Metadata describing the frame (URL, security origin, webView) credited with initiating a navigation or download.
targetItem
A back/forward-list item that a navigation is traversing to; its presence marks the navigation as history traversal rather than a fresh content-initiated load.
isRequestFromClientOrUserInput
A navigation flag indicating the request originated from the embedding client or direct user input rather than from executing web content.
PolicyAction::Download
The navigation policy outcome telling WebKit to turn a navigation/response into a download instead of rendering it.

Root Cause Analysis

WebPageProxy::receivedPolicyDecision() and receivedNavigationResponsePolicyDecision() run in the UIProcess and, when the policy action is PolicyAction::Download, construct a DownloadProxy via ProcessPool::createDownloadProxy(). The last argument to that call is the originating frame info, which becomes the download’s originatingFrame / associated origin exposed to the client (e.g. WKDownload.originatingFrame). Before the patch, both paths unconditionally passed navigation->originatingFrameInfo() (or the navigationAction’s originatingFrameInfoData) as the originating frame. The invariant being violated is that a download’s originating frame/origin should reflect the actual frame that initiated the resource load, and only when that association is meaningful. For navigations that are back/forward list traversals (navigation->targetItem() is set) or that came from client/user input rather than from web content (navigation->isRequestFromClientOrUserInput()), there is no legitimate web-content frame origin to attribute the download to — yet the old code still stamped the download with the previous/opener frame’s originatingFrameInfo, incorrectly associating the download with an origin (e.g. the prior page’s host) that did not actually initiate it.

The fix adds a condition: when navigation exists and it is either a targetItem (go-back/forward) navigation or a client/user-input-initiated request, the download proxy is created with std::nullopt for the originating frame info, so no misleading origin is attached (the client sees an empty host / about:blank-like frame). Otherwise the original originatingFrameInfo is preserved. The added API tests (OriginatingFrameHostWhenDownloadComesFromGoBackNavigation and …FromClientInputNavigation, plus the isClientOrUserInitiated branch in the opener test) assert exactly this: for go-back and client-initiated downloads, download.originatingFrame.securityOrigin.host is empty, while genuinely frame-initiated downloads still equal the opener main frame. This restores correct, non-spoofable origin attribution for downloads.

Key insight
Downloads arising from history traversal (targetItem) or client/user-input navigations have no legitimate web-content originating frame, yet the old code still attributed them to a prior frame’s origin; the fix passes std::nullopt in exactly those cases so no false origin is associated.

Attack Path

  1. Set up navigation state The attacker gets the victim to load attacker or third-party pages so that a back/forward list entry or a client/user-input navigation can later resolve to a download.
  2. Trigger a download via a non-content-initiated navigation A go-back navigation (targetItem) or a client/user-input request results in a response the client turns into a download (PolicyAction::Download).
  3. Reach the vulnerable proxy creation receivedPolicyDecision or receivedNavigationResponsePolicyDecision creates a DownloadProxy and, pre-patch, stamps it with navigation->originatingFrameInfo() from an unrelated/prior frame.
  4. Cause origin misattribution The download is now associated with an originating frame/origin that did not initiate it, so download.originatingFrame reports the wrong (attacker-influenced or victim) host.
  5. Exploit downstream origin-based decisions Any client logic or security prompt that trusts the download’s originating origin (attribution UI, per-origin download policies, provenance checks) can be misled by the spoofed association.

Impact Assessment

This is a logic/origin-attribution bug in the UIProcess download path, not a memory-safety issue; there is no corruption primitive and no path to code execution. The concrete impact is that a download can be labeled with an originating origin that did not initiate it, misleading origin-based UI or client policy and undermining download provenance (a spoofing/CrossOrigin-attribution weakness). It is confined to UIProcess metadata handling and its severity is medium because it can defeat security decisions keyed on the download’s origin without granting broader compromise.

Changed Functions

FunctionChangeNotes
WebPageProxy::receivedPolicyDecision
Source/WebKit/UIProcess/WebPageProxy.cpp
modified For Download actions, now passes std::nullopt as originating frame info when the navigation is a targetItem (back/forward) or isRequestFromClientOrUserInput, otherwise keeps the original originatingFrameInfo.
WebPageProxy::receivedNavigationResponsePolicyDecision
Source/WebKit/UIProcess/WebPageProxy.cpp
modified Same conditional fix applied to the navigation-response download path: nullopt originating frame for back/forward or client/user-input navigations.
WKDownload OriginatingFrame tests
Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm
modified Adds a local frameInfoShouldBeEqual block and isClientOrUserInitiated flag, plus two new tests asserting that go-back and client-input downloads report an empty originatingFrame host while genuinely frame-initiated downloads still match the opener frame.

Files Changed

  • Source/WebKit/UIProcess/WebPageProxy.cpp
  • Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm

Audit Directions

  • Every createDownloadProxy call site
    Grep for createDownloadProxy across UIProcess and verify each passes originating frame info only when a genuine content frame initiated it; look for callers still using originatingFrameInfo unconditionally.
  • Other consumers of originatingFrameInfo for provenance
    Search for originatingFrameInfo / originatingFrameInfoData and downloadOriginatingPage to find other features (e.g. app-initiated state, cross-origin checks) that may inherit an unrelated frame’s origin during back/forward or client-input navigations.
  • targetItem / isRequestFromClientOrUserInput gating parity
    Grep for navigation->targetItem() and isRequestFromClientOrUserInput() to see where these distinctions matter and audit paths that credit an origin without checking them, especially security-relevant attribution.
  • Origin association on other navigation-derived objects
    Look at how UserMediaPermission, WebsiteDataStore attribution, and referrer/opener plumbing choose an origin for navigations lacking a real initiating frame, applying the same nullopt-when-no-real-origin reasoning.

Original Bug Report

The reporter's bug is still restricted on the tracker.