// Copyright 2021 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/permissions_manager.h" #include "base/memory/raw_ptr.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_util.h" #include "extensions/browser/extensions_test.h" #include "extensions/browser/pref_types.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_icon_set.h" #include "extensions/common/extensions_client.h" #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/url_pattern_set.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/origin.h" namespace { std::unique_ptr SetTestingPermissionsManager( content::BrowserContext* browser_context) { return std::make_unique(browser_context); } } // namespace namespace extensions { using UserSiteSetting = PermissionsManager::UserSiteSetting; class PermissionsManagerUnittest : public ExtensionsTest { public: PermissionsManagerUnittest() = default; ~PermissionsManagerUnittest() override = default; PermissionsManagerUnittest(const PermissionsManagerUnittest&) = delete; PermissionsManagerUnittest& operator=(const PermissionsManagerUnittest&) = delete; scoped_refptr AddExtensionWithHostPermission( const std::string& name, const std::string& host_permission); // Returns the restricted sites stored in `manager_`. std::set GetRestrictedSitesFromManager(); // Returns the permittes sites stored in `manager_`. std::set GetPermittedSitesFromManager(); // Returns the restricted sites stored in `extension_prefs_`. const base::Value* GetRestrictedSitesFromPrefs(); // Returns the permitted sites stored in `extension_prefs_`. const base::Value* GetPermittedSitesFromPrefs(); // Returns the restricted sites stored in `PermissionsData`. std::set GetRestrictedSitesFromPermissionsData(); // Returns the permitted sites stored in `PermissionsData`. std::set GetPermittedSitesFromPermissionsData(); protected: // ExtensionsTest: void SetUp() override; // PermissionsManager being tested. raw_ptr manager_; raw_ptr extension_prefs_; }; void PermissionsManagerUnittest::SetUp() { ExtensionsTest::SetUp(); manager_ = static_cast( PermissionsManager::GetFactory()->SetTestingFactoryAndUse( browser_context(), base::BindRepeating(&SetTestingPermissionsManager))); extension_prefs_ = ExtensionPrefs::Get(browser_context()); } scoped_refptr PermissionsManagerUnittest::AddExtensionWithHostPermission( const std::string& name, const std::string& host_permission) { scoped_refptr extension = extensions::ExtensionBuilder(name) .SetManifestVersion(3) .SetManifestKey( "host_permissions", extensions::ListBuilder().Append(host_permission).Build()) .Build(); ExtensionRegistryFactory::GetForBrowserContext(browser_context()) ->AddEnabled(extension); return extension; } const base::Value* PermissionsManagerUnittest::GetRestrictedSitesFromPrefs() { const base::DictionaryValue* permissions = extension_prefs_->GetPrefAsDictionary(kUserPermissions); return permissions->FindKey("restricted_sites"); } const base::Value* PermissionsManagerUnittest::GetPermittedSitesFromPrefs() { const base::DictionaryValue* permissions = extension_prefs_->GetPrefAsDictionary(kUserPermissions); return permissions->FindKey("permitted_sites"); } std::set PermissionsManagerUnittest::GetRestrictedSitesFromManager() { const PermissionsManager::UserPermissionsSettings& permissions = manager_->GetUserPermissionsSettings(); return permissions.restricted_sites; } std::set PermissionsManagerUnittest::GetPermittedSitesFromManager() { const PermissionsManager::UserPermissionsSettings& permissions = manager_->GetUserPermissionsSettings(); return permissions.permitted_sites; } std::set PermissionsManagerUnittest::GetRestrictedSitesFromPermissionsData() { std::set string_patterns; URLPatternSet patterns = PermissionsData::GetUserBlockedHosts( util::GetBrowserContextId(browser_context())); for (const auto& pattern : patterns) string_patterns.insert(pattern.GetAsString()); return string_patterns; } std::set PermissionsManagerUnittest::GetPermittedSitesFromPermissionsData() { std::set string_patterns; URLPatternSet patterns = PermissionsData::GetUserAllowedHosts( util::GetBrowserContextId(browser_context())); for (const auto& pattern : patterns) string_patterns.insert(pattern.GetAsString()); return string_patterns; } TEST_F(PermissionsManagerUnittest, AddAndRemoveRestrictedSite) { const url::Origin url = url::Origin::Create(GURL("http://a.example.com")); const std::string expected_url_pattern = "http://a.example.com/*"; std::set set_with_url; set_with_url.insert(url); base::Value value_with_url(base::Value::Type::LIST); value_with_url.Append(url.Serialize()); // Verify the restricted sites list is empty. EXPECT_EQ(GetRestrictedSitesFromManager(), std::set()); EXPECT_EQ(GetRestrictedSitesFromPrefs(), nullptr); EXPECT_THAT(GetRestrictedSitesFromPermissionsData(), testing::IsEmpty()); EXPECT_EQ(manager_->GetUserSiteSetting(url), UserSiteSetting::kCustomizeByExtension); // Add `url` to restricted sites. Verify the site is stored both in manager // and prefs restricted sites. manager_->AddUserRestrictedSite(url); EXPECT_EQ(GetRestrictedSitesFromManager(), set_with_url); EXPECT_EQ(*GetRestrictedSitesFromPrefs(), value_with_url); EXPECT_THAT(GetRestrictedSitesFromPermissionsData(), testing::UnorderedElementsAre(expected_url_pattern)); EXPECT_EQ(manager_->GetUserSiteSetting(url), UserSiteSetting::kBlockAllExtensions); // Adding an existent restricted site. Verify the entry is not duplicated. manager_->AddUserRestrictedSite(url); EXPECT_EQ(GetRestrictedSitesFromManager(), set_with_url); EXPECT_EQ(*GetRestrictedSitesFromPrefs(), value_with_url); EXPECT_THAT(GetRestrictedSitesFromPermissionsData(), testing::UnorderedElementsAre(expected_url_pattern)); // Remove `url` from restricted sites. Verify the site is removed from both // manager and prefs restricted sites. manager_->RemoveUserRestrictedSite(url); EXPECT_EQ(GetRestrictedSitesFromManager(), std::set()); EXPECT_EQ(*GetRestrictedSitesFromPrefs(), base::Value(base::Value::Type::LIST)); EXPECT_THAT(GetRestrictedSitesFromPermissionsData(), testing::IsEmpty()); EXPECT_EQ(manager_->GetUserSiteSetting(url), UserSiteSetting::kCustomizeByExtension); } TEST_F(PermissionsManagerUnittest, AddAndRemovePermittedSite) { const url::Origin url = url::Origin::Create(GURL("http://a.example.com")); const std::string expected_url_pattern = "http://a.example.com/*"; std::set set_with_url; set_with_url.insert(url); base::Value value_with_url(base::Value::Type::LIST); value_with_url.Append(url.Serialize()); // Verify the permitted sites list is empty. EXPECT_EQ(GetPermittedSitesFromManager(), std::set()); EXPECT_EQ(GetPermittedSitesFromPrefs(), nullptr); EXPECT_THAT(GetPermittedSitesFromPermissionsData(), testing::IsEmpty()); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kCustomizeByExtension); // Add `url` to permitted sites. Verify the site is stored both in manager // and prefs permitted sites. manager_->AddUserPermittedSite(url); EXPECT_EQ(GetPermittedSitesFromManager(), set_with_url); EXPECT_EQ(*GetPermittedSitesFromPrefs(), value_with_url); EXPECT_THAT(GetPermittedSitesFromPermissionsData(), testing::UnorderedElementsAre(expected_url_pattern)); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kGrantAllExtensions); // Adding an existent permitted site. Verify the entry is not duplicated. manager_->AddUserPermittedSite(url); EXPECT_EQ(GetPermittedSitesFromManager(), set_with_url); EXPECT_EQ(*GetPermittedSitesFromPrefs(), value_with_url); EXPECT_THAT(GetPermittedSitesFromPermissionsData(), testing::UnorderedElementsAre(expected_url_pattern)); // Remove `url` from permitted sites. Verify the site is removed from both // manager and prefs permitted sites. manager_->RemoveUserPermittedSite(url); EXPECT_EQ(GetPermittedSitesFromManager(), std::set()); EXPECT_EQ(*GetPermittedSitesFromPrefs(), base::Value(base::Value::Type::LIST)); EXPECT_THAT(GetPermittedSitesFromPermissionsData(), testing::IsEmpty()); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kCustomizeByExtension); } TEST_F(PermissionsManagerUnittest, RestrictedAndPermittedSitesAreMutuallyExclusive) { const url::Origin url = url::Origin::Create(GURL("http://a.example.com")); std::set empty_set; std::set set_with_url; set_with_url.insert(url); { manager_->AddUserRestrictedSite(url); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, set_with_url); EXPECT_EQ(actual_permissions.permitted_sites, empty_set); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kBlockAllExtensions); } { // Adding an url to the permitted sites that is already in the restricted // sites should remove it from restricted sites and add it to permitted // sites. manager_->AddUserPermittedSite(url); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, empty_set); EXPECT_EQ(actual_permissions.permitted_sites, set_with_url); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kGrantAllExtensions); } { // Adding an url to the restricted sites that is already in the permitted // sites should remove it from permitted sites and add it to restricted // sites. manager_->AddUserRestrictedSite(url); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, set_with_url); EXPECT_EQ(actual_permissions.permitted_sites, empty_set); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kBlockAllExtensions); } } TEST_F(PermissionsManagerUnittest, UpdateUserSiteSetting) { const url::Origin url = url::Origin::Create(GURL("http://a.example.com")); std::set empty_set; std::set set_with_url; set_with_url.insert(url); { manager_->UpdateUserSiteSetting( url, PermissionsManager::UserSiteSetting::kGrantAllExtensions); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, empty_set); EXPECT_EQ(actual_permissions.permitted_sites, set_with_url); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kGrantAllExtensions); } { manager_->UpdateUserSiteSetting( url, PermissionsManager::UserSiteSetting::kBlockAllExtensions); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, set_with_url); EXPECT_EQ(actual_permissions.permitted_sites, empty_set); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kBlockAllExtensions); } { manager_->UpdateUserSiteSetting( url, PermissionsManager::UserSiteSetting::kCustomizeByExtension); const PermissionsManager::UserPermissionsSettings& actual_permissions = manager_->GetUserPermissionsSettings(); EXPECT_EQ(actual_permissions.restricted_sites, empty_set); EXPECT_EQ(actual_permissions.permitted_sites, empty_set); EXPECT_EQ(manager_->GetUserSiteSetting(url), PermissionsManager::UserSiteSetting::kCustomizeByExtension); } } TEST_F(PermissionsManagerUnittest, GetSiteAccess_AllUrls) { auto extension = AddExtensionWithHostPermission("AllUrls Extension", ""); const GURL non_restricted_url("https://www.non-restricted.com"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, non_restricted_url); EXPECT_TRUE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_TRUE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } // Chrome pages should be restricted, and the extension shouldn't have grant // or withheld site access. const GURL restricted_url("chrome://extensions"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, restricted_url); EXPECT_FALSE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_TRUE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } } TEST_F(PermissionsManagerUnittest, GetSiteAccess_RequestedUrl) { auto extension = AddExtensionWithHostPermission("RequestedUrl Extension", "*://*.requested.com/*"); const GURL requested_url("https://www.requested.com"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, requested_url); EXPECT_TRUE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_FALSE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } const GURL non_requested_url("https://non-requested.com"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, non_requested_url); EXPECT_FALSE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_FALSE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } } // Tests that for the purposes of displaying an extension's site access to the // user (or granting/revoking permissions), we ignore paths in the URL. We // always strip the path from host permissions directly, but we don't strip the // path from content scripts. TEST_F(PermissionsManagerUnittest, GetSiteAccess_ContentScript_RequestedUrlWithPath) { scoped_refptr extension = ExtensionBuilder("extension") .AddContentScript("foo.js", {"https://www.example.com/foo"}) .SetLocation(mojom::ManifestLocation::kInternal) .Build(); ExtensionRegistryFactory::GetForBrowserContext(browser_context()) ->AddEnabled(extension); const GURL other_path_url("https://www.example.com/bar"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, other_path_url); // Even though the path doesn't match the one requested, the domain does // match and thus we treat it as if the site was requested. EXPECT_TRUE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_FALSE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } } TEST_F(PermissionsManagerUnittest, GetSiteAccess_ActiveTab) { auto extension = AddExtensionWithHostPermission("ActiveTab Extension", "activeTab"); const GURL url("https://example.com"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, url); // The site access computation does not take into account active tab, and // therefore it does not have or withheld any access. EXPECT_FALSE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_FALSE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } } TEST_F(PermissionsManagerUnittest, GetSiteAccess_NoHostPermissions) { auto extension = AddExtensionWithHostPermission("", "Extension"); const GURL url("https://example.com"); { const PermissionsManager::ExtensionSiteAccess site_access = manager_->GetSiteAccess(*extension, url); // The site access computation does not take into account active tab, and // therefore it does not have or withheld any access. EXPECT_FALSE(site_access.has_site_access); EXPECT_FALSE(site_access.withheld_site_access); EXPECT_FALSE(site_access.has_all_sites_access); EXPECT_FALSE(site_access.withheld_all_sites_access); } } } // namespace extensions