Low chrome Logic Error 🔧 Commit mapped

Overview

Low
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient policy enforcement in Bluetooth
DescriptionInsufficient policy enforcement in Bluetooth
ComponentBluetooth
Bug ClassLogic Error
Tracker519981896
Fix commit1c56c2b5dc9a (chromium/src) +51/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-07-29

Changed Functions

FunctionChangeNotes
if
content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
modified
TEST_F
content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
modified

Files Changed

  • content/browser/bluetooth/web_bluetooth_service_impl.cc
  • content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
From 1c56c2b5dc9a01bdd62896a2bfd5e3394a7d1acc Mon Sep 17 00:00:00 2001
From: Alvin Ji <alvinji@chromium.org>
Date: Tue, 16 Jun 2026 17:33:59 -0700
Subject: [PATCH] bluetooth: Verify bluetooth is allowed in ForgetDevice

This CL adds the missing GetBluetoothAllowed() check to
WebBluetoothServiceImpl::ForgetDevice. This prevents compromised
renderers from bypassing Permissions-Policy or MayUseBluetooth checks by
calling this entry point.

Change-Id: I02c89437b7e5f7ef9fa7c3cf2a6a72bbeb6906a1
Bug: 519981896
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7904063
Reviewed-by: Matt Reynolds <mattreynolds@chromium.org>
Commit-Queue: Alvin Ji <alvinji@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1648006}
---

diff --git a/content/browser/bluetooth/web_bluetooth_service_impl.cc b/content/browser/bluetooth/web_bluetooth_service_impl.cc
index ccc16bb8..87d0e0f 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl.cc
@@ -855,6 +855,10 @@
 void WebBluetoothServiceImpl::ForgetDevice(
     const blink::WebBluetoothDeviceId& device_id,
     ForgetDeviceCallback callback) {
+  if (GetBluetoothAllowed() != blink::mojom::WebBluetoothResult::SUCCESS) {
+    std::move(callback).Run();
+    return;
+  }
   CHECK(back_forward_cache_feature_handle_.IsValid());
   if (!base::FeatureList::IsEnabled(
           features::kWebBluetoothNewPermissionsBackend)) {
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
index 4880d79..be04cdd 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -372,6 +372,23 @@
     has_device_permission_ = value;
   }
 
+  bool MayUseBluetooth(RenderFrameHost* frame) override {
+    return may_use_bluetooth_;
+  }
+
+  void set_may_use_bluetooth(bool value) { may_use_bluetooth_ = value; }
+
+  AllowWebBluetoothResult AllowWebBluetooth(
+      content::BrowserContext* browser_context,
+      const url::Origin& requesting_origin,
+      const url::Origin& embedding_origin) override {
+    return allow_web_bluetooth_;
+  }
+
+  void set_allow_web_bluetooth(AllowWebBluetoothResult value) {
+    allow_web_bluetooth_ = value;
+  }
+
   void RunBluetoothScanningPromptEventCallback(
       BluetoothScanningPrompt::Event event) {
     if (!prompt_) {
@@ -383,6 +400,9 @@
 
  private:
   bool has_device_permission_ = false;
+  bool may_use_bluetooth_ = true;
+  AllowWebBluetoothResult allow_web_bluetooth_ =
+      AllowWebBluetoothResult::kAllow;
   raw_ptr<FakeBluetoothScanningPrompt, AcrossTasksDanglingUntriaged> prompt_ =
       nullptr;
 };
@@ -694,6 +714,17 @@
         &service_ptr_.ExtractAsDangling()->render_frame_host());
   }
 
+  void ForgetDevice(
+      const blink::WebBluetoothDeviceId& device_id,
+      blink::mojom::WebBluetoothService::ForgetDeviceCallback callback) {
+    service_ptr_->ForgetDevice(device_id, std::move(callback));
+  }
+
+  std::string GetAllowedDeviceAddress(
+      const blink::WebBluetoothDeviceId& device_id) {
+    return service_ptr_->allowed_devices().GetDeviceAddress(device_id);
+  }
+
   scoped_refptr<FakeBluetoothAdapter> adapter_;
   raw_ptr<WebBluetoothServiceImpl> service_ptr_ = nullptr;
   mojo::Remote<blink::mojom::WebBluetoothService> service_;
@@ -1537,4 +1568,20 @@
   EXPECT_EQ(write_future.Get(), WebBluetoothResult::BLOCKLISTED_WRITE);
 }
 
+TEST_F(WebBluetoothServiceImplTest, ForgetDevice_NotAllowed) {
+  blink::WebBluetoothDeviceId device_id =
+      AddTestDevice(battery_device_bundle());
+  EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+
+  browser_client_.bluetooth_delegate()->set_may_use_bluetooth(false);
+
+  base::test::TestFuture<void> future;
+  ForgetDevice(device_id, future.GetCallback());
+  EXPECT_TRUE(future.Wait());
+
+  // The device should STILL be in allowed devices because ForgetDevice should
+  // return early.
+  EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+}
+
 }  // namespace content
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
index 4880d79..be04cdd 100644
--- a/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
+++ b/content/browser/bluetooth/web_bluetooth_service_impl_unittest.cc
@@ -372,6 +372,23 @@
     has_device_permission_ = value;
   }
 
+  bool MayUseBluetooth(RenderFrameHost* frame) override {
+    return may_use_bluetooth_;
+  }
+
+  void set_may_use_bluetooth(bool value) { may_use_bluetooth_ = value; }
+
+  AllowWebBluetoothResult AllowWebBluetooth(
+      content::BrowserContext* browser_context,
+      const url::Origin& requesting_origin,
+      const url::Origin& embedding_origin) override {
+    return allow_web_bluetooth_;
+  }
+
+  void set_allow_web_bluetooth(AllowWebBluetoothResult value) {
+    allow_web_bluetooth_ = value;
+  }
+
   void RunBluetoothScanningPromptEventCallback(
       BluetoothScanningPrompt::Event event) {
     if (!prompt_) {
@@ -383,6 +400,9 @@
 
  private:
   bool has_device_permission_ = false;
+  bool may_use_bluetooth_ = true;
+  AllowWebBluetoothResult allow_web_bluetooth_ =
+      AllowWebBluetoothResult::kAllow;
   raw_ptr<FakeBluetoothScanningPrompt, AcrossTasksDanglingUntriaged> prompt_ =
       nullptr;
 };
@@ -694,6 +714,17 @@
         &service_ptr_.ExtractAsDangling()->render_frame_host());
   }
 
+  void ForgetDevice(
+      const blink::WebBluetoothDeviceId& device_id,
+      blink::mojom::WebBluetoothService::ForgetDeviceCallback callback) {
+    service_ptr_->ForgetDevice(device_id, std::move(callback));
+  }
+
+  std::string GetAllowedDeviceAddress(
+      const blink::WebBluetoothDeviceId& device_id) {
+    return service_ptr_->allowed_devices().GetDeviceAddress(device_id);
+  }
+
   scoped_refptr<FakeBluetoothAdapter> adapter_;
   raw_ptr<WebBluetoothServiceImpl> service_ptr_ = nullptr;
   mojo::Remote<blink::mojom::WebBluetoothService> service_;
@@ -1537,4 +1568,20 @@
   EXPECT_EQ(write_future.Get(), WebBluetoothResult::BLOCKLISTED_WRITE);
 }
 
+TEST_F(WebBluetoothServiceImplTest, ForgetDevice_NotAllowed) {
+  blink::WebBluetoothDeviceId device_id =
+      AddTestDevice(battery_device_bundle());
+  EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+
+  browser_client_.bluetooth_delegate()->set_may_use_bluetooth(false);
+
+  base::test::TestFuture<void> future;
+  ForgetDevice(device_id, future.GetCallback());
+  EXPECT_TRUE(future.Wait());
+
+  // The device should STILL be in allowed devices because ForgetDevice should
+  // return early.
+  EXPECT_FALSE(GetAllowedDeviceAddress(device_id).empty());
+}
+
 }  // namespace content
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.