Low watch CVSS 3.1 chrome Policy Bypass 🔧 Commit mapped

Overview

Low
Severity
3.1
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in USB
DescriptionInsufficient policy enforcement in USB
ComponentUSB
Sub-componentPermissions
Bug ClassPolicy Bypass
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker521623907
Fix commit4fe44fddb85c (chromium/src) +208/-37
CWECWE-346 (Origin validation error)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-06-09 (per vendor advisory)
Fix landed2026-06-23 (public git)
Disclosed2026-07-29
Patch gap36 days — fix public in source before the advisory

Background

Guest context
a <webview>, <controlledframe>, or SlimWebView embedded frame that runs in an isolated, non-default StoragePartition for content separation.
Device permission scope
USB, Bluetooth, Serial, and File System Access grants are stored profile-wide, so any context sharing the profile inherits them regardless of its StoragePartition.
`WebViewGuest::FromRenderFrameHost`
the legacy, extension- and platform-specific check used to detect guest frames, compiled out when ENABLE_EXTENSIONS_CORE/ENABLE_GUEST_VIEW are disabled (e.g. Android SlimWebView).
`GetSecurityPrincipal().IsGuest()`
the platform-independent content::SiteInstance predicate that reports whether a frame is a guest without depending on the extensions build flags.

Root Cause Analysis

The device-permission delegates guarded guest access with extensions::WebViewGuest::FromRenderFrameHost(rfh), wrapped in #if BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW). On builds where those flags are off — notably Android SlimWebView — the entire guard was compiled out, so MayUseBluetooth and its USB/Serial/FSA siblings fell through to a weaker StoragePartition/URL-scheme fallback that could be bypassed by navigating to a non-http(s) scheme such as about:blank. Because device permissions are scoped profile-wide, a guest that slipped past the check would share the profile’s USB/Bluetooth/Serial/FSA grants, violating the isolation invariant that a non-default StoragePartition must not reach profile-level device permissions.

The fix replaces the extension-specific guard with rfh->GetSiteInstance()->GetSecurityPrincipal().IsGuest(), which identifies every guest context on every platform independent of build flags and returns denial before the fallback logic runs. The desktop <webview> File System Access compatibility exception is deliberately retained by keeping its prior conditional block ahead of the new IsGuest() check.

Key insight
The single mistake was gating a security-critical isolation check on the extensions/guest-view build flags and an extension-specific type (WebViewGuest), so the check vanished exactly where guests still existed (Android SlimWebView); the fix moves detection to the platform-independent GetSecurityPrincipal().IsGuest() so the guard is always compiled in and always fires.

Attack Path

  1. Embed a guest An attacker-influenced app or page instantiates a guest context (<controlledframe> or Android SlimWebView) running in a non-default StoragePartition under the victim’s profile.
  2. Evade the compiled-out check On Android the WebViewGuest::FromRenderFrameHost guard is compiled out, so no guest detection runs for MayUseBluetooth/USB/Serial/FSA.
  3. Defeat the scheme fallback The guest navigates to a non-http(s) URL such as about:blank to slip past the URL-scheme-based StoragePartition fallback that would otherwise deny access.
  4. Inherit profile grants Since device permissions are profile-scoped, the guest now reaches USB, Bluetooth, Serial, and File System Access grants belonging to the rest of the profile.
  5. Cross the isolation boundary The guest exercises those APIs against devices/files the embedder never authorized for that partition, completing the isolation bypass.

Impact Assessment

An attacker operating inside a guest context gains access to profile-scoped USB, Bluetooth, Serial, and File System Access permissions that should be walled off by the guest’s isolated StoragePartition, breaking the intended device-permission isolation boundary. This is reachable from the browser process’s permission-delegate path and is most severe on Android, where the extensions check is absent and the about:blank scheme bypass applies. Preconditions are the ability to embed or drive a guest context (<webview>/<controlledframe>/SlimWebView) within a profile that already holds device/FSA grants.

Changed Functions

FunctionChangeNotes
source_set
chrome/browser/bluetooth/BUILD.gn
modified
ChromeBluetoothDelegateTest
chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
modified
TEST_F
chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
modified

Files Changed

  • chrome/browser/bluetooth/BUILD.gn
  • chrome/browser/bluetooth/chrome_bluetooth_delegate.cc
  • chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
  • chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
  • chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc

Audit Directions

  • Build-flag-gated security checks
    Search for isolation or permission guards wrapped in #if BUILDFLAG(ENABLE_EXTENSIONS_CORE)/ENABLE_GUEST_VIEW; a guard that disappears on a build configuration where the threat still exists is a latent bypass.
  • Extension-specific guest detection
    Flag uses of WebViewGuest::FromRenderFrameHost or other extensions::-namespaced guest tests in cross-platform delegates, and prefer GetSecurityPrincipal().IsGuest() on the SiteInstance.
  • Profile-scoped grants in isolated partitions
    Audit other profile-wide permission stores (device APIs, notifications, storage) reached from non-default StoragePartition contexts, especially where URL-scheme fallbacks can be defeated by about:blank or similar schemes.
From 4fe44fddb85c9d62ae179e0a580d238bdb27236d Mon Sep 17 00:00:00 2001
From: Rob Pitkin <robpitkin@google.com>
Date: Tue, 23 Jun 2026 14:38:30 -0700
Subject: [PATCH] permissions: Block guest contexts from device APIs

Because device permissions are scoped profile-wide, guest contexts (like
<webview>, <controlledframe>, and SlimWebView) that run in non-default
StoragePartitions would share device/FSA permissions with the rest of
the profile. This could lead to isolation bypasses, especially on
Android where the extensions system check is compiled out and fallback
logic based on URL schemes can be bypassed (e.g., by navigating to
about:blank).

This CL replaces the platform-conditional and extension-specific check
(WebViewGuest::FromRenderFrameHost) with the platform-independent
GetSiteInstance()->GetSecurityPrincipal().IsGuest() check. This ensures
that all guest contexts, including SlimWebView on Android, are properly
identified and blocked from accessing USB, Bluetooth, Serial, and File
System Access APIs. For File System Access on desktop, the legacy
<webview> compatibility exception is preserved by keeping the existing
conditional block.

Bug: 521623907
Change-Id: I5b2a1bcf359f3668949ee09485fbc47251a7c736
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7953106
Commit-Queue: Rob Pitkin <robpitkin@chromium.org>
Reviewed-by: Antonio Sartori <antoniosartori@chromium.org>
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1651293}
---

diff --git a/chrome/browser/bluetooth/BUILD.gn b/chrome/browser/bluetooth/BUILD.gn
index 49918f23..0ec41931 100644
--- a/chrome/browser/bluetooth/BUILD.gn
+++ b/chrome/browser/bluetooth/BUILD.gn
@@ -102,7 +102,10 @@
 
 source_set("unit_tests") {
   testonly = true
-  sources = [ "bluetooth_chooser_context_unittest.cc" ]
+  sources = [
+    "bluetooth_chooser_context_unittest.cc",
+    "chrome_bluetooth_delegate_unittest.cc",
+  ]
   deps = [
     ":bluetooth",
     "//chrome/browser/content_settings:content_settings_factory",
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc
index 8724f87..d13b97618 100644
--- a/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc
+++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate.cc
@@ -11,31 +11,25 @@
 #include "chrome/browser/profiles/profile.h"
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/common/content_settings_types.h"
-#include "components/guest_view/buildflags/buildflags.h"
 #include "components/permissions/bluetooth_delegate_impl.h"
 #include "components/permissions/content_setting_permission_context_base.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_frame_host.h"
-#include "extensions/buildflags/buildflags.h"
+#include "content/public/browser/security_principal.h"
+#include "content/public/browser/site_instance.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
-#include "extensions/browser/guest_view/web_view/web_view_guest.h"
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
-
 ChromeBluetoothDelegate::ChromeBluetoothDelegate(std::unique_ptr<Client> client)
     : permissions::BluetoothDelegateImpl(std::move(client)) {}
 
 bool ChromeBluetoothDelegate::MayUseBluetooth(content::RenderFrameHost* rfh) {
-#if BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
-  // Because permission is scoped to profile, <webview> and <controlledframe>,
-  // despite having isolated StoragePartition, will share bluetooth permission
-  // with the rest of the profile. Therefore bluetooth is not allowed in these
-  // contexts.
-  if (extensions::WebViewGuest::FromRenderFrameHost(rfh)) {
+  // Because permission is scoped to the profile, guest contexts (like
+  // <webview>, <controlledframe>, and SlimWebView), despite having isolated
+  // StoragePartitions, would share Bluetooth permissions with the rest of the
+  // profile. Therefore, Bluetooth is not allowed in these contexts.
+  if (rfh->GetSiteInstance()->GetSecurityPrincipal().IsGuest()) {
     return false;
   }
-#endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
 
   // Disable any other non-default StoragePartition contexts, unless it has a
   // non-http/https scheme.
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
new file mode 100644
index 0000000..726601a
--- /dev/null
+++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
@@ -0,0 +1,70 @@
+// 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 "chrome/browser/bluetooth/chrome_bluetooth_delegate.h"
+
+#include <memory>
+
+#include "chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition_config.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+class ChromeBluetoothDelegateTest : public ChromeRenderViewHostTestHarness {
+ public:
+  std::unique_ptr<content::WebContents> CreateGuestWebContents(
+      const GURL& url) {
+    const content::StoragePartitionConfig kGuestConfig =
+        content::StoragePartitionConfig::Create(
+            profile(), "test_partition", "guest_partition", /*in_memory=*/true);
+    scoped_refptr<content::SiteInstance> guest_instance =
+        content::SiteInstance::CreateForGuest(profile(), kGuestConfig);
+    std::unique_ptr<content::WebContents> guest_contents =
+        content::WebContentsTester::CreateTestWebContents(profile(),
+                                                          guest_instance);
+    content::WebContentsTester::For(guest_contents.get())
+        ->NavigateAndCommit(url);
+    return guest_contents;
+  }
+};
+
+TEST_F(ChromeBluetoothDelegateTest, BlocksGuestViews) {
+  ChromeBluetoothDelegate bluetooth_delegate(
+      std::make_unique<ChromeBluetoothDelegateImplClient>());
+
+  // 1. Test HTTPS Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("https://example.com/"));
+    EXPECT_FALSE(
+        bluetooth_delegate.MayUseBluetooth(guest->GetPrimaryMainFrame()));
+  }
+
+  // 2. Test about:blank Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("about:blank"));
+    EXPECT_FALSE(
+        bluetooth_delegate.MayUseBluetooth(guest->GetPrimaryMainFrame()));
+  }
+
+  // 3. Test Non-Guest (should be allowed by default)
+  {
+    std::unique_ptr<content::WebContents> non_guest =
+        content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+    content::WebContentsTester::For(non_guest.get())
+        ->NavigateAndCommit(GURL("https://example.com/"));
+    EXPECT_TRUE(
+        bluetooth_delegate.MayUseBluetooth(non_guest->GetPrimaryMainFrame()));
+  }
+}
+
+}  // namespace
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
index b544f92..a739d4b0 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context.cc
@@ -65,6 +65,8 @@
 #include "content/public/browser/disallow_activation_reason.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/security_principal.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/buildflags/buildflags.h"
 #include "third_party/blink/public/common/features_generated.h"
@@ -2281,6 +2283,16 @@
   }
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE) && BUILDFLAG(ENABLE_GUEST_VIEW)
 
+  // Because permission is scoped to the profile, guest contexts (like
+  // <controlledframe> and SlimWebView), despite having isolated
+  // StoragePartitions, would share File System Access permissions with the rest
+  // of the profile. Therefore, we disable File System Access for guest
+  // contexts. Note that on desktop, <webview> is explicitly allowed to use FSA
+  // in the block above to avoid breaking existing usage.
+  if (rfh->GetSiteInstance()->GetSecurityPrincipal().IsGuest()) {
+    return base::unexpected(kDefaultNotAllowedMessage);
+  }
+
   // Disable any other non-default StoragePartition contexts. However, unique
   // schemes (e.g. isolated-app://) are exempt here.
   if (rfh->GetStoragePartition() !=
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index d6e6ae8..803f237 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc b/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
new file mode 100644
index 0000000..726601a
--- /dev/null
+++ b/chrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc
@@ -0,0 +1,70 @@
+// 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 "chrome/browser/bluetooth/chrome_bluetooth_delegate.h"
+
+#include <memory>
+
+#include "chrome/browser/bluetooth/chrome_bluetooth_delegate_impl_client.h"
+#include "chrome/test/base/chrome_render_view_host_test_harness.h"
+#include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition_config.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/test/web_contents_tester.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+namespace {
+
+class ChromeBluetoothDelegateTest : public ChromeRenderViewHostTestHarness {
+ public:
+  std::unique_ptr<content::WebContents> CreateGuestWebContents(
+      const GURL& url) {
+    const content::StoragePartitionConfig kGuestConfig =
+        content::StoragePartitionConfig::Create(
+            profile(), "test_partition", "guest_partition", /*in_memory=*/true);
+    scoped_refptr<content::SiteInstance> guest_instance =
+        content::SiteInstance::CreateForGuest(profile(), kGuestConfig);
+    std::unique_ptr<content::WebContents> guest_contents =
+        content::WebContentsTester::CreateTestWebContents(profile(),
+                                                          guest_instance);
+    content::WebContentsTester::For(guest_contents.get())
+        ->NavigateAndCommit(url);
+    return guest_contents;
+  }
+};
+
+TEST_F(ChromeBluetoothDelegateTest, BlocksGuestViews) {
+  ChromeBluetoothDelegate bluetooth_delegate(
+      std::make_unique<ChromeBluetoothDelegateImplClient>());
+
+  // 1. Test HTTPS Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("https://example.com/"));
+    EXPECT_FALSE(
+        bluetooth_delegate.MayUseBluetooth(guest->GetPrimaryMainFrame()));
+  }
+
+  // 2. Test about:blank Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("about:blank"));
+    EXPECT_FALSE(
+        bluetooth_delegate.MayUseBluetooth(guest->GetPrimaryMainFrame()));
+  }
+
+  // 3. Test Non-Guest (should be allowed by default)
+  {
+    std::unique_ptr<content::WebContents> non_guest =
+        content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
+    content::WebContentsTester::For(non_guest.get())
+        ->NavigateAndCommit(GURL("https://example.com/"));
+    EXPECT_TRUE(
+        bluetooth_delegate.MayUseBluetooth(non_guest->GetPrimaryMainFrame()));
+  }
+}
+
+}  // namespace
diff --git a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
index d6e6ae8..803f237 100644
--- a/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
+++ b/chrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
@@ -44,6 +44,8 @@
 #include "components/permissions/permission_util.h"
 #include "components/sync_preferences/testing_pref_service_syncable.h"
 #include "content/public/browser/render_process_host.h"
+#include "content/public/browser/site_instance.h"
+#include "content/public/browser/storage_partition_config.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/browser_task_environment.h"
 #include "content/public/test/test_renderer_host.h"
@@ -513,6 +515,21 @@
     }));
   }
 
+  std::unique_ptr<content::WebContents> CreateGuestWebContents(
+      const GURL& url) {
+    const content::StoragePartitionConfig kGuestConfig =
+        content::StoragePartitionConfig::Create(
+            profile(), "test_partition", "guest_partition", /*in_memory=*/true);
+    scoped_refptr<content::SiteInstance> guest_instance =
+        content::SiteInstance::CreateForGuest(profile(), kGuestConfig);
+    std::unique_ptr<content::WebContents> guest_contents =
+        content::WebContentsTester::CreateTestWebContents(profile(),
+                                                          guest_instance);
+    content::WebContentsTester::For(guest_contents.get())
+        ->NavigateAndCommit(url);
+    return guest_contents;
+  }
+
   content::BrowserTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
   base::ScopedTempDir temp_dir_;
@@ -549,6 +566,27 @@
 };
 
 TEST_F(ChromeFileSystemAccessPermissionContextTest,
+       CanShowFilePicker_BlocksGuestViews) {
+  // 1. Test HTTPS Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("https://example.com/"));
+    EXPECT_FALSE(permission_context()
+                     ->CanShowFilePicker(guest->GetPrimaryMainFrame())
+                     .has_value());
+  }
+
+  // 2. Test about:blank Guest (should be blocked)
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("about:blank"));
+    EXPECT_FALSE(permission_context()
+                     ->CanShowFilePicker(guest->GetPrimaryMainFrame())
+                     .has_value());
+  }
+}
+
+TEST_F(ChromeFileSystemAccessPermissionContextTest,
        ConfirmSensitiveEntryAccess_NoSpecialPath) {
   const PathInfo kTestPathInfo(FILE_PATH_LITERAL(
 #if defined(FILE_PATH_USES_DRIVE_LETTERS)
diff --git a/chrome/browser/serial/chrome_serial_delegate_unittest.cc b/chrome/browser/serial/chrome_serial_delegate_unittest.cc
index d933772..b499836 100644
--- a/chrome/browser/serial/chrome_serial_delegate_unittest.cc
+++ b/chrome/browser/serial/chrome_serial_delegate_unittest.cc
@@ -80,4 +80,26 @@
          "non-default-partition frames";
 }
 
+TEST_F(ChromeSerialDelegateStoragePartitionTest, SerialBlocksGuestViews) {
+  ChromeSerialDelegate serial_delegate;
+
+  // 1. Test HTTPS Guest (should be blocked)
+  {
+    const GURL kGuestUrl("https://example.com/");
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestPartitionWebContents(kGuestUrl);
+    content::RenderFrameHost* rfh = guest->GetPrimaryMainFrame();
+    EXPECT_FALSE(serial_delegate.CanRequestPortPermission(rfh));
+  }
+
+  // 2. Test about:blank Guest (should be blocked)
+  {
+    const GURL kGuestUrl("about:blank");
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestPartitionWebContents(kGuestUrl);
+    content::RenderFrameHost* rfh = guest->GetPrimaryMainFrame();
+    EXPECT_FALSE(serial_delegate.CanRequestPortPermission(rfh));
+  }
+}
+
 }  // namespace
diff --git a/chrome/browser/usb/chrome_usb_delegate_unittest.cc b/chrome/browser/usb/chrome_usb_delegate_unittest.cc
index c008fa9..9463c12 100644
--- a/chrome/browser/usb/chrome_usb_delegate_unittest.cc
+++ b/chrome/browser/usb/chrome_usb_delegate_unittest.cc
@@ -18,9 +18,11 @@
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "chrome/test/base/testing_profile_manager.h"
+#include "content/public/browser/site_instance.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/test/embedded_worker_instance_test_harness.h"
 #include "content/public/test/test_renderer_host.h"
+#include "content/public/test/web_contents_tester.h"
 #include "services/device/public/cpp/test/fake_usb_device_info.h"
 #include "services/device/public/cpp/test/fake_usb_device_manager.h"
 #include "services/device/public/cpp/test/scoped_usb_device_manager_overrider.h"
@@ -918,6 +920,42 @@
 
 #endif  // BUILDFLAG(ENABLE_EXTENSIONS_CORE)
 
+class ChromeUsbDelegateGuestTest : public ChromeRenderViewHostTestHarness {
+ public:
+  std::unique_ptr<content::WebContents> CreateGuestWebContents(
+      const GURL& url) {
+    const content::StoragePartitionConfig kGuestConfig =
+        content::StoragePartitionConfig::Create(
+            profile(), "test_partition", "guest_partition", /*in_memory=*/true);
+    scoped_refptr<content::SiteInstance> guest_instance =
+        content::SiteInstance::CreateForGuest(profile(), kGuestConfig);
+    std::unique_ptr<content::WebContents> guest_contents =
+        content::WebContentsTester::CreateTestWebContents(profile(),
+                                                          guest_instance);
+    content::WebContentsTester::For(guest_contents.get())
+        ->NavigateAndCommit(url);
+    return guest_contents;
+  }
+};
+
+TEST_F(ChromeUsbDelegateGuestTest, BlocksGuestViews) {
+  ChromeUsbDelegate usb_delegate;
+
+  // 1. Test HTTPS Guest
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("https://example.com/"));
+    EXPECT_FALSE(usb_delegate.PageMayUseUsb(guest->GetPrimaryPage()));
+  }
+
+  // 2. Test about:blank Guest
+  {
+    std::unique_ptr<content::WebContents> guest =
+        CreateGuestWebContents(GURL("about:blank"));
+    EXPECT_FALSE(usb_delegate.PageMayUseUsb(guest->GetPrimaryPage()));
+  }
+}
+
 }  // namespace
 
 TEST_F(ChromeUsbDelegateRenderFrameTest, NoPermissionDevice) {
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.