CVE-2026-87479
Overview
Background
- `DownloadURL` IPC
- a renderer-to-browser message carrying
blink::mojom::DownloadURLParams(URL,initiator_origin,referrer,has_user_gesture) that asks the browser process to start a download. - `HostsOrigin`
- a
ChildProcessSecurityPolicyImplcheck that confirms a given renderer process is actually allowed to host a particularurl::Origin, used to reject renderer-supplied URLs that the process could not legitimately have loaded. - Transient user activation
- a short-lived, browser-tracked signal (queried via
HasTransientUserActivation()) recording that a frame recently received a genuine user gesture; it gates behaviors that must not be script-driven. - Off-store install policy
- the extensions gate (referenced in the commit message as
IsOffstoreInstallAllowed) that decides whether an extension may be installed from outside the Web Store, keyed in part on the download’s referrer and user-gesture provenance.
Root Cause Analysis
Before the patch, the browser process accepted the referrer and has_user_gesture fields of a DownloadURL IPC on trust from the renderer. In ipc_utils.cc there was no verification that the renderer’s process was entitled to host the origin of params.referrer->url, so a compromised or malicious renderer could attach a referrer pointing at any site it did not actually host. Separately, in the DownloadURL handling in render_frame_host_impl.cc, the code did parameters->set_has_user_gesture(blink_parameters->has_user_gesture), propagating the renderer’s self-reported gesture claim verbatim without confirming the frame really had activation. This violated the invariant that security-relevant provenance fields crossing the renderer/browser trust boundary must be independently validated in the browser.
The fix adds a HostsOrigin check that terminates the renderer (RFH_DOWNLOAD_URL_INVALID_REFERRER) when the claimed referrer origin is not hosted by that process, and rewrites the gesture assignment to blink_parameters->has_user_gesture && HasTransientUserActivation() so the browser’s own activation state is authoritative. Both changes are added guards only — the patch tightens validation and does not alter the downstream download or install mechanism itself.
referrer and has_user_gesture) as authoritative for a download whose provenance feeds a policy decision; the fix makes the browser re-derive both facts itself — hosting the referrer origin via HostsOrigin, and gating the gesture on HasTransientUserActivation().Attack Path
- Reach the IPC from a controlled renderer
An attacker with script execution in a renderer (or a compromised renderer) constructs a
blink::mojom::DownloadURLParamsand sends it via theDownloadURLIPC, as the addedSecurityExploitBrowserTest.DownloadURLInvalidReferrertest does directly. - Spoof the referrer
The attacker sets
params.referrer->urlto an origin its process does not host (the test usesb.comfrom ana.comframe); pre-patch,ipc_utils.ccdid not callHostsOrigin, so the forged referrer was accepted by the browser. - Spoof the user gesture
The attacker sets
params.has_user_gesture = truefrom a frame that has no transient activation (the addedDownloadURLWithoutUserActivationtest assertsHasTransientUserActivation()is false yet the claim wastrue); pre-patch the browser copied this claim through unchanged. - Bypass the off-store install policy
With a forged allowlisted referrer and a fabricated gesture, the download’s provenance can satisfy the extensions off-store install gate that would otherwise block it, matching the advisory’s “Insufficient policy enforcement in Extensions” (inferred that this is the specific downstream policy, per the commit message’s reference to
IsOffstoreInstallAllowed). - Constraint noted by the fix author
The commit message states
IsOffstoreInstallAllowedalso requires the authoritative download URL to match the allowlist, so a spoofed referrer alone does not complete the bypass; the full policy bypass beyond what these two checks demonstrate is(inferred).
Impact Assessment
DownloadURL IPC — i.e., script execution in or compromise of a renderer process. The vendor advisory rates this medium severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.3. Any escalation beyond bypassing the download-provenance policy checks the patch adds (e.g., unwanted extension installation or further compromise) is (inferred).Changed Functions
| Function | Change | Notes |
|---|---|---|
IN_PROC_BROWSER_TEST_Fcontent/browser/download/download_browsertest.cc |
modified | |
IN_PROC_BROWSER_TEST_Fcontent/browser/security_exploit_browsertest.cc |
modified |
Files Changed
content/browser/download/download_browsertest.cctools/metrics/histograms/metadata/stability/enums.xmlcontent/browser/bad_message.hcontent/browser/renderer_host/render_frame_host_impl.cccontent/browser/renderer_host/ipc_utils.cccontent/browser/security_exploit_browsertest.cc
Audit Directions
- Renderer-supplied provenance fieldsAudit other IPCs whose
referrer,initiator_origin, or gesture/activation fields are consumed by the browser without aHostsOrigin/VerifyInitiatorOrigin/activation re-check, since those fields cross the trust boundary and may feed policy decisions. - Self-reported user-gesture claimsLook for any browser-side code that copies a renderer’s
has_user_gesture(or equivalent activation boolean) without confirming it againstHasTransientUserActivation(), especially where the value gates security-sensitive actions like installs, popups, or downloads. - Policy inputs derived from downloadsTrace where download parameters influence extension install allowlists or similar gates (e.g.,
IsOffstoreInstallAllowed) and verify each provenance input is browser-authoritative and not merely a match against a spoofable renderer-supplied value.
Patch
From f66e7ae1e551fe9220724dc26794e31a841ee631 Mon Sep 17 00:00:00 2001
From: Zainab Rizvi <rizvis@google.com>
Date: Mon, 10 Aug 2026 17:39:31 -0700
Subject: [PATCH] Validate referrer and user gesture in DownloadURL IPC
Verify in the browser process that DownloadURLParams referrer URLs are
hosted by the renderer process and that user gesture claims match the
frame's actual activation state.
Defense in depth: IsOffstoreInstallAllowed also requires the authoritative download URL to match the allowlist, so a spoofed referrer alone does not bypass the policy.
TAG=agy
CONV=8c8e3606-e3f0-4996-91a9-49faa1986cb5
Bug: 511772271
Change-Id: I631b9f68ba560c9492459bcb093e7a0b02b795f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8205905
Reviewed-by: Bo Liu <boliu@chromium.org>
Reviewed-by: Caitlin Fischer <caitlinfischer@google.com>
Commit-Queue: Zainab Rizvi <rizvis@google.com>
Cr-Commit-Position: refs/heads/main@{#1676852}
---
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 7830fbb..99953da0 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -399,6 +399,7 @@
RFHI_INVALID_CLIENT_SIDE_REDIRECT_URL = 371,
RFH_BLOB_URL_STORE_ASSOCIATED_PDF_PROCESS_BLOCKED = 372,
RFH_BLOB_URL_STORE_RECEIVER_PDF_PROCESS_BLOCKED = 373,
+ RFH_DOWNLOAD_URL_INVALID_REFERRER = 374,
// Please add new elements here. The naming convention is abbreviated class
// name (e.g. RenderFrameHost becomes RFH) plus a unique description of the
diff --git a/content/browser/renderer_host/ipc_utils.cc b/content/browser/renderer_host/ipc_utils.cc
index ee707728..66e72aff 100644
--- a/content/browser/renderer_host/ipc_utils.cc
+++ b/content/browser/renderer_host/ipc_utils.cc
@@ -200,6 +200,17 @@
!VerifyInitiatorOrigin(process_id, *params.initiator_origin))
return false;
+ // Verify |params.referrer|.
+ if (params.referrer && !params.referrer->url.is_empty()) {
+ auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+ if (!policy->HostsOrigin(process_id.GetUnsafeValue(),
+ url::Origin::Create(params.referrer->url))) {
+ bad_message::ReceivedBadMessage(
+ process_id, bad_message::RFH_DOWNLOAD_URL_INVALID_REFERRER);
+ return false;
+ }
+ }
+
// If |params.url| is not set, this must be a large data URL being passed
// through |params.data_url_blob|.
if (!params.url.is_valid() && !params.data_url_blob.is_valid())
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index 253d0b8..f383f99 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -8000,7 +8000,9 @@
std::unique_ptr<download::DownloadUrlParameters> parameters =
CreateDownloadUrlParameters(blink_parameters->url, traffic_annotation);
parameters->set_content_initiated(!blink_parameters->is_context_menu_save);
- parameters->set_has_user_gesture(blink_parameters->has_user_gesture);
+ // Ensure that user gesture claims match the current activation state.
+ parameters->set_has_user_gesture(blink_parameters->has_user_gesture &&
+ HasTransientUserActivation());
parameters->set_suggested_name(
blink_parameters->suggested_name.value_or(std::u16string()));
parameters->set_prompt(blink_parameters->is_context_menu_save);
diff --git a/tools/metrics/histograms/metadata/stability/enums.xml b/tools/metrics/histograms/metadata/stability/enums.xml
index 5015d75d..396a174 100644
--- a/tools/metrics/histograms/metadata/stability/enums.xml
+++ b/tools/metrics/histograms/metadata/stability/enums.xml
@@ -550,6 +550,7 @@
<int value="371" label="RFHI_INVALID_CLIENT_SIDE_REDIRECT_URL"/>
<int value="372" label="RFH_BLOB_URL_STORE_ASSOCIATED_PDF_PROCESS_BLOCKED"/>
<int value="373" label="RFH_BLOB_URL_STORE_RECEIVER_PDF_PROCESS_BLOCKED"/>
+ <int value="374" label="RFH_DOWNLOAD_URL_INVALID_REFERRER"/>
</enum>
<enum name="BadMessageReasonExtensions">
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index ae876f8..065ae24 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -5542,6 +5542,71 @@
DownloadManagerForShell(shell())->Shutdown();
}
+// Ensure that a real <a download> click preserves the user gesture claim since
+// it legitimately has transient user activation.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadURLWithGenuineClick) {
+ GURL download_url =
+ embedded_test_server()->GetURL("/download/download-test.lib");
+
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ EXPECT_TRUE(ExecJs(web_contents,
+ "let a = document.createElement('a');"
+ "a.id = 'downloadlink';"
+ "a.download = 'download-test.lib';"
+ "a.href = '" +
+ download_url.spec() +
+ "';"
+ "a.innerText = 'click me';"
+ "document.body.appendChild(a);"));
+
+ std::unique_ptr<DownloadTestObserver> observer(
+ CreateInProgressWaiter(shell(), 1));
+ EXPECT_TRUE(ExecJs(web_contents, "document.getElementById('downloadlink').click()"));
+
+ observer->WaitForFinished();
+
+ std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+ EXPECT_TRUE(downloads[0]->HasUserGesture());
+}
+
+// Ensure that calling DownloadURL from a frame without transient user
+// activation does not preserve a spoofed has_user_gesture claim.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadURLWithoutUserActivation) {
+ GURL download_url =
+ embedded_test_server()->GetURL("/download/download-test.lib");
+
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHostImpl* main_frame =
+ web_contents->GetPrimaryFrameTree().root()->current_frame_host();
+
+ EXPECT_FALSE(main_frame->HasTransientUserActivation());
+
+ std::unique_ptr<DownloadTestObserver> observer(
+ CreateInProgressWaiter(shell(), 1));
+
+ auto params = blink::mojom::DownloadURLParams::New();
+ params->url = download_url;
+ params->initiator_origin = main_frame->GetLastCommittedOrigin();
+ params->has_user_gesture = true;
+ main_frame->DownloadURL(std::move(params));
+
+ observer->WaitForFinished();
+
+ std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+ EXPECT_FALSE(downloads[0]->HasUserGesture());
+}
+
using DownloadRangeTestParams =
std::tuple<int64_t /*starting byte in range request*/,
int64_t /*ending byte in range request*/,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index ade25ac..19bb216 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -5701,4 +5701,33 @@
EXPECT_FALSE(subframe->IsRenderFrameLive());
}
+// Ensure that the renderer is terminated if it sends a DownloadURL IPC with a
+// referrer URL that the renderer's process is not allowed to host.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, DownloadURLInvalidReferrer) {
+ // Explicitly isolating a.com helps ensure that this test is applicable on
+ // platforms without site-per-process.
+ IsolateOrigin("a.com");
+
+ GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHostImpl* main_frame =
+ web_contents->GetPrimaryFrameTree().root()->current_frame_host();
+
+ // Simulate that the renderer sends a DownloadURL IPC claiming that the
+ // download was referred by a page on a different site.
+ auto params = blink::mojom::DownloadURLParams::New();
+ params->url = embedded_test_server()->GetURL("a.com", "/title2.html");
+ params->initiator_origin = main_frame->GetLastCommittedOrigin();
+ params->referrer = blink::mojom::Referrer::New();
+ params->referrer->url =
+ embedded_test_server()->GetURL("b.com", "/title1.html");
+ RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame->GetProcess());
+ main_frame->DownloadURL(std::move(params));
+ EXPECT_EQ(bad_message::RFH_DOWNLOAD_URL_INVALID_REFERRER, kill_waiter.Wait());
+ EXPECT_FALSE(main_frame->IsRenderFrameLive());
+}
+
} // namespace content
Regression Test / PoC
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index ae876f8..065ae24 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -5542,6 +5542,71 @@
DownloadManagerForShell(shell())->Shutdown();
}
+// Ensure that a real <a download> click preserves the user gesture claim since
+// it legitimately has transient user activation.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadURLWithGenuineClick) {
+ GURL download_url =
+ embedded_test_server()->GetURL("/download/download-test.lib");
+
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ EXPECT_TRUE(ExecJs(web_contents,
+ "let a = document.createElement('a');"
+ "a.id = 'downloadlink';"
+ "a.download = 'download-test.lib';"
+ "a.href = '" +
+ download_url.spec() +
+ "';"
+ "a.innerText = 'click me';"
+ "document.body.appendChild(a);"));
+
+ std::unique_ptr<DownloadTestObserver> observer(
+ CreateInProgressWaiter(shell(), 1));
+ EXPECT_TRUE(ExecJs(web_contents, "document.getElementById('downloadlink').click()"));
+
+ observer->WaitForFinished();
+
+ std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+ EXPECT_TRUE(downloads[0]->HasUserGesture());
+}
+
+// Ensure that calling DownloadURL from a frame without transient user
+// activation does not preserve a spoofed has_user_gesture claim.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, DownloadURLWithoutUserActivation) {
+ GURL download_url =
+ embedded_test_server()->GetURL("/download/download-test.lib");
+
+ EXPECT_TRUE(
+ NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHostImpl* main_frame =
+ web_contents->GetPrimaryFrameTree().root()->current_frame_host();
+
+ EXPECT_FALSE(main_frame->HasTransientUserActivation());
+
+ std::unique_ptr<DownloadTestObserver> observer(
+ CreateInProgressWaiter(shell(), 1));
+
+ auto params = blink::mojom::DownloadURLParams::New();
+ params->url = download_url;
+ params->initiator_origin = main_frame->GetLastCommittedOrigin();
+ params->has_user_gesture = true;
+ main_frame->DownloadURL(std::move(params));
+
+ observer->WaitForFinished();
+
+ std::vector<raw_ptr<download::DownloadItem, VectorExperimental>> downloads;
+ DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+ ASSERT_EQ(1u, downloads.size());
+ EXPECT_FALSE(downloads[0]->HasUserGesture());
+}
+
using DownloadRangeTestParams =
std::tuple<int64_t /*starting byte in range request*/,
int64_t /*ending byte in range request*/,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index ade25ac..19bb216 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -5701,4 +5701,33 @@
EXPECT_FALSE(subframe->IsRenderFrameLive());
}
+// Ensure that the renderer is terminated if it sends a DownloadURL IPC with a
+// referrer URL that the renderer's process is not allowed to host.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, DownloadURLInvalidReferrer) {
+ // Explicitly isolating a.com helps ensure that this test is applicable on
+ // platforms without site-per-process.
+ IsolateOrigin("a.com");
+
+ GURL main_url(embedded_test_server()->GetURL("a.com", "/title1.html"));
+ EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+ RenderFrameHostImpl* main_frame =
+ web_contents->GetPrimaryFrameTree().root()->current_frame_host();
+
+ // Simulate that the renderer sends a DownloadURL IPC claiming that the
+ // download was referred by a page on a different site.
+ auto params = blink::mojom::DownloadURLParams::New();
+ params->url = embedded_test_server()->GetURL("a.com", "/title2.html");
+ params->initiator_origin = main_frame->GetLastCommittedOrigin();
+ params->referrer = blink::mojom::Referrer::New();
+ params->referrer->url =
+ embedded_test_server()->GetURL("b.com", "/title1.html");
+ RenderProcessHostBadIpcMessageWaiter kill_waiter(main_frame->GetProcess());
+ main_frame->DownloadURL(std::move(params));
+ EXPECT_EQ(bad_message::RFH_DOWNLOAD_URL_INVALID_REFERRER, kill_waiter.Wait());
+ EXPECT_FALSE(main_frame->IsRenderFrameLive());
+}
+
} // namespace content
Original Bug Report
ExtensionInstallSources bypass via forged DownloadURLParams
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A compromised renderer can spoof the referrer and has_user_gesture fields in the DownloadURL Mojo IPC. The browser trusts these parameters without authoritative validation, allowing an attacker to bypass the ExtensionInstallSources policy and Safe Browsing warnings to prompt the user to install a malicious, self-signed extension.
Affected files:
content/browser/renderer_host/render_frame_host_impl.ccchrome/browser/extensions/extension_management.ccchrome/browser/download/download_target_determiner.ccextensions/browser/crx_installer.ccchrome/browser/download/download_crx_util.cccontent/browser/renderer_host/ipc_utils.cc
Estimated timestamp from git blame: 2024-12-10
Vulnerability Details
The browser process exposes the LocalFrameHost::DownloadURL Mojo IPC method to renderer processes. When a renderer sends a blink::mojom::DownloadURLParams message, RenderFrameHostImpl::DownloadURL processes it and calls VerifyDownloadUrlParams (content/browser/renderer_host/ipc_utils.cc). While this function validates elements like the blob token and initiator origin, it does not validate the referrer or has_user_gesture fields against authoritative browser-side state.
These unverified fields are copied into a download::DownloadUrlParameters object and passed to the download subsystem. This creates a critical security boundary violation when enterprise policies are in use:
- Warning Suppression: During download processing,
DownloadTargetDeterminer::GetDangerLevelevaluates if the download is dangerous. Ifdownload_->HasUserGesture()is true and the download is deemed a trusted extension (checked viaExtensionManagement::IsOffstoreInstallAllowed), it returnsDownloadFileType::NOT_DANGEROUS. This entirely suppresses Safe Browsing dangerous file warnings. - Policy Bypass:
ExtensionManagement::IsOffstoreInstallAllowedchecks if thereferrermatches theExtensionInstallSourcesenterprise policy. Because the referrer is fully controlled by the compromised renderer, an attacker can spoof a trusted domain (e.g.,https://trusted.example.corp/). - Signature Proof Bypass: Once the download completes,
ChromeDownloadManagerDelegate::ShouldOpenDownloadchecks the allowed status again. Because the spoofed referrer satisfies the policy check,CrxInstalleris initialized withOffStoreInstallAllowedBecausePref. This causesCrxInstaller::InstallCrxto useVerifierFormat::CRX3(which only requires a standard cryptographic signature) instead ofVerifierFormat::CRX3_WITH_PUBLISHER_PROOF(which requires Google Web Store keys).
As a result, a compromised renderer can force the browser to display a legitimate-looking “Add extension?” permissions prompt for an arbitrary, attacker-signed .crx file, bypassing strict enterprise controls.
Potential Reproduction Steps
Note: Our tooling agent does not currently have the ability to run code, so these are suggested/potential steps based on static analysis of the Chromium codebase.
- Assume a target browser is configured with an
ExtensionInstallSourcesenterprise policy (e.g.,["https://trusted.domain/*"]). - The attacker hosts a malicious, self-signed
.crxextension file. - The attacker compromises a renderer process (e.g., via a V8 vulnerability).
- From the compromised renderer, the attacker sends a
LocalFrameHost::DownloadURLMojo IPC to the browser. - In the
blink::mojom::DownloadURLParamspayload, the attacker sets:url: The location of the malicious.crx.referrer: Spoofed tohttps://trusted.domain/.has_user_gesture: Spoofed totrue.
- The browser receives the IPC, trusts the forged parameters, suppresses Safe Browsing warnings, and presents the user with a standard extension installation prompt. If the user accepts, the malicious extension is installed with requested privileges.
Suggested Fix
The browser process must not rely on renderer-provided data for security decisions (CWE-807).
- In
RenderFrameHostImpl::DownloadURL, validate thehas_user_gestureparameter against the browser’s authoritative state (e.g., by checkingHasTransientUserActivation()). If the renderer claims a gesture but the browser disagrees, the gesture claim should be dropped or the IPC terminated. - Similarly, validate the
referrerURL againstGetLastCommittedOrigin()orGetLastCommittedURL()for the given frame, ensuring a compromised renderer cannot claim a referrer outside of its actual origin.
Evaluated with Chrome root at commit: eca8648a4e1cdfdda68c495a6003059fed641955
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.