chore: Show FIDO devices in the chooser if allowed (#40216)

* chore: Show FIDO devices in the chooser if allowed

* chore: tweak HidChooserContext::IsFidoAllowedForOrigin

* chore: feedback from review

---------

Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
Shelley Vohr 2023-10-19 01:19:51 +02:00 committed by GitHub
parent 32a721fa2b
commit 025af3500c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 37 deletions

View file

@ -85,12 +85,16 @@ HidChooserController::HidChooserController(
filters_(std::move(filters)),
exclusion_filters_(std::move(exclusion_filters)),
callback_(std::move(callback)),
initiator_document_(render_frame_host->GetWeakDocumentPtr()),
origin_(content::WebContents::FromRenderFrameHost(render_frame_host)
->GetPrimaryMainFrame()
->GetLastCommittedOrigin()),
frame_tree_node_id_(render_frame_host->GetFrameTreeNodeId()),
hid_delegate_(hid_delegate),
render_frame_host_id_(render_frame_host->GetGlobalId()) {
// The use above of GetMainFrame is safe as content::HidService instances are
// not created for fenced frames.
DCHECK(!render_frame_host->IsNestedWithinFencedFrame());
chooser_context_ = HidChooserContextFactory::GetForBrowserContext(
web_contents->GetBrowserContext())
->AsWeakPtr();
@ -129,6 +133,7 @@ void HidChooserController::OnDeviceAdded(
const device::mojom::HidDeviceInfo& device) {
if (!DisplayDevice(device))
return;
if (AddDeviceInfo(device)) {
api::Session* session = GetSession();
if (session) {
@ -142,8 +147,6 @@ void HidChooserController::OnDeviceAdded(
session->Emit("hid-device-added", details);
}
}
return;
}
void HidChooserController::OnDeviceRemoved(
@ -151,7 +154,8 @@ void HidChooserController::OnDeviceRemoved(
if (!base::Contains(items_, PhysicalDeviceIdFromDeviceInfo(device)))
return;
if (api::Session* session = GetSession(); session != nullptr) {
api::Session* session = GetSession();
if (session) {
auto* rfh = content::RenderFrameHost::FromID(render_frame_host_id_);
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
@ -223,9 +227,8 @@ void HidChooserController::OnGotDevices(
for (auto& device : devices) {
if (DisplayDevice(*device)) {
if (AddDeviceInfo(*device)) {
if (AddDeviceInfo(*device))
devicesToDisplay.push_back(device->Clone());
}
}
}
@ -233,6 +236,7 @@ void HidChooserController::OnGotDevices(
// enumeration.
if (chooser_context_)
observation_.Observe(chooser_context_.get());
bool prevent_default = false;
api::Session* session = GetSession();
if (session) {
@ -255,26 +259,41 @@ void HidChooserController::OnGotDevices(
bool HidChooserController::DisplayDevice(
const device::mojom::HidDeviceInfo& device) const {
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableHidBlocklist)) {
// Do not pass the device to the chooser if it is excluded by the blocklist.
if (device.is_excluded_by_blocklist)
return false;
// Check if `device` has a top-level collection with a FIDO usage. FIDO
// devices may be displayed if the origin is privileged or the blocklist is
// disabled.
const bool has_fido_collection =
base::Contains(device.collections, device::mojom::kPageFido,
[](const auto& c) { return c->usage->usage_page; });
// Do not pass the device to the chooser if it has a top-level collection
// with the FIDO usage page.
//
// Note: The HID blocklist also blocks top-level collections with the FIDO
// usage page, but will not block the device if it has other (non-FIDO)
// collections. The check below will exclude the device from the chooser
// if it has any top-level FIDO collection.
auto find_it =
std::find_if(device.collections.begin(), device.collections.end(),
[](const device::mojom::HidCollectionInfoPtr& c) {
return c->usage->usage_page == device::mojom::kPageFido;
});
if (find_it != device.collections.end())
return false;
if (has_fido_collection) {
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableHidBlocklist) ||
(chooser_context_ &&
chooser_context_->IsFidoAllowedForOrigin(origin_))) {
return FilterMatchesAny(device) && !IsExcluded(device);
}
AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
base::StringPrintf(
"Chooser dialog is not displaying a FIDO HID device: vendorId=%d, "
"productId=%d, name='%s', serial='%s'",
device.vendor_id, device.product_id, device.product_name.c_str(),
device.serial_number.c_str()));
return false;
}
if (device.is_excluded_by_blocklist) {
AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kInfo,
base::StringPrintf(
"Chooser dialog is not displaying a device excluded by "
"the HID blocklist: vendorId=%d, "
"productId=%d, name='%s', serial='%s'",
device.vendor_id, device.product_id, device.product_name.c_str(),
device.serial_number.c_str()));
return false;
}
return FilterMatchesAny(device) && !IsExcluded(device);
@ -303,6 +322,15 @@ bool HidChooserController::IsExcluded(
return false;
}
void HidChooserController::AddMessageToConsole(
blink::mojom::ConsoleMessageLevel level,
const std::string& message) const {
if (content::RenderFrameHost* rfh =
initiator_document_.AsRenderFrameHostIfValid()) {
rfh->AddMessageToConsole(level, message);
}
}
bool HidChooserController::AddDeviceInfo(
const device::mojom::HidDeviceInfo& device) {
const auto& id = PhysicalDeviceIdFromDeviceInfo(device);
@ -340,10 +368,8 @@ void HidChooserController::UpdateDeviceInfo(
auto physical_device_it = device_map_.find(id);
DCHECK(physical_device_it != device_map_.end());
auto& device_infos = physical_device_it->second;
auto device_it = base::ranges::find_if(
device_infos, [&device](const device::mojom::HidDeviceInfoPtr& d) {
return d->guid == device.guid;
});
auto device_it = base::ranges::find(device_infos, device.guid,
&device::mojom::HidDeviceInfo::guid);
DCHECK(device_it != device_infos.end());
*device_it = device.Clone();
}