Medium CVSS 6.1 webkit Cross Origin 🔧 Commit mapped

Overview

Medium
Severity
6.1
CVSS
No
Exploited ITW
Fixed
Fix Status
DescriptionProcessing maliciously crafted web content may lead to universal cross site scripting
ComponentWebKit UIProcess
Bug ClassCross Origin
Tracker285927
Fix commitfeb7725d8aed (WebKit/WebKit) +43/-0
CWECWE-79 (Cross-site scripting)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
CISA KEVNot listed
CreditedMartin Bajanik of Fingerprint, Ammar Askar
Disclosed2025-07-29

Background

com.apple.quarantine / Gatekeeper
A macOS extended attribute set on files from untrusted sources, plus the Gatekeeper mechanism that requires explicit user approval before such content from an unidentified developer is opened.
Web Archive (.webarchive)
A single-file page snapshot whose main resource can claim an arbitrary origin and contain script, making its execution equivalent to running attacker code as a spoofed origin.
UXSS (universal cross-site scripting)
A bug class where attacker script bypasses the same-origin policy and runs against or reads state of arbitrary origins.
Provisional navigation cancellation
Failing an in-progress navigation by delivering a Cancellation-type ResourceError to didFailProvisionalNavigationWithError, which aborts the load before content is committed.

Root Cause Analysis

This is a Gatekeeper/quarantine-bypass logic bug on macOS in the Web Archive loading path that leads to universal cross-site scripting (UXSS). A .webarchive main resource can declare an arbitrary origin and carry script, so opening one runs content with the authority of spoofed origins; macOS normally protects users from such downloaded content via the com.apple.quarantine extended attribute and Gatekeeper’s “unidentified developer” approval prompt. The violated invariant is that a quarantined, not-yet-user-approved local .webarchive must not be silently loaded and executed. This CVE is a follow-up to the web-archive isolation work (CVE-2024-40857): that patch made WebPageProxy::receivedNavigationActionPolicyDecision() swap in a non-persistent data store for substitute or file:// .webarchive Use decisions, but it did not check the file’s quarantine state, so a webarchive delivered to disk and reached via a file:// navigation (for example a link click, as the added test exercises) would load and execute despite being quarantined and unapproved.

The fix adds, under PLATFORM(MAC), a guard inside that same branch: for a non-substitute (file://) web archive, if isQuarantinedAndNotUserApproved(webarchiveURL.fileSystemPath()) is true, it logs, builds a cancellation ResourceError, reports didFailProvisionalNavigationWithError, and returns before any data-store swap or load — cancelling the navigation exactly as Gatekeeper intends. The accompanying layout test loads a page that programmatically clicks a link to a quarantined .webarchive and asserts the load is cancelled, and mac.py is changed to set the com.apple.quarantine xattr on the test resource so the quarantine path is actually exercised. Note: isQuarantinedAndNotUserApproved(), cancelledError(), and the exact ResourceError plumbing live outside the shown diff, so their internals are inferred from the call site rather than shown by the patch.

Key insight
The fix closes a gap in the web-archive isolation work: local .webarchive loads honored the isolation data-store swap but not macOS quarantine/Gatekeeper, so a downloaded, unapproved archive could still execute and achieve UXSS; the guard now cancels the navigation when the file is quarantined and not user-approved.

Attack Path

  1. Deliver a quarantined web archive The attacker gets a crafted .webarchive onto the victim’s disk through a channel that sets com.apple.quarantine (download, email attachment, messaging), with a main resource spoofing a sensitive origin and embedding script.
  2. Induce a file:// navigation to it The victim (or attacker-controlled content, as the test’s document.querySelector(‘a’).click() shows) navigates to the local .webarchive via a file:// URL ending in .webarchive.
  3. Pre-patch: load proceeds despite quarantine receivedNavigationActionPolicyDecision reaches the web-archive branch and, without a quarantine check, swaps in the data store and loads the archive, running its script without the Gatekeeper unidentified-developer approval.
  4. UXSS execution The archive’s script executes as its spoofed origin, giving universal cross-site scripting against arbitrary sites.
  5. Fix cancels the load Post-patch, isQuarantinedAndNotUserApproved() causes the navigation to fail with a cancellation error before any load, so an unapproved quarantined archive never executes.

Impact Assessment

This is a same-origin-policy bypass (UXSS) gated on a quarantine/Gatekeeper check, not a memory-safety primitive: the diff contains no allocation or bounds logic and cannot be groomed toward memory corruption. Exploited pre-patch, a quarantined .webarchive that the user was never prompted to approve executes script as spoofed origins, a confidentiality/integrity break in the WebContent process and browsing profile rather than a route to native code execution. Because it requires getting a quarantined archive onto disk and a file:// navigation to it, real-world exploitation depends on delivery and some user interaction, consistent with the medium rating.

Changed Functions

FunctionChangeNotes
WebPageProxy::receivedNavigationActionPolicyDecision
Source/WebKit/UIProcess/WebPageProxy.cpp
modified Adds a PLATFORM(MAC) guard in the file:// web-archive branch: when the archive file isQuarantinedAndNotUserApproved(), it cancels the navigation via didFailProvisionalNavigationWithError and returns before swapping the data store or loading.
test-loading-archive-with-link.html
LayoutTests/webarchive/loading/test-loading-archive-with-link.html
added New test that clicks a link to resources/quarantined_top.webarchive and expects the load to be cancelled (the archive content never renders).
MacPort.setup_test_run
Tools/Scripts/webkitpy/port/mac.py
modified Runs xattr on the test's quarantined_top.webarchive so the quarantine attribute is present when the test runs, exercising the new guard (rdar://132098879).

Files Changed

  • LayoutTests/platform/mac-wk1/TestExpectations
  • LayoutTests/webarchive/loading/resources/quarantined_top.webarchive
  • LayoutTests/webarchive/loading/test-loading-archive-with-link-expected.txt
  • LayoutTests/webarchive/loading/test-loading-archive-with-link.html
  • Source/WebKit/UIProcess/WebPageProxy.cpp
  • Tools/Scripts/webkitpy/port/mac.py

Audit Directions

  • Other content types reachable via file:// that skip quarantine
    In receivedNavigationActionPolicyDecision and nearby load paths, check whether the isQuarantinedAndNotUserApproved() check covers all archive/executable-content branches (substitute-data archives, back/forward reloads, non-.webarchive extensions); grep isQuarantinedAndNotUserApproved, protocolIsFile, .webarchive.
  • Quarantine enforcement across ports/platforms
    The guard is PLATFORM(MAC)-only; audit whether iOS/Catalyst or other file-load entry points need equivalent quarantine/approval checks, and whether loadFile / substitute-data paths bypass it entirely.
  • Consistency with disallowWebArchive gating
    Cross-check the UIProcess quarantine cancel against WebCore’s DocumentLoader::disallowWebArchive / allowsWebArchiveForMainFrame and AlwaysAllowLocalWebarchive to ensure no combination of settings re-enables an unapproved local archive; grep disallowWebArchive, alwaysAllowLocalWebarchive, dataStoreForWebArchive.

Original Bug Report

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