serial: Implement policy controls for port permissions
This change implements a pair of policies which allow administrators to
automatically grant sites access to certain serial ports without user
interaction.
The permission settings UI for the Serial API is not updated to display
permissions granted by policy in this patch. That will be handled in a
follow-up. The policy will remain experimental until that work is
complete.
Design doc: go/web-serial-policy
Bug: 1001242
Change-Id: I74279e0663e5ce3cdf113f3ca24cc9b38adc56ba
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2739773
Commit-Queue: Reilly Grant <[email protected]>
Reviewed-by: Julian Pastarmov <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Reviewed-by: Matt Reynolds <[email protected]>
Cr-Commit-Position: refs/heads/master@{#863029}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index de3b455ed..a7e1d41 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3946,6 +3946,8 @@
"serial/serial_chooser_context_factory.cc",
"serial/serial_chooser_context_factory.h",
"serial/serial_chooser_histograms.h",
+ "serial/serial_policy_allowed_ports.cc",
+ "serial/serial_policy_allowed_ports.h",
"sessions/closed_tab_cache_service.cc",
"sessions/closed_tab_cache_service.h",
"sessions/closed_tab_cache_service_factory.cc",
diff --git a/chrome/browser/policy/configuration_policy_handler_list_factory.cc b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
index 24b0687a..8be5408 100644
--- a/chrome/browser/policy/configuration_policy_handler_list_factory.cc
+++ b/chrome/browser/policy/configuration_policy_handler_list_factory.cc
@@ -546,6 +546,7 @@
{ key::kWebUsbBlockedForUrls,
prefs::kManagedWebUsbBlockedForUrls,
base::Value::Type::LIST },
+#if !defined(OS_ANDROID)
{ key::kDefaultSerialGuardSetting,
prefs::kManagedDefaultSerialGuardSetting,
base::Value::Type::INTEGER },
@@ -555,6 +556,10 @@
{ key::kSerialBlockedForUrls,
prefs::kManagedSerialBlockedForUrls,
base::Value::Type::LIST },
+ { key::kSerialAllowAllPortsForUrls,
+ prefs::kManagedSerialAllowAllPortsForUrls,
+ base::Value::Type::LIST },
+#endif // !defined(OS_ANDROID)
{ key::kDefaultFileSystemReadGuardSetting,
prefs::kManagedDefaultFileSystemReadGuardSetting,
base::Value::Type::INTEGER },
@@ -1431,6 +1436,14 @@
handlers->AddHandler(std::make_unique<HomepageLocationPolicyHandler>());
handlers->AddHandler(std::make_unique<ProxyPolicyHandler>());
handlers->AddHandler(std::make_unique<SecureDnsPolicyHandler>());
+#if !defined(OS_ANDROID)
+ handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
+ key::kSerialAllowUsbDevicesForUrls,
+ prefs::kManagedSerialAllowUsbDevicesForUrls, chrome_schema,
+ SCHEMA_ALLOW_UNKNOWN,
+ SimpleSchemaValidatingPolicyHandler::RECOMMENDED_PROHIBITED,
+ SimpleSchemaValidatingPolicyHandler::MANDATORY_ALLOWED));
+#endif // !defined(OS_ANDROID)
handlers->AddHandler(std::make_unique<SimpleSchemaValidatingPolicyHandler>(
key::kCertificateTransparencyEnforcementDisabledForUrls,
certificate_transparency::prefs::kCTExcludedHosts, chrome_schema,
diff --git a/chrome/browser/prefs/browser_prefs.cc b/chrome/browser/prefs/browser_prefs.cc
index c03e8b8..ea09ada 100644
--- a/chrome/browser/prefs/browser_prefs.cc
+++ b/chrome/browser/prefs/browser_prefs.cc
@@ -235,6 +235,7 @@
#include "chrome/browser/search/promos/promo_service.h"
#include "chrome/browser/search/search_suggest/search_suggest_service.h"
#include "chrome/browser/search/task_module/task_module_service.h"
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
#include "chrome/browser/signin/signin_promo.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/webui/history/foreign_session_handler.h"
@@ -935,6 +936,9 @@
registry);
security_interstitials::InsecureFormBlockingPage::RegisterProfilePrefs(
registry);
+#if !defined(OS_ANDROID)
+ SerialPolicyAllowedPorts::RegisterProfilePrefs(registry);
+#endif
SessionStartupPref::RegisterProfilePrefs(registry);
SharingSyncPreference::RegisterProfilePrefs(registry);
site_engagement::SiteEngagementService::RegisterProfilePrefs(registry);
diff --git a/chrome/browser/serial/serial_chooser_context.cc b/chrome/browser/serial/serial_chooser_context.cc
index aba5316c..f9106775 100644
--- a/chrome/browser/serial/serial_chooser_context.cc
+++ b/chrome/browser/serial/serial_chooser_context.cc
@@ -96,7 +96,8 @@
: ChooserContextBase(ContentSettingsType::SERIAL_GUARD,
ContentSettingsType::SERIAL_CHOOSER_DATA,
HostContentSettingsMapFactory::GetForProfile(profile)),
- is_incognito_(profile->IsOffTheRecord()) {}
+ is_incognito_(profile->IsOffTheRecord()),
+ policy_(profile->GetPrefs()) {}
SerialChooserContext::~SerialChooserContext() = default;
@@ -222,6 +223,10 @@
return false;
}
+ if (policy_.HasPortPermission(origin, port)) {
+ return true;
+ }
+
if (!CanRequestObjectPermission(origin)) {
return false;
}
diff --git a/chrome/browser/serial/serial_chooser_context.h b/chrome/browser/serial/serial_chooser_context.h
index 490fa913..5427514 100644
--- a/chrome/browser/serial/serial_chooser_context.h
+++ b/chrome/browser/serial/serial_chooser_context.h
@@ -14,6 +14,7 @@
#include "base/memory/weak_ptr.h"
#include "base/unguessable_token.h"
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
#include "components/permissions/chooser_context_base.h"
#include "content/public/browser/serial_delegate.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
@@ -82,6 +83,8 @@
const bool is_incognito_;
+ SerialPolicyAllowedPorts policy_;
+
// Tracks the set of ports to which an origin has access to.
std::map<url::Origin, std::set<base::UnguessableToken>> ephemeral_ports_;
diff --git a/chrome/browser/serial/serial_chooser_context_unittest.cc b/chrome/browser/serial/serial_chooser_context_unittest.cc
index 275d8ee..fc3d5f9 100644
--- a/chrome/browser/serial/serial_chooser_context_unittest.cc
+++ b/chrome/browser/serial/serial_chooser_context_unittest.cc
@@ -14,6 +14,7 @@
#include "chrome/browser/serial/serial_blocklist.h"
#include "chrome/browser/serial/serial_chooser_context_factory.h"
#include "chrome/browser/serial/serial_chooser_histograms.h"
+#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/pref_names.h"
@@ -60,6 +61,14 @@
return port;
}
+std::unique_ptr<base::Value> ReadJson(base::StringPiece json) {
+ base::JSONReader::ValueWithError result =
+ base::JSONReader::ReadAndReturnValueWithError(json);
+ EXPECT_TRUE(result.value) << result.error_message;
+ return result.value ? base::Value::ToUniquePtrValue(std::move(*result.value))
+ : nullptr;
+}
+
class SerialChooserContextTest : public testing::Test {
public:
SerialChooserContextTest() {
@@ -407,9 +416,7 @@
prefs->SetManagedPref(prefs::kManagedDefaultSerialGuardSetting,
std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
prefs->SetManagedPref(prefs::kManagedSerialAskForUrls,
- base::JSONReader::ReadDeprecated(R"(
- [ "https://foo.origin" ]
- )"));
+ ReadJson(R"([ "https://foo.origin" ])"));
EXPECT_TRUE(context()->CanRequestObjectPermission(kFooOrigin));
EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *port));
@@ -438,9 +445,7 @@
auto* prefs = profile()->GetTestingPrefService();
prefs->SetManagedPref(prefs::kManagedSerialBlockedForUrls,
- base::JSONReader::ReadDeprecated(R"(
- [ "https://foo.origin" ]
- )"));
+ ReadJson(R"([ "https://foo.origin" ])"));
EXPECT_FALSE(context()->CanRequestObjectPermission(kFooOrigin));
EXPECT_FALSE(context()->HasPortPermission(kFooOrigin, *port));
@@ -458,6 +463,92 @@
EXPECT_EQ(1u, all_origin_objects.size());
}
+TEST_F(SerialChooserContextTest, PolicyAllowForUrls) {
+ const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
+ const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
+
+ auto* prefs = profile()->GetTestingPrefService();
+ prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+ ReadJson(R"([ "https://foo.origin" ])"));
+ prefs->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
+ ReadJson(R"([
+ {
+ "devices": [{ "vendor_id": 1234, "product_id": 5678 }],
+ "urls": [ "https://bar.origin" ]
+ }
+ ])"));
+
+ auto platform_port = device::mojom::SerialPortInfo::New();
+ platform_port->token = base::UnguessableToken::Create();
+
+ auto usb_port1 = device::mojom::SerialPortInfo::New();
+ usb_port1->token = base::UnguessableToken::Create();
+ usb_port1->has_vendor_id = true;
+ usb_port1->vendor_id = 1234;
+ usb_port1->has_product_id = true;
+ usb_port1->product_id = 5678;
+
+ auto usb_port2 = device::mojom::SerialPortInfo::New();
+ usb_port2->token = base::UnguessableToken::Create();
+ usb_port2->has_vendor_id = true;
+ usb_port2->vendor_id = 1234;
+ usb_port2->has_product_id = true;
+ usb_port2->product_id = 8765;
+
+ EXPECT_TRUE(context()->CanRequestObjectPermission(kFooOrigin));
+ EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *platform_port));
+ EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *usb_port1));
+ EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *usb_port2));
+
+ EXPECT_TRUE(context()->CanRequestObjectPermission(kBarOrigin));
+ EXPECT_FALSE(context()->HasPortPermission(kBarOrigin, *platform_port));
+ EXPECT_TRUE(context()->HasPortPermission(kBarOrigin, *usb_port1));
+ EXPECT_FALSE(context()->HasPortPermission(kBarOrigin, *usb_port2));
+
+ // TODO(crbug.com/1001242): Add tests for GetGrantedObjects() and
+ // GetAllGrantedObjects() once those have been updated to include device
+ // permissions granted by policy.
+}
+
+TEST_F(SerialChooserContextTest, PolicyAllowOverridesGuard) {
+ const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
+ const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
+
+ auto* prefs = profile()->GetTestingPrefService();
+ prefs->SetManagedPref(prefs::kManagedDefaultSerialGuardSetting,
+ std::make_unique<base::Value>(CONTENT_SETTING_BLOCK));
+ prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+ ReadJson(R"([ "https://foo.origin" ])"));
+
+ auto port = device::mojom::SerialPortInfo::New();
+ port->token = base::UnguessableToken::Create();
+
+ EXPECT_FALSE(context()->CanRequestObjectPermission(kFooOrigin));
+ EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *port));
+ EXPECT_FALSE(context()->CanRequestObjectPermission(kBarOrigin));
+ EXPECT_FALSE(context()->HasPortPermission(kBarOrigin, *port));
+}
+
+TEST_F(SerialChooserContextTest, PolicyAllowOverridesBlocked) {
+ const auto kFooOrigin = url::Origin::Create(GURL("https://foo.origin"));
+ const auto kBarOrigin = url::Origin::Create(GURL("https://bar.origin"));
+
+ auto* prefs = profile()->GetTestingPrefService();
+ prefs->SetManagedPref(
+ prefs::kManagedSerialBlockedForUrls,
+ ReadJson(R"([ "https://foo.origin", "https://bar.origin" ])"));
+ prefs->SetManagedPref(prefs::kManagedSerialAllowAllPortsForUrls,
+ ReadJson(R"([ "https://foo.origin" ])"));
+
+ auto port = device::mojom::SerialPortInfo::New();
+ port->token = base::UnguessableToken::Create();
+
+ EXPECT_FALSE(context()->CanRequestObjectPermission(kFooOrigin));
+ EXPECT_TRUE(context()->HasPortPermission(kFooOrigin, *port));
+ EXPECT_FALSE(context()->CanRequestObjectPermission(kBarOrigin));
+ EXPECT_FALSE(context()->HasPortPermission(kBarOrigin, *port));
+}
+
TEST_F(SerialChooserContextTest, Blocklist) {
const auto origin = url::Origin::Create(GURL("https://google.com"));
@@ -487,3 +578,29 @@
all_origin_objects = context()->GetAllGrantedObjects();
EXPECT_EQ(1u, all_origin_objects.size());
}
+
+TEST_F(SerialChooserContextTest, BlocklistOverridesPolicy) {
+ const auto origin = url::Origin::Create(GURL("https://google.com"));
+
+ auto* prefs = profile()->GetTestingPrefService();
+ prefs->SetManagedPref(prefs::kManagedSerialAllowUsbDevicesForUrls,
+ ReadJson(R"([
+ {
+ "devices": [{ "vendor_id": 6353, "product_id": 22768 }],
+ "urls": [ "https://google.com" ]
+ }
+ ])"));
+
+ auto port = device::mojom::SerialPortInfo::New();
+ port->token = base::UnguessableToken::Create();
+ port->has_vendor_id = true;
+ port->vendor_id = 0x18D1;
+ port->has_product_id = true;
+ port->product_id = 0x58F0;
+ EXPECT_TRUE(context()->HasPortPermission(origin, *port));
+
+ // Adding a USB device to the blocklist overrides permissions granted by
+ // policy.
+ SetDynamicBlocklist("usb:18D1:58F0");
+ EXPECT_FALSE(context()->HasPortPermission(origin, *port));
+}
diff --git a/chrome/browser/serial/serial_policy_allowed_ports.cc b/chrome/browser/serial/serial_policy_allowed_ports.cc
new file mode 100644
index 0000000..5b66bea
--- /dev/null
+++ b/chrome/browser/serial/serial_policy_allowed_ports.cc
@@ -0,0 +1,154 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
+
+#include <vector>
+
+#include "base/bind.h"
+#include "base/values.h"
+#include "chrome/common/pref_names.h"
+#include "components/pref_registry/pref_registry_syncable.h"
+#include "components/prefs/pref_service.h"
+#include "services/device/public/mojom/serial.mojom.h"
+#include "url/gurl.h"
+
+namespace {
+
+constexpr char kPrefDevicesKey[] = "devices";
+constexpr char kPrefUrlsKey[] = "urls";
+constexpr char kPrefVendorIdKey[] = "vendor_id";
+constexpr char kPrefProductIdKey[] = "product_id";
+
+} // namespace
+
+SerialPolicyAllowedPorts::SerialPolicyAllowedPorts(PrefService* pref_service) {
+ pref_change_registrar_.Init(pref_service);
+
+ // The lifetime of |pref_change_registrar_| is managed by this class so it is
+ // safe to use base::Unretained() here.
+ pref_change_registrar_.Add(
+ prefs::kManagedSerialAllowAllPortsForUrls,
+ base::BindRepeating(
+ &SerialPolicyAllowedPorts::LoadAllowAllPortsForUrlsPolicy,
+ base::Unretained(this)));
+ pref_change_registrar_.Add(
+ prefs::kManagedSerialAllowUsbDevicesForUrls,
+ base::BindRepeating(
+ &SerialPolicyAllowedPorts::LoadAllowUsbDevicesForUrlsPolicy,
+ base::Unretained(this)));
+
+ LoadAllowAllPortsForUrlsPolicy();
+ LoadAllowUsbDevicesForUrlsPolicy();
+}
+
+SerialPolicyAllowedPorts::~SerialPolicyAllowedPorts() = default;
+
+// static
+void SerialPolicyAllowedPorts::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterListPref(prefs::kManagedSerialAllowAllPortsForUrls);
+ registry->RegisterListPref(prefs::kManagedSerialAllowUsbDevicesForUrls);
+}
+
+bool SerialPolicyAllowedPorts::HasPortPermission(
+ const url::Origin& origin,
+ const device::mojom::SerialPortInfo& port_info) {
+ if (base::Contains(all_ports_policy_, origin)) {
+ return true;
+ }
+
+ if (port_info.has_vendor_id) {
+ auto it = usb_vendor_policy_.find(port_info.vendor_id);
+ if (it != usb_vendor_policy_.end() && base::Contains(it->second, origin)) {
+ return true;
+ }
+ }
+
+ if (port_info.has_vendor_id && port_info.has_product_id) {
+ auto it = usb_device_policy_.find(
+ std::make_pair(port_info.vendor_id, port_info.product_id));
+ if (it != usb_device_policy_.end() && base::Contains(it->second, origin)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void SerialPolicyAllowedPorts::LoadAllowAllPortsForUrlsPolicy() {
+ all_ports_policy_.clear();
+
+ const base::Value* pref_value = pref_change_registrar_.prefs()->Get(
+ prefs::kManagedSerialAllowAllPortsForUrls);
+ if (!pref_value) {
+ return;
+ }
+
+ // The pref value has already been validated by the policy handler, so it is
+ // safe to assume that |pref_value| follows the policy template.
+ std::vector<url::Origin> urls;
+ for (const auto& url_value : pref_value->GetList()) {
+ GURL url(url_value.GetString());
+ if (!url.is_valid()) {
+ continue;
+ }
+
+ urls.push_back(url::Origin::Create(url));
+ }
+
+ all_ports_policy_.insert(urls.begin(), urls.end());
+}
+
+void SerialPolicyAllowedPorts::LoadAllowUsbDevicesForUrlsPolicy() {
+ usb_device_policy_.clear();
+ usb_vendor_policy_.clear();
+
+ const base::Value* pref_value = pref_change_registrar_.prefs()->Get(
+ prefs::kManagedSerialAllowUsbDevicesForUrls);
+ if (!pref_value) {
+ return;
+ }
+
+ // The pref value has already been validated by the policy handler, so it is
+ // safe to assume that |pref_value| follows the policy template.
+ for (const auto& item : pref_value->GetList()) {
+ const base::Value* urls_value = item.FindKey(kPrefUrlsKey);
+ DCHECK(urls_value);
+
+ std::vector<url::Origin> urls;
+ for (const auto& url_value : urls_value->GetList()) {
+ GURL url(url_value.GetString());
+ if (!url.is_valid()) {
+ continue;
+ }
+
+ urls.push_back(url::Origin::Create(url));
+ }
+
+ if (urls.empty()) {
+ continue;
+ }
+
+ const base::Value* devices_value = item.FindKey(kPrefDevicesKey);
+ DCHECK(devices_value);
+ for (const auto& port_value : devices_value->GetList()) {
+ const base::Value* vendor_id_value = port_value.FindKey(kPrefVendorIdKey);
+ DCHECK(vendor_id_value);
+
+ const base::Value* product_id_value =
+ port_value.FindKey(kPrefProductIdKey);
+ // "product_id" is optional and the policy matches all devices with the
+ // given vendor ID if it is not specified.
+ if (product_id_value) {
+ usb_device_policy_[{vendor_id_value->GetInt(),
+ product_id_value->GetInt()}]
+ .insert(urls.begin(), urls.end());
+ } else {
+ usb_vendor_policy_[vendor_id_value->GetInt()].insert(urls.begin(),
+ urls.end());
+ }
+ }
+ }
+}
diff --git a/chrome/browser/serial/serial_policy_allowed_ports.h b/chrome/browser/serial/serial_policy_allowed_ports.h
new file mode 100644
index 0000000..46bbe8b
--- /dev/null
+++ b/chrome/browser/serial/serial_policy_allowed_ports.h
@@ -0,0 +1,70 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SERIAL_SERIAL_POLICY_ALLOWED_PORTS_H_
+#define CHROME_BROWSER_SERIAL_SERIAL_POLICY_ALLOWED_PORTS_H_
+
+#include <map>
+#include <set>
+
+#include "components/prefs/pref_change_registrar.h"
+#include "url/origin.h"
+
+namespace device {
+namespace mojom {
+class SerialPortInfo;
+} // namespace mojom
+} // namespace device
+
+namespace user_prefs {
+class PrefRegistrySyncable;
+}
+
+class PrefService;
+
+// This class is used to maintain and interpret the SerialAllowForUrls and
+// SerialAllowUsbDevicesForUrls policies.
+//
+// A PrefChangeRegistrar is used to observe changes to the preference values so
+// that the policy can be updated in real-time.
+class SerialPolicyAllowedPorts {
+ public:
+ explicit SerialPolicyAllowedPorts(PrefService* pref_service);
+ SerialPolicyAllowedPorts(SerialPolicyAllowedPorts& other) = delete;
+ SerialPolicyAllowedPorts& operator=(SerialPolicyAllowedPorts& other) = delete;
+ ~SerialPolicyAllowedPorts();
+
+ static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry);
+
+ // Checks if |origin| is allowed to use the port with |port_info|.
+ bool HasPortPermission(const url::Origin& origin,
+ const device::mojom::SerialPortInfo& port_info);
+
+ const std::map<std::pair<int, int>, std::set<url::Origin>>&
+ usb_device_policy() const {
+ return usb_device_policy_;
+ }
+ const std::map<int, std::set<url::Origin>>& usb_vendor_policy() const {
+ return usb_vendor_policy_;
+ }
+ const std::set<url::Origin>& all_ports_policy() const {
+ return all_ports_policy_;
+ }
+
+ private:
+ void LoadAllowAllPortsForUrlsPolicy();
+ void LoadAllowUsbDevicesForUrlsPolicy();
+
+ PrefChangeRegistrar pref_change_registrar_;
+
+ // Stores the current policy configuration for specific USB devices
+ // identified by vendor and product IDs (usb_device_policy_), all USB
+ // devices from a particular vendor ID (usb_vendor_policy_) and origins
+ // which are allowed to access all ports.
+ std::map<std::pair<int, int>, std::set<url::Origin>> usb_device_policy_;
+ std::map<int, std::set<url::Origin>> usb_vendor_policy_;
+ std::set<url::Origin> all_ports_policy_;
+};
+
+#endif // CHROME_BROWSER_SERIAL_SERIAL_POLICY_ALLOWED_PORTS_H_
diff --git a/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc b/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc
new file mode 100644
index 0000000..78520e6
--- /dev/null
+++ b/chrome/browser/serial/serial_policy_allowed_ports_unittest.cc
@@ -0,0 +1,339 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/serial/serial_policy_allowed_ports.h"
+
+#include "base/json/json_reader.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/test/base/testing_profile.h"
+#include "components/prefs/pref_service.h"
+#include "content/public/test/browser_task_environment.h"
+#include "services/device/public/mojom/serial.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+#include "url/origin.h"
+
+namespace {
+
+using ::testing::UnorderedElementsAre;
+
+base::Value ReadJson(base::StringPiece json) {
+ base::JSONReader::ValueWithError result =
+ base::JSONReader::ReadAndReturnValueWithError(json);
+ EXPECT_TRUE(result.value) << result.error_message;
+ return result.value ? std::move(*result.value) : base::Value();
+}
+
+device::mojom::SerialPortInfoPtr CreateUsbDevice(uint16_t vendor_id,
+ uint16_t product_id) {
+ auto port = device::mojom::SerialPortInfo::New();
+ port->token = base::UnguessableToken::Create();
+ port->has_vendor_id = true;
+ port->vendor_id = vendor_id;
+ port->has_product_id = true;
+ port->product_id = product_id;
+ return port;
+}
+
+device::mojom::SerialPortInfoPtr CreatePlatformPort() {
+ auto port = device::mojom::SerialPortInfo::New();
+ port->token = base::UnguessableToken::Create();
+ return port;
+}
+
+} // namespace
+
+class SerialPolicyAllowedPortsTest : public testing::Test {
+ public:
+ SerialPolicyAllowedPortsTest() = default;
+ ~SerialPolicyAllowedPortsTest() override = default;
+
+ void SetAllowAllPortsForUrlsPrefValue(const base::Value& value) {
+ profile_.GetPrefs()->Set(prefs::kManagedSerialAllowAllPortsForUrls, value);
+ }
+
+ void SetAllowUsbDevicesForUrlsPrefValue(const base::Value& value) {
+ profile_.GetPrefs()->Set(prefs::kManagedSerialAllowUsbDevicesForUrls,
+ value);
+ }
+
+ void InitializePolicy() {
+ EXPECT_FALSE(policy_);
+ policy_ = std::make_unique<SerialPolicyAllowedPorts>(profile_.GetPrefs());
+ }
+
+ protected:
+ SerialPolicyAllowedPorts* policy() { return policy_.get(); }
+
+ private:
+ content::BrowserTaskEnvironment task_environment_;
+ TestingProfile profile_;
+ std::unique_ptr<SerialPolicyAllowedPorts> policy_;
+};
+
+TEST_F(SerialPolicyAllowedPortsTest, InitializeWithMissingPrefValue) {
+ InitializePolicy();
+ EXPECT_TRUE(policy()->usb_device_policy().empty());
+ EXPECT_TRUE(policy()->usb_vendor_policy().empty());
+ EXPECT_TRUE(policy()->all_ports_policy().empty());
+}
+
+TEST_F(SerialPolicyAllowedPortsTest, InitializeWithEmptyPrefValues) {
+ SetAllowAllPortsForUrlsPrefValue(base::Value(base::Value::Type::LIST));
+ SetAllowUsbDevicesForUrlsPrefValue(base::Value(base::Value::Type::LIST));
+
+ InitializePolicy();
+ EXPECT_TRUE(policy()->usb_device_policy().empty());
+ EXPECT_TRUE(policy()->usb_vendor_policy().empty());
+ EXPECT_TRUE(policy()->all_ports_policy().empty());
+}
+
+TEST_F(SerialPolicyAllowedPortsTest, InitializeWithPrefValues) {
+ constexpr char kAllPortsPolicySetting[] = R"(["https://www.youtube.com"])";
+ constexpr char kUsbDevicesPolicySetting[] = R"(
+ [
+ {
+ "devices": [
+ { "vendor_id": 1234, "product_id": 5678 },
+ { "vendor_id": 4321 }
+ ],
+ "urls": [
+ "https://google.com",
+ "https://crbug.com"
+ ]
+ }
+ ])";
+ const auto kYoutubeOrigin =
+ url::Origin::Create(GURL("https://www.youtube.com"));
+ const auto kGoogleOrigin = url::Origin::Create(GURL("https://google.com"));
+ const auto kCrbugOrigin = url::Origin::Create(GURL("https://crbug.com"));
+
+ SetAllowAllPortsForUrlsPrefValue(ReadJson(kAllPortsPolicySetting));
+ SetAllowUsbDevicesForUrlsPrefValue(ReadJson(kUsbDevicesPolicySetting));
+ InitializePolicy();
+
+ EXPECT_EQ(1u, policy()->usb_device_policy().size());
+ EXPECT_EQ(1u, policy()->usb_vendor_policy().size());
+ EXPECT_EQ(1u, policy()->all_ports_policy().size());
+
+ EXPECT_THAT(policy()->all_ports_policy(),
+ UnorderedElementsAre(kYoutubeOrigin));
+
+ const auto device_key = std::make_pair(1234, 5678);
+ ASSERT_TRUE(base::Contains(policy()->usb_device_policy(), device_key));
+ EXPECT_THAT(policy()->usb_device_policy().at(device_key),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ ASSERT_TRUE(base::Contains(policy()->usb_vendor_policy(), 4321));
+ EXPECT_THAT(policy()->usb_vendor_policy().at(4321),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ auto vendor1_device = CreateUsbDevice(1234, 5678);
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor1_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor1_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor1_device));
+
+ auto vendor1_alternate_device = CreateUsbDevice(1234, 1234);
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kYoutubeOrigin, *vendor1_alternate_device));
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kGoogleOrigin, *vendor1_alternate_device));
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kCrbugOrigin, *vendor1_alternate_device));
+
+ auto vendor2_device = CreateUsbDevice(4321, 1234);
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor2_device));
+
+ auto platform_port = CreatePlatformPort();
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kGoogleOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kCrbugOrigin, *platform_port));
+}
+
+TEST_F(SerialPolicyAllowedPortsTest,
+ InitializeWithMissingPrefValuesThenUpdate) {
+ InitializePolicy();
+
+ constexpr char kAllPortsPolicySetting[] = R"(["https://www.youtube.com"])";
+ constexpr char kUsbDevicesPolicySetting[] = R"(
+ [
+ {
+ "devices": [
+ { "vendor_id": 1234, "product_id": 5678 },
+ { "vendor_id": 4321 }
+ ],
+ "urls": [
+ "https://google.com",
+ "https://crbug.com"
+ ]
+ }
+ ])";
+ const auto kYoutubeOrigin =
+ url::Origin::Create(GURL("https://www.youtube.com"));
+ const auto kGoogleOrigin = url::Origin::Create(GURL("https://google.com"));
+ const auto kCrbugOrigin = url::Origin::Create(GURL("https://crbug.com"));
+
+ SetAllowAllPortsForUrlsPrefValue(ReadJson(kAllPortsPolicySetting));
+ SetAllowUsbDevicesForUrlsPrefValue(ReadJson(kUsbDevicesPolicySetting));
+
+ EXPECT_EQ(1u, policy()->usb_device_policy().size());
+ EXPECT_EQ(1u, policy()->usb_vendor_policy().size());
+ EXPECT_EQ(1u, policy()->all_ports_policy().size());
+
+ EXPECT_THAT(policy()->all_ports_policy(),
+ UnorderedElementsAre(kYoutubeOrigin));
+
+ const auto device_key = std::make_pair(1234, 5678);
+ ASSERT_TRUE(base::Contains(policy()->usb_device_policy(), device_key));
+ EXPECT_THAT(policy()->usb_device_policy().at(device_key),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ ASSERT_TRUE(base::Contains(policy()->usb_vendor_policy(), 4321));
+ EXPECT_THAT(policy()->usb_vendor_policy().at(4321),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ auto vendor1_device = CreateUsbDevice(1234, 5678);
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor1_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor1_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor1_device));
+
+ auto vendor1_alternate_device = CreateUsbDevice(1234, 1234);
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kYoutubeOrigin, *vendor1_alternate_device));
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kGoogleOrigin, *vendor1_alternate_device));
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kCrbugOrigin, *vendor1_alternate_device));
+
+ auto vendor2_device = CreateUsbDevice(4321, 1234);
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor2_device));
+
+ auto platform_port = CreatePlatformPort();
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kGoogleOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kCrbugOrigin, *platform_port));
+}
+
+TEST_F(SerialPolicyAllowedPortsTest, InitializeWithPrefValuesThenRemovePolicy) {
+ constexpr char kAllPortsPolicySetting[] = R"(["https://www.youtube.com"])";
+ constexpr char kUsbDevicesPolicySetting[] = R"(
+ [
+ {
+ "devices": [
+ { "vendor_id": 1234, "product_id": 5678 },
+ { "vendor_id": 4321 }
+ ],
+ "urls": [
+ "https://google.com",
+ "https://crbug.com"
+ ]
+ }
+ ])";
+
+ SetAllowAllPortsForUrlsPrefValue(ReadJson(kAllPortsPolicySetting));
+ SetAllowUsbDevicesForUrlsPrefValue(ReadJson(kUsbDevicesPolicySetting));
+ InitializePolicy();
+
+ SetAllowAllPortsForUrlsPrefValue(base::Value(base::Value::Type::LIST));
+ SetAllowUsbDevicesForUrlsPrefValue(base::Value(base::Value::Type::LIST));
+ EXPECT_TRUE(policy()->usb_device_policy().empty());
+ EXPECT_TRUE(policy()->usb_vendor_policy().empty());
+ EXPECT_TRUE(policy()->all_ports_policy().empty());
+}
+
+TEST_F(SerialPolicyAllowedPortsTest, MultipleItemsWithOverlap) {
+ constexpr char kUsbDevicesPolicySetting[] = R"(
+ [
+ {
+ "devices": [
+ { "vendor_id": 1234, "product_id": 5678 },
+ { "vendor_id": 4321 }
+ ],
+ "urls": [
+ "https://google.com",
+ "https://crbug.com"
+ ]
+ },
+ {
+ "devices": [
+ { "vendor_id": 1234 },
+ { "vendor_id": 4321, "product_id": 8765 }
+ ],
+ "urls": [
+ "https://crbug.com",
+ "https://www.youtube.com"
+ ]
+ }
+ ])";
+ const auto kGoogleOrigin = url::Origin::Create(GURL("https://google.com"));
+ const auto kCrbugOrigin = url::Origin::Create(GURL("https://crbug.com"));
+ const auto kYoutubeOrigin =
+ url::Origin::Create(GURL("https://www.youtube.com"));
+
+ SetAllowUsbDevicesForUrlsPrefValue(ReadJson(kUsbDevicesPolicySetting));
+ InitializePolicy();
+
+ EXPECT_EQ(2u, policy()->usb_device_policy().size());
+ EXPECT_EQ(2u, policy()->usb_vendor_policy().size());
+ EXPECT_EQ(0u, policy()->all_ports_policy().size());
+
+ const auto device1_key = std::make_pair(1234, 5678);
+ ASSERT_TRUE(base::Contains(policy()->usb_device_policy(), device1_key));
+ EXPECT_THAT(policy()->usb_device_policy().at(device1_key),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ const auto device2_key = std::make_pair(4321, 8765);
+ ASSERT_TRUE(base::Contains(policy()->usb_device_policy(), device2_key));
+ EXPECT_THAT(policy()->usb_device_policy().at(device2_key),
+ UnorderedElementsAre(kCrbugOrigin, kYoutubeOrigin));
+
+ ASSERT_TRUE(base::Contains(policy()->usb_vendor_policy(), 1234));
+ EXPECT_THAT(policy()->usb_vendor_policy().at(1234),
+ UnorderedElementsAre(kCrbugOrigin, kYoutubeOrigin));
+
+ ASSERT_TRUE(base::Contains(policy()->usb_vendor_policy(), 4321));
+ EXPECT_THAT(policy()->usb_vendor_policy().at(4321),
+ UnorderedElementsAre(kGoogleOrigin, kCrbugOrigin));
+
+ auto vendor1_device1 = CreateUsbDevice(1234, 5678);
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor1_device1));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor1_device1));
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor1_device1));
+
+ auto vendor1_alternate_device = CreateUsbDevice(1234, 1234);
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kGoogleOrigin, *vendor1_alternate_device));
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kCrbugOrigin, *vendor1_alternate_device));
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kYoutubeOrigin, *vendor1_alternate_device));
+
+ auto vendor2_device = CreateUsbDevice(4321, 8765);
+ EXPECT_TRUE(policy()->HasPortPermission(kGoogleOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kCrbugOrigin, *vendor2_device));
+ EXPECT_TRUE(policy()->HasPortPermission(kYoutubeOrigin, *vendor2_device));
+
+ auto vendor2_alternate_device = CreateUsbDevice(4321, 1234);
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kGoogleOrigin, *vendor2_alternate_device));
+ EXPECT_TRUE(
+ policy()->HasPortPermission(kCrbugOrigin, *vendor2_alternate_device));
+ EXPECT_FALSE(
+ policy()->HasPortPermission(kYoutubeOrigin, *vendor2_alternate_device));
+
+ auto vendor3_device = CreateUsbDevice(9012, 3456);
+ EXPECT_FALSE(policy()->HasPortPermission(kGoogleOrigin, *vendor3_device));
+ EXPECT_FALSE(policy()->HasPortPermission(kCrbugOrigin, *vendor3_device));
+ EXPECT_FALSE(policy()->HasPortPermission(kYoutubeOrigin, *vendor3_device));
+
+ auto platform_port = CreatePlatformPort();
+ EXPECT_FALSE(policy()->HasPortPermission(kGoogleOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kCrbugOrigin, *platform_port));
+ EXPECT_FALSE(policy()->HasPortPermission(kYoutubeOrigin, *platform_port));
+}
diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc
index ea96052..b64008c 100644
--- a/chrome/common/pref_names.cc
+++ b/chrome/common/pref_names.cc
@@ -118,6 +118,16 @@
// Boolean that is true when user feedback to Google is allowed.
const char kUserFeedbackAllowed[] = "feedback_allowed";
+#if !defined(OS_ANDROID)
+// Used to store the value of the SerialAllowAllPortsForUrls policy.
+const char kManagedSerialAllowAllPortsForUrls[] =
+ "profile.managed.serial_allow_all_ports_for_urls";
+
+// Used to store the value of the SerialAllowUsbDevicesForUrls policy.
+const char kManagedSerialAllowUsbDevicesForUrls[] =
+ "profile.managed.serial_allow_usb_devices_for_urls";
+#endif // !defined(OS_ANDROID)
+
#if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
// DictionaryValue that maps extension ids to the approved version of this
// extension for a supervised user. Missing extensions are not approved.
diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h
index 2556a59d7..a1f3d357 100644
--- a/chrome/common/pref_names.h
+++ b/chrome/common/pref_names.h
@@ -43,6 +43,10 @@
extern const char kRestoreOnStartup[];
extern const char kSessionExitedCleanly[];
extern const char kSessionExitType[];
+#if !defined(OS_ANDROID)
+extern const char kManagedSerialAllowAllPortsForUrls[];
+extern const char kManagedSerialAllowUsbDevicesForUrls[];
+#endif // !defined(OS_ANDROID)
#if BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
extern const char kSupervisedUserApprovedExtensions[];
#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) && BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index ab156d7a..7ee32ad 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -4627,6 +4627,7 @@
"../browser/send_tab_to_self/send_tab_to_self_util_unittest.cc",
"../browser/serial/serial_blocklist_unittest.cc",
"../browser/serial/serial_chooser_context_unittest.cc",
+ "../browser/serial/serial_policy_allowed_ports_unittest.cc",
"../browser/sessions/tab_restore_service_unittest.cc",
"../browser/signin/signin_promo_unittest.cc",
"../browser/speech/extension_api/extension_manifests_tts_unittest.cc",
diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json
index f010119..849320593 100644
--- a/chrome/test/data/policy/policy_test_cases.json
+++ b/chrome/test/data/policy/policy_test_cases.json
@@ -2563,6 +2563,35 @@
]
},
+ "SerialAllowAllPortsForUrls": {
+ "os": ["win", "linux", "mac", "chromeos"],
+ "policy_pref_mapping_tests": [
+ {
+ "policies": {"SerialAllowAllPortsForUrls": ["https://www.google.com"]},
+ "prefs": { "profile.managed.serial_allow_all_ports_for_urls": {} }
+ }
+ ]
+ },
+
+ "SerialAllowUsbDevicesForUrls": {
+ "os": ["win", "linux", "mac", "chromeos"],
+ "policy_pref_mapping_tests": [
+ {
+ "policies": {"SerialAllowUsbDevicesForUrls": [
+ {
+ "devices": [{"vendor_id": 6353, "product_id": 19985}],
+ "urls": ["https://flash.android.com"]
+ },
+ {
+ "devices": [{"vendor_id": 6353}],
+ "urls": ["https://www.google.com"]
+ }
+ ]},
+ "prefs": { "profile.managed.serial_allow_usb_devices_for_urls": {} }
+ }
+ ]
+ },
+
"Disable3DAPIs": {
"os": ["win", "linux", "mac", "chromeos"],
"policy_pref_mapping_tests": [
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index 7f9f1e5..4b3c6fe 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -574,6 +574,8 @@
'WebUsbBlockedForUrls',
'SerialAskForUrls',
'SerialBlockedForUrls',
+ 'SerialAllowAllPortsForUrls',
+ 'SerialAllowUsbDevicesForUrls',
],
},
{
@@ -6155,9 +6157,9 @@
Deprecated: The USB permission model used to support specifying both the requesting and embedding URLs. This is deprecated and only supported for backwards compatiblity in this manner: if both a requesting and embedding URL is specified, then the embedding URL will be granted the permission as top-level origin and the requsting URL will be ignored entirely.
- Leaving the policy unset means <ph name="DEFAULT_WEB_USB_GUARD_SETTING_POLICY_NAME">DefaultWebUsbGuardSetting</ph> applies, if it's set. If not, the user's personal setting applies.
+ This policy overrides <ph name="DEFAULT_WEB_USB_GUARD_SETTING_POLICY_NAME">DefaultWebUsbGuardSetting</ph>, <ph name="WEB_USB_ASK_FOR_URLS_POLICY_NAME">WebUsbAskForUrls</ph>, <ph name="WEB_USB_BLOCKED_FOR_URLS_POLICY_NAME">WebUsbBlockedForUrls</ph> and the user's preferences.
- URL patterns in this policy shouldn't conflict with those configured through <ph name="WEB_USB_BLOCKED_FOR_URLS_POLICY_NAME">WebUsbBlockedForUrls</ph>. If they do, this policy takes precedence over <ph name="WEB_USB_BLOCKED_FOR_URLS_POLICY_NAME">WebUsbBlockedForUrls</ph> and <ph name="WEB_USB_ASK_FOR_URLS_POLICY_NAME">WebUsbAskForUrls</ph>.''',
+ This policy only affects access to USB devices through the WebUSB API. To grant access to USB devices through the Web Serial API see the <ph name="SERIAL_ALLOW_USB_DEVICES_FOR_URLS_POLICY_NAME">SerialAllowUsbDevicesForUrls</ph> policy.''',
},
{
'name': 'DeviceLoginScreenWebUsbAllowDevicesForUrls',
@@ -6346,6 +6348,83 @@
For detailed information on valid <ph name="URL_LABEL">url</ph> patterns, please see https://cloud.google.com/docs/chrome-enterprise/policies/url-patterns. <ph name="WILDCARD_VALUE">*</ph> is not an accepted value for this policy.''',
},
{
+ 'name': 'SerialAllowAllPortsForUrls',
+ 'owners': ['[email protected]', 'file://content/browser/serial/OWNERS'],
+ 'type': 'list',
+ 'schema': {
+ 'type': 'array',
+ 'items': { 'type': 'string' },
+ },
+ 'future_on': ['chrome_os', 'chrome.*'],
+ 'features': {
+ 'dynamic_refresh': True,
+ 'per_profile': True,
+ },
+ 'example_value': ['https://www.example.com'],
+ 'id': 837,
+ 'caption': '''Automatically grant permission to sites to connect all serial ports.''',
+ 'tags': [],
+ 'desc': '''Setting the policy allows you to list sites which are automatically granted permission to access all available serial ports.
+
+ The URLs must be valid, otherwise the policy is ignored. Only the origin (scheme, host and port) of the URL is considered.
+
+ This policy overrides <ph name="DEFAULT_SERIAL_GUARD_SETTING_POLICY_NAME">DefaultSerialGuardSetting</ph>, <ph name="SERIAL_ASK_FOR_URLS_POLICY_NAME">SerialAskForUrls</ph>, <ph name="SERIAL_BLOCKED_FOR_URLS_POLICY_NAME">SerialBlockedForUrls</ph> and the user's preferences.''',
+ },
+ {
+ 'name': 'SerialAllowUsbDevicesForUrls',
+ 'owners': ['[email protected]', 'file://content/browser/serial/OWNERS'],
+ 'type': 'dict',
+ 'schema': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'devices': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'object',
+ 'properties': {
+ 'vendor_id': { 'type': 'integer', 'minimum': 0, 'maximum': 65535 },
+ 'product_id': { 'type': 'integer', 'minimum': 0, 'maximum': 65535 }
+ },
+ 'required': ['vendor_id']
+ }
+ },
+ 'urls': {
+ 'type': 'array',
+ 'items': { 'type': 'string' }
+ }
+ },
+ 'required': ['devices', 'urls']
+ }
+ },
+ 'future_on': ['chrome_os', 'chrome.*'],
+ 'features': {
+ 'dynamic_refresh': True,
+ 'per_profile': True,
+ },
+ 'example_value': [
+ {
+ 'devices': [{'vendor_id': 1234, 'product_id': 5678}],
+ 'urls': ['https://specific-device.example.com']
+ },
+ {
+ 'devices': [{'vendor_id': 1234}],
+ 'urls': ['https://all-vendor-devices.example.com']
+ }
+ ],
+ 'id': 838,
+ 'caption': '''Automatically grant permission to sites to connect to USB serial devices.''',
+ 'tags': ['website-sharing'],
+ 'desc': '''Setting the policy allows you to list sites which are automatically granted permission to access USB serial devices with vendor and product IDs matching the <ph name="VENDOR_ID_FIELD_NAME">vendor_id</ph> and <ph name="PRODUCT_ID_FIELD_NAME">product_id</ph> fields. Omitting the <ph name="PRODUCT_ID_FIELD_NAME">product_id</ph> field allows the given sites permission to access devices with a vendor ID matching the <ph name="VENDOR_ID_FIELD_NAME">vendor_id</ph> field and any product ID.
+
+ The URLs must be valid, otherwise the policy is ignored. Only the origin (scheme, host and port) of the URL is considered.
+
+ This policy overrides <ph name="DEFAULT_SERIAL_GUARD_SETTING_POLICY_NAME">DefaultSerialGuardSetting</ph>, <ph name="SERIAL_ASK_FOR_URLS_POLICY_NAME">SerialAskForUrls</ph>, <ph name="SERIAL_BLOCKED_FOR_URLS_POLICY_NAME">SerialBlockedForUrls</ph> and the user's preferences.
+
+ This policy only affects access to USB devices through the Web Serial API. To grant access to USB devices through the WebUSB API see the <ph name="WEB_USB_ALLOW_DEVICES_FOR_URLS_POLICY_NAME">WebUsbAllowDevicesForUrls</ph> policy.''',
+ },
+ {
'name': 'DefaultFileSystemReadGuardSetting',
'owners': ['[email protected]', 'file://content/browser/file_system_access/OWNERS'],
'type': 'int-enum',
@@ -25984,6 +26063,6 @@
'placeholders': [],
'deleted_policy_ids': [114, 115, 204, 205, 206, 412, 476, 544, 546, 562, 569, 578, 583, 585, 586, 587, 588, 589, 590, 591, 600, 668, 669],
'deleted_atomic_policy_group_ids': [19],
- 'highest_id_currently_used': 836,
+ 'highest_id_currently_used': 838,
'highest_atomic_group_id_currently_used': 40
}
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index a8939c5..b6a4a40 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -23903,6 +23903,8 @@
<int value="834" label="SamlLockScreenOfflineSigninTimeLimitDays"/>
<int value="835" label="ReportDevicePrintJobs"/>
<int value="836" label="AudioProcessHighPriorityEnabled"/>
+ <int value="837" label="SerialAllowAllPortsForUrls"/>
+ <int value="838" label="SerialAllowUsbDevicesForUrls"/>
</enum>
<enum name="EnterprisePolicyDeviceIdValidity">