Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactMissing authorization in BrowserTag
DescriptionMissing authorization in BrowserTag
ComponentBrowserTag
Bug ClassLogic Error
Tracker513486883
Fix commitb4f89038dec6 (chromium/src) +105/-45
CISA KEVNot listed
CreditedGoogle
Disclosed2026-08-25

Changed Functions

FunctionChangeNotes
if
chrome/browser/controlled_frame/controlled_frame_apitest.cc
modified
IN_PROC_BROWSER_TEST_F
chrome/browser/controlled_frame/controlled_frame_apitest.cc
modified

Files Changed

  • chrome/browser/controlled_frame/controlled_frame_apitest.cc
  • chrome/browser/extensions/api/tabs/tabs_api.cc
  • chrome/browser/extensions/api/tabs/tabs_api.h
  • chrome/browser/extensions/chrome_extensions_browser_client.cc
  • chrome/browser/extensions/chrome_extensions_browser_client.h
From b4f89038dec62d6ed9f362391a048aa126c419f8 Mon Sep 17 00:00:00 2001
From: Giovanni Pezzino <[email protected]>
Date: Tue, 07 Jul 2026 01:20:54 -0700
Subject: [PATCH] Enforce DisableScreenshots enterprise policy in webview and controlledframe

The captureVisibleRegion API used by <webview> and <controlledframe>
components failed to check the DisableScreenshots enterprise policy
preference. Because WebViewInternalCaptureVisibleRegionFunction resides
in the extensions/ layer, it only checked IsScreenshotRestricted (DLP
restrictions) and omitted global preference checks.

This CL unifies screenshot restriction checks by defining
ScreenshotAccessError in extensions/browser/screenshot_access.h and
updating ExtensionsBrowserClient::IsScreenshotRestricted() and
WebContentsCaptureClient::GetScreenshotAccess() to return
base::expected<void, ScreenshotAccessError>. In
ChromeExtensionsBrowserClient, this method checks both
prefs::kDisableScreenshots and DLP restrictions. Both
WebViewInternalCaptureVisibleRegionFunction::GetScreenshotAccess() and
TabsCaptureVisibleTabFunction::GetScreenshotAccess() now directly
forward to this unified method.

BUG=b:513486883
TAG=agy
TEST=ControlledFrameApiTest.CaptureVisibleRegionDisabledByPolicy

Change-Id: I9ef600a16bf56b78980cb54b3c246eb96a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8036583
Reviewed-by: Andrew Rayskiy <[email protected]>
Reviewed-by: Devlin Cronin <[email protected]>
Commit-Queue: Giovanni Pezzino <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1657764}
---

diff --git a/chrome/browser/controlled_frame/controlled_frame_apitest.cc b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
index 357088d7..3f70244 100644
--- a/chrome/browser/controlled_frame/controlled_frame_apitest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
@@ -25,9 +25,11 @@
 #include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/service_worker_running_info.h"
@@ -479,6 +481,44 @@
   EXPECT_EQ(kEvalSuccessStr, VerifyBackgroundColorIsRed(web_view_guest));
 }
 
+IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest,
+                       CaptureVisibleRegionDisabledByPolicy) {
+  std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app =
+      web_app::IsolatedWebAppBuilder(
+          web_app::ManifestBuilder().AddPermissionsPolicy(
+              network::mojom::PermissionsPolicyFeature::kControlledFrame,
+              /*self=*/true,
+              /*origins=*/{}))
+          .BuildBundle();
+  app->TrustSigningKey();
+  ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+                       app->Install(profile()));
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+  profile()->GetPrefs()->SetBoolean(prefs::kDisableScreenshots, true);
+
+  const std::string script = R"(
+    (async function() {
+      const frame = document.getElementsByTagName('controlledframe')[0];
+      if (!frame || !frame.request) {
+        return 'NO_FRAME';
+      }
+      try {
+        await frame.captureVisibleRegion();
+        return 'UNEXPECTED_SUCCESS';
+      } catch (err) {
+        return err.message ? err.message : String(err);
+      }
+    })();
+  )";
+
+  EXPECT_EQ("Failed to capture webview: screenshot has been disabled",
+            content::EvalJs(app_frame, script));
+}
+
 IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, EnterFullScreenControlledFrame) {
   const GURL embed_url = embedded_https_test_server().GetURL("/index.html");
   const url::Origin embed_origin = url::Origin::Create(embed_url);
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.cc b/chrome/browser/extensions/api/tabs/tabs_api.cc
index d09c83330..a8808f5 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.cc
+++ b/chrome/browser/extensions/api/tabs/tabs_api.cc
@@ -3593,20 +3593,10 @@
 TabsCaptureVisibleTabFunction::TabsCaptureVisibleTabFunction()
     : chrome_details_(this) {}
 
-WebContentsCaptureClient::ScreenshotAccess
+base::expected<void, extensions::ScreenshotAccessError>
 TabsCaptureVisibleTabFunction::GetScreenshotAccess(
     content::WebContents* web_contents) const {
-  PrefService* service =
-      Profile::FromBrowserContext(browser_context())->GetPrefs();
-  if (service->GetBoolean(prefs::kDisableScreenshots)) {
-    return ScreenshotAccess::kDisabledByPreferences;
-  }
-
-  if (ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents)) {
-    return ScreenshotAccess::kDisabledByDlp;
-  }
-
-  return ScreenshotAccess::kEnabled;
+  return ExtensionsBrowserClient::Get()->IsScreenshotRestricted(web_contents);
 }
 
 bool TabsCaptureVisibleTabFunction::ClientAllowsTransparency() {
diff --git a/chrome/browser/extensions/api/tabs/tabs_api.h b/chrome/browser/extensions/api/tabs/tabs_api.h
index 065f696..a5c36f80 100644
--- a/chrome/browser/extensions/api/tabs/tabs_api.h
+++ b/chrome/browser/extensions/api/tabs/tabs_api.h
@@ -489,7 +489,7 @@
   content::WebContents* GetWebContentsForID(int window_id, std::string* error);
 
   // extensions::WebContentsCaptureClient:
-  ScreenshotAccess GetScreenshotAccess(
+  base::expected<void, extensions::ScreenshotAccessError> GetScreenshotAccess(
       content::WebContents* web_contents) const override;
   bool ClientAllowsTransparency() override;
   void OnCaptureSuccess(const SkBitmap& bitmap) override;
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.cc b/chrome/browser/extensions/chrome_extensions_browser_client.cc
index 29c7642..459bf07 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.cc
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.cc
@@ -93,6 +93,7 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/pref_names.h"
 #include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
 #include "components/privacy_sandbox/privacy_sandbox_prefs.h"
 #include "components/proxy_config/proxy_config_pref_names.h"
 #include "components/search_engines/template_url_service.h"
@@ -122,6 +123,7 @@
 #include "extensions/browser/process_manager_delegate.h"
 #include "extensions/browser/safe_browsing_delegate.h"
 #include "extensions/browser/scoped_extension_keep_alive.h"
+#include "extensions/browser/screenshot_access.h"
 #include "extensions/browser/unpacked_installer.h"
 #include "extensions/browser/url_request_util.h"
 #include "extensions/common/extension_id.h"
@@ -814,14 +816,22 @@
   return util::HasIsolatedStorage(extension_id, context);
 }
 
-bool ChromeExtensionsBrowserClient::IsScreenshotRestricted(
+base::expected<void, extensions::ScreenshotAccessError>
+ChromeExtensionsBrowserClient::IsScreenshotRestricted(
     content::WebContents* web_contents) const {
-#if !BUILDFLAG(IS_CHROMEOS)
-  return false;
-#else
-  return policy::DlpContentManager::Get()->IsScreenshotApiRestricted(
-      web_contents);
+  Profile* profile =
+      Profile::FromBrowserContext(web_contents->GetBrowserContext());
+  if (profile->GetPrefs()->GetBoolean(prefs::kDisableScreenshots)) {
+    return base::unexpected(
+        extensions::ScreenshotAccessError::kDisabledByPreferences);
+  }
+#if BUILDFLAG(IS_CHROMEOS)
+  if (policy::DlpContentManager::Get()->IsScreenshotApiRestricted(
+          web_contents)) {
+    return base::unexpected(extensions::ScreenshotAccessError::kDisabledByDlp);
+  }
 #endif
+  return base::ok();
 }
 
 bool ChromeExtensionsBrowserClient::IsValidTabId(
diff --git a/chrome/browser/extensions/chrome_extensions_browser_client.h b/chrome/browser/extensions/chrome_extensions_browser_client.h
index 8e3cbeb..5fa368c 100644
--- a/chrome/browser/extensions/chrome_extensions_browser_client.h
+++ b/chrome/browser/extensions/chrome_extensions_browser_client.h
@@ -12,6 +12,7 @@
 #include "base/functional/callback_forward.h"
 #include "base/lazy_instance.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/types/expected.h"
 #include "base/values.h"
 #include "build/build_config.h"
 #include "build/chromeos_buildflags.h"
@@ -222,8 +223,8 @@
                            const base::FilePath& path) override;
   bool HasIsolatedStorage(const ExtensionId& extension_id,
                           content::BrowserContext* context) override;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/controlled_frame/controlled_frame_apitest.cc b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
index 357088d7..3f70244 100644
--- a/chrome/browser/controlled_frame/controlled_frame_apitest.cc
+++ b/chrome/browser/controlled_frame/controlled_frame_apitest.cc
@@ -25,9 +25,11 @@
 #include "chrome/browser/web_applications/isolated_web_apps/test/isolated_web_app_builder.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/common/chrome_paths.h"
+#include "chrome/common/pref_names.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings.h"
 #include "components/embedder_support/user_agent_utils.h"
+#include "components/prefs/pref_service.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/service_worker_context.h"
 #include "content/public/browser/service_worker_running_info.h"
@@ -479,6 +481,44 @@
   EXPECT_EQ(kEvalSuccessStr, VerifyBackgroundColorIsRed(web_view_guest));
 }
 
+IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest,
+                       CaptureVisibleRegionDisabledByPolicy) {
+  std::unique_ptr<web_app::ScopedBundledIsolatedWebApp> app =
+      web_app::IsolatedWebAppBuilder(
+          web_app::ManifestBuilder().AddPermissionsPolicy(
+              network::mojom::PermissionsPolicyFeature::kControlledFrame,
+              /*self=*/true,
+              /*origins=*/{}))
+          .BuildBundle();
+  app->TrustSigningKey();
+  ASSERT_OK_AND_ASSIGN(web_app::IsolatedWebAppUrlInfo url_info,
+                       app->Install(profile()));
+  content::RenderFrameHost* app_frame = OpenApp(url_info.app_id());
+
+  ASSERT_TRUE(CreateControlledFrame(
+      app_frame, embedded_https_test_server().GetURL("/index.html")));
+
+  profile()->GetPrefs()->SetBoolean(prefs::kDisableScreenshots, true);
+
+  const std::string script = R"(
+    (async function() {
+      const frame = document.getElementsByTagName('controlledframe')[0];
+      if (!frame || !frame.request) {
+        return 'NO_FRAME';
+      }
+      try {
+        await frame.captureVisibleRegion();
+        return 'UNEXPECTED_SUCCESS';
+      } catch (err) {
+        return err.message ? err.message : String(err);
+      }
+    })();
+  )";
+
+  EXPECT_EQ("Failed to capture webview: screenshot has been disabled",
+            content::EvalJs(app_frame, script));
+}
+
 IN_PROC_BROWSER_TEST_F(ControlledFrameApiTest, EnterFullScreenControlledFrame) {
   const GURL embed_url = embedded_https_test_server().GetURL("/index.html");
   const url::Origin embed_origin = url::Origin::Create(embed_url);
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.