Medium CVSS 6.5 chrome Policy Bypass 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Glic
DescriptionInsufficient policy enforcement in Glic
ComponentGlic
Bug ClassPolicy Bypass
Tracker516420806
Fix commitbb3673b3f6be (chromium/src) +134/-0
CWECWE-602
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-05-25 (per vendor advisory)
Fix landed2026-06-18 (public git)
Disclosed2026-07-29
Patch gap41 days — fix public in source before the advisory

Changed Functions

FunctionChangeNotes
MockActorUiTabController
chrome/browser/android/tab_android_unittest.cc
modified
TabAndroidTest
chrome/browser/android/tab_android_unittest.cc
modified
GlicTabAndroidTest
chrome/browser/android/tab_android_unittest.cc
modified
GlicTabAndroidTest
chrome/browser/android/tab_android_unittest.cc
modified
TEST_F
chrome/browser/android/tab_android_unittest.cc
modified

Files Changed

  • chrome/browser/android/tab_android_unittest.cc
  • chrome/browser/android/tab_web_contents_delegate_android.cc
From bb3673b3f6be7e1b2936783f141bd4de2575e509 Mon Sep 17 00:00:00 2001
From: Johann Hofmann <johannhof@chromium.org>
Date: Thu, 18 Jun 2026 13:48:27 -0700
Subject: [PATCH] Prevent iframe sandbox escape in Android Glic popup rewrite

On Android, when a Glic (Actor) task is active on a tab, popup creation
requests are intercepted and rewritten into same-tab top-level
navigations.

However, the Android implementation lacked a sandbox check to verify if the
initiating frame is restricted from performing top-level navigations.
This allows a sandboxed cross-origin iframe with 'allow-popups' (but not
'allow-top-navigation') to bypass its sandbox restrictions and navigate the
top-level page.

This CL adds the missing sandbox check to
TabWebContentsDelegateAndroid::IsWebContentsCreationOverridden, returning
false if the opener is sandboxed with kTopNavigation. This aligns the
Android behavior with Desktop.

Also added a C++ unit test `IsWebContentsCreationOverridden_GlicSandboxCheck`
in `tab_android_unittest.cc` to verify this behavior on Android.

Bug: 516420806
Change-Id: Ifbd89370ae7a56fe064385c45032683d2633eb60
Fixed: 516420806
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7915889
Commit-Queue: Johann Hofmann <johannhof@chromium.org>
Auto-Submit: Johann Hofmann <johannhof@chromium.org>
Reviewed-by: Siddhartha S <ssid@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1649290}
---

diff --git a/chrome/browser/android/tab_android_unittest.cc b/chrome/browser/android/tab_android_unittest.cc
index 61a189d..1260e29 100644
--- a/chrome/browser/android/tab_android_unittest.cc
+++ b/chrome/browser/android/tab_android_unittest.cc
@@ -13,25 +13,63 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chrome/android/chrome_jni_headers/TabAndroidTestHelper_jni.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/actor_task.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/actor/actor_util.h"
+#include "chrome/browser/actor/ui/actor_ui_tab_controller_interface.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/android/tab_features.h"
 #include "chrome/browser/android/tab_group_android.h"
 #include "chrome/browser/android/tab_interface_android.h"
+#include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/actor/core/actor_features.h"
 #include "components/tabs/public/pinned_tab_collection.h"
 #include "components/tabs/public/tab_collection.h"
 #include "components/tabs/public/tab_group_tab_collection.h"
+#include "content/public/common/window_container_type.mojom.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/http/http_response_headers.h"
+#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 constexpr int kTabId = 1;
+
+class MockActorUiTabController
+    : public actor::ui::ActorUiTabControllerInterface {
+ public:
+  explicit MockActorUiTabController(tabs::TabInterface& tab)
+      : ActorUiTabControllerInterface(tab) {}
+  ~MockActorUiTabController() override = default;
+
+  // ActorUiTabControllerInterface:
+  void OnUiTabStateChange(const actor::ui::UiTabState& ui_tab_state,
+                          actor::ui::UiResultCallback callback) override {
+    std::move(callback).Run(true);
+  }
+  void SetActorTaskPaused() override {}
+  void SetActorTaskResume() override {}
+  base::WeakPtr<ActorUiTabControllerInterface> GetWeakPtr() override {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+  actor::ui::UiTabState GetCurrentUiTabState() const override {
+    return actor::ui::UiTabState();
+  }
+
+ private:
+  base::WeakPtrFactory<MockActorUiTabController> weak_ptr_factory_{this};
+};
 }  // namespace
 
 class TabAndroidTest : public testing::Test {
@@ -154,6 +192,92 @@
   EXPECT_EQ(tab.get(), webui::GetTabInterface(raw_web_contents));
 }
 
+class GlicTabAndroidTest : public TabAndroidTest {
+ public:
+  GlicTabAndroidTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kGlic, features::kGlicActor},
+        /*disabled_features=*/{});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(GlicTabAndroidTest, IsWebContentsCreationOverridden_GlicSandboxCheck) {
+  content::RenderViewHostTestEnabler rvh_test_enabler;
+
+  // Create a WebContents.
+  std::unique_ptr<content::WebContents> web_contents =
+      content::WebContents::Create(
+          content::WebContents::CreateParams(profile_.get()));
+  content::WebContents* raw_web_contents = web_contents.get();
+
+  // Create TabAndroid for testing.
+  std::unique_ptr<TabAndroid> tab = TabAndroid::CreateForTesting(
+      profile_.get(), kTabId + 1, std::move(web_contents));
+
+  // Register the tab lookup helper.
+  tabs::TabLookupFromWebContents::CreateForWebContents(raw_web_contents,
+                                                       tab.get());
+
+  // Create the delegate with a null Java reference.
+  auto delegate = std::make_unique<android::TabWebContentsDelegateAndroid>(
+      env_, base::android::ScopedJavaLocalRef<jobject>());
+
+  // Set the delegate on the web contents.
+  raw_web_contents->SetDelegate(delegate.get());
+
+  content::RenderFrameHost* main_frame =
+      raw_web_contents->GetPrimaryMainFrame();
+
+  // 1. Without Glic Actor active, it should return false.
+  EXPECT_FALSE(delegate->IsWebContentsCreationOverridden(
+      main_frame, nullptr, content::mojom::WindowContainerType::NORMAL, GURL(),
+      "", GURL()));
+
+  // 2. Start Glic Actor task and attach the tab to it.
+  actor::ActorKeyedService* service =
+      actor::ActorKeyedService::Get(profile_.get());
+  ASSERT_NE(nullptr, service);
+  actor::TaskId task_id = service->CreateTask(
+      actor::TestTaskSourceInfo(), actor::NoEnterprisePolicyChecker());
+  actor::ActorTask* task = service->GetTask(task_id);
+  ASSERT_NE(nullptr, task);
+
+  // Add the tab to the task.
+  MockActorUiTabController mock_controller(*tab);
+  actor::AddTabToTask(*tab, *task);
+
+  // Ensure HasActorTaskPreventingNewWebContents returns true.
+  ASSERT_TRUE(actor::HasActorTaskPreventingNewWebContents(main_frame));
+
+  // 3. Under Glic Actor control, if the frame is NOT sandboxed, it should
+  // override creation.
+  EXPECT_TRUE(delegate->IsWebContentsCreationOverridden(
+      main_frame, nullptr, content::mojom::WindowContainerType::NORMAL, GURL(),
+      "", GURL()));
+
+  // 4. Under Glic Actor control, if the frame IS sandboxed with kTopNavigation,
+  // it should NOT override creation.
+  auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
+      GURL("https://sandboxed.example.com"), raw_web_contents);
+  auto headers =
+      base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+  headers->AddHeader("Content-Security-Policy", "sandbox allow-popups");
+  simulator->SetResponseHeaders(headers);
+  simulator->Commit();
+
+  content::RenderFrameHost* sandboxed_frame =
+      raw_web_contents->GetPrimaryMainFrame();
+  ASSERT_TRUE(sandboxed_frame->IsSandboxed(
+      network::mojom::WebSandboxFlags::kTopNavigation));
+
+  EXPECT_FALSE(delegate->IsWebContentsCreationOverridden(
+      sandboxed_frame, nullptr, content::mojom::WindowContainerType::NORMAL,
+      GURL(), "", GURL()));
+}
+
 TEST_F(TabAndroidTest, Getters) {
   TabInterfaceAndroid tab_interface(tab_android_);
   EXPECT_EQ(u"about:blank", tab_interface.GetTitle());
diff --git a/chrome/browser/android/tab_web_contents_delegate_android.cc b/chrome/browser/android/tab_web_contents_delegate_android.cc
index d72ab20..483f52a 100644
--- a/chrome/browser/android/tab_web_contents_delegate_android.cc
+++ b/chrome/browser/android/tab_web_contents_delegate_android.cc
@@ -77,6 +77,7 @@
 #include "content/public/browser/render_widget_host_view.h"
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/android/tab_android_unittest.cc b/chrome/browser/android/tab_android_unittest.cc
index 61a189d..1260e29 100644
--- a/chrome/browser/android/tab_android_unittest.cc
+++ b/chrome/browser/android/tab_android_unittest.cc
@@ -13,25 +13,63 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "chrome/android/chrome_jni_headers/TabAndroidTestHelper_jni.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/actor_task.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/actor/actor_util.h"
+#include "chrome/browser/actor/ui/actor_ui_tab_controller_interface.h"
 #include "chrome/browser/android/tab_android.h"
 #include "chrome/browser/android/tab_features.h"
 #include "chrome/browser/android/tab_group_android.h"
 #include "chrome/browser/android/tab_interface_android.h"
+#include "chrome/browser/android/tab_web_contents_delegate_android.h"
 #include "chrome/browser/flags/android/chrome_feature_list.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/android/tab_model/tab_model.h"
 #include "chrome/browser/ui/webui/webui_embedding_context.h"
+#include "chrome/common/chrome_features.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "components/actor/core/actor_features.h"
 #include "components/tabs/public/pinned_tab_collection.h"
 #include "components/tabs/public/tab_collection.h"
 #include "components/tabs/public/tab_group_tab_collection.h"
+#include "content/public/common/window_container_type.mojom.h"
 #include "content/public/test/browser_task_environment.h"
+#include "content/public/test/navigation_simulator.h"
+#include "content/public/test/test_renderer_host.h"
+#include "net/http/http_response_headers.h"
+#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace {
 constexpr int kTabId = 1;
+
+class MockActorUiTabController
+    : public actor::ui::ActorUiTabControllerInterface {
+ public:
+  explicit MockActorUiTabController(tabs::TabInterface& tab)
+      : ActorUiTabControllerInterface(tab) {}
+  ~MockActorUiTabController() override = default;
+
+  // ActorUiTabControllerInterface:
+  void OnUiTabStateChange(const actor::ui::UiTabState& ui_tab_state,
+                          actor::ui::UiResultCallback callback) override {
+    std::move(callback).Run(true);
+  }
+  void SetActorTaskPaused() override {}
+  void SetActorTaskResume() override {}
+  base::WeakPtr<ActorUiTabControllerInterface> GetWeakPtr() override {
+    return weak_ptr_factory_.GetWeakPtr();
+  }
+  actor::ui::UiTabState GetCurrentUiTabState() const override {
+    return actor::ui::UiTabState();
+  }
+
+ private:
+  base::WeakPtrFactory<MockActorUiTabController> weak_ptr_factory_{this};
+};
 }  // namespace
 
 class TabAndroidTest : public testing::Test {
@@ -154,6 +192,92 @@
   EXPECT_EQ(tab.get(), webui::GetTabInterface(raw_web_contents));
 }
 
+class GlicTabAndroidTest : public TabAndroidTest {
+ public:
+  GlicTabAndroidTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kGlic, features::kGlicActor},
+        /*disabled_features=*/{});
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(GlicTabAndroidTest, IsWebContentsCreationOverridden_GlicSandboxCheck) {
+  content::RenderViewHostTestEnabler rvh_test_enabler;
+
+  // Create a WebContents.
+  std::unique_ptr<content::WebContents> web_contents =
+      content::WebContents::Create(
+          content::WebContents::CreateParams(profile_.get()));
+  content::WebContents* raw_web_contents = web_contents.get();
+
+  // Create TabAndroid for testing.
+  std::unique_ptr<TabAndroid> tab = TabAndroid::CreateForTesting(
+      profile_.get(), kTabId + 1, std::move(web_contents));
+
+  // Register the tab lookup helper.
+  tabs::TabLookupFromWebContents::CreateForWebContents(raw_web_contents,
+                                                       tab.get());
+
+  // Create the delegate with a null Java reference.
+  auto delegate = std::make_unique<android::TabWebContentsDelegateAndroid>(
+      env_, base::android::ScopedJavaLocalRef<jobject>());
+
+  // Set the delegate on the web contents.
+  raw_web_contents->SetDelegate(delegate.get());
+
+  content::RenderFrameHost* main_frame =
+      raw_web_contents->GetPrimaryMainFrame();
+
+  // 1. Without Glic Actor active, it should return false.
+  EXPECT_FALSE(delegate->IsWebContentsCreationOverridden(
+      main_frame, nullptr, content::mojom::WindowContainerType::NORMAL, GURL(),
+      "", GURL()));
+
+  // 2. Start Glic Actor task and attach the tab to it.
+  actor::ActorKeyedService* service =
+      actor::ActorKeyedService::Get(profile_.get());
+  ASSERT_NE(nullptr, service);
+  actor::TaskId task_id = service->CreateTask(
+      actor::TestTaskSourceInfo(), actor::NoEnterprisePolicyChecker());
+  actor::ActorTask* task = service->GetTask(task_id);
+  ASSERT_NE(nullptr, task);
+
+  // Add the tab to the task.
+  MockActorUiTabController mock_controller(*tab);
+  actor::AddTabToTask(*tab, *task);
+
+  // Ensure HasActorTaskPreventingNewWebContents returns true.
+  ASSERT_TRUE(actor::HasActorTaskPreventingNewWebContents(main_frame));
+
+  // 3. Under Glic Actor control, if the frame is NOT sandboxed, it should
+  // override creation.
+  EXPECT_TRUE(delegate->IsWebContentsCreationOverridden(
+      main_frame, nullptr, content::mojom::WindowContainerType::NORMAL, GURL(),
+      "", GURL()));
+
+  // 4. Under Glic Actor control, if the frame IS sandboxed with kTopNavigation,
+  // it should NOT override creation.
+  auto simulator = content::NavigationSimulator::CreateBrowserInitiated(
+      GURL("https://sandboxed.example.com"), raw_web_contents);
+  auto headers =
+      base::MakeRefCounted<net::HttpResponseHeaders>("HTTP/1.1 200 OK");
+  headers->AddHeader("Content-Security-Policy", "sandbox allow-popups");
+  simulator->SetResponseHeaders(headers);
+  simulator->Commit();
+
+  content::RenderFrameHost* sandboxed_frame =
+      raw_web_contents->GetPrimaryMainFrame();
+  ASSERT_TRUE(sandboxed_frame->IsSandboxed(
+      network::mojom::WebSandboxFlags::kTopNavigation));
+
+  EXPECT_FALSE(delegate->IsWebContentsCreationOverridden(
+      sandboxed_frame, nullptr, content::mojom::WindowContainerType::NORMAL,
+      GURL(), "", GURL()));
+}
+
 TEST_F(TabAndroidTest, Getters) {
   TabInterfaceAndroid tab_interface(tab_android_);
   EXPECT_EQ(u"about:blank", tab_interface.GetTitle());
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.