| // Copyright 2015 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 <vector> |
| |
| #include "base/bind_helpers.h" |
| #include "base/json/json_reader.h" |
| #include "base/run_loop.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/usb/usb_chooser_context.h" |
| #include "chrome/browser/usb/usb_chooser_context_factory.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" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "device/usb/public/cpp/fake_usb_device_manager.h" |
| #include "device/usb/public/mojom/device.mojom.h" |
| #include "device/usb/public/mojom/device_manager.mojom.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using device::mojom::UsbDeviceInfoPtr; |
| |
| namespace { |
| |
| class UsbChooserContextTest : public testing::Test { |
| public: |
| UsbChooserContextTest() {} |
| ~UsbChooserContextTest() override {} |
| |
| protected: |
| Profile* profile() { return &profile_; } |
| |
| UsbChooserContext* GetChooserContext(Profile* profile) { |
| auto* chooser_context = UsbChooserContextFactory::GetForProfile(profile); |
| device::mojom::UsbDeviceManagerPtr device_manager_ptr; |
| device_manager_.AddBinding(mojo::MakeRequest(&device_manager_ptr)); |
| chooser_context->SetDeviceManagerForTesting(std::move(device_manager_ptr)); |
| |
| // Call GetDevices once to make sure the connection with DeviceManager has |
| // been set up, so that it can be notified when device is removed. |
| chooser_context->GetDevices( |
| base::DoNothing::Once<std::vector<UsbDeviceInfoPtr>>()); |
| base::RunLoop().RunUntilIdle(); |
| return chooser_context; |
| } |
| |
| device::FakeUsbDeviceManager device_manager_; |
| |
| private: |
| content::TestBrowserThreadBundle thread_bundle_; |
| TestingProfile profile_; |
| }; |
| |
| } // namespace |
| |
| TEST_F(UsbChooserContextTest, CheckGrantAndRevokePermission) { |
| GURL origin("https://www.google.com"); |
| UsbDeviceInfoPtr device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC"); |
| UsbChooserContext* store = GetChooserContext(profile()); |
| |
| base::DictionaryValue object_dict; |
| object_dict.SetString("name", "Gizmo"); |
| object_dict.SetInteger("vendor-id", 0); |
| object_dict.SetInteger("product-id", 0); |
| object_dict.SetString("serial-number", "123ABC"); |
| |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| store->GrantDevicePermission(origin, origin, *device_info); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info)); |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(origin, origin); |
| ASSERT_EQ(1u, objects.size()); |
| EXPECT_TRUE(object_dict.Equals(objects[0].get())); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects = |
| store->GetAllGrantedObjects(); |
| ASSERT_EQ(1u, all_origin_objects.size()); |
| EXPECT_EQ(origin, all_origin_objects[0]->requesting_origin); |
| EXPECT_EQ(origin, all_origin_objects[0]->embedding_origin); |
| EXPECT_TRUE(object_dict.Equals(&all_origin_objects[0]->object)); |
| EXPECT_FALSE(all_origin_objects[0]->incognito); |
| |
| store->RevokeObjectPermission(origin, origin, *objects[0]); |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(0u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(0u, all_origin_objects.size()); |
| } |
| |
| TEST_F(UsbChooserContextTest, CheckGrantAndRevokeEphemeralPermission) { |
| GURL origin("https://www.google.com"); |
| UsbDeviceInfoPtr device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| UsbDeviceInfoPtr other_device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| |
| UsbChooserContext* store = GetChooserContext(profile()); |
| |
| base::DictionaryValue object_dict; |
| object_dict.SetString("name", "Gizmo"); |
| object_dict.SetString("ephemeral-guid", device_info->guid); |
| |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| store->GrantDevicePermission(origin, origin, *device_info); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info)); |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *other_device_info)); |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| EXPECT_TRUE(object_dict.Equals(objects[0].get())); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects = |
| store->GetAllGrantedObjects(); |
| EXPECT_EQ(1u, all_origin_objects.size()); |
| EXPECT_EQ(origin, all_origin_objects[0]->requesting_origin); |
| EXPECT_EQ(origin, all_origin_objects[0]->embedding_origin); |
| EXPECT_TRUE(object_dict.Equals(&all_origin_objects[0]->object)); |
| EXPECT_FALSE(all_origin_objects[0]->incognito); |
| |
| store->RevokeObjectPermission(origin, origin, *objects[0]); |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(0u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(0u, all_origin_objects.size()); |
| } |
| |
| TEST_F(UsbChooserContextTest, DisconnectDeviceWithPermission) { |
| GURL origin("https://www.google.com"); |
| UsbDeviceInfoPtr device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC"); |
| |
| UsbChooserContext* store = GetChooserContext(profile()); |
| |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| store->GrantDevicePermission(origin, origin, *device_info); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info)); |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects = |
| store->GetAllGrantedObjects(); |
| EXPECT_EQ(1u, all_origin_objects.size()); |
| |
| device_manager_.RemoveDevice(device_info->guid); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(1u, all_origin_objects.size()); |
| |
| UsbDeviceInfoPtr reconnected_device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "123ABC"); |
| |
| EXPECT_TRUE( |
| store->HasDevicePermission(origin, origin, *reconnected_device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(1u, all_origin_objects.size()); |
| } |
| |
| TEST_F(UsbChooserContextTest, DisconnectDeviceWithEphemeralPermission) { |
| GURL origin("https://www.google.com"); |
| UsbDeviceInfoPtr device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| |
| UsbChooserContext* store = GetChooserContext(profile()); |
| |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| store->GrantDevicePermission(origin, origin, *device_info); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info)); |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects = |
| store->GetAllGrantedObjects(); |
| EXPECT_EQ(1u, all_origin_objects.size()); |
| |
| device_manager_.RemoveDevice(device_info->guid); |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(0u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(0u, all_origin_objects.size()); |
| |
| UsbDeviceInfoPtr reconnected_device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| |
| EXPECT_FALSE( |
| store->HasDevicePermission(origin, origin, *reconnected_device_info)); |
| objects = store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(0u, objects.size()); |
| all_origin_objects = store->GetAllGrantedObjects(); |
| EXPECT_EQ(0u, all_origin_objects.size()); |
| } |
| |
| TEST_F(UsbChooserContextTest, GrantPermissionInIncognito) { |
| GURL origin("https://www.google.com"); |
| UsbDeviceInfoPtr device_info_1 = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| UsbDeviceInfoPtr device_info_2 = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| UsbChooserContext* store = GetChooserContext(profile()); |
| UsbChooserContext* incognito_store = |
| GetChooserContext(profile()->GetOffTheRecordProfile()); |
| |
| store->GrantDevicePermission(origin, origin, *device_info_1); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info_1)); |
| EXPECT_FALSE( |
| incognito_store->HasDevicePermission(origin, origin, *device_info_1)); |
| |
| incognito_store->GrantDevicePermission(origin, origin, *device_info_2); |
| EXPECT_TRUE(store->HasDevicePermission(origin, origin, *device_info_1)); |
| EXPECT_FALSE(store->HasDevicePermission(origin, origin, *device_info_2)); |
| EXPECT_FALSE( |
| incognito_store->HasDevicePermission(origin, origin, *device_info_1)); |
| EXPECT_TRUE( |
| incognito_store->HasDevicePermission(origin, origin, *device_info_2)); |
| |
| { |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> |
| all_origin_objects = store->GetAllGrantedObjects(); |
| ASSERT_EQ(1u, all_origin_objects.size()); |
| EXPECT_FALSE(all_origin_objects[0]->incognito); |
| } |
| { |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| incognito_store->GetGrantedObjects(origin, origin); |
| EXPECT_EQ(1u, objects.size()); |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> |
| all_origin_objects = incognito_store->GetAllGrantedObjects(); |
| ASSERT_EQ(1u, all_origin_objects.size()); |
| EXPECT_TRUE(all_origin_objects[0]->incognito); |
| } |
| } |
| |
| TEST_F(UsbChooserContextTest, UsbGuardPermission) { |
| const GURL kFooOrigin("https://foo.com"); |
| const GURL kBarOrigin("https://bar.com"); |
| UsbDeviceInfoPtr device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", "ABC123"); |
| UsbDeviceInfoPtr ephemeral_device_info = |
| device_manager_.CreateAndAddDevice(0, 0, "Google", "Gizmo", ""); |
| |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetContentSettingDefaultScope(kFooOrigin, kFooOrigin, |
| CONTENT_SETTINGS_TYPE_USB_GUARD, |
| std::string(), CONTENT_SETTING_BLOCK); |
| |
| auto* store = GetChooserContext(profile()); |
| store->GrantDevicePermission(kFooOrigin, kFooOrigin, *device_info); |
| store->GrantDevicePermission(kFooOrigin, kFooOrigin, *ephemeral_device_info); |
| store->GrantDevicePermission(kBarOrigin, kBarOrigin, *device_info); |
| store->GrantDevicePermission(kBarOrigin, kBarOrigin, *ephemeral_device_info); |
| |
| std::vector<std::unique_ptr<base::DictionaryValue>> objects = |
| store->GetGrantedObjects(kFooOrigin, kFooOrigin); |
| EXPECT_EQ(0u, objects.size()); |
| |
| objects = store->GetGrantedObjects(kBarOrigin, kBarOrigin); |
| EXPECT_EQ(2u, objects.size()); |
| |
| std::vector<std::unique_ptr<ChooserContextBase::Object>> all_origin_objects = |
| store->GetAllGrantedObjects(); |
| for (const auto& object : all_origin_objects) { |
| EXPECT_EQ(object->requesting_origin, kBarOrigin); |
| EXPECT_EQ(object->embedding_origin, kBarOrigin); |
| } |
| EXPECT_EQ(2u, all_origin_objects.size()); |
| |
| EXPECT_FALSE( |
| store->HasDevicePermission(kFooOrigin, kFooOrigin, *device_info)); |
| EXPECT_FALSE(store->HasDevicePermission(kFooOrigin, kFooOrigin, |
| *ephemeral_device_info)); |
| EXPECT_TRUE(store->HasDevicePermission(kBarOrigin, kBarOrigin, *device_info)); |
| EXPECT_TRUE(store->HasDevicePermission(kBarOrigin, kBarOrigin, |
| *ephemeral_device_info)); |
| } |
| |
| namespace { |
| |
| constexpr char kPolicySetting[] = R"( |
| [ |
| { |
| "devices": [{ "vendor_id": 1234, "product_id": 5678 }], |
| "urls": ["https://product.vendor.com"] |
| }, { |
| "devices": [{ "vendor_id": 1234 }], |
| "urls": ["https://vendor.com"] |
| }, { |
| "devices": [{}], |
| "urls": ["https://anydevice.com"] |
| }, { |
| "devices": [{ "vendor_id": 2468, "product_id": 1357 }], |
| "urls": ["https://gadget.com,https://cool.com"] |
| } |
| ])"; |
| |
| const GURL kPolicyOrigins[] = { |
| GURL("https://product.vendor.com"), GURL("https://vendor.com"), |
| GURL("https://anydevice.com"), GURL("https://gadget.com"), |
| GURL("https://cool.com")}; |
| |
| void ExpectNoPermissions(UsbChooserContext* store, |
| const device::mojom::UsbDeviceInfo& device_info) { |
| for (const auto& kRequestingOrigin : kPolicyOrigins) { |
| for (const auto& kEmbeddingOrigin : kPolicyOrigins) { |
| EXPECT_FALSE(store->HasDevicePermission(kRequestingOrigin, |
| kEmbeddingOrigin, device_info)); |
| } |
| } |
| } |
| |
| void ExpectCorrectPermissions( |
| UsbChooserContext* store, |
| const std::vector<GURL>& kValidRequestingOrigins, |
| const std::vector<GURL>& kInvalidRequestingOrigins, |
| const device::mojom::UsbDeviceInfo& device_info) { |
| // Ensure that only |kValidRequestingOrigin| as the requesting origin has |
| // permission to access the device described by |device_info|. |
| for (const auto& kEmbeddingOrigin : kPolicyOrigins) { |
| for (const auto& kValidRequestingOrigin : kValidRequestingOrigins) { |
| EXPECT_TRUE(store->HasDevicePermission(kValidRequestingOrigin, |
| kEmbeddingOrigin, device_info)); |
| } |
| |
| for (const auto& kInvalidRequestingOrigin : kInvalidRequestingOrigins) { |
| EXPECT_FALSE(store->HasDevicePermission(kInvalidRequestingOrigin, |
| kEmbeddingOrigin, device_info)); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| TEST_F(UsbChooserContextTest, |
| UsbAllowDevicesForUrlsPermissionForSpecificDevice) { |
| const std::vector<GURL> kValidRequestingOrigins = { |
| GURL("https://product.vendor.com"), GURL("https://vendor.com"), |
| GURL("https://anydevice.com")}; |
| const std::vector<GURL> kInvalidRequestingOrigins = { |
| GURL("https://gadget.com"), GURL("https://cool.com")}; |
| |
| UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice( |
| 1234, 5678, "Google", "Gizmo", "ABC123"); |
| |
| auto* store = GetChooserContext(profile()); |
| |
| ExpectNoPermissions(store, *specific_device_info); |
| |
| profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls, |
| *base::JSONReader::Read(kPolicySetting)); |
| |
| ExpectCorrectPermissions(store, kValidRequestingOrigins, |
| kInvalidRequestingOrigins, *specific_device_info); |
| } |
| |
| TEST_F(UsbChooserContextTest, |
| UsbAllowDevicesForUrlsPermissionForVendorRelatedDevice) { |
| const std::vector<GURL> kValidRequestingOrigins = { |
| GURL("https://vendor.com"), GURL("https://anydevice.com")}; |
| const std::vector<GURL> kInvalidRequestingOrigins = { |
| GURL("https://product.vendor.com"), GURL("https://gadget.com"), |
| GURL("https://cool.com")}; |
| |
| UsbDeviceInfoPtr vendor_related_device_info = |
| device_manager_.CreateAndAddDevice(1234, 8765, "Google", "Widget", |
| "XYZ987"); |
| |
| auto* store = GetChooserContext(profile()); |
| |
| ExpectNoPermissions(store, *vendor_related_device_info); |
| |
| profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls, |
| *base::JSONReader::Read(kPolicySetting)); |
| |
| ExpectCorrectPermissions(store, kValidRequestingOrigins, |
| kInvalidRequestingOrigins, |
| *vendor_related_device_info); |
| } |
| |
| TEST_F(UsbChooserContextTest, |
| UsbAllowDevicesForUrlsPermissionForUnrelatedDevice) { |
| const std::vector<GURL> kValidRequestingOrigins = { |
| GURL("https://anydevice.com")}; |
| const std::vector<GURL> kInvalidRequestingOrigins = { |
| GURL("https://product.vendor.com"), GURL("https://vendor.com"), |
| GURL("https://cool.com")}; |
| const GURL kGadgetOrigin("https://gadget.com"); |
| const GURL& kCoolOrigin = kInvalidRequestingOrigins[2]; |
| |
| UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice( |
| 2468, 1357, "Cool", "Gadget", "4W350M3"); |
| |
| auto* store = GetChooserContext(profile()); |
| |
| ExpectNoPermissions(store, *unrelated_device_info); |
| |
| profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls, |
| *base::JSONReader::Read(kPolicySetting)); |
| |
| EXPECT_TRUE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin, |
| *unrelated_device_info)); |
| for (const auto& kEmbeddingOrigin : kPolicyOrigins) { |
| if (kEmbeddingOrigin != kCoolOrigin) { |
| EXPECT_FALSE(store->HasDevicePermission(kGadgetOrigin, kEmbeddingOrigin, |
| *unrelated_device_info)); |
| } |
| } |
| ExpectCorrectPermissions(store, kValidRequestingOrigins, |
| kInvalidRequestingOrigins, *unrelated_device_info); |
| } |
| |
| TEST_F(UsbChooserContextTest, |
| UsbAllowDevicesForUrlsPermissionOverrulesUsbGuardPermission) { |
| const GURL kProductVendorOrigin("https://product.vendor.com"); |
| const GURL kGadgetOrigin("https://gadget.com"); |
| const GURL kCoolOrigin("https://cool.com"); |
| |
| UsbDeviceInfoPtr specific_device_info = device_manager_.CreateAndAddDevice( |
| 1234, 5678, "Google", "Gizmo", "ABC123"); |
| UsbDeviceInfoPtr unrelated_device_info = device_manager_.CreateAndAddDevice( |
| 2468, 1357, "Cool", "Gadget", "4W350M3"); |
| |
| auto* store = GetChooserContext(profile()); |
| |
| ExpectNoPermissions(store, *specific_device_info); |
| ExpectNoPermissions(store, *unrelated_device_info); |
| |
| auto* map = HostContentSettingsMapFactory::GetForProfile(profile()); |
| map->SetContentSettingDefaultScope(kProductVendorOrigin, kProductVendorOrigin, |
| CONTENT_SETTINGS_TYPE_USB_GUARD, |
| std::string(), CONTENT_SETTING_BLOCK); |
| map->SetContentSettingDefaultScope(kGadgetOrigin, kCoolOrigin, |
| CONTENT_SETTINGS_TYPE_USB_GUARD, |
| std::string(), CONTENT_SETTING_BLOCK); |
| EXPECT_FALSE(store->HasDevicePermission( |
| kProductVendorOrigin, kProductVendorOrigin, *specific_device_info)); |
| EXPECT_FALSE(store->HasDevicePermission( |
| kProductVendorOrigin, kProductVendorOrigin, *unrelated_device_info)); |
| EXPECT_FALSE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin, |
| *specific_device_info)); |
| EXPECT_FALSE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin, |
| *unrelated_device_info)); |
| |
| profile()->GetPrefs()->Set(prefs::kManagedWebUsbAllowDevicesForUrls, |
| *base::JSONReader::Read(kPolicySetting)); |
| |
| EXPECT_TRUE(store->HasDevicePermission( |
| kProductVendorOrigin, kProductVendorOrigin, *specific_device_info)); |
| EXPECT_FALSE(store->HasDevicePermission( |
| kProductVendorOrigin, kProductVendorOrigin, *unrelated_device_info)); |
| EXPECT_FALSE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin, |
| *specific_device_info)); |
| EXPECT_TRUE(store->HasDevicePermission(kGadgetOrigin, kCoolOrigin, |
| *unrelated_device_info)); |
| } |