1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/bluetooth/bluetooth_blocklist.h"
6
7 #include "base/check.h"
8 #include "base/metrics/histogram_macros.h"
9 #include "base/optional.h"
10 #include "base/strings/string_split.h"
11 #include "content/public/browser/content_browser_client.h"
12 #include "content/public/common/content_client.h"
13
14 using device::BluetoothUUID;
15
16 namespace {
17
18 static base::LazyInstance<content::BluetoothBlocklist>::Leaky g_singleton =
19 LAZY_INSTANCE_INITIALIZER;
20
21 } // namespace
22
23 namespace content {
24
~BluetoothBlocklist()25 BluetoothBlocklist::~BluetoothBlocklist() {}
26
27 // static
Get()28 BluetoothBlocklist& BluetoothBlocklist::Get() {
29 return g_singleton.Get();
30 }
31
Add(const BluetoothUUID & uuid,Value value)32 void BluetoothBlocklist::Add(const BluetoothUUID& uuid, Value value) {
33 CHECK(uuid.IsValid());
34 auto insert_result = blocklisted_uuids_.insert(std::make_pair(uuid, value));
35 bool inserted = insert_result.second;
36 if (!inserted) {
37 Value& stored = insert_result.first->second;
38 if (stored != value)
39 stored = Value::EXCLUDE;
40 }
41 }
42
Add(base::StringPiece blocklist_string)43 void BluetoothBlocklist::Add(base::StringPiece blocklist_string) {
44 if (blocklist_string.empty())
45 return;
46 base::StringPairs kv_pairs;
47 bool parsed_values = false;
48 bool invalid_values = false;
49 SplitStringIntoKeyValuePairs(blocklist_string,
50 ':', // Key-value delimiter
51 ',', // Key-value pair delimiter
52 &kv_pairs);
53 for (const auto& pair : kv_pairs) {
54 BluetoothUUID uuid(pair.first);
55 if (uuid.IsValid() && pair.second.size() == 1u) {
56 switch (pair.second[0]) {
57 case 'e':
58 Add(uuid, Value::EXCLUDE);
59 parsed_values = true;
60 continue;
61 case 'r':
62 Add(uuid, Value::EXCLUDE_READS);
63 parsed_values = true;
64 continue;
65 case 'w':
66 Add(uuid, Value::EXCLUDE_WRITES);
67 parsed_values = true;
68 continue;
69 }
70 }
71 invalid_values = true;
72 }
73 }
74
IsExcluded(const BluetoothUUID & uuid) const75 bool BluetoothBlocklist::IsExcluded(const BluetoothUUID& uuid) const {
76 CHECK(uuid.IsValid());
77 const auto& it = blocklisted_uuids_.find(uuid);
78 if (it == blocklisted_uuids_.end())
79 return false;
80 return it->second == Value::EXCLUDE;
81 }
82
IsExcluded(const std::vector<blink::mojom::WebBluetoothLeScanFilterPtr> & filters)83 bool BluetoothBlocklist::IsExcluded(
84 const std::vector<blink::mojom::WebBluetoothLeScanFilterPtr>& filters) {
85 for (const blink::mojom::WebBluetoothLeScanFilterPtr& filter : filters) {
86 if (!filter->services) {
87 continue;
88 }
89 for (const BluetoothUUID& service : filter->services.value()) {
90 if (IsExcluded(service)) {
91 return true;
92 }
93 }
94 }
95 return false;
96 }
97
IsExcludedFromReads(const BluetoothUUID & uuid) const98 bool BluetoothBlocklist::IsExcludedFromReads(const BluetoothUUID& uuid) const {
99 CHECK(uuid.IsValid());
100 const auto& it = blocklisted_uuids_.find(uuid);
101 if (it == blocklisted_uuids_.end())
102 return false;
103 return it->second == Value::EXCLUDE || it->second == Value::EXCLUDE_READS;
104 }
105
IsExcludedFromWrites(const BluetoothUUID & uuid) const106 bool BluetoothBlocklist::IsExcludedFromWrites(const BluetoothUUID& uuid) const {
107 CHECK(uuid.IsValid());
108 const auto& it = blocklisted_uuids_.find(uuid);
109 if (it == blocklisted_uuids_.end())
110 return false;
111 return it->second == Value::EXCLUDE || it->second == Value::EXCLUDE_WRITES;
112 }
113
RemoveExcludedUUIDs(blink::mojom::WebBluetoothRequestDeviceOptions * options)114 void BluetoothBlocklist::RemoveExcludedUUIDs(
115 blink::mojom::WebBluetoothRequestDeviceOptions* options) {
116 std::vector<device::BluetoothUUID> optional_services_blocklist_filtered;
117 for (const BluetoothUUID& uuid : options->optional_services) {
118 if (!IsExcluded(uuid)) {
119 optional_services_blocklist_filtered.push_back(uuid);
120 }
121 }
122 options->optional_services = std::move(optional_services_blocklist_filtered);
123 }
124
ResetToDefaultValuesForTest()125 void BluetoothBlocklist::ResetToDefaultValuesForTest() {
126 blocklisted_uuids_.clear();
127 PopulateWithDefaultValues();
128 PopulateWithServerProvidedValues();
129 }
130
BluetoothBlocklist()131 BluetoothBlocklist::BluetoothBlocklist() {
132 PopulateWithDefaultValues();
133 PopulateWithServerProvidedValues();
134 }
135
PopulateWithDefaultValues()136 void BluetoothBlocklist::PopulateWithDefaultValues() {
137 blocklisted_uuids_.clear();
138
139 // Testing from Web Tests Note:
140 //
141 // Random UUIDs for object & exclude permutations that do not exist in the
142 // standard blocklist are included to facilitate integration testing from
143 // Web Tests. Unit tests can dynamically modify the blocklist, but don't
144 // offer the full integration test to the Web Bluetooth Javascript bindings.
145 //
146 // This is done for simplicity as opposed to exposing a testing API that can
147 // add to the blocklist over time, which would be over engineered.
148 //
149 // Remove testing UUIDs if the specified blocklist is updated to include UUIDs
150 // that match the specific permutations.
151 DCHECK(BluetoothUUID("00001800-0000-1000-8000-00805f9b34fb") ==
152 BluetoothUUID("1800"));
153
154 // Blocklist UUIDs updated 2016-09-01 from:
155 // https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt
156 // Short UUIDs are used for readability of this list.
157 //
158 // Services:
159 Add(BluetoothUUID("1812"), Value::EXCLUDE);
160 Add(BluetoothUUID("00001530-1212-efde-1523-785feabcd123"), Value::EXCLUDE);
161 Add(BluetoothUUID("f000ffc0-0451-4000-b000-000000000000"), Value::EXCLUDE);
162 Add(BluetoothUUID("00060000"), Value::EXCLUDE);
163 Add(BluetoothUUID("fffd"), Value::EXCLUDE);
164 // Characteristics:
165 Add(BluetoothUUID("2a02"), Value::EXCLUDE_WRITES);
166 Add(BluetoothUUID("2a03"), Value::EXCLUDE);
167 Add(BluetoothUUID("2a25"), Value::EXCLUDE);
168 // Characteristics for Web Tests:
169 Add(BluetoothUUID("bad1c9a2-9a5b-4015-8b60-1579bbbf2135"),
170 Value::EXCLUDE_READS);
171 // Descriptors:
172 Add(BluetoothUUID("2902"), Value::EXCLUDE_WRITES);
173 Add(BluetoothUUID("2903"), Value::EXCLUDE_WRITES);
174 // Descriptors for Web Tests:
175 Add(BluetoothUUID("bad2ddcf-60db-45cd-bef9-fd72b153cf7c"), Value::EXCLUDE);
176 Add(BluetoothUUID("bad3ec61-3cc3-4954-9702-7977df514114"),
177 Value::EXCLUDE_READS);
178 }
179
PopulateWithServerProvidedValues()180 void BluetoothBlocklist::PopulateWithServerProvidedValues() {
181 Add(GetContentClient()->browser()->GetWebBluetoothBlocklist());
182 }
183
184 } // namespace content
185