CVE-2026-14007
Overview
Files Changed
services/network/public/cpp/permissions_policy/permissions_policy.cc
Patch
From 9dc4d407d0ea69da553d0ffb13a3722854dbfd34 Mon Sep 17 00:00:00 2001 From: Ari Chivukula <[email protected]> Date: Tue, 26 May 2026 08:00:14 -0700 Subject: [PATCH] Correct comment about returning nullopt. The comment was updated to reflect that std::nullopt is returned, not an empty allowlist. I think the related bug is a false positive. Fixed: 516425999 Change-Id: If39292d32bf459fe08cf4e35f1b37d1479403446 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7875202 Reviewed-by: Ian Clelland <[email protected]> Commit-Queue: Ari Chivukula <[email protected]> Auto-Submit: Ari Chivukula <[email protected]> Cr-Commit-Position: refs/heads/main@{#1636186} --- diff --git a/services/network/public/cpp/permissions_policy/permissions_policy.cc b/services/network/public/cpp/permissions_policy/permissions_policy.cc index a10462d..0e21991b 100644 --- a/services/network/public/cpp/permissions_policy/permissions_policy.cc +++ b/services/network/public/cpp/permissions_policy/permissions_policy.cc @@ -377,7 +377,7 @@ std::optional<const PermissionsPolicy::Allowlist> PermissionsPolicy::GetAllowlistForFeatureIfExists( network::mojom::PermissionsPolicyFeature feature) const { - // Return an empty allowlist when disabled through inheritance. + // Return nullopt when disabled through inheritance. if (!IsFeatureEnabledByInheritedPolicy(feature)) { return std::nullopt; }
Original Bug Report
Permissions-Policy bypass for fixed-permission fenced frames in nested embedders
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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.
Overview: A logic flaw in Permissions-Policy validation allows nested cross-origin iframes to bypass a top-level page’s policy opt-out. When validating fixed-permission fenced frames, the browser incorrectly allows restricted features because GetAllowlistForFeatureIfExists returns std::nullopt instead of an empty allowlist when disabled via inheritance. This allows third-party ad-tech frames to load fenced frames with unauthorized permissions enabled.
Affected files:
services/network/public/cpp/permissions_policy/permissions_policy.cccontent/browser/renderer_host/navigation_request.cccontent/browser/renderer_host/render_frame_host_impl.cc
Estimated timestamp from git blame: 2023-05-23
Root Cause Analysis
There is a potential logic flaw in how permissions policies are validated during the navigation of fixed-permission fenced frames. Specifically, NavigationRequest::IsFencedFrameRequiredPolicyFeatureAllowed acts as the browser-side gate to prevent fixed-permission fenced frames (such as Protected Audience or Shared Storage selectURL) from navigating if their embedder lacks a required Privacy Sandbox feature.
During validation, the function checks the embedder’s allowlist in content/browser/renderer_host/navigation_request.cc:
std::optional<const network::PermissionsPolicy::Allowlist>
embedder_allowlist = GetParentFrameOrOuterDocument()
->GetPermissionsPolicy()
->GetAllowlistForFeatureIfExists(feature);
if (embedder_allowlist && !embedder_allowlist->MatchesAll()) {
return false;
}
However, PermissionsPolicy::GetAllowlistForFeatureIfExists in services/network/public/cpp/permissions_policy/permissions_policy.cc is implemented to return std::nullopt (rather than an empty allowlist) when a feature is disabled via inheritance, despite the explicit developer comment stating the opposite:
std::optional<const PermissionsPolicy::Allowlist>
PermissionsPolicy::GetAllowlistForFeatureIfExists(
network::mojom::PermissionsPolicyFeature feature) const {
// Return an empty allowlist when disabled through inheritance.
if (!IsFeatureEnabledByInheritedPolicy(feature)) {
return std::nullopt; // Note: returns nullopt instead of Allowlist{}
}
...
}
Because embedder_allowlist evaluates to std::nullopt, the check embedder_allowlist && !embedder_allowlist->MatchesAll() evaluates to false. As a result, the embedder-allowlist check passes incorrectly for any feature that has been disabled in the embedder via its inheritance chain.
When the gate returns true, the fenced frame commits. During this process, the frame’s permissions policy is initialized via CreateFixedForFencedFrame, which unconditionally enables every effective_enabled_permission in the fenced frame, fully bypassing the top-level page’s opt-out.
Potential Attack / Trigger Path
(Note: These are suggested/potential steps, as our tooling agent does not have the ability to execute the code.)
- A top-level publisher page at
https://publisher.testserves a response header opting out of Privacy Sandbox features:Permissions-Policy: shared-storage=(), private-aggregation=(). - The publisher page embeds a cross-origin ad-tech iframe E (
https://adtech.test). - E’s permissions policy inheritance correctly sets its inherited policy for these features to
false(e.g.inherited_policies_[kSharedStorage] = false). - E obtains a fixed-permission
FencedFrameConfig(e.g., viarunAdAuction(), assumingrun-ad-auctionis allowed/default). - E creates a
<fencedframe>and navigates it using the config. - During the navigation’s permission policy checks (
CheckPermissionsPoliciesForFencedFrames), the validation succeeds becauseGetAllowlistForFeatureIfExistsreturnsstd::nulloptfor the inherited-disabled features, bypassing the check. - The fenced frame navigates and commits. The third-party document inside the fenced frame successfully accesses the restricted APIs (such as writing to cross-site Shared Storage or sending Private Aggregation reports) despite the publisher’s top-level opt-out policy.
Suggested Fix
Update PermissionsPolicy::GetAllowlistForFeatureIfExists to return an empty PermissionsPolicy::Allowlist() instead of std::nullopt when a feature is disabled through inheritance. This aligns the return value with the other helper methods (GetAllowlistForDevTools and GetAllowlistForFeature) and satisfies the method’s documented behavior:
std::optional<const PermissionsPolicy::Allowlist>
PermissionsPolicy::GetAllowlistForFeatureIfExists(
network::mojom::PermissionsPolicyFeature feature) const {
// Return an empty allowlist when disabled through inheritance.
if (!IsFeatureEnabledByInheritedPolicy(feature)) {
return PermissionsPolicy::Allowlist();
}
...
}
Evaluated with Chrome root at commit: a2bea94528f4bd6cc57739c43fa3bb890b8367d3
Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:
- If you are familiar with the severity guidelines, you may adjust the severity.
- If this is a false positive, and there’s no work to be done, please close as WAI.
- If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.
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.