Chrome · USB
CVE-2026-18000
Logic Error in USB
Overview
Low
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
source_setchrome/browser/bluetooth/BUILD.gn |
modified | |
ChromeBluetoothDelegateTestchrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc |
modified | |
TEST_Fchrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.cc |
modified |
Files Changed
chrome/browser/bluetooth/BUILD.gnchrome/browser/bluetooth/chrome_bluetooth_delegate.ccchrome/browser/bluetooth/chrome_bluetooth_delegate_unittest.ccchrome/browser/file_system_access/chrome_file_system_access_permission_context.ccchrome/browser/file_system_access/chrome_file_system_access_permission_context_unittest.cc
Patch
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.
References
On This Page