Medium watch CVSS 3.1 chrome Authorization Error 🔧 Commit mapped

Overview

Medium
Severity
3.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in Navigation
DescriptionIncorrect authorization in Navigation
ComponentNavigation
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker511774376
Fix commit35ec6fdc64cf (chromium/src) +32/-13
CWECWE-863
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-10 (per vendor advisory)
Fix landed2026-09-03 (public git)
Disclosed2026-09-01

Background

Navigation API `navigate` event
a same-origin-only event fired in the renderer so a document can observe/intercept its own navigations, including cross-document history traversals; it must not fire when the destination is cross-origin.
`MaybeDispatchNavigateEventForCrossDocumentTraversal`
the browser-side NavigationRequest method that decides, at navigation start, whether the destination of a history traversal is same-origin with the current document and thus whether to dispatch the navigate event.
CSP sandbox / opaque origin
a Content-Security-Policy sandbox directive can force a main-frame document to commit at an opaque (unique) origin, which is not same-origin with anything, even though its URL is unchanged.
`origin_related_state_` / `committed_origin`
per-FrameNavigationEntry state on NavigationRequest; the patch adds committed_origin, the origin actually recorded when that entry previously committed.

Root Cause Analysis

MaybeDispatchNavigateEventForCrossDocumentTraversal decided same-origin-ness by resolving the destination’s URL (url::Origin::Resolve(common_params_->url, initiator_origin)), which for a history entry that had committed at an opaque origin due to CSP sandbox does not reflect that opaque origin — the URL alone looks like an ordinary same-origin URL. As a result the check concluded “same-origin” and fired the navigate event for a cross-document history traversal to a CSP-sandboxed entry that was in fact cross-origin (opaque), violating the invariant that the event is same-origin-only.

The fix records the entry’s real committed_origin into origin_related_state_ when it is populated, and MaybeDispatchNavigateEventForCrossDocumentTraversal now prefers *origin_related_state_->committed_origin as destination_origin, falling back to the URL-derived origin only when no committed origin is available. A secondary correctness issue the reland fixes: when DeferSpeculativeRFHCreation is disabled, GetUrlInfo ran before response headers arrived and estimated sandbox flags only from commit_params_->frame_policy.sandbox_flags (empty for main-frame CSP sandbox), so UrlInfo looked unsandboxed, CanUseDestinationInstance() rejected the sandboxed destination SiteInstance, and the resulting ResetStateForSiteInstanceChange() wiped origin_related_state_ — erasing the very committed_origin the fix relies on; GetUrlInfo now also considers dest_site_instance_->GetSiteInfo().IsSandboxed() for history navigations so the estimate matches and the state is preserved.

Key insight
The code derived the destination origin from the URL, which cannot represent the opaque origin produced by CSP sandbox, so a sandboxed history entry was misjudged same-origin and the navigate event fired cross-origin; the fix compares against the entry’s actual previously-committed_origin (and keeps the sandbox estimate consistent so that state is not reset away).

Attack Path

  1. Create a CSP-sandboxed entry A page navigates (in a frame it controls) to a document served with a CSP sandbox directive, which commits at an opaque origin while its URL remains an ordinary same-origin-looking URL, recorded as a FrameNavigationEntry.
  2. Register a `navigate` listener The current same-origin document installs a Navigation API navigate handler expecting to observe/intercept only same-origin navigations.
  3. Traverse to the sandboxed entry A cross-document history traversal (e.g. history.back()/go) targets the sandboxed entry; pre-patch, MaybeDispatchNavigateEventForCrossDocumentTraversal resolves the destination from the pre-redirect URL, judges it same-origin, and dispatches the navigate event even though the true destination origin is opaque/cross-origin.
  4. Cross-origin event exposure The listener receives (and can intercept/inspect) a navigation to a cross-origin destination it should not have visibility into, an incorrect-authorization/information-exposure condition (inferred) for the exact data leaked.

Impact Assessment

The advisory rates this as incorrect authorization in Navigation; a web page gains the ability to have its navigate event fire for a cross-document history traversal whose destination is actually a cross-origin opaque (CSP-sandboxed) origin, i.e. an event/observation that the same-origin policy should have withheld, in the renderer of the initiating document. Preconditions are a controllable CSP-sandboxed history entry and a same-origin navigate listener performing the traversal. No memory-safety impact or further escalation is demonstrated by the patch, and any beyond-spec leakage of destination details is (inferred).

Files Changed

  • content/browser/renderer_host/navigation_controller_impl_browsertest.cc
  • content/browser/renderer_host/navigation_request.cc
  • content/browser/renderer_host/navigation_request.h

Audit Directions

  • Origin decisions derived from URL instead of committed origin
    Search for same-origin/authorization checks that build an origin via url::Origin::Resolve(url, ...) rather than the actually-committed origin; these mishandle opaque origins from CSP sandbox, data:, and sandboxed iframes.
  • Speculative estimates that diverge from final state
    Review pre-response estimates (sandbox flags, SiteInstance suitability) that can trigger a state reset when they later mismatch, silently discarding fields other logic depends on.
  • Navigation API / cross-document traversal gating
    Audit other navigate-event and history-traversal same-origin gates for opaque-origin handling, especially where the destination origin is not yet known at navigation start.
From 35ec6fdc64cf53e460fcf54bb1aebe291889c53b Mon Sep 17 00:00:00 2001
From: Noam Rosenthal <nrosenthal@chromium.org>
Date: Thu, 03 Sep 2026 11:12:46 -0700
Subject: [PATCH] Reland "Use committed origin when deciding whether to dispatch navigate event"

This is a reland of commit cc3f09b3a5179e1232fa27f898fa165cb06fc98b.

Original change caused test failures in
All/NavigationControllerBrowserTest.NavigateEventNotFiredForTraversalToCSPSandboxedEntry/*
on builders where DeferSpeculativeRFHCreation is disabled or not applicable
(e.g., fuchsia-arm64-cast-receiver-rel).

Root cause:
In DidCreateNavigationRequest, when DeferSpeculativeRFHCreation is disabled,
RenderFrameHostManager::GetFrameHostForNavigation() runs synchronously at
kBeforeNetworkRequest to create a speculative RFH. At this stage, HTTP
response headers have not yet arrived. NavigationRequest::GetUrlInfo()
only estimated sandbox flags from commit_params_->frame_policy.sandbox_flags,
which is empty for main-frame CSP sandboxing. As a result, UrlInfo was
estimated as unsandboxed, causing CanUseDestinationInstance() to conclude
that the sandboxed destination SiteInstance was unsuitable.
RenderFrameHostManager::GetSiteInstanceForNavigationRequest() observed
this mismatch (request->dest_site_instance() != dest_site_instance) and
called ResetStateForSiteInstanceChange(), which reset origin_related_state_.
When MaybeDispatchNavigateEventForCrossDocumentTraversal() subsequently ran,
origin_related_state_->committed_origin was missing, causing it to fall
back to resolving the pre-redirect URL as same-origin and firing the navigate
event unexpectedly.

Fix:
When computing UrlInfo before policies are computed, consider
dest_site_instance_->GetSiteInfo().IsSandboxed() when estimating sandbox
flags for history navigations.

Original change description:
> Use committed origin when deciding whether to dispatch navigate event
>
> The committed origin can be opaque in sandboxed iframes, and is also
> pre-redirect.
>
> Bug: 511774376
> Change-Id: Ia5f4257a907274f24abb32186ae0e074eb488716
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8041464
> Reviewed-by: Charlie Reis <creis@chromium.org>
> Commit-Queue: Noam Rosenthal <nrosenthal@google.com>
> Reviewed-by: Nate Chapin <japhet@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1662779}

Bug: 511774376
Change-Id: Id05f189baabd0ef75e52dbf040ac5463b6d23cde
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8345238
Commit-Queue: Noam Rosenthal <nrosenthal@google.com>
Reviewed-by: Charlie Reis <creis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1691774}
---
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 63b4aeb..b7e23d3 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1889,6 +1889,7 @@
     origin_related_state_.emplace(OriginRelatedState{
         .item_sequence_number = frame_entry->item_sequence_number(),
         .document_sequence_number = frame_entry->document_sequence_number(),
+        .committed_origin = frame_entry->committed_origin(),
     });
   }
 
@@ -4759,10 +4760,12 @@
   //
   // If PolicyContainer::ComputePoliciesToCommit() has run
   // `policy_container_builder_` will be valid, but even if it hasn't, we can
-  // speculatively take `commit_params_->frame_policy.sandbox_flags` if we
-  // haven't received the response yet and don't have the final
-  // `policy_container_builder_`, and if the state of the kOrigin flag changes,
-  // we'll detect the change and recompute the target SiteInstance elsewhere.
+  // speculatively take `commit_params_->frame_policy.sandbox_flags` (or
+  // `dest_site_instance_` for history navigations) if we haven't received the
+  // response yet and don't have the final `policy_container_builder_`, to
+  // reduce the chance that recomputing will be needed. If the state of the
+  // kOrigin flag changes, we'll detect the change and recompute the target
+  // SiteInstance elsewhere.
   //
   // In general, about:blank documents should stay in their initiator's process.
   // If neither the initiator or about:blank is sandboxed, or if both are, then
@@ -4792,12 +4795,15 @@
       // Note: We'll end up here if this function is called before
       // ComputePoliciesToCommit(), such as when computing a speculative
       // RenderFrameHost's SiteInstance before receiving a response. In that
-      // event we use the sandbox flags in commit_params_ as a current "best
+      // event we use the sandbox flags in commit_params_ (or the destination
+      // SiteInstance, if this is a history navigation) as a current "best
       // estimate".
       has_origin_restricted_sandbox_flag =
-          (commit_params_->frame_policy.sandbox_flags &
-           network::mojom::WebSandboxFlags::kOrigin) ==
-          network::mojom::WebSandboxFlags::kOrigin;
+          ((commit_params_->frame_policy.sandbox_flags &
+            network::mojom::WebSandboxFlags::kOrigin) ==
+           network::mojom::WebSandboxFlags::kOrigin) ||
+          (dest_site_instance_ &&
+           dest_site_instance_->GetSiteInfo().IsSandboxed());
     }
 
     // It's possible that a sandbox attribute can disappear from a frame that
@@ -12030,13 +12036,19 @@
       blink::mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT) {
     return;
   }
-  // Only fire the navigate event if the destination is same-origin. Because
-  // this check is performed at navigation start time, `destination_origin` is
+  // Only fire the navigate event if the destination is same-origin. Prefer
+  // the origin recorded when the destination entry previously committed, since
+  // the URL alone does not reflect opaque origins resulting from CSP sandbox.
+  // See also PopulateSingleNavigationApiHistoryEntryVector(). Because this
+  // check is performed at navigation start time, the URL-derived fallback is
   // based on the pre-redirect URL, which is consistent with the renderer
   // process logic for firing the navigate event for non-history navigations.
-  url::Origin destination_origin = url::Origin::Resolve(
-      common_params_->url,
-      common_params_->initiator_origin.value_or(url::Origin()));
+  url::Origin destination_origin =
+      origin_related_state_ && origin_related_state_->committed_origin
+          ? *origin_related_state_->committed_origin
+          : url::Origin::Resolve(
+                common_params_->url,
+                common_params_->initiator_origin.value_or(url::Origin()));
   if (!frame_tree_node_->current_origin().IsSameOriginWith(
           destination_origin)) {
     return;
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index da65f8c..78a097fc 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -3209,6 +3209,9 @@
   struct OriginRelatedState {
     int64_t item_sequence_number;
     int64_t document_sequence_number;
+    // The origin that was recorded when the FrameNavigationEntry was previously
+    // committed, if any. May be nullopt for entries that have never committed.
+    std::optional<url::Origin> committed_origin;
   };
   std::optional<OriginRelatedState> origin_related_state_;
 
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 7e83a45..65e762e 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -22020,6 +22020,10 @@
   EXPECT_EQ(initial_site_instance, contents()->GetSiteInstance());
 }
 
+// TODO(crbug.com/511774376): Add a browser test to verify that the navigate
+// event for a cross-document history traversal is not dispatched when the
+// destination entry was committed at an opaque origin due to CSP sandbox.
+
 IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
                        NavigateToNavigationApiKey_NullCommittedOrigin) {
   // Ensure there's a history entry before the error page.
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index 7e83a45..65e762e 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -22020,6 +22020,10 @@
   EXPECT_EQ(initial_site_instance, contents()->GetSiteInstance());
 }
 
+// TODO(crbug.com/511774376): Add a browser test to verify that the navigate
+// event for a cross-document history traversal is not dispatched when the
+// destination entry was committed at an opaque origin due to CSP sandbox.
+
 IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
                        NavigateToNavigationApiKey_NullCommittedOrigin) {
   // Ensure there's a history entry before the error page.
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.