| // Copyright 2019 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 <memory> |
| |
| #include "base/files/file_util.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/background/background_contents_service.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/extensions/chrome_content_verifier_delegate.h" |
| #include "chrome/browser/extensions/chrome_extension_test_notification_observer.h" |
| #include "chrome/browser/extensions/component_loader.h" |
| #include "chrome/browser/extensions/crx_installer.h" |
| #include "chrome/browser/extensions/extension_management_constants.h" |
| #include "chrome/browser/extensions/extension_management_test_util.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/forced_extensions/install_stage_tracker.h" |
| #include "chrome/browser/extensions/install_verifier.h" |
| #include "chrome/browser/extensions/shared_module_service.h" |
| #include "chrome/browser/extensions/unpacked_installer.h" |
| #include "chrome/browser/extensions/updater/extension_updater.h" |
| #include "chrome/browser/installable/installable_metrics.h" |
| #include "chrome/browser/policy/policy_test_utils.h" |
| #include "chrome/browser/policy/profile_policy_connector_builder.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/web_applications/components/app_registrar.h" |
| #include "chrome/browser/web_applications/components/install_manager.h" |
| #include "chrome/browser/web_applications/components/os_integration_manager.h" |
| #include "chrome/browser/web_applications/components/web_app_provider_base.h" |
| #include "chrome/browser/web_applications/test/web_app_install_observer.h" |
| #include "chrome/common/extensions/extension_test_util.h" |
| #include "chrome/common/extensions/manifest_handlers/app_launch_info.h" |
| #include "chrome/common/web_application_info.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/version_info/channel.h" |
| #include "content/public/browser/render_process_host.h" |
| #include "content/public/common/result_codes.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/download_test_observer.h" |
| #include "content/public/test/url_loader_interceptor.h" |
| #include "extensions/browser/content_verifier/test_utils.h" |
| #include "extensions/browser/extension_dialog_auto_confirm.h" |
| #include "extensions/browser/extension_host.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/extensions_browser_client.h" |
| #include "extensions/browser/notification_types.h" |
| #include "extensions/browser/scoped_ignore_content_verifier_for_test.h" |
| #include "extensions/browser/test_extension_registry_observer.h" |
| #include "extensions/browser/updater/extension_cache_fake.h" |
| #include "extensions/common/constants.h" |
| #include "extensions/common/features/feature_channel.h" |
| #include "extensions/common/file_util.h" |
| #include "extensions/common/manifest.h" |
| #include "extensions/common/manifest_handlers/shared_module_info.h" |
| #include "extensions/common/permissions/permissions_data.h" |
| #include "extensions/common/value_builder.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| |
| #if defined(OS_WIN) |
| #include "base/win/win_util.h" |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/web_applications/default_web_app_ids.h" |
| #include "chrome/browser/extensions/updater/local_extension_cache.h" |
| #include "chrome/browser/web_applications/components/externally_installed_web_app_prefs.h" |
| #include "chromeos/constants/chromeos_switches.h" |
| #endif |
| |
| using testing::AtLeast; |
| using testing::Sequence; |
| |
| namespace policy { |
| |
| // Called when an additional profile has been created. |
| // The created profile is stored in *|out_created_profile|. |
| void OnProfileInitialized(Profile** out_created_profile, |
| const base::Closure& closure, |
| Profile* profile, |
| Profile::CreateStatus status) { |
| if (status == Profile::CREATE_STATUS_INITIALIZED) { |
| *out_created_profile = profile; |
| closure.Run(); |
| } |
| } |
| namespace { |
| |
| const base::FilePath::CharType kGoodCrxName[] = FILE_PATH_LITERAL("good.crx"); |
| const base::FilePath::CharType kSimpleWithIconCrxName[] = |
| FILE_PATH_LITERAL("simple_with_icon.crx"); |
| const base::FilePath::CharType kHostedAppCrxName[] = |
| FILE_PATH_LITERAL("hosted_app.crx"); |
| |
| const char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf"; |
| const char kSimpleWithIconCrxId[] = "dehdlahnlebladnfleagmjdapdjdcnlp"; |
| const char kHostedAppCrxId[] = "kbmnembihfiondgfjekmnmcbddelicoi"; |
| |
| const char kGoodCrxVersion[] = "1.0.0.1"; |
| |
| const base::FilePath::CharType kGoodV1CrxName[] = |
| FILE_PATH_LITERAL("good_v1.crx"); |
| const base::FilePath::CharType kSimpleWithPopupExt[] = |
| FILE_PATH_LITERAL("simple_with_popup"); |
| const base::FilePath::CharType kAppUnpackedExt[] = FILE_PATH_LITERAL("app"); |
| |
| // This is to enforce zero initial delay. |
| constexpr net::BackoffEntry::Policy kDefaultBackOffPolicyForTesting = { |
| // Number of initial errors (in sequence) to ignore before applying |
| // exponential back-off rules. |
| 0, |
| |
| // Initial delay for exponential back-off in ms. |
| 0, |
| |
| // Factor by which the waiting time will be multiplied. |
| 2, |
| |
| // Fuzzing percentage. ex: 10% will spread requests randomly |
| // between 90%-100% of the calculated time. |
| 0.1, |
| |
| // Maximum amount of time we are willing to delay our request in ms. |
| 600000, // Ten minutes. |
| |
| // Time to keep an entry from being discarded even when it |
| // has no significant state, -1 to never discard. |
| -1, |
| |
| // Don't use initial delay unless the last request was an error. |
| false, |
| }; |
| |
| // Registers a handler to respond to requests whose path matches |match_path|. |
| // The response contents are generated from |template_file|, by replacing all |
| // "${URL_PLACEHOLDER}" substrings in the file with the request URL excluding |
| // filename, query values and fragment. |
| void RegisterURLReplacingHandler(net::EmbeddedTestServer* test_server, |
| const std::string& match_path, |
| const base::FilePath& template_file) { |
| test_server->RegisterRequestHandler(base::Bind( |
| [](net::EmbeddedTestServer* test_server, const std::string& match_path, |
| const base::FilePath& template_file, |
| const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| GURL url = test_server->GetURL(request.relative_url); |
| if (url.path() != match_path) |
| return nullptr; |
| |
| std::string contents; |
| CHECK(base::ReadFileToString(template_file, &contents)); |
| |
| GURL url_base = url.GetWithoutFilename(); |
| base::ReplaceSubstringsAfterOffset(&contents, 0, "${URL_PLACEHOLDER}", |
| url_base.spec()); |
| |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content(contents); |
| response->set_content_type("text/plain"); |
| return response; |
| }, |
| base::Unretained(test_server), match_path, template_file)); |
| } |
| |
| class ExtensionPolicyTest : public PolicyTest { |
| protected: |
| void SetUp() override { |
| // Set default verification mode for content verifier to be enabled. |
| extensions::ChromeContentVerifierDelegate::SetDefaultModeForTesting( |
| extensions::ChromeContentVerifierDelegate::VerifyInfo::Mode:: |
| ENFORCE_STRICT); |
| ignore_content_verifier_ = |
| std::make_unique<extensions::ScopedIgnoreContentVerifierForTest>(); |
| test_extension_cache_ = std::make_unique<extensions::ExtensionCacheFake>(); |
| // Base class SetUp() should be invoked at the end as it runs the test body. |
| PolicyTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| PolicyTest::SetUpOnMainThread(); |
| if (extension_service()->updater()) { |
| extension_service()->updater()->SetExtensionCacheForTesting( |
| test_extension_cache_.get()); |
| } |
| |
| os_hooks_suppress_ = |
| web_app::OsIntegrationManager::ScopedSuppressOsHooksForTesting(); |
| } |
| |
| extensions::ExtensionCacheFake* extension_cache() { |
| return test_extension_cache_.get(); |
| } |
| |
| extensions::ExtensionService* extension_service() { |
| extensions::ExtensionSystem* system = |
| extensions::ExtensionSystem::Get(browser()->profile()); |
| return system->extension_service(); |
| } |
| |
| extensions::ExtensionRegistry* extension_registry() { |
| return extensions::ExtensionRegistry::Get(browser()->profile()); |
| } |
| |
| web_app::WebAppProviderBase* web_app_provider_base() { |
| return web_app::WebAppProviderBase::GetProviderBase(browser()->profile()); |
| } |
| |
| const extensions::Extension* InstallExtension( |
| const base::FilePath::StringType& name) { |
| base::FilePath extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(kTestExtensionsDir), base::FilePath(name))); |
| scoped_refptr<extensions::CrxInstaller> installer = |
| extensions::CrxInstaller::CreateSilent(extension_service()); |
| installer->set_allow_silent_install(true); |
| installer->set_install_cause(extension_misc::INSTALL_CAUSE_UPDATE); |
| installer->set_creation_flags(extensions::Extension::FROM_WEBSTORE); |
| installer->set_off_store_install_allow_reason( |
| extensions::CrxInstaller::OffStoreInstallAllowReason:: |
| OffStoreInstallAllowedInTest); |
| |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::NotificationService::AllSources()); |
| installer->InstallCrx(extension_path); |
| observer.Wait(); |
| content::Details<const extensions::Extension> details = observer.details(); |
| return details.ptr(); |
| } |
| |
| const extensions::Extension* InstallBookmarkApp() { |
| WebApplicationInfo web_app; |
| web_app.title = base::ASCIIToUTF16("Bookmark App"); |
| web_app.start_url = GURL("http://www.google.com"); |
| |
| scoped_refptr<extensions::CrxInstaller> installer = |
| extensions::CrxInstaller::CreateSilent(extension_service()); |
| |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::NotificationService::AllSources()); |
| installer->InstallWebApp(web_app); |
| observer.Wait(); |
| content::Details<const extensions::Extension> details = observer.details(); |
| return details.ptr(); |
| } |
| |
| const web_app::AppId InstallWebApp() { |
| // Waits for the shadow bookmark app to be installed to avoid extension |
| // system races. |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::NotificationService::AllSources()); |
| |
| std::unique_ptr<WebApplicationInfo> web_application = |
| std::make_unique<WebApplicationInfo>(); |
| web_application->title = base::ASCIIToUTF16("Web App"); |
| web_application->start_url = GURL("http://www.google.com"); |
| base::RunLoop loop; |
| web_app::AppId return_app_id; |
| web_app_provider_base()->install_manager().InstallWebAppFromInfo( |
| std::move(web_application), web_app::ForInstallableSite::kYes, |
| WebappInstallSource::SYNC, |
| base::BindLambdaForTesting( |
| [&](const web_app::AppId& app_id, web_app::InstallResultCode code) { |
| EXPECT_EQ(code, web_app::InstallResultCode::kSuccessNewInstall); |
| return_app_id = app_id; |
| loop.Quit(); |
| })); |
| loop.Run(); |
| |
| observer.Wait(); |
| |
| return return_app_id; |
| } |
| |
| #if defined(OS_CHROMEOS) |
| const extensions::Extension* InstallOSSettings() { |
| WebApplicationInfo web_app; |
| web_app.title = base::ASCIIToUTF16("Settings"); |
| web_app.start_url = GURL("chrome://os-settings/"); |
| |
| scoped_refptr<extensions::CrxInstaller> installer = |
| extensions::CrxInstaller::CreateSilent(extension_service()); |
| installer->set_install_source(extensions::Manifest::EXTERNAL_COMPONENT); |
| installer->set_creation_flags( |
| extensions::Extension::WAS_INSTALLED_BY_DEFAULT); |
| |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::NotificationService::AllSources()); |
| installer->InstallWebApp(web_app); |
| observer.Wait(); |
| |
| web_app::ExternallyInstalledWebAppPrefs web_app_prefs( |
| browser()->profile()->GetPrefs()); |
| web_app_prefs.Insert(GURL("chrome://os-settings/"), |
| chromeos::default_web_apps::kOsSettingsAppId, |
| web_app::ExternalInstallSource::kSystemInstalled); |
| |
| content::Details<const extensions::Extension> details = observer.details(); |
| return details.ptr(); |
| } |
| #endif |
| |
| void UninstallExtension(const std::string& id, bool expect_success) { |
| if (expect_success) { |
| extensions::TestExtensionRegistryObserver observer(extension_registry()); |
| extension_service()->UninstallExtension( |
| id, extensions::UNINSTALL_REASON_FOR_TESTING, nullptr); |
| observer.WaitForExtensionUninstalled(); |
| } else { |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_EXTENSION_UNINSTALL_NOT_ALLOWED, |
| content::NotificationService::AllSources()); |
| extension_service()->UninstallExtension( |
| id, extensions::UNINSTALL_REASON_FOR_TESTING, nullptr); |
| observer.Wait(); |
| } |
| } |
| |
| void DisableExtension(const std::string& id) { |
| extensions::TestExtensionRegistryObserver observer(extension_registry()); |
| extension_service()->DisableExtension( |
| id, extensions::disable_reason::DISABLE_USER_ACTION); |
| observer.WaitForExtensionUnloaded(); |
| } |
| |
| void AddExtensionToForceList(PolicyMap* policies, |
| const std::string& id, |
| const GURL& update_url) { |
| // Setting the forcelist extension should install extension with ExtensionId |
| // equal to id. |
| base::ListValue forcelist; |
| forcelist.AppendString( |
| base::StringPrintf(update_url.is_empty() ? "%s" : "%s;%s", id.c_str(), |
| update_url.spec().c_str())); |
| policies->Set(key::kExtensionInstallForcelist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(forcelist), |
| nullptr); |
| } |
| |
| const extensions::Extension* InstallForceListExtension( |
| const std::string& id) { |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| if (registry->GetExtensionById(id, |
| extensions::ExtensionRegistry::EVERYTHING)) |
| return nullptr; |
| |
| GURL update_url = embedded_test_server()->GetURL( |
| "/extensions/good_v1_update_manifest.xml"); |
| |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, id, update_url); |
| |
| extensions::TestExtensionRegistryObserver observer(extension_registry()); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionWillBeInstalled(); |
| |
| return registry->enabled_extensions().GetByID(id); |
| } |
| |
| std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache_; |
| std::unique_ptr<extensions::ScopedIgnoreContentVerifierForTest> |
| ignore_content_verifier_; |
| extensions::ExtensionUpdater::ScopedSkipScheduledCheckForTest |
| skip_scheduled_extension_checks_; |
| |
| private: |
| web_app::ScopedOsHooksSuppress os_hooks_suppress_; |
| }; |
| |
| } // namespace |
| |
| #if defined(OS_CHROMEOS) |
| // Check that component extension can't be blocklisted, besides the camera app |
| // that can be disabled by extension policy. This is a temporary solution until |
| // there's a dedicated policy to disable the camera, at which point the special |
| // check should be removed. |
| // TODO(http://crbug.com/1002935) |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallBlocklistComponentApps) { |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(browser()->profile()); |
| |
| // Load all component extensions. |
| extensions::ComponentLoader::EnableBackgroundExtensionsForTesting(); |
| extension_service()->component_loader()->AddDefaultComponentExtensions(false); |
| base::RunLoop().RunUntilIdle(); |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_TRUE( |
| registry->enabled_extensions().GetByID(extension_misc::kCameraAppId)); |
| ASSERT_TRUE( |
| registry->enabled_extensions().GetByID(extensions::kWebStoreAppId)); |
| const size_t enabled_count = registry->enabled_extensions().size(); |
| |
| // Verify that only Camera app can be blocklisted. |
| base::ListValue blocklist; |
| blocklist.AppendString(extension_misc::kCameraAppId); |
| blocklist.AppendString(extensions::kWebStoreAppId); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| ASSERT_FALSE( |
| registry->enabled_extensions().GetByID(extension_misc::kCameraAppId)); |
| ASSERT_TRUE( |
| registry->disabled_extensions().GetByID(extension_misc::kCameraAppId)); |
| EXPECT_EQ(1u, registry->disabled_extensions().size()); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_BLOCKED_BY_POLICY, |
| extension_prefs->GetDisableReasons(extension_misc::kCameraAppId)); |
| ASSERT_TRUE( |
| registry->enabled_extensions().GetByID(extensions::kWebStoreAppId)); |
| EXPECT_EQ(enabled_count - 1, registry->enabled_extensions().size()); |
| } |
| |
| // Ensures that OS Settings can't be disabled by ExtensionInstallBlocklist |
| // policy. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallBlocklistOsSettings) { |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| const extensions::Extension* bookmark_app = InstallOSSettings(); |
| ASSERT_TRUE(bookmark_app); |
| ASSERT_TRUE(registry->enabled_extensions().GetByID( |
| chromeos::default_web_apps::kOsSettingsAppId)); |
| |
| base::ListValue blocklist; |
| blocklist.AppendString(chromeos::default_web_apps::kOsSettingsAppId); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled( |
| chromeos::default_web_apps::kOsSettingsAppId)); |
| } |
| #endif // defined(OS_CHROMEOS) |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallBlocklistSelective) { |
| // Verifies that blocklisted extensions can't be installed. |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kSimpleWithIconCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| base::ListValue blocklist; |
| blocklist.AppendString(kGoodCrxId); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // "good.crx" is blocklisted. |
| EXPECT_FALSE(InstallExtension(kGoodCrxName)); |
| EXPECT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // "simple_with_icon.crx" is not. |
| const extensions::Extension* simple_with_icon = |
| InstallExtension(kSimpleWithIconCrxName); |
| ASSERT_TRUE(simple_with_icon); |
| EXPECT_EQ(kSimpleWithIconCrxId, simple_with_icon->id()); |
| EXPECT_EQ(simple_with_icon, |
| registry->enabled_extensions().GetByID(kSimpleWithIconCrxId)); |
| } |
| |
| // Ensure that bookmark apps are not blocked by the ExtensionInstallBlocklist |
| // policy. |
| // Also see ExtensionInstallBlocklist_WebApp counterpart. |
| // TODO(https://crbug.com/1079435): Delete this test for bookmark apps removal. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallBlocklist_BookmarkApp) { |
| const extensions::Extension* bookmark_app = InstallBookmarkApp(); |
| ASSERT_TRUE(bookmark_app); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| |
| // Now set ExtensionInstallBlocklist policy to block all extensions. |
| PolicyMap policies; |
| base::Value list(base::Value::Type::LIST); |
| list.Append("*"); |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(list), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // The bookmark app should still be enabled, with |kGoodCrxId| being disabled. |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| } |
| |
| // Ensure that web apps are not blocked by the ExtensionInstallBlocklist policy. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionInstallBlocklist_WebApp) { |
| web_app::AppId web_app_id = InstallWebApp(); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| web_app::WebAppProviderBase* provider = web_app_provider_base(); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| |
| // Now set ExtensionInstallBlocklist policy to block all extensions. |
| PolicyMap policies; |
| base::Value list(base::Value::Type::LIST); |
| list.Append("*"); |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(list), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| EXPECT_TRUE(provider->registrar().IsLocallyInstalled(web_app_id)); |
| } |
| |
| // Ensure that when INSTALLATION_REMOVED is set |
| // that blocklisted extensions are removed from the device. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionInstallRemovedPolicy) { |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| EXPECT_TRUE(registry->GetInstalledExtension(kGoodCrxId)); |
| |
| // Should uninstall good_v1.crx. |
| base::DictionaryValue dict_value; |
| dict_value.SetString(std::string(kGoodCrxId) + "." + |
| extensions::schema_constants::kInstallationMode, |
| extensions::schema_constants::kRemoved); |
| PolicyMap policies; |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, dict_value.Clone(), |
| nullptr); |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionUnloaded(); |
| |
| EXPECT_FALSE(registry->GetInstalledExtension(kGoodCrxId)); |
| } |
| |
| // Ensure that when INSTALLATION_REMOVED is set for wildcard |
| // that blocklisted extensions are removed from the device. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionWildcardRemovedPolicy) { |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| EXPECT_TRUE(registry->GetInstalledExtension(kGoodCrxId)); |
| |
| // Should uninstall good_v1.crx. |
| base::DictionaryValue dict_value; |
| dict_value.SetString( |
| std::string("*") + "." + extensions::schema_constants::kInstallationMode, |
| extensions::schema_constants::kRemoved); |
| PolicyMap policies; |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, dict_value.Clone(), |
| nullptr); |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionUnloaded(); |
| |
| EXPECT_FALSE(registry->GetInstalledExtension(kGoodCrxId)); |
| } |
| |
| // Ensure that bookmark apps are not blocked by the ExtensionAllowedTypes |
| // policy. |
| // Also see ExtensionAllowedTypes_WebApp counterpart. |
| // TODO(https://crbug.com/1079435): Delete this test for bookmark apps removal. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionAllowedTypes_BookmarkApp) { |
| const extensions::Extension* bookmark_app = InstallBookmarkApp(); |
| ASSERT_TRUE(bookmark_app); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| |
| // Now set policy to only allow themes. Note: Bookmark apps are hosted |
| // apps. |
| PolicyMap policies; |
| base::Value list(base::Value::Type::LIST); |
| list.Append("theme"); |
| policies.Set(key::kExtensionAllowedTypes, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(list), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // The bookmark app should still be enabled, with |kGoodCrxId| being disabled. |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| } |
| |
| // Ensure that web apps are not blocked by the ExtensionInstallBlocklist policy. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionAllowedTypes_WebApp) { |
| web_app::AppId web_app_id = InstallWebApp(); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| web_app::WebAppProviderBase* provider = web_app_provider_base(); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| |
| // Now set policy to only allow themes. |
| PolicyMap policies; |
| base::Value list(base::Value::Type::LIST); |
| list.Append("theme"); |
| policies.Set(key::kExtensionAllowedTypes, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(list), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| EXPECT_TRUE(provider->registrar().IsLocallyInstalled(web_app_id)); |
| } |
| |
| // Ensure that bookmark apps are not blocked by the ExtensionSettings |
| // policy. |
| // Also see ExtensionSettings_WebApp counterpart. |
| // TODO(https://crbug.com/1079435): Delete this test for bookmark apps removal. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionSettings_BookmarkApp) { |
| const extensions::Extension* bookmark_app = InstallBookmarkApp(); |
| ASSERT_TRUE(bookmark_app); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| |
| // Now set policy to block all extensions. |
| PolicyMap policies; |
| base::Value dict(base::Value::Type::DICTIONARY), |
| key_dict(base::Value::Type::DICTIONARY); |
| key_dict.SetStringKey("installation_mode", "blocked"); |
| dict.SetKey("*", std::move(key_dict)); |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(dict), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // The bookmark app should still be enabled, with |kGoodCrxId| being disabled. |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| |
| // Clear all policies. |
| policies.Clear(); |
| UpdateProviderPolicy(policies); |
| |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| |
| // Now set policy to only allow themes. Note: Bookmark apps are hosted |
| // apps. |
| base::Value dict2(base::Value::Type::DICTIONARY), |
| key_dict2(base::Value::Type::DICTIONARY); |
| base::Value list(base::Value::Type::LIST); |
| list.Append("theme"); |
| key_dict2.SetKey("allowed_types", std::move(list)); |
| dict2.SetKey("*", std::move(key_dict2)); |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(dict2), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // The bookmark app should still be enabled, with |kGoodCrxId| being disabled. |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(service->IsExtensionEnabled(bookmark_app->id())); |
| } |
| |
| // Ensure that web apps are not blocked by the ExtensionInstallBlocklist policy. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionSettings_WebApp) { |
| web_app::AppId web_app_id = InstallWebApp(); |
| EXPECT_TRUE(InstallExtension(kGoodCrxName)); |
| |
| web_app::WebAppProviderBase* provider = web_app_provider_base(); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| extensions::ExtensionService* service = extension_service(); |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| |
| // Now set policy to block all extensions. |
| PolicyMap policies; |
| base::Value dict(base::Value::Type::DICTIONARY), |
| key_dict(base::Value::Type::DICTIONARY); |
| key_dict.SetStringKey("installation_mode", "blocked"); |
| dict.SetKey("*", std::move(key_dict)); |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, std::move(dict), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| EXPECT_TRUE(provider->registrar().IsInstalled(web_app_id)); |
| EXPECT_TRUE(provider->registrar().IsLocallyInstalled(web_app_id)); |
| } |
| |
| // Flaky on windows; http://crbug.com/307994. |
| #if defined(OS_WIN) |
| #define MAYBE_ExtensionInstallBlocklistWildcard \ |
| DISABLED_ExtensionInstallBlocklistWildcard |
| #else |
| #define MAYBE_ExtensionInstallBlocklistWildcard \ |
| ExtensionInstallBlocklistWildcard |
| #endif |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| MAYBE_ExtensionInstallBlocklistWildcard) { |
| // Verify that a wildcard blocklist takes effect. |
| EXPECT_TRUE(InstallExtension(kSimpleWithIconCrxName)); |
| extensions::ExtensionService* service = extension_service(); |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_TRUE(registry->enabled_extensions().GetByID(kSimpleWithIconCrxId)); |
| base::ListValue blocklist; |
| blocklist.AppendString("*"); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // "simple_with_icon" should be disabled. |
| EXPECT_TRUE(registry->disabled_extensions().GetByID(kSimpleWithIconCrxId)); |
| EXPECT_FALSE(service->IsExtensionEnabled(kSimpleWithIconCrxId)); |
| |
| // It shouldn't be possible to re-enable "simple_with_icon", until it |
| // satisfies management policy. |
| service->EnableExtension(kSimpleWithIconCrxId); |
| EXPECT_FALSE(service->IsExtensionEnabled(kSimpleWithIconCrxId)); |
| |
| // It shouldn't be possible to install good.crx. |
| EXPECT_FALSE(InstallExtension(kGoodCrxName)); |
| EXPECT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallBlocklistSharedModules) { |
| // Verifies that shared_modules are not affected by the blocklist. |
| |
| base::FilePath base_path; |
| GetTestDataDirectory(&base_path); |
| base::FilePath update_xml_template_path = |
| base_path.Append(kTestExtensionsDir) |
| .AppendASCII("policy_shared_module") |
| .AppendASCII("update_template.xml"); |
| |
| std::string update_xml_path = |
| "/" + base::FilePath(kTestExtensionsDir).MaybeAsASCII() + |
| "/policy_shared_module/gen_update.xml"; |
| RegisterURLReplacingHandler(embedded_test_server(), update_xml_path, |
| update_xml_template_path); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const char kImporterId[] = "pchakhniekfaeoddkifplhnfbffomabh"; |
| const char kSharedModuleId[] = "nfgclafboonjbiafbllihiailjlhelpm"; |
| |
| // Make sure that "import" and "export" are available to these extension IDs |
| // by mocking the release channel. |
| extensions::ScopedCurrentChannel channel(version_info::Channel::DEV); |
| |
| // Verify that the extensions are not installed initially. |
| extensions::ExtensionService* service = extension_service(); |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kImporterId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kSharedModuleId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // Mock the webstore update URL. This is where the shared module extension |
| // will be installed from. |
| GURL update_xml_url = embedded_test_server()->GetURL(update_xml_path); |
| extension_test_util::SetGalleryUpdateURL(update_xml_url); |
| ui_test_utils::NavigateToURL(browser(), update_xml_url); |
| |
| // Blocklist "*" but force-install the importer extension. The shared module |
| // should be automatically installed too. |
| base::ListValue blocklist; |
| blocklist.AppendString("*"); |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kImporterId, update_xml_url); |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| |
| extensions::TestExtensionRegistryObserver observe_importer(registry, |
| kImporterId); |
| extensions::TestExtensionRegistryObserver observe_shared_module( |
| registry, kSharedModuleId); |
| UpdateProviderPolicy(policies); |
| observe_importer.WaitForExtensionLoaded(); |
| observe_shared_module.WaitForExtensionLoaded(); |
| |
| // Verify that both extensions got installed. |
| const extensions::Extension* importer = |
| registry->enabled_extensions().GetByID(kImporterId); |
| ASSERT_TRUE(importer); |
| EXPECT_EQ(kImporterId, importer->id()); |
| const extensions::Extension* shared_module = |
| registry->enabled_extensions().GetByID(kSharedModuleId); |
| ASSERT_TRUE(shared_module); |
| EXPECT_EQ(kSharedModuleId, shared_module->id()); |
| EXPECT_TRUE(shared_module->is_shared_module()); |
| |
| // Verify the dependency. |
| std::unique_ptr<extensions::ExtensionSet> set = |
| service->shared_module_service()->GetDependentExtensions(shared_module); |
| ASSERT_TRUE(set); |
| EXPECT_EQ(1u, set->size()); |
| EXPECT_TRUE(set->Contains(importer->id())); |
| |
| std::vector<extensions::SharedModuleInfo::ImportInfo> imports = |
| extensions::SharedModuleInfo::GetImports(importer); |
| ASSERT_EQ(1u, imports.size()); |
| EXPECT_EQ(kSharedModuleId, imports[0].extension_id); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionInstallAllowlist) { |
| // Verifies that the allowlist can open exceptions to the blocklist. |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kSimpleWithIconCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| base::ListValue blocklist; |
| blocklist.AppendString("*"); |
| base::ListValue allowlist; |
| allowlist.AppendString(kGoodCrxId); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallBlacklist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, blocklist.Clone(), |
| nullptr); |
| policies.Set(key::kExtensionInstallAllowlist, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, allowlist.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| // "simple_with_icon.crx" is blocklisted. |
| EXPECT_FALSE(InstallExtension(kSimpleWithIconCrxName)); |
| EXPECT_FALSE(registry->GetExtensionById( |
| kSimpleWithIconCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| // "good.crx" has a allowlist exception. |
| const extensions::Extension* good = InstallExtension(kGoodCrxName); |
| ASSERT_TRUE(good); |
| EXPECT_EQ(kGoodCrxId, good->id()); |
| EXPECT_EQ(good, registry->enabled_extensions().GetByID(kGoodCrxId)); |
| // The user can also remove this extension. |
| UninstallExtension(kGoodCrxId, true); |
| } |
| |
| namespace { |
| |
| class ExtensionRequestInterceptor { |
| public: |
| ExtensionRequestInterceptor() |
| : interceptor_( |
| base::BindRepeating(&ExtensionRequestInterceptor::OnRequest, |
| base::Unretained(this))) {} |
| |
| void set_interceptor_hook( |
| content::URLLoaderInterceptor::InterceptCallback callback) { |
| callback_ = std::move(callback); |
| } |
| |
| private: |
| bool OnRequest(content::URLLoaderInterceptor::RequestParams* params) { |
| if (callback_ && callback_.Run(params)) |
| return true; |
| // Mock out requests to the Web Store. |
| if (params->url_request.url.host() == "clients2.google.com" && |
| params->url_request.url.path() == "/service/update2/crx") { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good2_update_manifest.xml", |
| params->client.get()); |
| return true; |
| } |
| |
| if (params->url_request.url.path() == "/good_update_manifest.xml") { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good2_update_manifest.xml", |
| params->client.get()); |
| return true; |
| } |
| if (params->url_request.url.path() == "/extensions/good_v1.crx") { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good_v1.crx", params->client.get()); |
| return true; |
| } |
| if (params->url_request.url.path() == "/extensions/good2.crx") { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good2.crx", params->client.get()); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| content::URLLoaderInterceptor::InterceptCallback callback_; |
| content::URLLoaderInterceptor interceptor_; |
| }; |
| |
| class MockedInstallationCollectorObserver |
| : public extensions::InstallStageTracker::Observer { |
| public: |
| explicit MockedInstallationCollectorObserver( |
| const content::BrowserContext* context) |
| : context_(context) {} |
| ~MockedInstallationCollectorObserver() override = default; |
| |
| MOCK_METHOD1(ExtensionStageChanged, |
| void(extensions::InstallStageTracker::Stage)); |
| |
| void OnExtensionDataChangedForTesting( |
| const extensions::ExtensionId& id, |
| const content::BrowserContext* context, |
| const extensions::InstallStageTracker::InstallationData& data) override { |
| // For simplicity policies are pushed into all profiles, so we need to track |
| // only one here. |
| if (context != context_) { |
| return; |
| } |
| if (data.install_stage && stage_ != data.install_stage.value()) { |
| stage_ = data.install_stage.value(); |
| ExtensionStageChanged(stage_); |
| } |
| } |
| |
| private: |
| extensions::InstallStageTracker::Stage stage_ = |
| extensions::InstallStageTracker::Stage::CREATED; |
| const content::BrowserContext* context_; |
| }; |
| |
| } // namespace |
| |
| // Verifies that if extension is installed manually by user and then added to |
| // force-installed policy, it can't be uninstalled. And then if it removed |
| // from the force installed list, it should be uninstalled. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionAddedAndRemovedFromForceInstalledList) { |
| ExtensionRequestInterceptor interceptor; |
| |
| ASSERT_FALSE(extension_registry()->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| ASSERT_TRUE(InstallExtension(kGoodCrxName)); |
| EXPECT_TRUE(extension_registry()->enabled_extensions().GetByID(kGoodCrxId)); |
| |
| EXPECT_EQ(extension_registry() |
| ->enabled_extensions() |
| .GetByID(kGoodCrxId) |
| ->location(), |
| extensions::Manifest::INTERNAL); |
| |
| // The user is allowed to disable the added extension. |
| EXPECT_TRUE(extension_service()->IsExtensionEnabled(kGoodCrxId)); |
| DisableExtension(kGoodCrxId); |
| EXPECT_FALSE(extension_service()->IsExtensionEnabled(kGoodCrxId)); |
| |
| // Explicitly re-enable the extension. |
| extension_service()->EnableExtension(kGoodCrxId); |
| // Extensions that are force-installed come from an update URL, which defaults |
| // to the webstore. Use a test URL for this test with an update manifest |
| // that includes "good_v1.crx". |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| UpdateProviderPolicy(policies); |
| const extensions::Extension* extension = |
| extension_registry()->enabled_extensions().GetByID(kGoodCrxId); |
| EXPECT_TRUE(extension); |
| |
| // The user is not allowed to uninstall force-installed extensions. |
| UninstallExtension(kGoodCrxId, /*expect_success=*/false); |
| EXPECT_EQ(extension->location(), |
| extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD); |
| |
| // Remove the force installed policy. |
| policies.Erase(policy::key::kExtensionInstallForcelist); |
| UpdateProviderPolicy(policies); |
| |
| // TODO(crbug.com/1042187) |
| // Extension should be uninstalled now. It would be better to keep it, but it |
| // doesn't happen for now. |
| ASSERT_FALSE(extension_registry()->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| } |
| |
| // Verifies that extension is not installed if its version does not match |
| // with that in the update manifest. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| CrxVersionInconsistencyFromManifest) { |
| // Intercepts the call to download the crx file and responds with the test crx |
| // file. |
| ExtensionRequestInterceptor interceptor; |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // Allow caching for the extension in case it is inserted in the cache. |
| extension_cache()->AllowCaching(kGoodCrxId); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = embedded_test_server()->GetURL( |
| "/extensions/good_v1_wrong_version_update_manifest.xml"); |
| PolicyMap policies; |
| // Add an entry in the extension force list policy. |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| |
| content::WindowedNotificationObserver observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::NotificationService::AllSources()); |
| |
| // Updating the policy triggers the extension installation process. |
| UpdateProviderPolicy(policies); |
| // Wait till the installer has finished by receiving the notification |
| // NOTIFICATION_CRX_INSTALLER_DONE. |
| observer.Wait(); |
| content::Details<const extensions::Extension> details = observer.details(); |
| // Check the extension is not installed. |
| EXPECT_FALSE(details.ptr()); |
| EXPECT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| // Check the extension in not inserted in the cache. |
| EXPECT_FALSE( |
| extension_cache()->GetExtension(kGoodCrxId, "", nullptr, nullptr)); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| // Verifies that if the cache entry contains inconsistent extension version, |
| // the crx installation fails and download of a new crx file is attempted. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, CrxVersionInconsistencyInCache) { |
| base::ScopedAllowBlockingForTesting allow_io; |
| // Intercepts the call to download the crx file and responds with the test crx |
| // file. |
| ExtensionRequestInterceptor interceptor; |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // Override the fake extension cache set in SetUpOnMainThread() as the test |
| // requires real extension cache to retry download of crx file when |
| // installation fails due to version mismatch. |
| extensions::ExtensionCache* cache = |
| extensions::ExtensionsBrowserClient::Get()->GetExtensionCache(); |
| extension_service()->updater()->SetExtensionCacheForTesting(cache); |
| |
| base::FilePath extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(kTestExtensionsDir), base::FilePath(kGoodCrxName))); |
| cache->AllowCaching(kGoodCrxId); |
| |
| // Copy the crx file to a temp directory so that the test file is not deleted |
| // when cache entry is removed on version mismatch. |
| base::ScopedTempDir tmp_dir; |
| ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); |
| const base::FilePath tmp_path = tmp_dir.GetPath(); |
| const base::FilePath filename = |
| tmp_path.Append(extensions::LocalExtensionCache::ExtensionFileName( |
| kGoodCrxId, kGoodCrxVersion, "" /* hash */)); |
| EXPECT_TRUE(CopyFile(extension_path, filename)); |
| |
| // Wait for the extension cache to get ready. |
| base::RunLoop cache_init_run_loop; |
| cache->Start(cache_init_run_loop.QuitClosure()); |
| cache_init_run_loop.Run(); |
| |
| base::RunLoop put_extension_run_loop; |
| // Insert a cache entry with version "1.0.0.1" while the crx file it points to |
| // belongs to version "1.0.0.0". |
| cache->PutExtension( |
| kGoodCrxId, "" /* expected hash */, filename, kGoodCrxVersion, |
| base::BindLambdaForTesting( |
| [&put_extension_run_loop](const base::FilePath& file_path, |
| bool file_ownership_passed) { |
| put_extension_run_loop.Quit(); |
| })); |
| put_extension_run_loop.Run(); |
| EXPECT_TRUE(cache->GetExtension(kGoodCrxId, "", nullptr, nullptr)); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good2_update_manifest.xml"); |
| PolicyMap policies; |
| // Add an entry in the extension force list policy. |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| |
| // Observes notification for failed crx installation. |
| content::WindowedNotificationObserver failed_installation_observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| base::BindLambdaForTesting( |
| [&](const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| return content::Details<const extensions::Extension>(details) |
| .ptr() == nullptr; |
| })); |
| // Observes notification for passed crx installation. |
| content::WindowedNotificationObserver passed_installation_observer( |
| extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| base::BindLambdaForTesting( |
| [&](const content::NotificationSource& source, |
| const content::NotificationDetails& details) { |
| return content::Details<const extensions::Extension>(details) |
| .ptr() != nullptr; |
| })); |
| // Updating the policy triggers the extension installation process. |
| UpdateProviderPolicy(policies); |
| // Wait till extension entry is found in the cache and installation fails due |
| // to version mismatch as the cache entry informs extension version as |
| // "1.0.0.1" while the crx file it points to belongs to "1.0.0.0". |
| failed_installation_observer.Wait(); |
| |
| // Wait till extension is freshly downloaded from the server and installation |
| // succeeds. |
| passed_installation_observer.Wait(); |
| |
| EXPECT_TRUE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| std::string version; |
| base::FilePath file_path; |
| // Check extension is inserted in the cache with a new filepath to the |
| // downloaded correct crx. |
| EXPECT_TRUE(cache->GetExtension(kGoodCrxId, "", &file_path, &version)); |
| EXPECT_EQ(version, kGoodCrxVersion); |
| EXPECT_NE(file_path, filename); |
| } |
| #endif // defined(OS_CHROMEOS) |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionInstallForcelist) { |
| // Verifies that extensions that are force-installed by policies are |
| // installed and can't be uninstalled. |
| |
| ExtensionRequestInterceptor interceptor; |
| |
| extensions::ExtensionService* service = extension_service(); |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // Extensions that are force-installed come from an update URL, which defaults |
| // to the webstore. Use a test URL for this test with an update manifest |
| // that includes "good_v1.crx". |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| extension_cache()->AllowCaching(kGoodCrxId); |
| EXPECT_FALSE( |
| extension_cache()->GetExtension(kGoodCrxId, "", nullptr, nullptr)); |
| |
| extensions::TestExtensionRegistryObserver registry_observer( |
| extension_registry()); |
| MockedInstallationCollectorObserver collector_observer(browser()->profile()); |
| // CREATED is the default stage in MockedInstallationCollectorObserver, so it |
| // wouldn't be reported here. |
| Sequence sequence; |
| EXPECT_CALL( |
| collector_observer, |
| ExtensionStageChanged(extensions::InstallStageTracker::Stage::PENDING)) |
| .InSequence(sequence); |
| EXPECT_CALL(collector_observer, |
| ExtensionStageChanged( |
| extensions::InstallStageTracker::Stage::DOWNLOADING)) |
| .InSequence(sequence); |
| EXPECT_CALL( |
| collector_observer, |
| ExtensionStageChanged(extensions::InstallStageTracker::Stage::INSTALLING)) |
| .InSequence(sequence); |
| EXPECT_CALL( |
| collector_observer, |
| ExtensionStageChanged(extensions::InstallStageTracker::Stage::COMPLETE)) |
| .InSequence(sequence); |
| |
| extensions::InstallStageTracker* install_stage_tracker = |
| extensions::InstallStageTracker::Get(browser()->profile()); |
| install_stage_tracker->AddObserver(&collector_observer); |
| UpdateProviderPolicy(policies); |
| registry_observer.WaitForExtensionWillBeInstalled(); |
| install_stage_tracker->RemoveObserver(&collector_observer); |
| // Note: Cannot check that the notification details match the expected |
| // exception, since the details object has already been freed prior to |
| // the completion of registry_observer.WaitForExtensionWillBeInstalled(). |
| EXPECT_TRUE( |
| extension_cache()->GetExtension(kGoodCrxId, "", nullptr, nullptr)); |
| EXPECT_TRUE(registry->enabled_extensions().GetByID(kGoodCrxId)); |
| |
| // The user is not allowed to uninstall force-installed extensions. |
| UninstallExtension(kGoodCrxId, false); |
| |
| scoped_refptr<extensions::UnpackedInstaller> installer = |
| extensions::UnpackedInstaller::Create(extension_service()); |
| |
| // The user is not allowed to load an unpacked extension with the |
| // same ID as a force-installed extension. |
| base::FilePath good_extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(kTestExtensionsDir), base::FilePath(kSimpleWithPopupExt))); |
| content::WindowedNotificationObserver extension_load_error_observer( |
| extensions::NOTIFICATION_EXTENSION_LOAD_ERROR, |
| content::NotificationService::AllSources()); |
| installer->Load(good_extension_path); |
| extension_load_error_observer.Wait(); |
| |
| // Loading other unpacked extensions are not blocked. |
| scoped_refptr<const extensions::Extension> extension = |
| LoadUnpackedExtension(kAppUnpackedExt); |
| ASSERT_TRUE(extension); |
| |
| const std::string old_version_number = |
| registry->enabled_extensions().GetByID(kGoodCrxId)->version().GetString(); |
| |
| content::WindowedNotificationObserver new_process_observer( |
| content::NOTIFICATION_RENDERER_PROCESS_CREATED, |
| content::NotificationService::AllSources()); |
| |
| // Updating the force-installed extension. |
| extensions::ExtensionUpdater* updater = service->updater(); |
| extensions::ExtensionUpdater::CheckParams params; |
| params.install_immediately = true; |
| extensions::TestExtensionRegistryObserver update_observer( |
| extension_registry()); |
| updater->CheckNow(std::move(params)); |
| update_observer.WaitForExtensionWillBeInstalled(); |
| |
| const base::Version& new_version = |
| registry->enabled_extensions().GetByID(kGoodCrxId)->version(); |
| ASSERT_TRUE(new_version.IsValid()); |
| base::Version old_version(old_version_number); |
| ASSERT_TRUE(old_version.IsValid()); |
| |
| EXPECT_EQ(1, new_version.CompareTo(old_version)); |
| |
| // Wait for the new extension process to launch. |
| new_process_observer.Wait(); |
| |
| // Wait until any background pages belonging to force-installed extensions |
| // have been loaded. |
| extensions::ProcessManager* manager = |
| extensions::ProcessManager::Get(browser()->profile()); |
| extensions::ProcessManager::FrameSet all_frames = manager->GetAllFrames(); |
| for (auto iter = all_frames.begin(); iter != all_frames.end();) { |
| content::WebContents* web_contents = |
| content::WebContents::FromRenderFrameHost(*iter); |
| ASSERT_TRUE(web_contents); |
| if (!web_contents->IsLoading()) { |
| ++iter; |
| } else { |
| base::RunLoop().RunUntilIdle(); |
| |
| // Test activity may have modified the set of extension processes during |
| // message processing, so re-start the iteration to catch added/removed |
| // processes. |
| all_frames = manager->GetAllFrames(); |
| iter = all_frames.begin(); |
| } |
| } |
| |
| // Test policy-installed extensions are reloaded when killed. |
| { |
| BackgroundContentsService:: |
| SetRestartDelayForForceInstalledAppsAndExtensionsForTesting(0); |
| content::WindowedNotificationObserver extension_crashed_observer( |
| extensions::NOTIFICATION_EXTENSION_PROCESS_TERMINATED, |
| content::NotificationService::AllSources()); |
| extensions::TestExtensionRegistryObserver extension_loaded_observer( |
| extension_registry(), kGoodCrxId); |
| extensions::ExtensionHost* extension_host = |
| extensions::ProcessManager::Get(browser()->profile()) |
| ->GetBackgroundHostForExtension(kGoodCrxId); |
| content::RenderProcessHost* process = extension_host->render_process_host(); |
| content::ScopedAllowRendererCrashes allow_renderer_crashes(process); |
| process->Shutdown(content::RESULT_CODE_KILLED); |
| extension_crashed_observer.Wait(); |
| extension_loaded_observer.WaitForExtensionLoaded(); |
| } |
| } |
| |
| // Verifies that corrupted non-webstore policy-based extension is automatically |
| // repaired (reinstalled). |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| CorruptedNonWebstoreExtensionRepaired) { |
| ignore_content_verifier_.reset(); |
| ExtensionRequestInterceptor interceptor; |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const base::FilePath kResourcePath(FILE_PATH_LITERAL("script1.js")); |
| |
| extensions::ExtensionService* service = extension_service(); |
| |
| // Step 1: Setup a policy and force-install an extension. |
| const extensions::Extension* extension = |
| InstallForceListExtension(kGoodCrxId); |
| ASSERT_TRUE(extension); |
| |
| // Step 2: Corrupt extension's resource. |
| { |
| base::FilePath resource_path = extension->path().Append(kResourcePath); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| // Temporarily disable extension, we don't want to tackle with resources of |
| // enabled one. Not using command DISABLE_USER_ACTION reason since |
| // force-installed extension may not be disabled by user action. |
| service->DisableExtension(kGoodCrxId, |
| extensions::disable_reason::DISABLE_RELOAD); |
| |
| const std::string kCorruptedContent("// corrupted\n"); |
| ASSERT_TRUE(base::WriteFile(resource_path, kCorruptedContent)); |
| |
| service->EnableExtension(kGoodCrxId); |
| } |
| |
| extensions::TestContentVerifyJobObserver content_verify_job_observer; |
| extensions::TestExtensionRegistryObserver registry_observer( |
| extension_registry()); |
| |
| // Step 3: Fetch resource to trigger corruption check and wait for content |
| // verify job completion. |
| { |
| content_verify_job_observer.ExpectJobResult( |
| kGoodCrxId, kResourcePath, |
| extensions::TestContentVerifyJobObserver::Result::FAILURE); |
| |
| GURL resource_url = extension->GetResourceURL("script1.js"); |
| FetchSubresource(browser()->tab_strip_model()->GetActiveWebContents(), |
| resource_url); |
| |
| EXPECT_TRUE(content_verify_job_observer.WaitForExpectedJobs()); |
| } |
| |
| // Step 4: Check that we are going to reinstall the extension and wait for |
| // extension reinstall. |
| EXPECT_TRUE(service->pending_extension_manager() |
| ->IsPolicyReinstallForCorruptionExpected(kGoodCrxId)); |
| registry_observer.WaitForExtensionWillBeInstalled(); |
| |
| // Extension was reloaded, old extension object is invalid. |
| extension = extension_registry()->enabled_extensions().GetByID(kGoodCrxId); |
| |
| // Step 5: Check that resource has its original contents. |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath resource_path = extension->path().Append(kResourcePath); |
| std::string contents; |
| ASSERT_TRUE(base::ReadFileToString(resource_path, &contents)); |
| EXPECT_EQ("// script1\n", contents); |
| } |
| } |
| |
| // Verifies that corrupted non-webstore policy-based extension is automatically |
| // repaired (reinstalled) even if hashes file is damaged too. |
| // crbug.com/1131634: flaky on win |
| #if defined(OS_WIN) |
| #define MAYBE_CorruptedNonWebstoreExtensionWithDamagedHashesRepaired \ |
| DISABLED_CorruptedNonWebstoreExtensionWithDamagedHashesRepaired |
| #else |
| #define MAYBE_CorruptedNonWebstoreExtensionWithDamagedHashesRepaired \ |
| CorruptedNonWebstoreExtensionWithDamagedHashesRepaired |
| #endif |
| IN_PROC_BROWSER_TEST_F( |
| ExtensionPolicyTest, |
| MAYBE_CorruptedNonWebstoreExtensionWithDamagedHashesRepaired) { |
| ignore_content_verifier_.reset(); |
| ExtensionRequestInterceptor interceptor; |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const base::FilePath kResourcePath(FILE_PATH_LITERAL("script1.js")); |
| |
| extensions::ExtensionService* service = extension_service(); |
| |
| // Step 1: Setup a policy and force-install an extension. |
| const extensions::Extension* extension = |
| InstallForceListExtension(kGoodCrxId); |
| ASSERT_TRUE(extension); |
| |
| // Step 2: Corrupt extension's resource and hashes file. |
| { |
| base::FilePath resource_path = extension->path().Append(kResourcePath); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| // Temporarily disable extension, we don't want to tackle with resources of |
| // enabled one. Not using command DISABLE_USER_ACTION reason since |
| // force-installed extension may not be disabled by user action. |
| service->DisableExtension(kGoodCrxId, |
| extensions::disable_reason::DISABLE_RELOAD); |
| |
| const std::string kCorruptedContent("// corrupted\n"); |
| ASSERT_TRUE(base::WriteFile(resource_path, kCorruptedContent)); |
| |
| const std::string kInvalidJson("not a json"); |
| ASSERT_TRUE(base::WriteFile( |
| extensions::file_util::GetComputedHashesPath(extension->path()), |
| kInvalidJson)); |
| |
| service->EnableExtension(kGoodCrxId); |
| } |
| |
| extensions::TestExtensionRegistryObserver observer(extension_registry()); |
| |
| // Step 3: Fetch resource to trigger corruption check and wait for content |
| // verify job completion. |
| { |
| extensions::TestContentVerifyJobObserver content_verify_job_observer; |
| content_verify_job_observer.ExpectJobResult( |
| kGoodCrxId, kResourcePath, |
| extensions::TestContentVerifyJobObserver::Result::FAILURE); |
| |
| GURL resource_url = extension->GetResourceURL("script1.js"); |
| FetchSubresource(browser()->tab_strip_model()->GetActiveWebContents(), |
| resource_url); |
| |
| EXPECT_TRUE(content_verify_job_observer.WaitForExpectedJobs()); |
| } |
| |
| // Step 4: Check that we are going to reinstall the extension and wait for |
| // extension reinstall. |
| EXPECT_TRUE(service->pending_extension_manager() |
| ->IsPolicyReinstallForCorruptionExpected(kGoodCrxId)); |
| observer.WaitForExtensionWillBeInstalled(); |
| |
| // Extension was reloaded, old extension object is invalid. |
| extension = extension_registry()->enabled_extensions().GetByID(kGoodCrxId); |
| |
| // Step 5: Check that resource has its original contents. |
| { |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::FilePath resource_path = extension->path().Append(kResourcePath); |
| std::string contents; |
| ASSERT_TRUE(base::ReadFileToString(resource_path, &contents)); |
| EXPECT_EQ("// script1\n", contents); |
| } |
| } |
| |
| // Verifies that corrupted non-webstore policy-based extension is not repaired |
| // if there are no computed_hashes.json for it. Note that this behavior will |
| // change in the future. |
| // See https://crbug.com/958794#c22 for details. |
| // TODO(https://crbug.com/1044572): Change this test so extension without hashes |
| // will be also reinstalled. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| CorruptedNonWebstoreExtensionWithoutHashesRemained) { |
| ignore_content_verifier_.reset(); |
| ExtensionRequestInterceptor interceptor; |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| base::HistogramTester histogram_tester; |
| |
| const base::FilePath kResourcePath(FILE_PATH_LITERAL("script1.js")); |
| |
| extensions::ExtensionService* service = extension_service(); |
| |
| // Step 1: Setup a policy and force-install an extension. |
| const extensions::Extension* extension = |
| InstallForceListExtension(kGoodCrxId); |
| ASSERT_TRUE(extension); |
| |
| // Step 2: Corrupt extension's resource and remove hashes. |
| { |
| base::FilePath resource_path = extension->path().Append(kResourcePath); |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| // Temporarily disable extension, we don't want to tackle with resources of |
| // enabled one. Not using command DISABLE_USER_ACTION reason since |
| // force-installed extension may not be disabled by user action. |
| service->DisableExtension(kGoodCrxId, |
| extensions::disable_reason::DISABLE_RELOAD); |
| |
| const std::string kCorruptedContent("// corrupted\n"); |
| ASSERT_TRUE(base::WriteFile(resource_path, kCorruptedContent)); |
| ASSERT_TRUE(base::DeleteFile( |
| extensions::file_util::GetComputedHashesPath(extension->path()))); |
| |
| service->EnableExtension(kGoodCrxId); |
| } |
| |
| extensions::TestContentVerifyJobObserver content_verify_job_observer; |
| |
| // Step 3: Fetch resource to trigger corruption check and wait for content |
| // verify job completion. |
| { |
| content_verify_job_observer.ExpectJobResult( |
| kGoodCrxId, kResourcePath, |
| extensions::TestContentVerifyJobObserver::Result::FAILURE); |
| |
| GURL resource_url = extension->GetResourceURL("script1.js"); |
| FetchSubresource(browser()->tab_strip_model()->GetActiveWebContents(), |
| resource_url); |
| |
| EXPECT_TRUE(content_verify_job_observer.WaitForExpectedJobs()); |
| } |
| |
| // Step 4: Check that we are not going to reinstall the extension, but we have |
| // detected a corruption. |
| EXPECT_FALSE(service->pending_extension_manager() |
| ->IsPolicyReinstallForCorruptionExpected(kGoodCrxId)); |
| histogram_tester.ExpectUniqueSample( |
| "Extensions.CorruptPolicyExtensionDetected3", |
| extensions::PendingExtensionManager::PolicyReinstallReason:: |
| NO_UNSIGNED_HASHES_FOR_NON_WEBSTORE_SKIP, |
| 1); |
| } |
| |
| // Verifies that the extension is installed when the manifest is not fetched in |
| // case the remote update server is down. |
| // TODO(http://crbug.com/1086148): Add a check whether the cache entry is |
| // recorded in ForceInstalledMetrics after the bug is fixed. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallForcelistServerShutDown) { |
| base::HistogramTester histogram_tester; |
| ExtensionRequestInterceptor interceptor; |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| extension_service()->updater()->SetBackoffPolicyForTesting( |
| &kDefaultBackOffPolicyForTesting); |
| |
| base::FilePath extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(kTestExtensionsDir), base::FilePath(kGoodV1CrxName))); |
| |
| test_extension_cache_->AllowCaching(kGoodCrxId); |
| test_extension_cache_->PutExtension( |
| kGoodCrxId, "" /* expected hash, ignored by ExtensionCacheFake */, |
| extension_path, "1.0", base::DoNothing()); |
| // Shut down test update server to make update manifest and CRX queries fail |
| // with network error code net::ERR_CONNECTION_REFUSED. |
| EXPECT_TRUE(embedded_test_server()->ShutdownAndWaitUntilComplete()); |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| |
| observer.WaitForExtensionInstalled(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().GetByID(kGoodCrxId)); |
| } |
| |
| // Verifies that the extension is installed when the manifest is not fetched in |
| // case the device is offline. This test mimics a server providing |
| // ERR_INTERNET_DISCONNECTED response instead of actually having the device |
| // offline. |
| // TODO(http://crbug.com/1086148): Add a check whether the cache entry is |
| // recorded in ForceInstalledMetrics after the bug is fixed. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionInstallForcelistOffline) { |
| base::HistogramTester histogram_tester; |
| ExtensionRequestInterceptor interceptor; |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| // This simulates inability to make network requests for fetching the |
| // extension update manifest and CRX files. |
| { |
| interceptor.set_interceptor_hook(base::BindLambdaForTesting( |
| [&](content::URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url.path() != |
| "/extensions/good_v1_update_manifest.xml") |
| return false; |
| params->client->OnComplete(network::URLLoaderCompletionStatus( |
| net::ERR_INTERNET_DISCONNECTED)); |
| return true; |
| })); |
| } |
| extension_service()->updater()->SetBackoffPolicyForTesting( |
| &kDefaultBackOffPolicyForTesting); |
| |
| base::FilePath extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(kTestExtensionsDir), base::FilePath(kGoodV1CrxName))); |
| |
| test_extension_cache_->AllowCaching(kGoodCrxId); |
| test_extension_cache_->PutExtension( |
| kGoodCrxId, "" /* expected hash, ignored by ExtensionCacheFake */, |
| extension_path, "1.0", base::DoNothing()); |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionInstalled(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().GetByID(kGoodCrxId)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallForcelist_DefaultedUpdateUrl) { |
| // Verifies the ExtensionInstallForcelist policy with an empty (defaulted) |
| // "update" URL. |
| |
| ExtensionRequestInterceptor interceptor; |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, GURL()); |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionWillBeInstalled(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().GetByID(kGoodCrxId)); |
| } |
| |
| // Verifies that the browser doesn't crash on shutdown. If the extensions are |
| // being installed, and the browser is shutdown, it should not lead to a crash |
| // as in (crbug/1114191). |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionInstallForcelistShutdownBeforeInstall) { |
| ExtensionRequestInterceptor interceptor; |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| PolicyMap policies; |
| AddExtensionToForceList(&policies, kGoodCrxId, url); |
| UpdateProviderPolicy(policies); |
| // The extension is not yet installed, shutdown the browser now and there |
| // should be no crash. |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionRecommendedInstallationMode) { |
| // Verifies that extensions that are recommended-installed by policies are |
| // installed, can be disabled but not uninstalled. |
| |
| ExtensionRequestInterceptor interceptor; |
| |
| // Extensions that are force-installed come from an update URL, which defaults |
| // to the webstore. Use a test URL for this test with an update manifest |
| // that includes "good_v1.crx". |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| // Mark as enterprise managed. |
| #if defined(OS_WIN) |
| base::win::ScopedDomainStateForTesting scoped_domain(true); |
| #endif |
| |
| extensions::ExtensionService* service = extension_service(); |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // Setting the forcelist extension should install "good_v1.crx". |
| base::DictionaryValue dict_value; |
| dict_value.SetString(std::string(kGoodCrxId) + "." + |
| extensions::schema_constants::kInstallationMode, |
| extensions::schema_constants::kNormalInstalled); |
| dict_value.SetString( |
| std::string(kGoodCrxId) + "." + extensions::schema_constants::kUpdateUrl, |
| url.spec()); |
| PolicyMap policies; |
| policies.Set(key::kExtensionSettings, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, dict_value.Clone(), |
| nullptr); |
| extensions::TestExtensionRegistryObserver observer(registry); |
| UpdateProviderPolicy(policies); |
| observer.WaitForExtensionWillBeInstalled(); |
| |
| // TODO(crbug.com/1006342): There is a race condition here where the extension |
| // may or may not be enabled by the time we get here. |
| EXPECT_TRUE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::ENABLED | |
| extensions::ExtensionRegistry::DISABLED)); |
| |
| // The user is not allowed to uninstall recommended-installed extensions. |
| UninstallExtension(kGoodCrxId, false); |
| |
| // Explicitly re-enables the extension. |
| service->EnableExtension(kGoodCrxId); |
| |
| // But the user is allowed to disable them. |
| EXPECT_TRUE(service->IsExtensionEnabled(kGoodCrxId)); |
| DisableExtension(kGoodCrxId); |
| EXPECT_FALSE(service->IsExtensionEnabled(kGoodCrxId)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionAllowedTypes) { |
| // Verifies that extensions are blocked if policy specifies an allowed types |
| // list and the extension's type is not on that list. |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| ASSERT_FALSE(registry->GetExtensionById( |
| kHostedAppCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| base::ListValue allowed_types; |
| allowed_types.AppendString("hosted_app"); |
| PolicyMap policies; |
| policies.Set(key::kExtensionAllowedTypes, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, allowed_types.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| // "good.crx" is blocked. |
| EXPECT_FALSE(InstallExtension(kGoodCrxName)); |
| EXPECT_FALSE(registry->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| |
| // "hosted_app.crx" is of a allowlisted type. |
| const extensions::Extension* hosted_app = InstallExtension(kHostedAppCrxName); |
| ASSERT_TRUE(hosted_app); |
| EXPECT_EQ(kHostedAppCrxId, hosted_app->id()); |
| EXPECT_EQ(hosted_app, |
| registry->enabled_extensions().GetByID(kHostedAppCrxId)); |
| |
| // The user can remove the extension. |
| UninstallExtension(kHostedAppCrxId, true); |
| } |
| |
| // Checks that a click on an extension CRX download triggers the extension |
| // installation prompt without further user interaction when the source is |
| // allowlisted by policy. |
| // Flaky on windows; http://crbug.com/295729 . |
| #if defined(OS_WIN) |
| #define MAYBE_ExtensionInstallSources DISABLED_ExtensionInstallSources |
| #else |
| #define MAYBE_ExtensionInstallSources ExtensionInstallSources |
| #endif |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, MAYBE_ExtensionInstallSources) { |
| extensions::ScopedTestDialogAutoConfirm auto_confirm( |
| extensions::ScopedTestDialogAutoConfirm::ACCEPT); |
| extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass; |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL download_page_url = embedded_test_server()->GetURL( |
| "/policy/extension_install_sources_test.html"); |
| ui_test_utils::NavigateToURL(browser(), download_page_url); |
| |
| const GURL install_source_url( |
| embedded_test_server()->GetURL("/extensions/*")); |
| const GURL referrer_url(embedded_test_server()->GetURL("/policy/*")); |
| |
| // As long as the policy is not present, extensions are considered dangerous. |
| content::DownloadTestObserverTerminal download_observer( |
| content::BrowserContext::GetDownloadManager(browser()->profile()), 1, |
| content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_DENY); |
| PerformClick(0, 0); |
| download_observer.WaitForFinished(); |
| |
| // Install the policy and trigger another download. |
| base::ListValue install_sources; |
| install_sources.AppendString(install_source_url.spec()); |
| install_sources.AppendString(referrer_url.spec()); |
| PolicyMap policies; |
| policies.Set(key::kExtensionInstallSources, POLICY_LEVEL_MANDATORY, |
| POLICY_SCOPE_USER, POLICY_SOURCE_CLOUD, install_sources.Clone(), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| |
| extensions::TestExtensionRegistryObserver observer(extension_registry()); |
| PerformClick(1, 0); |
| observer.WaitForExtensionWillBeInstalled(); |
| // Note: Cannot check that the notification details match the expected |
| // exception, since the details object has already been freed prior to |
| // the completion of observer.WaitForExtensionWillBeInstalled(). |
| |
| // The first extension shouldn't be present, the second should be there. |
| EXPECT_FALSE(extension_registry()->GetExtensionById( |
| kGoodCrxId, extensions::ExtensionRegistry::EVERYTHING)); |
| EXPECT_TRUE( |
| extension_registry()->enabled_extensions().GetByID(kSimpleWithIconCrxId)); |
| } |
| |
| // Verifies that extensions with version older than the minimum version required |
| // by policy will get disabled, and will be auto-updated and/or re-enabled upon |
| // policy changes as well as regular auto-updater scheduled updates. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, ExtensionMinimumVersionRequired) { |
| ExtensionRequestInterceptor interceptor; |
| |
| base::AtomicRefCount update_extension_count; |
| base::RunLoop first_update_extension_runloop; |
| interceptor.set_interceptor_hook(base::BindLambdaForTesting( |
| [&](content::URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url.host() != "update.extension") |
| return false; |
| |
| if (!update_extension_count.IsZero() && !update_extension_count.IsOne()) |
| return false; |
| |
| if (update_extension_count.IsZero()) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "400 Bad request", std::string(), params->client.get()); |
| } else { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good2_update_manifest.xml", |
| params->client.get()); |
| } |
| if (update_extension_count.IsZero()) |
| first_update_extension_runloop.Quit(); |
| update_extension_count.Increment(); |
| return true; |
| })); |
| |
| extensions::ExtensionService* service = extension_service(); |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(browser()->profile()); |
| |
| // Install the extension. |
| EXPECT_TRUE(InstallExtension(kGoodV1CrxName)); |
| EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| |
| // Update policy to set a minimum version of 1.0.0.0, the extension (with |
| // version 1.0.0.0) should still be enabled. |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.SetMinimumVersionRequired(kGoodCrxId, "1.0.0.0"); |
| } |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| |
| // Update policy to set a minimum version of 1.0.0.1, the extension (with |
| // version 1.0.0.0) should now be disabled. |
| EXPECT_TRUE(update_extension_count.IsZero()); |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.SetMinimumVersionRequired(kGoodCrxId, kGoodCrxVersion); |
| } |
| first_update_extension_runloop.Run(); |
| EXPECT_TRUE(update_extension_count.IsOne()); |
| |
| EXPECT_TRUE(registry->disabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY, |
| extension_prefs->GetDisableReasons(kGoodCrxId)); |
| |
| // Provide a new version (1.0.0.1) which is expected to be auto updated to |
| // via the update URL in the manifest of the older version. |
| EXPECT_TRUE(update_extension_count.IsOne()); |
| { |
| extensions::TestExtensionRegistryObserver update_observer(registry); |
| service->updater()->CheckSoon(); |
| update_observer.WaitForExtensionWillBeInstalled(); |
| } |
| EXPECT_EQ(2, update_extension_count.SubtleRefCountForDebug()); |
| |
| // The extension should be auto-updated to newer version and re-enabled. |
| EXPECT_EQ(kGoodCrxVersion, |
| registry->GetInstalledExtension(kGoodCrxId)->version().GetString()); |
| EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| } |
| |
| // Similar to ExtensionMinimumVersionRequired test, but with different settings |
| // and orders. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionMinimumVersionRequiredAlt) { |
| ExtensionRequestInterceptor interceptor; |
| |
| base::AtomicRefCount update_extension_count; |
| interceptor.set_interceptor_hook(base::BindLambdaForTesting( |
| [&](content::URLLoaderInterceptor::RequestParams* params) { |
| if (params->url_request.url.host() == "update.extension" && |
| update_extension_count.IsZero()) { |
| content::URLLoaderInterceptor::WriteResponse( |
| "chrome/test/data/extensions/good2_update_manifest.xml", |
| params->client.get()); |
| update_extension_count.Increment(); |
| return true; |
| } |
| return false; |
| })); |
| |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(browser()->profile()); |
| |
| // Set the policy to require an even higher minimum version this time. |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.SetMinimumVersionRequired(kGoodCrxId, "1.0.0.2"); |
| } |
| base::RunLoop().RunUntilIdle(); |
| |
| // Install the 1.0.0.0 version, it should be installed but disabled. |
| EXPECT_TRUE(InstallExtension(kGoodV1CrxName)); |
| EXPECT_TRUE(registry->disabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY, |
| extension_prefs->GetDisableReasons(kGoodCrxId)); |
| EXPECT_EQ("1.0.0.0", |
| registry->GetInstalledExtension(kGoodCrxId)->version().GetString()); |
| |
| // An extension management policy update should trigger an update as well. |
| EXPECT_TRUE(update_extension_count.IsZero()); |
| { |
| extensions::TestExtensionRegistryObserver update_observer(registry); |
| { |
| // Set a higher minimum version, just intend to trigger a policy update. |
| extensions::ExtensionManagementPolicyUpdater management_policy( |
| &provider_); |
| management_policy.SetMinimumVersionRequired(kGoodCrxId, "1.0.0.3"); |
| } |
| base::RunLoop().RunUntilIdle(); |
| update_observer.WaitForExtensionWillBeInstalled(); |
| } |
| EXPECT_TRUE(update_extension_count.IsOne()); |
| |
| // It should be updated to 1.0.0.1 but remain disabled. |
| EXPECT_EQ(kGoodCrxVersion, |
| registry->GetInstalledExtension(kGoodCrxId)->version().GetString()); |
| EXPECT_TRUE(registry->disabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY, |
| extension_prefs->GetDisableReasons(kGoodCrxId)); |
| |
| // Remove the minimum version requirement. The extension should be re-enabled. |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.UnsetMinimumVersionRequired(kGoodCrxId); |
| } |
| base::RunLoop().RunUntilIdle(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_FALSE(extension_prefs->HasDisableReason( |
| kGoodCrxId, |
| extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY)); |
| } |
| |
| // Verifies that a force-installed extension which does not meet a subsequently |
| // set minimum version requirement is handled well. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest, |
| ExtensionMinimumVersionForceInstalled) { |
| ExtensionRequestInterceptor interceptor; |
| |
| // Mark as enterprise managed. |
| #if defined(OS_WIN) |
| base::win::ScopedDomainStateForTesting scoped_domain(true); |
| #endif |
| extensions::ExtensionRegistry* registry = extension_registry(); |
| extensions::ExtensionPrefs* extension_prefs = |
| extensions::ExtensionPrefs::Get(browser()->profile()); |
| |
| // Prepare the update URL for force installing. |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| GURL url = |
| embedded_test_server()->GetURL("/extensions/good_v1_update_manifest.xml"); |
| |
| // Set policy to force-install the extension, it should be installed and |
| // enabled. |
| extensions::TestExtensionRegistryObserver install_observer(registry); |
| EXPECT_FALSE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.SetIndividualExtensionAutoInstalled(kGoodCrxId, |
| url.spec(), true); |
| } |
| base::RunLoop().RunUntilIdle(); |
| install_observer.WaitForExtensionWillBeInstalled(); |
| |
| EXPECT_TRUE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| |
| // Set policy a minimum version of "1.0.0.1", the extension now should be |
| // disabled. |
| { |
| extensions::ExtensionManagementPolicyUpdater management_policy(&provider_); |
| management_policy.SetMinimumVersionRequired(kGoodCrxId, kGoodCrxVersion); |
| } |
| base::RunLoop().RunUntilIdle(); |
| EXPECT_FALSE(registry->enabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_TRUE(registry->disabled_extensions().Contains(kGoodCrxId)); |
| EXPECT_EQ(extensions::disable_reason::DISABLE_UPDATE_REQUIRED_BY_POLICY, |
| extension_prefs->GetDisableReasons(kGoodCrxId)); |
| } |
| |
| // Similar to ExtensionPolicyTest but sets the WebAppInstallForceList policy |
| // before the browser is started. |
| class WebAppInstallForceListPolicyTest : public ExtensionPolicyTest { |
| public: |
| WebAppInstallForceListPolicyTest() {} |
| ~WebAppInstallForceListPolicyTest() override {} |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| ExtensionPolicyTest::SetUpInProcessBrowserTestFixture(); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| policy_app_url_ = |
| embedded_test_server()->GetURL("/banners/manifest_test_page.html"); |
| base::Value url(policy_app_url_.spec()); |
| base::Value launch_container("window"); |
| |
| base::Value item(base::Value::Type::DICTIONARY); |
| item.SetKey("url", std::move(url)); |
| item.SetKey("default_launch_container", std::move(launch_container)); |
| |
| base::Value list(base::Value::Type::LIST); |
| list.Append(std::move(item)); |
| |
| PolicyMap policies; |
| SetPolicy(&policies, key::kWebAppInstallForceList, std::move(list)); |
| provider_.UpdateChromePolicy(policies); |
| } |
| |
| protected: |
| GURL policy_app_url_; |
| |
| private: |
| DISALLOW_COPY_AND_ASSIGN(WebAppInstallForceListPolicyTest); |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(WebAppInstallForceListPolicyTest, StartUpInstallation) { |
| const web_app::AppRegistrar& registrar = |
| web_app::WebAppProviderBase::GetProviderBase(browser()->profile()) |
| ->registrar(); |
| web_app::WebAppInstallObserver install_observer(browser()->profile()); |
| base::Optional<web_app::AppId> app_id = |
| registrar.FindAppWithUrlInScope(policy_app_url_); |
| if (!app_id) |
| app_id = install_observer.AwaitNextInstall(); |
| EXPECT_EQ(policy_app_url_, registrar.GetAppStartUrl(*app_id)); |
| } |
| |
| // Fixture for tests that have two profiles with a different policy for each. |
| class ExtensionPolicyTest2Contexts : public PolicyTest { |
| public: |
| ExtensionPolicyTest2Contexts() = default; |
| ExtensionPolicyTest2Contexts(const ExtensionPolicyTest2Contexts& other) = |
| delete; |
| ~ExtensionPolicyTest2Contexts() override = default; |
| |
| protected: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| #if defined(OS_CHROMEOS) |
| command_line->AppendSwitch( |
| chromeos::switches::kIgnoreUserProfileMappingForTests); |
| #endif |
| PolicyTest::SetUpCommandLine(command_line); |
| } |
| |
| void SetUp() override { |
| PolicyTest::SetUp(); |
| test_extension_cache1_ = std::make_unique<extensions::ExtensionCacheFake>(); |
| test_extension_cache2_ = std::make_unique<extensions::ExtensionCacheFake>(); |
| } |
| |
| void TearDown() override { |
| test_extension_cache1_.reset(); |
| test_extension_cache2_.reset(); |
| PolicyTest::TearDown(); |
| } |
| |
| void SetUpInProcessBrowserTestFixture() override { |
| PolicyTest::SetUpInProcessBrowserTestFixture(); |
| EXPECT_CALL(profile1_policy_, IsInitializationComplete(testing::_)) |
| .WillRepeatedly(testing::Return(true)); |
| policy::PushProfilePolicyConnectorProviderForTesting(&profile1_policy_); |
| } |
| |
| void SetUpOnMainThread() override { |
| PolicyTest::SetUpOnMainThread(); |
| profile1_ = browser()->profile(); |
| |
| profile2_ = CreateProfile(&profile2_policy_); |
| |
| service1_ = CreateExtensionService(profile1_); |
| service2_ = CreateExtensionService(profile2_); |
| service1_->updater()->SetExtensionCacheForTesting( |
| test_extension_cache1_.get()); |
| service2_->updater()->SetExtensionCacheForTesting( |
| test_extension_cache2_.get()); |
| registry1_ = CreateExtensionRegistry(profile1_); |
| registry2_ = CreateExtensionRegistry(profile2_); |
| } |
| |
| protected: |
| const extensions::Extension* InstallExtension( |
| const base::FilePath::StringType& path, |
| const base::FilePath::StringType& name, |
| content::BrowserContext* browser_context, |
| extensions::ExtensionService* extension_service, |
| extensions::ExtensionRegistry* extension_registry) { |
| base::FilePath extension_path(ui_test_utils::GetTestFilePath( |
| base::FilePath(path), base::FilePath(name))); |
| scoped_refptr<extensions::CrxInstaller> installer = |
| extensions::CrxInstaller::CreateSilent(extension_service); |
| installer->set_allow_silent_install(true); |
| installer->set_install_cause(extension_misc::INSTALL_CAUSE_AUTOMATION); |
| installer->set_creation_flags(extensions::Extension::FROM_WEBSTORE); |
| installer->set_off_store_install_allow_reason( |
| extensions::CrxInstaller::OffStoreInstallAllowReason:: |
| OffStoreInstallAllowedInTest); |
| |
| extensions::ChromeExtensionTestNotificationObserver observer( |
| browser_context); |
| observer.Watch(extensions::NOTIFICATION_CRX_INSTALLER_DONE, |
| content::Source<extensions::CrxInstaller>(installer.get())); |
| installer->InstallCrx(extension_path); |
| observer.Wait(); |
| if (!observer.WaitForExtensionViewsToLoad()) |
| return nullptr; |
| return extension_registry->GetExtensionById( |
| observer.last_loaded_extension_id(), |
| extensions::ExtensionRegistry::ENABLED); |
| } |
| |
| void SetTabSpecificPermissionsForURL(const extensions::Extension* extension, |
| int tab_id, |
| GURL& url, |
| int url_scheme) { |
| extensions::URLPatternSet new_hosts; |
| new_hosts.AddOrigin(url_scheme, url); |
| extension->permissions_data()->UpdateTabSpecificPermissions( |
| 1, extensions::PermissionSet(extensions::APIPermissionSet(), |
| extensions::ManifestPermissionSet(), |
| std::move(new_hosts), |
| extensions::URLPatternSet())); |
| } |
| |
| MockConfigurationPolicyProvider* GetProfile1Policy() { |
| return &profile1_policy_; |
| } |
| MockConfigurationPolicyProvider* GetProfile2Policy() { |
| return &profile2_policy_; |
| } |
| Profile* GetProfile1() { return browser()->profile(); } |
| Profile* GetProfile2() { return profile2_; } |
| Browser* GetBrowser1() { return browser(); } |
| extensions::ExtensionRegistry* GetExtensionRegistry1() { return registry1_; } |
| extensions::ExtensionRegistry* GetExtensionRegistry2() { return registry2_; } |
| extensions::ExtensionService* GetExtensionService1() { return service1_; } |
| extensions::ExtensionService* GetExtensionService2() { return service2_; } |
| |
| private: |
| // Creates a Profile for testing. The Profile is returned. |
| // The policy for the profile has to be passed via policy_for_profile. |
| // This method is called from SetUp and only from there. |
| Profile* CreateProfile(MockConfigurationPolicyProvider* policy_for_profile) { |
| EXPECT_CALL(*policy_for_profile, IsInitializationComplete(testing::_)) |
| .WillRepeatedly(testing::Return(true)); |
| Profile* profile = nullptr; |
| policy::PushProfilePolicyConnectorProviderForTesting(policy_for_profile); |
| |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| |
| // Create an additional profile. |
| base::FilePath path_profile = |
| profile_manager->GenerateNextProfileDirectoryPath(); |
| base::RunLoop run_loop; |
| profile_manager->CreateProfileAsync( |
| path_profile, |
| base::BindRepeating(&policy::OnProfileInitialized, &profile, |
| run_loop.QuitClosure()), |
| base::string16(), std::string()); |
| |
| // Run the message loop to allow profile creation to take place; the loop is |
| // terminated by OnProfileInitialized calling the loop's QuitClosure when |
| // the profile is created. |
| run_loop.Run(); |
| return profile; |
| } |
| |
| extensions::ExtensionService* CreateExtensionService( |
| content::BrowserContext* context) { |
| extensions::ExtensionSystem* system = |
| extensions::ExtensionSystem::Get(context); |
| return system->extension_service(); |
| } |
| |
| extensions::ExtensionRegistry* CreateExtensionRegistry( |
| content::BrowserContext* context) { |
| return extensions::ExtensionRegistry::Get(context); |
| } |
| |
| std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache1_; |
| std::unique_ptr<extensions::ExtensionCacheFake> test_extension_cache2_; |
| extensions::ScopedIgnoreContentVerifierForTest ignore_content_verifier_; |
| Profile* profile1_; |
| Profile* profile2_; |
| MockConfigurationPolicyProvider profile1_policy_; |
| MockConfigurationPolicyProvider profile2_policy_; |
| extensions::ExtensionRegistry* registry1_; |
| extensions::ExtensionRegistry* registry2_; |
| extensions::ExtensionService* service1_; |
| extensions::ExtensionService* service2_; |
| }; |
| |
| // Verifies that default policy host block/allow settings are applied as |
| // expected. |
| IN_PROC_BROWSER_TEST_F(ExtensionPolicyTest2Contexts, |
| ExtensionDefaultPolicyBlockedHost) { |
| GURL test_url = GURL("http://www.google.com"); |
| std::string* error = nullptr; |
| int tab_id = 1; |
| |
| const extensions::Extension* app1 = |
| InstallExtension(kTestExtensionsDir, kGoodCrxName, GetProfile1(), |
| GetExtensionService1(), GetExtensionRegistry1()); |
| ASSERT_TRUE(app1); |
| const extensions::Extension* app2 = |
| InstallExtension(kTestExtensionsDir, kGoodCrxName, GetProfile2(), |
| GetExtensionService2(), GetExtensionRegistry2()); |
| ASSERT_TRUE(app2); |
| SetTabSpecificPermissionsForURL(app1, tab_id, test_url, |
| URLPattern::SCHEME_ALL); |
| SetTabSpecificPermissionsForURL(app2, tab_id, test_url, |
| URLPattern::SCHEME_ALL); |
| |
| ASSERT_TRUE(GetExtensionService1()->IsExtensionEnabled(app1->id())); |
| ASSERT_TRUE(GetExtensionService2()->IsExtensionEnabled(app2->id())); |
| |
| ASSERT_TRUE(app1->permissions_data()->CanAccessPage(test_url, tab_id, error)); |
| ASSERT_TRUE(app2->permissions_data()->CanAccessPage(test_url, tab_id, error)); |
| |
| { |
| extensions::ExtensionManagementPolicyUpdater pref(GetProfile1Policy()); |
| pref.AddPolicyBlockedHost("*", "*://*.google.com"); |
| } |
| |
| EXPECT_FALSE( |
| app1->permissions_data()->CanAccessPage(test_url, tab_id, error)); |
| EXPECT_TRUE(app2->permissions_data()->CanAccessPage(test_url, tab_id, error)); |
| } |
| |
| } // namespace policy |