Medium chrome Authorization Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in UI
DescriptionIncorrect authorization in UI
ComponentUI
Sub-componentUI
Bug ClassAuthorization Error
Tracker557288890
Fix commit378f4aa2ccd6 (chromium/src) +2/-1
CISA KEVNot listed
CreditedQuyen Son at VinFast (@zer0qs)
Disclosed2026-10-06

Files Changed

  • chrome/browser/chrome_content_browser_client.cc
From 378f4aa2ccd6d1a54dec41b6fd882d9ca4db3d47 Mon Sep 17 00:00:00 2001
From: Dan Harrington <harringtond@chromium.org>
Date: Fri, 11 Sep 2026 08:42:29 -0700
Subject: [PATCH] Only enable mojo bindings in the glic guest for main frame

Previously, mojo bindings were enabled on subframes, but this is not
needed. Note that the glic mojo interface itself will not be bound
unless the origin is allowed. This further restricts access.

Bug: 557288890
Change-Id: I393a309ac99c0e90e86242d84976d08e6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8373184
Reviewed-by: Bryant Chandler <bryantchandler@chromium.org>
Auto-Submit: Dan H <harringtond@chromium.org>
Commit-Queue: Dan H <harringtond@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1696294}
---
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 5d156f8a..6ec5dbb0 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -2273,7 +2273,8 @@
     content::RenderFrameHost& render_frame_host) {
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(&render_frame_host);
-  if (web_contents && glic::IsGlicGuest(web_contents)) {
+  if (web_contents && glic::IsGlicGuest(web_contents) &&
+      !render_frame_host.GetParentOrOuterDocument()) {
     return true;
   }
   // TODO(crbug.com/539909218): Prototype shortcut. Enabling MojoJS for any PWC
Loading diff…

Original Bug Report

reported by tr...@gmail.com

MojoJS bindings are granted per-WebContents rather than per-frame in the Glic guest, so any subframe it hosts receives the full Mojo interface surface

VULNERABILITY DETAILS

Summary

GlicGuestObserver enables MojoJS — which exposes the entire Mojo interface surface to JavaScript — based on a property of the WebContents, not of the frame or its committed origin. The grant runs from RenderFrameCreated, which fires for every RenderFrameHost in that WebContents, subframes included.

The sibling overload of the same function, ten lines away, does check both the frame and the origin. So does the narrow, intended API. Only the broad grant checks neither.

The defect

chrome/browser/glic/host/glic_guest_observer.cc:87-92

void GlicGuestObserver::MaybeEnableMojoJsBindings(
    content::RenderFrameHost* render_frame_host) {
  if (IsGlicGuest(web_contents())) {
    render_frame_host->EnableMojoJsBindings(/*features=*/nullptr);
  }
}

Called from :57-60:

void GlicGuestObserver::RenderFrameCreated(
    content::RenderFrameHost* render_frame_host) {
  MaybeEnableMojoJsBindings(render_frame_host);
  MaybeSetBackgroundColor(render_frame_host);
}

WebContentsObserver::RenderFrameCreated is documented at content/public/browser/web_contents_observer.h:134-137 as “Called when a RenderFrame for |render_frame_host| is created in the renderer process” — with no main-frame qualifier. It fires for subframes.

The sole condition, IsGlicGuest(web_contents()) (chrome/browser/glic/host/guest_util.cc:256-259), is GlicGuestObserver::FromWebContents(web_contents) != nullptr — a property of the WebContents.

The asymmetry, all within the same feature

Site Frame checked Origin checked
glic_guest_observer.cc:94-107 — the NavigationHandle overload IsInMainFrame() at :96 IsOriginAllowedGlicApi() at :103
glic_guest_observer.cc:110-112 — MaybeSetBackgroundColor, purely cosmetic GetParentOrOuterDocument() at :112 —
guest_util.cc:424-431 — IsFrameAllowedGlicApi, gating the narrow intended API — IsOriginAllowedGlicApi(GetLastCommittedOrigin())
glic_guest_observer.cc:87-92 — the MojoJS grant no no

The function that sets a background colour checks for a main frame. The function that grants the entire Mojo surface does not.

The origin check in the sibling overload does not save it

A cross-document navigation creates a new RenderFrameHost. RenderFrameCreated fires for that new host first and grants unconditionally, before ReadyToCommitNavigation reaches the overload that would have checked the origin.

The CHECK does not catch it

content/browser/renderer_host/render_frame_host_impl.cc:19230-19241

void RenderFrameHostImpl::EnableMojoJsBindings(
    content::mojom::ExtraMojoJsFeaturesPtr features) {
  CHECK(
      WebUIControllerFactoryRegistry::GetInstance()->GetWebUIType(...) != WebUI::kNoWebUI ||
      GetContentClient()->browser()->ShouldAllowMojoJsBindingsForFrame(*this));
  GetFrameBindingsControl()->EnableMojoJsBindings(std::move(features));
}

ChromeContentBrowserClient::ShouldAllowMojoJsBindingsForFrame (chrome/browser/chrome_content_browser_client.cc:2276-2280) returns true for any frame whose WebContents is a Glic guest — the same container-granularity test. The CHECK therefore passes for a subframe, and the grant is real rather than a crash.

The renderer-side flag is never reset

enable_mojo_js_bindings_ has exactly three references outside tests: content/renderer/render_frame_impl.h:1456 (declaration), content/renderer/render_frame_impl.cc:2599 (set to true), content/renderer/render_frame_impl.cc:4952 (read). There is no reset on commit.

Note also that EnableMojoJsBindings does not set enabled_bindings_, so the commit-time gate CheckWebUIRendererDoesNotDisplayNormalURL — which keys off GetEnabledBindings() — never applies to embedder-path grants such as this one.

chrome/browser/chrome_content_browser_client.cc:2282-2290 grants MojoJS to any outermost main frame of a PrivilegedWebContents, and its own TODO states the problem:

// TODO(crbug.com/539909218): Prototype shortcut. Enabling MojoJS for any PWC
// exposes the entire Mojo interface surface rather than only GeicApi, and the
// committed origin is not checked against the capability allowlist here.

That path is separately significant because Glic’s own policy delegate deliberately makes the navigation allowlist a superset of the capability allowlist (chrome/browser/glic/host/glic_no_webview_contents_manager.cc:62-69):

bool IsNavigationAllowed(const url::Origin& o) const override { return IsGuestOriginAllowed(o); }
bool IsCapabilityOrigin(const url::Origin& o) const override { return IsOriginAllowedGlicApi(o); }

IsGuestOriginAllowed (guest_util.cc:360-384) additionally admits accounts.google.com, login.corp.google.com, accounts.googlers.com, gaiastaging.corp.google.com and *.corp.google.com. Those origins are, by the design’s own statement, meant to be able to navigate but not to hold capabilities — yet they receive the entire Mojo surface, which is strictly more than the capability they were denied.

I am aware the TODO indicates this is known and that a scoped capability mechanism is being designed. The RenderFrameCreated grant above is a distinct defect: the TODO concerns the outermost main frame of a PWC, whereas that grant reaches subframes and applies no origin check at all.

What I have established, and what I have not

Established by reading the code, each point quoted above: the grant is keyed on the WebContents; it fires for subframes; it performs no origin check; the sibling overload’s origin check is bypassed by RenderFrameHost creation ordering; the CHECK does not catch it; the renderer-side flag is never reset.

Now measured (poc_browsertest.cc, browser_tests, no flags):

[CONTROL] guest main frame:            typeof Mojo = function
[RESULT]  cross-origin subframe:       typeof Mojo = function,
                                        typeof Mojo.bindInterface = function

The control is non-zero, so the grant mechanism demonstrably ran; the subframe at a different origin holds a working Mojo.bindInterface. That settles the question of whether the grant reaches subframes at all.

Still not established: that an attacker-controlled document can be placed inside the Glic guest WebContents. That depends on whether the deployed guest page embeds third-party frames, which is not visible from the Chromium source. If it does not, this is a defence-in-depth failure rather than a directly reachable escape. I would rather state that plainly than overstate the finding.


VERSION Chrome Version: 155.0.8043.0 dev Dev/Canary only. The grant landed in bbbe503702 on 2026-09-01, after the M155 branch cut, so it is NOT in 154 (beta) or 152 (stable). Operating System: macOS 26 (Tahoe), arm64


REPRODUCTION CASE

Attached: poc_browsertest.cc — a browser_tests case built on the existing Glic harness (chrome/browser/glic/test_support/glic_browser_test.h). It opens Glic, waits for the guest to reach the ready state, appends a cross-origin iframe to the guest’s main frame, and evaluates typeof Mojo in that subframe. The test asserts the SECURE behaviour, so it fails on an affected build.

It logs the guest main frame’s own typeof Mojo first as a positive control: a zero result in the subframe means nothing unless the mechanism is shown to have run at all.


FOR CRASHES, PLEASE INCLUDE THE FOLLOWING ADDITIONAL INFORMATION

Type of crash: N/A - this is not a crash. Crash State: N/A - no ASan report. The ASan build was used as the test configuration, not as the detector. Client ID (if relevant): N/A


CREDIT INFORMATION Reporter credit: Quyền Sơn (@zer0qs)

View on issue tracker