CVE-2026-11260
Overview
Files Changed
components/permissions/permission_util.cccontent/browser/permissions/permission_util.cc
Patch
From a0d89547f245e097100233e36011b94431b3c075 Mon Sep 17 00:00:00 2001 From: Antonio Sartori <[email protected]> Date: Tue, 28 Apr 2026 03:04:57 -0700 Subject: [PATCH] [permissions] Fall back to visible URL only for main frame origins Permissions for top-level sandboxed origins (i.e. network URLs served with CSP: sandbox) use the actual URLs origin. However, for non-top-level opaque origins, it does not make sense to fall-back to the visible URL origin). Bug: 499257860 Fixed: 499257860 Change-Id: If77a4cdbbe8096d6968d77669f121aaf6eed7b20 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7791424 Reviewed-by: Andy Paicu <[email protected]> Commit-Queue: Antonio Sartori <[email protected]> Cr-Commit-Position: refs/heads/main@{#1621636} --- diff --git a/components/permissions/permission_util.cc b/components/permissions/permission_util.cc index 33019cb5..7ed353d3 100644 --- a/components/permissions/permission_util.cc +++ b/components/permissions/permission_util.cc @@ -452,7 +452,7 @@ // content/browser/permissions/permission_util.cc. GURL PermissionUtil::GetLastCommittedOriginAsURL( content::RenderFrameHost* render_frame_host) { - DCHECK(render_frame_host); + CHECK(render_frame_host); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); @@ -471,12 +471,13 @@ } #endif - if (render_frame_host->GetLastCommittedOrigin().GetURL().is_empty()) { + GURL origin = render_frame_host->GetLastCommittedOrigin().GetURL(); + if (origin.is_empty() && render_frame_host->IsInPrimaryMainFrame()) { if (!web_contents->GetVisibleURL().is_empty()) { - return web_contents->GetVisibleURL(); + origin = web_contents->GetVisibleURL(); } } - return render_frame_host->GetLastCommittedOrigin().GetURL(); + return origin; } ContentSettingsType PermissionUtil::PermissionTypeToContentSettingsTypeSafe( diff --git a/content/browser/permissions/permission_util.cc b/content/browser/permissions/permission_util.cc index 1246c68..c03cac1 100644 --- a/content/browser/permissions/permission_util.cc +++ b/content/browser/permissions/permission_util.cc @@ -69,7 +69,7 @@ // components/permissions/permission_util.cc. GURL PermissionUtil::GetLastCommittedOriginAsURL( content::RenderFrameHost* render_frame_host) { - DCHECK(render_frame_host); + CHECK(render_frame_host); content::WebContents* web_contents = content::WebContents::FromRenderFrameHost(render_frame_host); @@ -88,12 +88,13 @@ } #endif - if (render_frame_host->GetLastCommittedOrigin().GetURL().is_empty()) { + GURL origin = render_frame_host->GetLastCommittedOrigin().GetURL(); + if (origin.is_empty() && render_frame_host->IsInPrimaryMainFrame()) { if (!web_contents->GetVisibleURL().is_empty()) { - return web_contents->GetVisibleURL(); + origin = web_contents->GetVisibleURL(); } } - return render_frame_host->GetLastCommittedOrigin().GetURL(); + return origin; } bool PermissionUtil::IsDomainOverride(
Original Bug Report
CSP sandbox bypass in permission requests via GetVisibleURL fallback
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 security team.
Overview: A potential logic flaw in PermissionUtil::GetLastCommittedOriginAsURL causes it to incorrectly fall back to the visible URL when a top-level document has an opaque origin. This allows sandboxed documents to request delegated permissions (like Geolocation) using the hosting origin’s identity. An attacker could exploit this to bypass CSP sandbox isolation, leading to silent grant reuse or permission prompt spoofing.
Affected files:
components/permissions/permission_util.cccontent/browser/permissions/permission_util.cccomponents/permissions/permission_manager.cccomponents/permissions/permission_context_base.cccontent/browser/permissions/permission_controller_impl.cccomponents/permissions/permission_request_manager.cc
Estimated timestamp from git blame: 2024-08-13
Background
The Content-Security-Policy (CSP) sandbox directive is designed to isolate untrusted content by assigning it an opaque origin, which prevents it from inheriting the authority of its hosting origin. However, a potential logic flaw allows a sandboxed top-level document to bypass this restriction for certain delegated permissions.
Vulnerability Details
Initial logic and parameters are validated: When a top-level document is served with Content-Security-Policy: sandbox allow-scripts (without allow-same-origin), it commits with an opaque origin. If the document requests a delegated permission such as Geolocation via navigator.geolocation.getCurrentPosition(), standard Permissions Policy and secure context checks in Blink succeed, as the opaque origin is same-origin with itself and inherits the precursor’s trustworthy status. The request is subsequently routed to the browser process’s PermissionManager::RequestPermissionsInternal.
At this point, the requesting_origin is correctly identified as an empty URL due to the opaque origin.
However, the logic leaps directly to a fallback mechanism in PermissionUtil::GetLastCommittedOriginAsURL. Because the origin’s URL is empty, the function falls back to web_contents->GetVisibleURL():
if (render_frame_host->GetLastCommittedOrigin().GetURL().is_empty()) {
if (!web_contents->GetVisibleURL().is_empty()) {
return web_contents->GetVisibleURL();
}
}
Because Geolocation uses PermissionDelegationMode::kDelegated, PermissionUtil::GetCanonicalOrigin ignores the empty requesting origin and adopts this visible URL as the embedding origin. Consequently, the request is validated against the hosting origin’s visible URL rather than being rejected for having an invalid/opaque origin, completely bypassing the sandbox isolation.
Potential Attack Steps
Note: These are suggested steps based on static analysis; our tooling does not currently run active proof-of-concept exploits.
- An attacker places user-generated content on a victim site (e.g.,
https://victim.com/ugc-viewer). - The endpoint serves the content with the response header
Content-Security-Policy: sandbox allow-scripts. - The attacker’s HTML executes a permission request, such as
navigator.geolocation.getCurrentPosition(...). - Impact 1 (Silent Grant Reuse): If the victim previously granted location access to
victim.com, the sandboxed document silently inherits the permission and receives location data without prompting the user. - Impact 2 (Prompt Spoofing): If no grant exists, the browser prompts the user, identifying the requester as
victim.cominstead of an untrusted or null origin, deceiving them into granting access.
Suggested Fix
Modify PermissionUtil::GetLastCommittedOriginAsURL so that it does not fall back to GetVisibleURL for properly committed opaque origins. The fallback should be restricted to specific edge cases (e.g., pending navigations or specific Android file:/// schemes). Ensure that permission canonicalization correctly rejects or maintains the empty URL state for opaque origins to prevent unauthorized delegation.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
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.