Low chrome Authorization Error 🔧 Commit mapped

Overview

Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in DevTools
DescriptionIncorrect authorization in DevTools
ComponentDevTools
Sub-componentDevtools
Bug ClassAuthorization Error
Tracker513383360
Fix commit319af5bb13c8 (chromium/src) +129/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-10-06

Changed Functions

FunctionChangeNotes
DevToolsAllowedByCommandLineSwitch
chrome/browser/devtools/devtools_browsertest.cc
modified

Files Changed

  • chrome/browser/devtools/devtools_browsertest.cc
  • chrome/browser/devtools/devtools_availability_checker.cc
From 319af5bb13c8bfa9f7057f74f952217acf9c12af Mon Sep 17 00:00:00 2001
From: Jack Franklin <jacktfranklin@google.com>
Date: Tue, 01 Sep 2026 06:45:19 -0700
Subject: [PATCH] devtools: Enforce extension policy at target level for subframes

Bug: When an allowlisted page embeds a restricted extension subframe
(e.g. a force-installed enterprise extension):
* IsInspectionAllowed(DevToolsAgentHost*) fell back to the parent WebContents without checking extension policy on the subframe target.
* Because the parent page was allowlisted, the extension subframe target inherited allowlist status, bypassing the restricted extension policy.

Fix: Enforce extension policy directly in
IsInspectionAllowed(DevToolsAgentHost*) for chrome-extension:// targets
before falling back to WebContents. This blocks DevTools on restricted
extension subframe targets while leaving the allowlisted parent tab
inspectable.

Control flow in IsInspectionAllowed(Profile*, DevToolsAgentHost*):

Prior to this CL: 1. Check URL blocklist for agent_host->GetURL()
(passes, as the extension URL is not blocklisted). 2. Fall back to
IsInspectionAllowed(WebContents*) because no extension check existed on
agent_host.
   * This is the bug: agent_host->GetWebContents() evaluates the parent tab's allowlisted URL, which early-returns true and incorrectly allows the extension subframe target.

With this CL: 1. Check URL blocklist for agent_host->GetURL(). 2. [NEW]
For chrome-extension:// targets, evaluate extension policy via
IsInspectionAllowed(Profile*, const Extension*) (returns false if
restricted). 3. Fall back to IsInspectionAllowed(WebContents*).

TAG=agy
CONV=3e995ef1-3928-4e76-b6a4-193b1360f070

Bug: 513383360
Change-Id: I7e74f5fde027bd7b7df49d73a3007eff7361c77f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8296678
Auto-Submit: Jack Franklin <jacktfranklin@chromium.org>
Commit-Queue: Jack Franklin <jacktfranklin@chromium.org>
Reviewed-by: Kateryna Prokopenko <kprokopenko@chromium.org>
Reviewed-by: Nikolay Vitkov <nvitkov@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1689811}
---
diff --git a/chrome/browser/devtools/devtools_availability_checker.cc b/chrome/browser/devtools/devtools_availability_checker.cc
index 752e82f..3e4d71a 100644
--- a/chrome/browser/devtools/devtools_availability_checker.cc
+++ b/chrome/browser/devtools/devtools_availability_checker.cc
@@ -93,6 +93,22 @@
       return false;
     }
 
+#if BUILDFLAG(ENABLE_EXTENSIONS_CORE)
+    // Enforce extension policy at the target level before falling back to the
+    // parent WebContents.
+    if (target_url.SchemeIs(extensions::kExtensionScheme)) {
+      if (auto* registry = extensions::ExtensionRegistry::Get(profile)) {
+        if (const extensions::Extension* extension =
+                registry->GetInstalledExtension(
+                    std::string(target_url.host()))) {
+          if (!IsInspectionAllowed(profile, extension)) {
+            return false;
+          }
+        }
+      }
+    }
+#endif
+
     if (content::WebContents* web_contents = agent_host->GetWebContents()) {
       return IsInspectionAllowed(profile, web_contents);
     }
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index 7db1b09..f5298ac 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -3264,6 +3264,119 @@
   auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
   EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
 }
+
+IN_PROC_BROWSER_TEST_F(
+    DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
+    AllowlistedMainFrameWithRestrictedExtensionIframeBlocked) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL allowed_url(embedded_test_server()->GetURL("/title1.html"));
+
+  base::ListValue allowlist;
+  allowlist.Append(allowed_url.spec());
+  profile()->GetPrefs()->SetList(prefs::kDeveloperToolsAvailabilityAllowlist,
+                                 std::move(allowlist));
+
+  base::FilePath extension_path =
+      test_data_dir_.AppendASCII("web_accessible_resources")
+          .AppendASCII("subframe");
+  const Extension* extension = InstallExtension(
+      extension_path, 1, ManifestLocation::kExternalPolicyDownload);
+  ASSERT_TRUE(extension);
+  std::string extension_id = extension->id();
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  GURL extension_url("chrome-extension://" + extension_id +
+                     "/web_accessible_page.html");
+  content::TestNavigationObserver nav_observer(extension_url);
+  nav_observer.WatchExistingWebContents();
+  ASSERT_TRUE(content::ExecJs(
+      web_contents->GetPrimaryMainFrame(),
+      content::JsReplace("let iframe = document.createElement('iframe');"
+                         "iframe.src = $1;"
+                         "document.body.appendChild(iframe);",
+                         extension_url)));
+  nav_observer.Wait();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  DevToolsWindow::OpenDevToolsWindow(web_contents,
+                                     DevToolsOpenedByAction::kUnknown);
+  auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
+  EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+
+  scoped_refptr<content::DevToolsAgentHost> subframe_agent_host;
+  for (const auto& host : content::DevToolsAgentHost::GetOrCreateAll()) {
+    if (host->GetURL() == extension_url) {
+      subframe_agent_host = host;
+      break;
+    }
+  }
+  ASSERT_TRUE(subframe_agent_host);
+  EXPECT_FALSE(IsInspectionAllowed(profile(), subframe_agent_host.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
+    AllowlistedMainFrameWithRestrictedExtensionIframeAllowedWhenPolicyAllowed) {
+  AllowDevTools(browser_window_interface());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL allowed_url(embedded_test_server()->GetURL("/title1.html"));
+
+  base::ListValue allowlist;
+  allowlist.Append("chrome-extension://*");
+  allowlist.Append(allowed_url.spec());
+  profile()->GetPrefs()->SetList(prefs::kDeveloperToolsAvailabilityAllowlist,
+                                 std::move(allowlist));
+
+  base::FilePath extension_path =
+      test_data_dir_.AppendASCII("web_accessible_resources")
+          .AppendASCII("subframe");
+  const Extension* extension = InstallExtension(
+      extension_path, 1, ManifestLocation::kExternalPolicyDownload);
+  ASSERT_TRUE(extension);
+  std::string extension_id = extension->id();
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  GURL extension_url("chrome-extension://" + extension_id +
+                     "/web_accessible_page.html");
+  content::TestNavigationObserver nav_observer(extension_url);
+  nav_observer.WatchExistingWebContents();
+  ASSERT_TRUE(content::ExecJs(
+      web_contents->GetPrimaryMainFrame(),
+      content::JsReplace("let iframe = document.createElement('iframe');"
+                         "iframe.src = $1;"
+                         "document.body.appendChild(iframe);",
+                         extension_url)));
+  nav_observer.Wait();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  DevToolsWindow::OpenDevToolsWindow(web_contents,
+                                     DevToolsOpenedByAction::kUnknown);
+  auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
+  EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+
+  scoped_refptr<content::DevToolsAgentHost> subframe_agent_host;
+  for (const auto& host : content::DevToolsAgentHost::GetOrCreateAll()) {
+    if (host->GetURL() == extension_url) {
+      subframe_agent_host = host;
+      break;
+    }
+  }
+  ASSERT_TRUE(subframe_agent_host);
+  EXPECT_TRUE(IsInspectionAllowed(profile(), subframe_agent_host.get()));
+}
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 class DevToolsAllowedByCommandLineSwitch
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/devtools/devtools_browsertest.cc b/chrome/browser/devtools/devtools_browsertest.cc
index 7db1b09..f5298ac 100644
--- a/chrome/browser/devtools/devtools_browsertest.cc
+++ b/chrome/browser/devtools/devtools_browsertest.cc
@@ -3264,6 +3264,119 @@
   auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
   EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
 }
+
+IN_PROC_BROWSER_TEST_F(
+    DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
+    AllowlistedMainFrameWithRestrictedExtensionIframeBlocked) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL allowed_url(embedded_test_server()->GetURL("/title1.html"));
+
+  base::ListValue allowlist;
+  allowlist.Append(allowed_url.spec());
+  profile()->GetPrefs()->SetList(prefs::kDeveloperToolsAvailabilityAllowlist,
+                                 std::move(allowlist));
+
+  base::FilePath extension_path =
+      test_data_dir_.AppendASCII("web_accessible_resources")
+          .AppendASCII("subframe");
+  const Extension* extension = InstallExtension(
+      extension_path, 1, ManifestLocation::kExternalPolicyDownload);
+  ASSERT_TRUE(extension);
+  std::string extension_id = extension->id();
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  GURL extension_url("chrome-extension://" + extension_id +
+                     "/web_accessible_page.html");
+  content::TestNavigationObserver nav_observer(extension_url);
+  nav_observer.WatchExistingWebContents();
+  ASSERT_TRUE(content::ExecJs(
+      web_contents->GetPrimaryMainFrame(),
+      content::JsReplace("let iframe = document.createElement('iframe');"
+                         "iframe.src = $1;"
+                         "document.body.appendChild(iframe);",
+                         extension_url)));
+  nav_observer.Wait();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  DevToolsWindow::OpenDevToolsWindow(web_contents,
+                                     DevToolsOpenedByAction::kUnknown);
+  auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
+  EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+
+  scoped_refptr<content::DevToolsAgentHost> subframe_agent_host;
+  for (const auto& host : content::DevToolsAgentHost::GetOrCreateAll()) {
+    if (host->GetURL() == extension_url) {
+      subframe_agent_host = host;
+      break;
+    }
+  }
+  ASSERT_TRUE(subframe_agent_host);
+  EXPECT_FALSE(IsInspectionAllowed(profile(), subframe_agent_host.get()));
+}
+
+IN_PROC_BROWSER_TEST_F(
+    DevToolsDisallowedForForceInstalledExtensionsPolicyTest,
+    AllowlistedMainFrameWithRestrictedExtensionIframeAllowedWhenPolicyAllowed) {
+  AllowDevTools(browser_window_interface());
+
+  ASSERT_TRUE(embedded_test_server()->Start());
+  GURL allowed_url(embedded_test_server()->GetURL("/title1.html"));
+
+  base::ListValue allowlist;
+  allowlist.Append("chrome-extension://*");
+  allowlist.Append(allowed_url.spec());
+  profile()->GetPrefs()->SetList(prefs::kDeveloperToolsAvailabilityAllowlist,
+                                 std::move(allowlist));
+
+  base::FilePath extension_path =
+      test_data_dir_.AppendASCII("web_accessible_resources")
+          .AppendASCII("subframe");
+  const Extension* extension = InstallExtension(
+      extension_path, 1, ManifestLocation::kExternalPolicyDownload);
+  ASSERT_TRUE(extension);
+  std::string extension_id = extension->id();
+
+  ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), allowed_url));
+  content::WebContents* web_contents =
+      browser()->tab_strip_model()->GetActiveWebContents();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  GURL extension_url("chrome-extension://" + extension_id +
+                     "/web_accessible_page.html");
+  content::TestNavigationObserver nav_observer(extension_url);
+  nav_observer.WatchExistingWebContents();
+  ASSERT_TRUE(content::ExecJs(
+      web_contents->GetPrimaryMainFrame(),
+      content::JsReplace("let iframe = document.createElement('iframe');"
+                         "iframe.src = $1;"
+                         "document.body.appendChild(iframe);",
+                         extension_url)));
+  nav_observer.Wait();
+
+  EXPECT_TRUE(DevToolsWindow::AllowDevToolsFor(profile(), web_contents));
+
+  DevToolsWindow::OpenDevToolsWindow(web_contents,
+                                     DevToolsOpenedByAction::kUnknown);
+  auto agent_host = GetOrCreateDevToolsHostForWebContents(web_contents);
+  EXPECT_TRUE(DevToolsWindow::FindDevToolsWindow(agent_host.get()));
+
+  scoped_refptr<content::DevToolsAgentHost> subframe_agent_host;
+  for (const auto& host : content::DevToolsAgentHost::GetOrCreateAll()) {
+    if (host->GetURL() == extension_url) {
+      subframe_agent_host = host;
+      break;
+    }
+  }
+  ASSERT_TRUE(subframe_agent_host);
+  EXPECT_TRUE(IsInspectionAllowed(profile(), subframe_agent_host.get()));
+}
 #endif  // !BUILDFLAG(IS_ANDROID)
 
 class DevToolsAllowedByCommandLineSwitch
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.