CVE-2026-28971
Overview
Background
- Safe Browsing check
- An asynchronous check that flags navigations to known-unsafe URLs and shows a warning before proceeding.
- Download policy action
- A navigation whose response is handled as a download rather than displayed; it must still respect Safe Browsing.
- Main frame vs subframe
- Downloads and warnings are handled differently for the top-level frame versus an embedded iframe.
- Per-navigation state
- Safe Browsing results are tied to a specific API::Navigation; using another navigation’s state confuses the security decision.
Root Cause Analysis
When a navigation resolves to a Download, WebKit must still honor the Safe Browsing check associated with that navigation.
Pre-patch, a download policy decision could proceed without waiting for the navigation’s Safe Browsing check to finish (and without correctly applying its result per frame), so a download initiated from a subframe/iframe could bypass or mis-apply the Safe Browsing warning – described as a malicious iframe using another website’s download settings, i.e. the download was not gated on the correct, per-navigation Safe Browsing state.
The fix adds a completion mechanism on API::Navigation – whenSafeBrowsingCheckCompletes(callback), which runs immediately if no check is ongoing else queues the callback, and fireSafeBrowsingCheckCompletionCallbacks(), invoked from WebPageProxy::beginSafeBrowsingCheck when the check finishes (using std::exchange to drain the queue). In WebPageProxy::decidePolicyForNavigationAction, when policyAction == Download and a Safe Browsing check is ongoing, the download decision is deferred via whenSafeBrowsingCheckCompletes; on completion, if a Safe Browsing warning matched, the download is refused: for a non-main frame it fails the provisional navigation and returns PolicyAction::Ignore, and for the main frame it shows the browsing warning and only proceeds per the user’s choice.
The restored invariant is that a download is authorized only after the navigation’s own Safe Browsing check completes and only if it did not match, so a subframe cannot slip a download past the warning or inherit another frame’s settings.
Attack Path
- Embed a malicious iframe A page hosts a cross-origin iframe that initiates a navigation which will resolve to a download.
- Race the Safe Browsing check The iframe triggers the download policy decision while the navigation’s Safe Browsing check is still ongoing.
- Bypass the warning Pre-patch, the download proceeds without waiting for / correctly applying the per-navigation Safe Browsing result, effectively using the wrong frame’s download handling.
- Unsafe download A download that Safe Browsing would have blocked is allowed, or attributed to another site’s settings, in the UIProcess.
Impact Assessment
Changed Functions
| Function | Change | Notes |
|---|---|---|
Navigation::whenSafeBrowsingCheckCompletes / fireSafeBrowsingCheckCompletionCallbacksSource/WebKit/UIProcess/API/APINavigation.cpp |
added | Adds a per-navigation completion queue: run now if no check is ongoing, else queue; fire drains it via std::exchange when the check finishes. |
WebPageProxy::beginSafeBrowsingCheckSource/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mm |
modified | Fires the navigation's Safe Browsing completion callbacks once the check is no longer ongoing. |
WebPageProxy::decidePolicyForNavigationActionSource/WebKit/UIProcess/WebPageProxy.cpp |
modified | For PolicyAction::Download with an ongoing Safe Browsing check, defers via whenSafeBrowsingCheckCompletes and, on a match, ignores the download (subframe: fail provisional navigation; main frame: show warning). |
Files Changed
Source/WebKit/UIProcess/API/APINavigation.cppSource/WebKit/UIProcess/API/APINavigation.hSource/WebKit/UIProcess/Cocoa/WebPageProxyCocoa.mmSource/WebKit/UIProcess/WebPageProxy.cppTools/TestWebKitAPI/Tests/WebKit/WKWebView/SafeBrowsing.mm
Audit Directions
- Other policy actions vs Safe BrowsingAudit decidePolicyForNavigationAction/Response branches (Use, Download, Ignore) to confirm each waits for and applies the navigation’s Safe Browsing result.
- Per-frame attributionReview how frame vs main-frame and frameInfo are used in download and warning handling to prevent one frame inheriting another’s settings.
- Async check completion racesgrep for safeBrowsingCheckOngoing()/completion callbacks to ensure no security decision proceeds while a check is pending.