feat: support Web Serial & WebUSB blocklists (#46600)

This commit is contained in:
Shelley Vohr 2025-04-17 20:34:34 +02:00 committed by GitHub
parent 352a403efd
commit a29e1170b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 106 additions and 4 deletions

View file

@ -85,6 +85,8 @@ static_library("chrome") {
"//chrome/browser/process_singleton.h", "//chrome/browser/process_singleton.h",
"//chrome/browser/process_singleton_internal.cc", "//chrome/browser/process_singleton_internal.cc",
"//chrome/browser/process_singleton_internal.h", "//chrome/browser/process_singleton_internal.h",
"//chrome/browser/serial/serial_blocklist.cc",
"//chrome/browser/serial/serial_blocklist.h",
"//chrome/browser/themes/browser_theme_pack.cc", "//chrome/browser/themes/browser_theme_pack.cc",
"//chrome/browser/themes/browser_theme_pack.h", "//chrome/browser/themes/browser_theme_pack.h",
"//chrome/browser/themes/custom_theme_supplier.cc", "//chrome/browser/themes/custom_theme_supplier.cc",
@ -144,6 +146,8 @@ static_library("chrome") {
"//chrome/browser/ui/views/overlay/video_overlay_window_views.h", "//chrome/browser/ui/views/overlay/video_overlay_window_views.h",
"//chrome/browser/ui/webui/accessibility/accessibility_ui.cc", "//chrome/browser/ui/webui/accessibility/accessibility_ui.cc",
"//chrome/browser/ui/webui/accessibility/accessibility_ui.h", "//chrome/browser/ui/webui/accessibility/accessibility_ui.h",
"//chrome/browser/usb/usb_blocklist.cc",
"//chrome/browser/usb/usb_blocklist.h",
"//extensions/browser/app_window/size_constraints.cc", "//extensions/browser/app_window/size_constraints.cc",
"//extensions/browser/app_window/size_constraints.h", "//extensions/browser/app_window/size_constraints.h",
"//ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc", "//ui/base/accelerators/global_accelerator_listener/global_accelerator_listener.cc",

View file

@ -14,6 +14,12 @@ This document uses the following convention to categorize breaking changes:
## Planned Breaking API Changes (37.0) ## Planned Breaking API Changes (37.0)
### Behavior Changed: WebUSB and WebSerial Blocklist Support
[WebUSB](https://developer.mozilla.org/en-US/docs/Web/API/WebUSB_API) and [Web Serial](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API) now support the [WebUSB Blocklist](https://wicg.github.io/webusb/#blocklist) and [Web Serial Blocklist](https://wicg.github.io/serial/#blocklist) used by Chromium and outlined in their respective specifications.
To disable these, users can pass `disable-usb-blocklist` and `disable-serial-blocklist` as command line flags.
### Removed: `null` value for `session` property in `ProtocolResponse` ### Removed: `null` value for `session` property in `ProtocolResponse`
This deprecated feature has been removed. This deprecated feature has been removed.

View file

@ -57,7 +57,7 @@ the WebHID API:
### Blocklist ### Blocklist
By default Electron employs the same [blocklist](https://github.com/WICG/webhid/blob/main/blocklist.txt) By default Electron employs the same [blocklist](https://wicg.github.io/webhid/#blocklist)
used by Chromium. If you wish to override this behavior, you can do so by used by Chromium. If you wish to override this behavior, you can do so by
setting the `disable-hid-blocklist` flag: setting the `disable-hid-blocklist` flag:
@ -104,6 +104,16 @@ There are several additional APIs for working with the Web Serial API:
* [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler) * [`ses.setPermissionCheckHandler(handler)`](../api/session.md#sessetpermissioncheckhandlerhandler)
can be used to disable serial access for specific origins. can be used to disable serial access for specific origins.
### Blocklist
By default Electron employs the same [blocklist](https://wicg.github.io/serial/#blocklist)
used by Chromium. If you wish to override this behavior, you can do so by
setting the `disable-serial-blocklist` flag:
```js
app.commandLine.appendSwitch('disable-serial-blocklist')
```
### Example ### Example
This example demonstrates an Electron application that automatically selects This example demonstrates an Electron application that automatically selects
@ -145,6 +155,16 @@ Electron provides several APIs for working with the WebUSB API:
* [`ses.setUSBProtectedClassesHandler](../api/session.md#sessetusbprotectedclasseshandlerhandler) * [`ses.setUSBProtectedClassesHandler](../api/session.md#sessetusbprotectedclasseshandlerhandler)
can be used to allow usage of [protected USB classes](https://wicg.github.io/webusb/#usbinterface-interface) that are not available by default. can be used to allow usage of [protected USB classes](https://wicg.github.io/webusb/#usbinterface-interface) that are not available by default.
### Blocklist
By default Electron employs the same [blocklist](https://wicg.github.io/webusb/#blocklist)
used by Chromium. If you wish to override this behavior, you can do so by
setting the `disable-usb-blocklist` flag:
```js
app.commandLine.appendSwitch('disable-usb-blocklist')
```
### Example ### Example
This example demonstrates an Electron application that automatically selects This example demonstrates an Electron application that automatically selects

View file

@ -9,7 +9,9 @@
#include <utility> #include <utility>
#include "base/base64.h" #include "base/base64.h"
#include "base/command_line.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/serial/serial_blocklist.h"
#include "content/public/browser/device_service.h" #include "content/public/browser/device_service.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/pending_remote.h"
@ -104,6 +106,12 @@ bool SerialChooserContext::HasPortPermission(
const url::Origin& origin, const url::Origin& origin,
const device::mojom::SerialPortInfo& port, const device::mojom::SerialPortInfo& port,
content::RenderFrameHost* render_frame_host) { content::RenderFrameHost* render_frame_host) {
bool blocklist_disabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
kDisableSerialBlocklist);
if (!blocklist_disabled && SerialBlocklist::Get().IsExcluded(port)) {
return false;
}
auto it = ephemeral_ports_.find(origin); auto it = ephemeral_ports_.find(origin);
if (it != ephemeral_ports_.end()) { if (it != ephemeral_ports_.end()) {
const std::set<base::UnguessableToken>& ports = it->second; const std::set<base::UnguessableToken>& ports = it->second;

View file

@ -36,6 +36,8 @@ namespace electron {
class ElectronBrowserContext; class ElectronBrowserContext;
const char kDisableSerialBlocklist[] = "disable-serial-blocklist";
inline constexpr std::string_view kPortNameKey = "name"; inline constexpr std::string_view kPortNameKey = "name";
inline constexpr std::string_view kTokenKey = "token"; inline constexpr std::string_view kTokenKey = "token";
inline constexpr std::string_view kBluetoothDevicePathKey = inline constexpr std::string_view kBluetoothDevicePathKey =

View file

@ -7,9 +7,12 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include "base/command_line.h"
#include "base/containers/contains.h" #include "base/containers/contains.h"
#include "base/functional/bind.h" #include "base/functional/bind.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "chrome/browser/serial/serial_blocklist.h"
#include "content/public/browser/console_message.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_adapter_factory.h"
@ -120,8 +123,7 @@ SerialChooserController::SerialChooserController(
allowed_bluetooth_service_class_ids_( allowed_bluetooth_service_class_ids_(
std::move(allowed_bluetooth_service_class_ids)), std::move(allowed_bluetooth_service_class_ids)),
callback_(std::move(callback)), callback_(std::move(callback)),
serial_delegate_(serial_delegate), initiator_document_(render_frame_host->GetWeakDocumentPtr()) {
render_frame_host_id_(render_frame_host->GetGlobalId()) {
origin_ = web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin(); origin_ = web_contents_->GetPrimaryMainFrame()->GetLastCommittedOrigin();
chooser_context_ = SerialChooserContextFactory::GetForBrowserContext( chooser_context_ = SerialChooserContextFactory::GetForBrowserContext(
@ -210,7 +212,7 @@ void SerialChooserController::OnDeviceChosen(const std::string& port_id) {
return ptr->token.ToString() == port_id; return ptr->token.ToString() == port_id;
}); });
if (it != ports_.end()) { if (it != ports_.end()) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_); auto* rfh = initiator_document_.AsRenderFrameHostIfValid();
chooser_context_->GrantPortPermission(origin_, *it->get(), rfh); chooser_context_->GrantPortPermission(origin_, *it->get(), rfh);
RunCallback(it->Clone()); RunCallback(it->Clone());
} else { } else {
@ -246,6 +248,34 @@ void SerialChooserController::OnGetDevices(
bool SerialChooserController::DisplayDevice( bool SerialChooserController::DisplayDevice(
const device::mojom::SerialPortInfo& port) const { const device::mojom::SerialPortInfo& port) const {
bool blocklist_disabled = base::CommandLine::ForCurrentProcess()->HasSwitch(
kDisableSerialBlocklist);
if (!blocklist_disabled && SerialBlocklist::Get().IsExcluded(port)) {
if (port.has_vendor_id && port.has_product_id) {
AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
base::StringPrintf(
"Skipping a port blocked by "
"the Serial blocklist: vendorId=%d, "
"productId=%d, name='%s', serial='%s'",
port.vendor_id, port.product_id,
port.display_name ? port.display_name.value().c_str() : "",
port.serial_number ? port.serial_number.value().c_str() : ""));
} else if (port.bluetooth_service_class_id) {
AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
base::StringPrintf(
"Skipping a port blocked by "
"the Serial blocklist: bluetoothServiceClassId=%s, "
"name='%s'",
port.bluetooth_service_class_id->value().c_str(),
port.display_name ? port.display_name.value().c_str() : ""));
} else {
NOTREACHED();
}
return false;
}
if (filters_.empty()) { if (filters_.empty()) {
return BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port); return BluetoothPortIsAllowed(allowed_bluetooth_service_class_ids_, port);
} }
@ -260,6 +290,15 @@ bool SerialChooserController::DisplayDevice(
return false; return false;
} }
void SerialChooserController::AddMessageToConsole(
blink::mojom::ConsoleMessageLevel level,
const std::string& message) const {
if (content::RenderFrameHost* rfh =
initiator_document_.AsRenderFrameHostIfValid()) {
rfh->AddMessageToConsole(level, message);
}
}
void SerialChooserController::RunCallback( void SerialChooserController::RunCallback(
device::mojom::SerialPortInfoPtr port) { device::mojom::SerialPortInfoPtr port) {
if (callback_) { if (callback_) {

View file

@ -12,9 +12,11 @@
#include "base/scoped_observation.h" #include "base/scoped_observation.h"
#include "content/public/browser/global_routing_id.h" #include "content/public/browser/global_routing_id.h"
#include "content/public/browser/serial_chooser.h" #include "content/public/browser/serial_chooser.h"
#include "content/public/browser/weak_document_ptr.h"
#include "device/bluetooth/bluetooth_adapter.h" #include "device/bluetooth/bluetooth_adapter.h"
#include "services/device/public/mojom/serial.mojom-forward.h" #include "services/device/public/mojom/serial.mojom-forward.h"
#include "shell/browser/serial/serial_chooser_context.h" #include "shell/browser/serial/serial_chooser_context.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
#include "third_party/blink/public/mojom/serial/serial.mojom-forward.h" #include "third_party/blink/public/mojom/serial/serial.mojom-forward.h"
namespace content { namespace content {
@ -66,6 +68,8 @@ class SerialChooserController final
void GetDevices(); void GetDevices();
void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports); void OnGetDevices(std::vector<device::mojom::SerialPortInfoPtr> ports);
bool DisplayDevice(const device::mojom::SerialPortInfo& port) const; bool DisplayDevice(const device::mojom::SerialPortInfo& port) const;
void AddMessageToConsole(blink::mojom::ConsoleMessageLevel level,
const std::string& message) const;
void RunCallback(device::mojom::SerialPortInfoPtr port); void RunCallback(device::mojom::SerialPortInfoPtr port);
void OnDeviceChosen(const std::string& port_id); void OnDeviceChosen(const std::string& port_id);
void OnGetAdapter(base::OnceClosure callback, void OnGetAdapter(base::OnceClosure callback,
@ -78,6 +82,7 @@ class SerialChooserController final
std::vector<blink::mojom::SerialPortFilterPtr> filters_; std::vector<blink::mojom::SerialPortFilterPtr> filters_;
std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_; std::vector<::device::BluetoothUUID> allowed_bluetooth_service_class_ids_;
content::SerialChooser::Callback callback_; content::SerialChooser::Callback callback_;
content::WeakDocumentPtr initiator_document_;
url::Origin origin_; url::Origin origin_;
base::WeakPtr<SerialChooserContext> chooser_context_; base::WeakPtr<SerialChooserContext> chooser_context_;

View file

@ -8,9 +8,11 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "base/command_line.h"
#include "base/functional/bind.h" #include "base/functional/bind.h"
#include "base/task/sequenced_task_runner.h" #include "base/task/sequenced_task_runner.h"
#include "base/values.h" #include "base/values.h"
#include "chrome/browser/usb/usb_blocklist.h"
#include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings.h"
#include "content/public/browser/device_service.h" #include "content/public/browser/device_service.h"
#include "services/device/public/cpp/usb/usb_ids.h" #include "services/device/public/cpp/usb/usb_ids.h"
@ -215,6 +217,12 @@ void UsbChooserContext::GrantDevicePermission(
bool UsbChooserContext::HasDevicePermission( bool UsbChooserContext::HasDevicePermission(
const url::Origin& origin, const url::Origin& origin,
const device::mojom::UsbDeviceInfo& device_info) { const device::mojom::UsbDeviceInfo& device_info) {
bool blocklist_disabled =
base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableUSBBlocklist);
if (!blocklist_disabled && UsbBlocklist::Get().IsExcluded(device_info)) {
return false;
}
auto it = ephemeral_devices_.find(origin); auto it = ephemeral_devices_.find(origin);
if (it != ephemeral_devices_.end() && it->second.contains(device_info.guid)) { if (it != ephemeral_devices_.end() && it->second.contains(device_info.guid)) {
return true; return true;

View file

@ -33,6 +33,8 @@ namespace electron {
class ElectronBrowserContext; class ElectronBrowserContext;
const char kDisableUSBBlocklist[] = "disable-usb-blocklist";
class UsbChooserContext : public KeyedService, class UsbChooserContext : public KeyedService,
public device::mojom::UsbDeviceManagerClient { public device::mojom::UsbDeviceManagerClient {
public: public:

View file

@ -8,7 +8,9 @@
#include <cstddef> #include <cstddef>
#include <utility> #include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h" #include "base/functional/bind.h"
#include "chrome/browser/usb/usb_blocklist.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
@ -140,6 +142,8 @@ void UsbChooserController::GotUsbDeviceList(
bool UsbChooserController::DisplayDevice( bool UsbChooserController::DisplayDevice(
const device::mojom::UsbDeviceInfo& device_info) const { const device::mojom::UsbDeviceInfo& device_info) const {
bool blocklist_disabled =
base::CommandLine::ForCurrentProcess()->HasSwitch(kDisableUSBBlocklist);
if (!device::UsbDeviceFilterMatchesAny(options_->filters, device_info)) { if (!device::UsbDeviceFilterMatchesAny(options_->filters, device_info)) {
return false; return false;
} }
@ -151,6 +155,10 @@ bool UsbChooserController::DisplayDevice(
return false; return false;
} }
if (!blocklist_disabled && UsbBlocklist::Get().IsExcluded(device_info)) {
return false;
}
return true; return true;
} }