Medium chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Glic
DescriptionInsufficient policy enforcement in Glic
ComponentGlic
Bug ClassLogic Error
Tracker501820076
Fix commitb169fa305dbf (chromium/src) +165/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-30

Changed Functions

FunctionChangeNotes
TestToolRequest
chrome/browser/actor/ui/actor_popup_browsertest.cc
modified
requires_opening_web_contents_
chrome/browser/actor/ui/actor_popup_browsertest.cc
modified
ActorPopupBrowserTest
chrome/browser/actor/ui/actor_popup_browsertest.cc
modified
ActorPopupBrowserTest
chrome/browser/actor/ui/actor_popup_browsertest.cc
modified

Files Changed

  • chrome/browser/actor/ui/BUILD.gn
  • chrome/browser/actor/ui/actor_popup_browsertest.cc
  • chrome/browser/ui/browser.cc
From b169fa305dbf0a4c56351a24b4275f34aa10e242 Mon Sep 17 00:00:00 2001
From: Johann Hofmann <johannhof@chromium.org>
Date: Fri, 08 May 2026 07:33:40 -0700
Subject: [PATCH] Respect sandbox flags in Actor popup interception

When an Actor task restricts new window creation, the browser intercepts window opening requests and redirects them to the same tab. This CL ensures that the opener's sandbox flags (specifically top-level navigation restrictions) are respected during this redirection.

Added a regression test in actor_util_browsertest.cc.

Bug: 501820076
Change-Id: I1cb0295c64532934b5d192f2327f3b013fdc058e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7813777
Reviewed-by: Dana Fried <dfried@chromium.org>
Auto-Submit: Johann Hofmann <johannhof@chromium.org>
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Commit-Queue: Johann Hofmann <johannhof@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1627639}
---

diff --git a/chrome/browser/actor/ui/BUILD.gn b/chrome/browser/actor/ui/BUILD.gn
index fea1fde5..a021959 100644
--- a/chrome/browser/actor/ui/BUILD.gn
+++ b/chrome/browser/actor/ui/BUILD.gn
@@ -207,6 +207,7 @@
     sources = [
       "actor_overlay_browsertest.cc",
       "actor_overlay_ui_browsertest.cc",
+      "actor_popup_browsertest.cc",
       "actor_ui_tab_controller_browsertest.cc",
       "dom_node_geometry_browsertest.cc",
       "handoff_button_controller_pixel_test.cc",
diff --git a/chrome/browser/actor/ui/actor_popup_browsertest.cc b/chrome/browser/actor/ui/actor_popup_browsertest.cc
new file mode 100644
index 0000000..336633d
--- /dev/null
+++ b/chrome/browser/actor/ui/actor_popup_browsertest.cc
@@ -0,0 +1,154 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/actor_task_metadata.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/actor/actor_util.h"
+#include "chrome/browser/actor/tools/wait_tool_request.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/actor/core/actor_features.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace actor {
+
+namespace {
+
+// A tool request that behaves like a Wait action but allows us to control
+// RequiresOpeningWebContents.
+class TestToolRequest : public WaitToolRequest {
+ public:
+  explicit TestToolRequest(bool requires_opening_web_contents)
+      : WaitToolRequest(base::Hours(1)),
+        requires_opening_web_contents_(requires_opening_web_contents) {}
+
+  bool RequiresOpeningWebContents() const override {
+    return requires_opening_web_contents_;
+  }
+
+ private:
+  const bool requires_opening_web_contents_;
+};
+
+}  // namespace
+
+class ActorPopupBrowserTest : public InProcessBrowserTest {
+ public:
+  ActorPopupBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kGlic, features::kGlicActor},
+        /*disabled_features=*/{features::kGlicWarming});
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+ protected:
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  content::RenderFrameHost* main_frame() {
+    return web_contents()->GetPrimaryMainFrame();
+  }
+
+  ActorKeyedService* actor_keyed_service() {
+    return ActorKeyedService::Get(browser()->profile());
+  }
+
+  void AddTabToTask(tabs::TabHandle tab_handle, ActorTask* actor_task) {
+    base::test::TestFuture<mojom::ActionResultPtr> add_tab_future;
+    actor_task->AddTab(tab_handle, /*stop_task_on_detach=*/true,
+                       add_tab_future.GetCallback());
+    ASSERT_TRUE(add_tab_future.Get());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests that a sandboxed iframe without allow-top-navigation opens a new popup
+// window instead of trying to navigate the top-level document (which is blocked
+// by sandbox) when Actor popup interception is active.
+IN_PROC_BROWSER_TEST_F(ActorPopupBrowserTest,
+                       SandboxedIframeOpensPopupInsteadOfSameTabNavigation) {
+  TaskId task_id = actor_keyed_service()->CreateTask(
+      TestTaskSourceInfo(), NoEnterprisePolicyChecker());
+  const GURL initial_url = embedded_test_server()->GetURL("/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  ActorTask* task = actor_keyed_service()->GetTask(task_id);
+  tabs::TabInterface* tab = browser()->tab_strip_model()->GetActiveTab();
+  ASSERT_TRUE(tab);
+  AddTabToTask(tab->GetHandle(), task);
+
+  auto test_request = std::make_unique<TestToolRequest>(
+      /*requires_opening_web_contents=*/false);
+
+  PerformActionsFuture result_future;
+  actor_keyed_service()->PerformActions(
+      task_id,
+      ToRequestList<std::unique_ptr<ToolRequest>>(std::move(test_request)),
+      ActorTaskMetadata(), result_future.GetCallback());
+
+  base::RunLoop run_loop;
+  ExecutionEngineStateWaiter waiter(run_loop.QuitClosure(),
+                                    task->GetExecutionEngine(),
+                                    ExecutionEngine::State::kToolInvoke);
+  run_loop.Run();
+
+  EXPECT_TRUE(actor::HasActorTaskPreventingNewWebContents(main_frame()));
+
+  // Create a sandboxed iframe.
+  EXPECT_TRUE(content::ExecJs(main_frame(),
+                              "var iframe = document.createElement('iframe');"
+                              "iframe.sandbox = 'allow-scripts allow-popups';"
+                              "iframe.src = '/title1.html';"
+                              "document.body.appendChild(iframe);"));
+
+  // Wait for the iframe to load.
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+  content::RenderFrameHost* iframe_rfh = content::ChildFrameAt(main_frame(), 0);
+  ASSERT_TRUE(iframe_rfh);
+
+  const GURL new_url = embedded_test_server()->GetURL("/title2.html");
+
+  ui_test_utils::BrowserCreatedObserver popup_observer;
+
+  // Try to open a popup from the sandboxed iframe. We expect it to open in a
+  // new browser window (popup) rather than navigating the current tab.
+  EXPECT_TRUE(content::ExecJs(
+      iframe_rfh,
+      base::StringPrintf("window.open('%s', '_blank', 'width=100,height=100')",
+                         new_url.spec().c_str())));
+
+  // Wait for the new popup window to open.
+  Browser* popup_browser = popup_observer.Wait();
+  ASSERT_TRUE(popup_browser);
+
+  // Verify that the new window has the correct URL.
+  content::WebContents* popup_contents =
+      popup_browser->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
+  EXPECT_EQ(popup_contents->GetLastCommittedURL(), new_url);
+
+  // Verify that the original main frame URL did NOT change.
+  EXPECT_EQ(web_contents()->GetLastCommittedURL(), initial_url);
+}
+
+}  // namespace actor
diff --git a/chrome/browser/ui/browser.cc b/chrome/browser/ui/browser.cc
index 8ad020f..80284fee 100644
--- a/chrome/browser/ui/browser.cc
+++ b/chrome/browser/ui/browser.cc
@@ -243,6 +243,7 @@
 #include "extensions/common/extension.h"
 #include "extensions/common/manifest_handlers/background_info.h"
 #include "net/base/filename_util.h"
+#include "services/network/public/mojom/web_sandbox_flags.mojom.h"
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/actor/ui/actor_popup_browsertest.cc b/chrome/browser/actor/ui/actor_popup_browsertest.cc
new file mode 100644
index 0000000..336633d
--- /dev/null
+++ b/chrome/browser/actor/ui/actor_popup_browsertest.cc
@@ -0,0 +1,154 @@
+// Copyright 2026 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/scoped_feature_list.h"
+#include "chrome/browser/actor/actor_keyed_service.h"
+#include "chrome/browser/actor/actor_task_metadata.h"
+#include "chrome/browser/actor/actor_test_util.h"
+#include "chrome/browser/actor/actor_util.h"
+#include "chrome/browser/actor/tools/wait_tool_request.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/common/chrome_features.h"
+#include "chrome/test/base/in_process_browser_test.h"
+#include "chrome/test/base/ui_test_utils.h"
+#include "components/actor/core/actor_features.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+
+namespace actor {
+
+namespace {
+
+// A tool request that behaves like a Wait action but allows us to control
+// RequiresOpeningWebContents.
+class TestToolRequest : public WaitToolRequest {
+ public:
+  explicit TestToolRequest(bool requires_opening_web_contents)
+      : WaitToolRequest(base::Hours(1)),
+        requires_opening_web_contents_(requires_opening_web_contents) {}
+
+  bool RequiresOpeningWebContents() const override {
+    return requires_opening_web_contents_;
+  }
+
+ private:
+  const bool requires_opening_web_contents_;
+};
+
+}  // namespace
+
+class ActorPopupBrowserTest : public InProcessBrowserTest {
+ public:
+  ActorPopupBrowserTest() {
+    scoped_feature_list_.InitWithFeatures(
+        /*enabled_features=*/{features::kGlic, features::kGlicActor},
+        /*disabled_features=*/{features::kGlicWarming});
+  }
+
+  void SetUpOnMainThread() override {
+    InProcessBrowserTest::SetUpOnMainThread();
+    ASSERT_TRUE(embedded_test_server()->Start());
+  }
+
+ protected:
+  content::WebContents* web_contents() {
+    return browser()->tab_strip_model()->GetActiveWebContents();
+  }
+
+  content::RenderFrameHost* main_frame() {
+    return web_contents()->GetPrimaryMainFrame();
+  }
+
+  ActorKeyedService* actor_keyed_service() {
+    return ActorKeyedService::Get(browser()->profile());
+  }
+
+  void AddTabToTask(tabs::TabHandle tab_handle, ActorTask* actor_task) {
+    base::test::TestFuture<mojom::ActionResultPtr> add_tab_future;
+    actor_task->AddTab(tab_handle, /*stop_task_on_detach=*/true,
+                       add_tab_future.GetCallback());
+    ASSERT_TRUE(add_tab_future.Get());
+  }
+
+ private:
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+// Tests that a sandboxed iframe without allow-top-navigation opens a new popup
+// window instead of trying to navigate the top-level document (which is blocked
+// by sandbox) when Actor popup interception is active.
+IN_PROC_BROWSER_TEST_F(ActorPopupBrowserTest,
+                       SandboxedIframeOpensPopupInsteadOfSameTabNavigation) {
+  TaskId task_id = actor_keyed_service()->CreateTask(
+      TestTaskSourceInfo(), NoEnterprisePolicyChecker());
+  const GURL initial_url = embedded_test_server()->GetURL("/title1.html");
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), initial_url));
+
+  ActorTask* task = actor_keyed_service()->GetTask(task_id);
+  tabs::TabInterface* tab = browser()->tab_strip_model()->GetActiveTab();
+  ASSERT_TRUE(tab);
+  AddTabToTask(tab->GetHandle(), task);
+
+  auto test_request = std::make_unique<TestToolRequest>(
+      /*requires_opening_web_contents=*/false);
+
+  PerformActionsFuture result_future;
+  actor_keyed_service()->PerformActions(
+      task_id,
+      ToRequestList<std::unique_ptr<ToolRequest>>(std::move(test_request)),
+      ActorTaskMetadata(), result_future.GetCallback());
+
+  base::RunLoop run_loop;
+  ExecutionEngineStateWaiter waiter(run_loop.QuitClosure(),
+                                    task->GetExecutionEngine(),
+                                    ExecutionEngine::State::kToolInvoke);
+  run_loop.Run();
+
+  EXPECT_TRUE(actor::HasActorTaskPreventingNewWebContents(main_frame()));
+
+  // Create a sandboxed iframe.
+  EXPECT_TRUE(content::ExecJs(main_frame(),
+                              "var iframe = document.createElement('iframe');"
+                              "iframe.sandbox = 'allow-scripts allow-popups';"
+                              "iframe.src = '/title1.html';"
+                              "document.body.appendChild(iframe);"));
+
+  // Wait for the iframe to load.
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents()));
+
+  content::RenderFrameHost* iframe_rfh = content::ChildFrameAt(main_frame(), 0);
+  ASSERT_TRUE(iframe_rfh);
+
+  const GURL new_url = embedded_test_server()->GetURL("/title2.html");
+
+  ui_test_utils::BrowserCreatedObserver popup_observer;
+
+  // Try to open a popup from the sandboxed iframe. We expect it to open in a
+  // new browser window (popup) rather than navigating the current tab.
+  EXPECT_TRUE(content::ExecJs(
+      iframe_rfh,
+      base::StringPrintf("window.open('%s', '_blank', 'width=100,height=100')",
+                         new_url.spec().c_str())));
+
+  // Wait for the new popup window to open.
+  Browser* popup_browser = popup_observer.Wait();
+  ASSERT_TRUE(popup_browser);
+
+  // Verify that the new window has the correct URL.
+  content::WebContents* popup_contents =
+      popup_browser->tab_strip_model()->GetActiveWebContents();
+  EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
+  EXPECT_EQ(popup_contents->GetLastCommittedURL(), new_url);
+
+  // Verify that the original main frame URL did NOT change.
+  EXPECT_EQ(web_contents()->GetLastCommittedURL(), initial_url);
+}
+
+}  // namespace actor
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.