| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/settings/safety_hub_handler.h" |
| |
| #include <ctime> |
| #include <memory> |
| #include <string_view> |
| |
| #include "base/containers/fixed_flat_set.h" |
| #include "base/functional/bind.h" |
| #include "base/json/values_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/gtest_util.h" |
| #include "base/test/simple_test_clock.h" |
| #include "base/time/clock.h" |
| #include "base/time/time.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/extensions/cws_info_service.h" |
| #include "chrome/browser/extensions/cws_info_service_factory.h" |
| #include "chrome/browser/history/history_service_factory.h" |
| #include "chrome/browser/password_manager/password_manager_test_util.h" |
| #include "chrome/browser/permissions/notifications_engagement_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/safe_browsing/test_safe_browsing_service.h" |
| #include "chrome/browser/ui/promos/ios_promo_trigger_service.h" |
| #include "chrome/browser/ui/promos/ios_promo_trigger_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/mock_safe_browsing_database_manager.h" |
| #include "chrome/browser/ui/safety_hub/notification_permission_review_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/password_status_check_service.h" |
| #include "chrome/browser/ui/safety_hub/password_status_check_service_factory.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_constants.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_test_util.h" |
| #include "chrome/browser/ui/safety_hub/safety_hub_util.h" |
| #include "chrome/browser/ui/safety_hub/unused_site_permissions_manager.h" |
| #include "chrome/browser/ui/webui/settings/site_settings_helper.h" |
| #include "chrome/browser/ui/webui/version/version_ui.h" |
| #include "chrome/browser/upgrade_detector/build_state.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_version.h" |
| #include "chrome/grit/branded_strings.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "chrome/test/base/testing_profile.h" |
| #include "components/content_settings/core/browser/content_settings_registry.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/browser/website_settings_registry.h" |
| #include "components/content_settings/core/common/content_settings.h" |
| #include "components/content_settings/core/common/content_settings_pattern.h" |
| #include "components/content_settings/core/common/content_settings_types.h" |
| #include "components/content_settings/core/common/features.h" |
| #include "components/crx_file/id_util.h" |
| #include "components/password_manager/core/browser/password_store/test_password_store.h" |
| #include "components/permissions/constants.h" |
| #include "components/safe_browsing/core/common/features.h" |
| #include "components/safe_browsing/core/common/safe_browsing_prefs.h" |
| #include "components/sharing_message/features.h" |
| #include "components/sync_preferences/features.h" |
| #include "components/sync_preferences/testing_pref_service_syncable.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "content/public/test/test_web_ui.h" |
| #include "extensions/browser/extension_prefs.h" |
| #include "extensions/browser/extension_prefs_factory.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/extension_builder.h" |
| #include "extensions/common/extension_id.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "url/gurl.h" |
| |
| using extensions::mojom::ManifestLocation; |
| using password_manager::TestPasswordStore; |
| using safety_hub::SafetyHubCardState; |
| |
| enum SettingManager { USER, ADMIN, EXTENSION }; |
| constexpr char kUnusedTestSite[] = "https://example1.com"; |
| constexpr char kUsedTestSite[] = "https://example2.com"; |
| constexpr char kAbusiveTestSite[] = "https://example3.com"; |
| constexpr char kAbusiveAndUnusedTestSite[] = "https://example4.com"; |
| constexpr char16_t kUsername[] = u"bob"; |
| constexpr char16_t kCompromisedPassword[] = u"fnlsr4@cm^mdls@fkspnsg3d"; |
| constexpr ContentSettingsType kUnusedRegularPermission = |
| ContentSettingsType::GEOLOCATION; |
| constexpr ContentSettingsType kUnusedChooserPermission = |
| ContentSettingsType::FILE_SYSTEM_ACCESS_CHOOSER_DATA; |
| const base::TimeDelta kLifetime = base::Days(30); |
| |
| namespace { |
| |
| class MockIOSPromoTriggerService : public IOSPromoTriggerService { |
| public: |
| explicit MockIOSPromoTriggerService(Profile* profile) |
| : IOSPromoTriggerService(profile) {} |
| ~MockIOSPromoTriggerService() override = default; |
| |
| MOCK_METHOD(void, |
| NotifyPromoShouldBeShown, |
| (IOSPromoType promo_type), |
| (override)); |
| MOCK_METHOD(const syncer::DeviceInfo*, GetIOSDeviceToRemind, (), (override)); |
| MOCK_METHOD(void, |
| SetReminderForIOSDevice, |
| (IOSPromoType promo_type, const std::string& device_guid), |
| (override)); |
| }; |
| |
| std::unique_ptr<KeyedService> BuildMockIOSPromoTriggerService( |
| content::BrowserContext* context) { |
| return std::make_unique<MockIOSPromoTriggerService>( |
| Profile::FromBrowserContext(context)); |
| } |
| |
| } // namespace |
| |
| class SafetyHubHandlerTest : public testing::Test { |
| public: |
| SafetyHubHandlerTest() { |
| feature_list_.InitWithFeaturesAndParameters( |
| /*enabled_features=*/ |
| {{content_settings::features::kSafetyCheckUnusedSitePermissions, {}}, |
| {content_settings::features:: |
| kSafetyCheckUnusedSitePermissionsForSupportedChooserPermissions, |
| {}}, |
| {features::kSafetyHubExtensionsUwSTrigger, {}}, |
| {features::kSafetyHubExtensionsOffStoreTrigger, {}}, |
| {kMobilePromoOnDesktop, {{kMobilePromoOnDesktopPromoTypeParam, "2"}}}, |
| {sync_preferences::features::kEnableCrossDevicePrefTracker, {}}}, |
| /*disabled_features=*/{}); |
| } |
| |
| void SetUp() override { |
| // Set clock for HostContentSettingsMap. |
| base::Time time; |
| ASSERT_TRUE(base::Time::FromString("2022-09-07 13:00", &time)); |
| clock_.SetNow(time); |
| |
| safety_hub_test_util::CreateRevokedPermissionsService(profile()); |
| safety_hub_test_util::CreateNotificationPermissionsReviewService(profile()); |
| |
| hcsm_ = HostContentSettingsMapFactory::GetForProfile(profile()); |
| hcsm_->SetClockForTesting(&clock_); |
| |
| // Set up safe browsing service. |
| SetUpSafeBrowsingService(); |
| |
| handler_ = std::make_unique<SafetyHubHandler>(profile()); |
| handler()->set_web_ui(web_ui()); |
| handler()->AllowJavascript(); |
| |
| // Run password check to fetch latest result from disk. |
| safety_hub_test_util::UpdatePasswordCheckServiceAsync( |
| safety_hub_test_util::CreateAndUsePasswordStatusService(profile())); |
| } |
| |
| void TearDown() override { |
| auto* partition = profile()->GetDefaultStoragePartition(); |
| if (partition) { |
| partition->WaitForDeletionTasksForTesting(); |
| } |
| TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr); |
| } |
| |
| void AddNotificationPermissionsForReview() { |
| // Set a host to have large number of notifications and keep engagement as |
| // NONE. |
| GURL url = GURL("https://example0.org:443"); |
| hcsm()->SetContentSettingDefaultScope( |
| url, GURL(), ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_ALLOW); |
| auto* notifications_engagement_service = |
| NotificationsEngagementServiceFactory::GetForProfile(profile()); |
| notifications_engagement_service->RecordNotificationDisplayed(url, 35); |
| |
| // Trigger the update for changes to be seen. |
| NotificationPermissionsReviewService* service = |
| NotificationPermissionsReviewServiceFactory::GetForProfile(profile()); |
| CHECK(service); |
| service->UpdateAsync(); |
| RunUntilIdle(); |
| } |
| |
| void AddRevokedPermission() { |
| auto dict = base::Value::Dict() |
| .Set(permissions::kRevokedKey, |
| base::Value::List() |
| .Append(UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedRegularPermission)) |
| .Append(UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedChooserPermission))) |
| .Set(permissions::kRevokedChooserPermissionsKey, |
| base::Value::Dict().Set( |
| UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedChooserPermission), |
| base::Value::Dict().Set("foo", "bar"))); |
| |
| content_settings::ContentSettingConstraints constraint(clock()->Now()); |
| constraint.set_lifetime(kLifetime); |
| |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kUnusedTestSite), GURL(kUnusedTestSite), |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| base::Value(dict.Clone()), constraint); |
| } |
| |
| void AddAbusiveNotificationPermission() { |
| mock_database_manager()->SetThreatTypeForUrl( |
| GURL(kAbusiveTestSite), |
| safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING); |
| content_settings::ContentSettingConstraints constraint(clock()->Now()); |
| constraint.set_lifetime(kLifetime); |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kAbusiveTestSite), GURL(kAbusiveTestSite), |
| ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS, |
| base::Value(base::Value::Dict().Set( |
| safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr)), |
| constraint); |
| } |
| |
| void AddAbusiveAndUnusedNotificationPermission() { |
| content_settings::ContentSettingConstraints constraint(clock()->Now()); |
| constraint.set_lifetime(kLifetime); |
| mock_database_manager()->SetThreatTypeForUrl( |
| GURL(kAbusiveAndUnusedTestSite), |
| safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING); |
| // Setup abusive notification permissions. |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kAbusiveAndUnusedTestSite), GURL(kAbusiveAndUnusedTestSite), |
| ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS, |
| base::Value(base::Value::Dict().Set( |
| safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr)), |
| constraint); |
| |
| // Setup unused permissions. |
| auto dict = |
| base::Value::Dict() |
| .Set(permissions::kRevokedKey, |
| base::Value::List() |
| .Append(UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedRegularPermission)) |
| .Append(UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedChooserPermission))) |
| .Set(permissions::kRevokedChooserPermissionsKey, |
| base::Value::Dict().Set(UnusedSitePermissionsManager:: |
| ConvertContentSettingsTypeToKey( |
| kUnusedChooserPermission), |
| base::Value::Dict().Set("foo", "bar"))) |
| .Set(safety_hub::kExpirationKey, |
| base::TimeToValue(constraint.expiration())) |
| .Set(safety_hub::kLifetimeKey, |
| base::TimeDeltaToValue(constraint.lifetime())); |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kAbusiveAndUnusedTestSite), GURL(kAbusiveAndUnusedTestSite), |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| base::Value(dict.Clone()), constraint); |
| } |
| |
| void CreateLeakedCredential() { |
| profile_store().AddLogin( |
| MakeForm(kUsername, kCompromisedPassword, kUsedTestSite, true)); |
| PasswordStatusCheckService* password_service = |
| PasswordStatusCheckServiceFactory::GetForProfile(profile()); |
| safety_hub_test_util::UpdatePasswordCheckServiceAsync(password_service); |
| EXPECT_EQ(password_service->compromised_credential_count(), 1UL); |
| } |
| |
| void FixLeakedCredential() { |
| profile_store().UpdateLogin( |
| MakeForm(kUsername, u"new_fnlsr4@cm^mls@fkspnsg3d")); |
| PasswordStatusCheckService* password_service = |
| PasswordStatusCheckServiceFactory::GetForProfile(profile()); |
| safety_hub_test_util::UpdatePasswordCheckServiceAsync(password_service); |
| EXPECT_EQ(password_service->compromised_credential_count(), 0UL); |
| } |
| |
| void AddExtensionsForReview() { |
| extensions::CWSInfoServiceFactory::GetInstance()->SetTestingFactory( |
| profile(), base::BindRepeating([](content::BrowserContext* context) |
| -> std::unique_ptr<KeyedService> { |
| return safety_hub_test_util::GetMockCWSInfoService( |
| Profile::FromBrowserContext(context)); |
| })); |
| safety_hub_test_util::CreateMockExtensions(profile()); |
| } |
| |
| void CreatMockCWSInfoService() { |
| extensions::CWSInfoServiceFactory::GetInstance()->SetTestingFactory( |
| profile(), base::BindRepeating([](content::BrowserContext* context) |
| -> std::unique_ptr<KeyedService> { |
| return safety_hub_test_util::GetMockCWSInfoServiceNoTriggers( |
| Profile::FromBrowserContext(context)); |
| })); |
| } |
| |
| void AddTriggeringExtension() { |
| handler_->SetTriggeringExtensionForTesting("Test"); |
| } |
| |
| void ExpectRevokedUnusedSitePermission(const std::string& url) { |
| EXPECT_EQ(ContentSetting::CONTENT_SETTING_ASK, |
| hcsm()->GetContentSetting(GURL(url), GURL(url), |
| kUnusedRegularPermission)); |
| EXPECT_EQ(base::Value(), |
| hcsm()->GetWebsiteSetting(GURL(url), GURL(url), |
| kUnusedChooserPermission)); |
| } |
| |
| void ExpectRevokedAbusiveNotificationPermission(const std::string& url) { |
| EXPECT_EQ(ContentSetting::CONTENT_SETTING_ASK, |
| hcsm()->GetContentSetting(GURL(url), GURL(url), |
| ContentSettingsType::NOTIFICATIONS)); |
| } |
| |
| void ValidateNotificationPermissionUpdate() { |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| EXPECT_EQ("cr.webUIListenerCallback", data.function_name()); |
| |
| ASSERT_TRUE(data.arg1()->is_string()); |
| EXPECT_EQ("notification-permission-review-list-maybe-changed", |
| data.arg1()->GetString()); |
| |
| ASSERT_TRUE(data.arg2()->is_list()); |
| } |
| |
| void SetPrefsForSafeBrowsing(bool is_enabled, |
| bool is_enhanced, |
| SettingManager managed_by) { |
| auto* prefs = profile()->GetTestingPrefService(); |
| |
| switch (managed_by) { |
| case USER: |
| prefs->SetUserPref(prefs::kSafeBrowsingEnabled, |
| std::make_unique<base::Value>(is_enabled)); |
| prefs->SetUserPref(prefs::kSafeBrowsingEnhanced, |
| std::make_unique<base::Value>(is_enhanced)); |
| break; |
| case ADMIN: |
| prefs->SetManagedPref(prefs::kSafeBrowsingEnabled, |
| std::make_unique<base::Value>(is_enabled)); |
| prefs->SetManagedPref(prefs::kSafeBrowsingEnhanced, |
| std::make_unique<base::Value>(is_enhanced)); |
| break; |
| case EXTENSION: |
| prefs->SetExtensionPref(prefs::kSafeBrowsingEnabled, |
| std::make_unique<base::Value>(is_enabled)); |
| prefs->SetExtensionPref(prefs::kSafeBrowsingEnhanced, |
| std::make_unique<base::Value>(is_enhanced)); |
| break; |
| default: |
| NOTREACHED() << "Unexpected value for managed_by argument. \n"; |
| } |
| } |
| |
| void ValidateHandleSafeBrowsingCardData(std::string header, |
| std::string subheader, |
| SafetyHubCardState state) { |
| base::Value::List args; |
| args.Append("getSafeBrowsingState"); |
| |
| handler()->HandleGetSafeBrowsingCardData(args); |
| |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| |
| EXPECT_EQ("cr.webUIResponse", data.function_name()); |
| ASSERT_TRUE(data.arg1()->is_string()); |
| EXPECT_EQ("getSafeBrowsingState", data.arg1()->GetString()); |
| // arg2 is a boolean that is true if the callback is successful. |
| ASSERT_TRUE(data.arg2()->is_bool()); |
| ASSERT_TRUE(data.arg2()); |
| ASSERT_TRUE(data.arg3()->is_dict()); |
| |
| EXPECT_EQ(header, *data.arg3()->GetDict().FindString("header")); |
| EXPECT_EQ(subheader, *data.arg3()->GetDict().FindString("subheader")); |
| EXPECT_EQ(static_cast<int>(state), |
| *data.arg3()->GetDict().FindInt("state")); |
| } |
| |
| void ValidateEntryPointHasRecommendationsAndHeader(bool hasRecommendations) { |
| base::Value::List args; |
| args.Append("getSafetyHubEntryPointData"); |
| |
| handler()->HandleGetSafetyHubEntryPointData(args); |
| |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| |
| EXPECT_EQ("cr.webUIResponse", data.function_name()); |
| ASSERT_TRUE(data.arg1()->is_string()); |
| EXPECT_EQ("getSafetyHubEntryPointData", data.arg1()->GetString()); |
| // arg2 is a boolean that is true if the callback is successful. |
| ASSERT_TRUE(data.arg2()->is_bool()); |
| ASSERT_TRUE(data.arg2()); |
| |
| // Validate the hasRecommendations value. |
| ASSERT_TRUE(data.arg3()->is_dict()); |
| EXPECT_EQ(hasRecommendations, |
| data.arg3()->GetDict().FindBool("hasRecommendations")); |
| // Validate the header value. |
| std::string header = hasRecommendations |
| ? l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_HEADER) |
| : ""; |
| EXPECT_EQ(header, *data.arg3()->GetDict().FindString("header")); |
| } |
| |
| // For a given Safety Hub module, configure the test environment so that tests |
| // can run when there is a recommendation for the module and when there is |
| // not. |
| void SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule module, |
| bool isModuleRecommended) { |
| switch (module) { |
| case SafetyHubHandler::SafetyHubModule::kPasswords: |
| isModuleRecommended ? CreateLeakedCredential() : FixLeakedCredential(); |
| break; |
| case SafetyHubHandler::SafetyHubModule::kVersion: |
| isModuleRecommended |
| ? g_browser_process->GetBuildState()->SetUpdate( |
| BuildState::UpdateType::kNormalUpdate, |
| base::Version({CHROME_VERSION_MAJOR, CHROME_VERSION_MINOR, |
| CHROME_VERSION_BUILD, |
| CHROME_VERSION_PATCH + 1}), |
| std::nullopt) |
| : g_browser_process->GetBuildState()->SetUpdate( |
| BuildState::UpdateType::kNone, base::Version(), std::nullopt); |
| break; |
| case SafetyHubHandler::SafetyHubModule::kSafeBrowsing: |
| isModuleRecommended |
| ? SetPrefsForSafeBrowsing(false, false, SettingManager::USER) |
| : SetPrefsForSafeBrowsing(true, true, SettingManager::USER); |
| break; |
| case SafetyHubHandler::SafetyHubModule::kExtensions: |
| isModuleRecommended ? AddTriggeringExtension() |
| : ClearTriggeringExtensions(); |
| break; |
| case SafetyHubHandler::SafetyHubModule::kNotifications: |
| hcsm()->ClearSettingsForOneType( |
| ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW); |
| isModuleRecommended |
| ? AddNotificationPermissionsForReview() |
| : handler()->HandleIgnoreOriginsForNotificationPermissionReview( |
| base::Value::List().Append(GetOriginList(1))); |
| break; |
| case SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions: |
| isModuleRecommended |
| ? AddRevokedPermission() |
| : handler()->HandleAcknowledgeRevokedUnusedSitePermissionsList( |
| base::Value::List()); |
| break; |
| default: |
| NOTREACHED() |
| << "Unexpected SafetyHubModule for test setup. A proper setup for " |
| "the module can be done only for supported modules.\n"; |
| } |
| } |
| |
| void AddUnusedPermission() { |
| // Create a revoked permission. |
| AddRevokedPermission(); |
| |
| // There should be only an unused URL in the revoked permissions list. |
| const auto& revoked_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions.size(), 1UL); |
| EXPECT_EQ(GURL(kUnusedTestSite), |
| GURL(*revoked_permissions[0].GetDict().FindString( |
| site_settings::kOrigin))); |
| } |
| |
| void ClearTriggeringExtensions() { |
| handler_->ClearExtensionResultsForTesting(); |
| } |
| |
| void ValidateEntryPointSubheader( |
| std::string subheader, |
| std::optional<SafetyHubHandler::SafetyHubModule> module = std::nullopt) { |
| // If there is a module provided, expect the module to be in a subheader. |
| // For that, setup the test environment so that the module has a |
| // recommendation. |
| if (module.has_value()) { |
| SetupTestToShowOrHideRecommendationForModule(module.value(), true); |
| } |
| |
| // Send a message to handler to get the current state of the subheader. |
| base::Value::List args; |
| args.Append("getSafetyHubEntryPointData"); |
| handler()->HandleGetSafetyHubEntryPointData(args); |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| |
| // Check that response from the handler follows the right format. |
| EXPECT_EQ("cr.webUIResponse", data.function_name()); |
| ASSERT_TRUE(data.arg1()->is_string()); |
| EXPECT_EQ("getSafetyHubEntryPointData", data.arg1()->GetString()); |
| // arg2 is a boolean that is true if the callback is successful. |
| ASSERT_TRUE(data.arg2()->is_bool()); |
| ASSERT_TRUE(data.arg2()); |
| |
| // Validate that the subheader we get is equal to the one we expect. |
| ASSERT_TRUE(data.arg3()->is_dict()); |
| EXPECT_EQ(subheader, *data.arg3()->GetDict().FindString("subheader")); |
| |
| // If in the beginning of the method the test environment is set for a |
| // module to have a recommendation, reset that back. |
| if (module.has_value()) { |
| SetupTestToShowOrHideRecommendationForModule(module.value(), false); |
| } |
| } |
| |
| base::Value::List GetOriginList(int size) { |
| base::Value::List origins; |
| for (int i = 0; i < size; i++) { |
| origins.Append("https://example" + base::NumberToString(i) + ".org:443"); |
| } |
| return origins; |
| } |
| |
| // TODO(crbug.com/40267370): Consider moving common test util functions |
| // between this file and password_status_check_service_unittest.cc to a util |
| // class. |
| password_manager::PasswordForm MakeForm(std::u16string_view username, |
| std::u16string_view password, |
| std::string origin = kUsedTestSite, |
| bool is_leaked = false) { |
| password_manager::PasswordForm form; |
| form.username_value = username; |
| form.password_value = password; |
| form.signon_realm = origin; |
| form.url = GURL(origin); |
| |
| if (is_leaked) { |
| // Credential issues for weak and reused are detected automatically and |
| // don't need to be specified explicitly. |
| form.password_issues.insert_or_assign( |
| password_manager::InsecureType::kLeaked, |
| password_manager::InsecurityMetadata( |
| base::Time::Now(), password_manager::IsMuted(false), |
| password_manager::TriggerBackendNotification(false))); |
| } |
| return form; |
| } |
| |
| void RunUntilIdle() { task_environment_.RunUntilIdle(); } |
| |
| TestingProfile* profile() { return &profile_; } |
| content::TestWebUI* web_ui() { return &web_ui_; } |
| SafetyHubHandler* handler() { return handler_.get(); } |
| HostContentSettingsMap* hcsm() { return hcsm_.get(); } |
| base::SimpleTestClock* clock() { return &clock_; } |
| password_manager::TestPasswordStore& profile_store() { |
| return *profile_store_; |
| } |
| password_manager::TestPasswordStore& account_store() { |
| return *account_store_; |
| } |
| MockSafeBrowsingDatabaseManager* mock_database_manager() { |
| return mock_database_manager_.get(); |
| } |
| |
| private: |
| void SetUpSafeBrowsingService() { |
| mock_database_manager_ = |
| base::MakeRefCounted<MockSafeBrowsingDatabaseManager>(); |
| safe_browsing_factory_ = |
| std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>(); |
| safe_browsing_factory_->SetTestDatabaseManager( |
| mock_database_manager_.get()); |
| TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService( |
| safe_browsing_factory_->CreateSafeBrowsingService()); |
| } |
| |
| base::test::ScopedFeatureList feature_list_; |
| content::BrowserTaskEnvironment task_environment_; |
| TestingProfile profile_; |
| content::TestWebUI web_ui_; |
| scoped_refptr<HostContentSettingsMap> hcsm_; |
| base::SimpleTestClock clock_; |
| scoped_refptr<TestPasswordStore> profile_store_ = |
| CreateAndUseTestPasswordStore(&profile_); |
| scoped_refptr<password_manager::TestPasswordStore> account_store_ = |
| CreateAndUseTestAccountPasswordStore(&profile_); |
| std::unique_ptr<SafetyHubHandler> handler_; |
| scoped_refptr<MockSafeBrowsingDatabaseManager> mock_database_manager_; |
| std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory> |
| safe_browsing_factory_; |
| }; |
| |
| TEST_F(SafetyHubHandlerTest, PopulateUnusedSitePermissionsData) { |
| AddUnusedPermission(); |
| // Add GEOLOCATION setting for url but do not add to revoked list. |
| content_settings::ContentSettingConstraints constraint; |
| constraint.set_track_last_visit_for_autoexpiration(true); |
| hcsm()->SetContentSettingDefaultScope( |
| GURL(kUsedTestSite), GURL(kUsedTestSite), |
| ContentSettingsType::GEOLOCATION, ContentSetting::CONTENT_SETTING_ALLOW, |
| constraint); |
| |
| // Revoked permissions list should still only contain the initial unused site. |
| const auto& revoked_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions.size(), 1UL); |
| const auto& revoked_permission_dict = revoked_permissions[0].GetDict(); |
| |
| EXPECT_EQ(GURL(kUnusedTestSite), |
| GURL(*revoked_permission_dict.FindString(site_settings::kOrigin))); |
| |
| const auto expiration = base::ValueToTime( |
| *revoked_permission_dict.Find(safety_hub::kExpirationKey)); |
| EXPECT_EQ(expiration, clock()->Now() + kLifetime); |
| |
| const auto lifetime = base::ValueToTimeDelta( |
| *revoked_permission_dict.Find(safety_hub::kLifetimeKey)); |
| EXPECT_EQ(lifetime, kLifetime); |
| |
| const auto* chooser_permissions_data = revoked_permission_dict.FindDict( |
| safety_hub::kSafetyHubChooserPermissionsData); |
| EXPECT_TRUE(chooser_permissions_data->contains( |
| UnusedSitePermissionsManager::ConvertContentSettingsTypeToKey( |
| kUnusedChooserPermission))); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleAllowPermissionsAgainForUnusedSite) { |
| AddUnusedPermission(); |
| base::Value::List initial_unused_site_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| ExpectRevokedUnusedSitePermission(kUnusedTestSite); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 1U); |
| |
| // Allow the revoked permission for the unused site again. |
| base::Value::List args; |
| args.Append(base::Value(kUnusedTestSite)); |
| handler()->HandleAllowPermissionsAgainForUnusedSite(args); |
| |
| // Check there is no origin in revoked permissions list. |
| ContentSettingsForOneType revoked_permissions_list = |
| hcsm()->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| EXPECT_EQ(0U, revoked_permissions_list.size()); |
| // Check if the permissions of url is regranted. |
| EXPECT_EQ( |
| ContentSetting::CONTENT_SETTING_ALLOW, |
| hcsm()->GetContentSetting(GURL(kUnusedTestSite), GURL(kUnusedTestSite), |
| kUnusedRegularPermission)); |
| EXPECT_EQ( |
| base::Value::Dict().Set("foo", "bar"), |
| hcsm()->GetWebsiteSetting(GURL(kUnusedTestSite), GURL(kUnusedTestSite), |
| kUnusedChooserPermission)); |
| |
| // Undoing restores the initial state. |
| handler()->HandleUndoAllowPermissionsAgainForUnusedSite( |
| std::move(initial_unused_site_permissions)); |
| ExpectRevokedUnusedSitePermission(kUnusedTestSite); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 1U); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleAcknowledgeRevokedUnusedSitePermissionsList) { |
| AddUnusedPermission(); |
| const auto& revoked_permissions_before = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_GT(revoked_permissions_before.size(), 0U); |
| // Acknowledging revoked permissions from unused sites clears the list. |
| base::Value::List args; |
| handler()->HandleAcknowledgeRevokedUnusedSitePermissionsList(args); |
| const auto& revoked_permissions_after = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions_after.size(), 0U); |
| |
| // Undo reverts the list to its initial state. |
| base::Value::List undo_args; |
| undo_args.Append(revoked_permissions_before.Clone()); |
| handler()->HandleUndoAcknowledgeRevokedUnusedSitePermissionsList(undo_args); |
| EXPECT_EQ(revoked_permissions_before, |
| handler()->PopulateUnusedSitePermissionsData()); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, PopulateAbusiveAndUnusedSitePermissionsData) { |
| AddAbusiveNotificationPermission(); |
| AddRevokedPermission(); |
| AddAbusiveAndUnusedNotificationPermission(); |
| |
| // Revoked permissions list should contain all 3 urls. |
| const auto& revoked_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions.size(), 3UL); |
| EXPECT_EQ(GURL(kUnusedTestSite), |
| GURL(*revoked_permissions[0].GetDict().FindString( |
| site_settings::kOrigin))); |
| EXPECT_EQ(GURL(kAbusiveAndUnusedTestSite), |
| GURL(*revoked_permissions[1].GetDict().FindString( |
| site_settings::kOrigin))); |
| EXPECT_EQ(GURL(kAbusiveTestSite), |
| GURL(*revoked_permissions[2].GetDict().FindString( |
| site_settings::kOrigin))); |
| |
| // Unused site url should have unused permissions in permission list. |
| auto* revoked_permission_list_unused = |
| revoked_permissions[0].GetDict().FindList(site_settings::kPermissions); |
| EXPECT_EQ((*revoked_permission_list_unused)[0], "location"); |
| EXPECT_EQ((*revoked_permission_list_unused)[1], |
| "file-system-access-handles-data"); |
| |
| // Abusive and unused site url should have both notifications and unused |
| // permissions in permission list. |
| auto* revoked_permission_list_abusive_and_unused = |
| revoked_permissions[1].GetDict().FindList(site_settings::kPermissions); |
| EXPECT_EQ((*revoked_permission_list_abusive_and_unused)[0], "location"); |
| EXPECT_EQ((*revoked_permission_list_abusive_and_unused)[1], "notifications"); |
| EXPECT_EQ((*revoked_permission_list_abusive_and_unused)[2], |
| "file-system-access-handles-data"); |
| |
| // Abusive notification url should have notifications in permission list. |
| auto* revoked_permission_list_abusive = |
| revoked_permissions[2].GetDict().FindList(site_settings::kPermissions); |
| EXPECT_EQ((*revoked_permission_list_abusive)[0], "notifications"); |
| |
| // Notifications should not be allowed. |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveAndUnusedTestSite); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleAllowPermissionsAgainForAbusiveSite) { |
| AddAbusiveNotificationPermission(); |
| base::Value::List initial_abusive_site_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| |
| // Allow the revoked permission for the unused site again. |
| base::Value::List args; |
| args.Append(base::Value(kAbusiveTestSite)); |
| handler()->HandleAllowPermissionsAgainForUnusedSite(args); |
| |
| // Check there is no origin in revoked permissions list. |
| EXPECT_TRUE(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| // Check if the permissions of url is regranted. |
| EXPECT_EQ( |
| ContentSetting::CONTENT_SETTING_ALLOW, |
| hcsm()->GetContentSetting(GURL(kAbusiveTestSite), GURL(kAbusiveTestSite), |
| ContentSettingsType::NOTIFICATIONS)); |
| |
| // Undoing restores the initial state. |
| handler()->HandleUndoAllowPermissionsAgainForUnusedSite( |
| std::move(initial_abusive_site_permissions)); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleAllowPermissionsAgainForAbusiveAndUnusedSite) { |
| AddAbusiveAndUnusedNotificationPermission(); |
| base::Value::List initial_abusive_and_unused_site_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| |
| // Allow the revoked permission for the unused site again. |
| base::Value::List args; |
| args.Append(base::Value(kAbusiveAndUnusedTestSite)); |
| handler()->HandleAllowPermissionsAgainForUnusedSite(args); |
| |
| // Check there is no origin in revoked permissions list. |
| EXPECT_TRUE(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| EXPECT_EQ(0U, hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size()); |
| // Check if the permissions of url is regranted. |
| EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW, |
| hcsm()->GetContentSetting(GURL(kAbusiveAndUnusedTestSite), |
| GURL(kAbusiveAndUnusedTestSite), |
| ContentSettingsType::NOTIFICATIONS)); |
| EXPECT_EQ(ContentSetting::CONTENT_SETTING_ALLOW, |
| hcsm()->GetContentSetting(GURL(kAbusiveAndUnusedTestSite), |
| GURL(kAbusiveAndUnusedTestSite), |
| ContentSettingsType::GEOLOCATION)); |
| |
| // Undoing restores the initial state. |
| handler()->HandleUndoAllowPermissionsAgainForUnusedSite( |
| std::move(initial_abusive_and_unused_site_permissions)); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveAndUnusedTestSite); |
| EXPECT_EQ( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()).size(), |
| 1U); |
| ExpectRevokedUnusedSitePermission(kAbusiveAndUnusedTestSite); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 1U); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleAcknowledgeRevokedAbusiveAndUnusedSitePermissionsList) { |
| AddUnusedPermission(); |
| AddAbusiveNotificationPermission(); |
| AddAbusiveAndUnusedNotificationPermission(); |
| const auto& revoked_permissions_before = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions_before.size(), 3U); |
| ExpectRevokedUnusedSitePermission(kUnusedTestSite); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| ExpectRevokedUnusedSitePermission(kAbusiveAndUnusedTestSite); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveAndUnusedTestSite); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 2U); |
| EXPECT_EQ( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()).size(), |
| 2U); |
| |
| // Acknowledging revoked permissions clears the list. |
| base::Value::List args; |
| handler()->HandleAcknowledgeRevokedUnusedSitePermissionsList(args); |
| const auto& revoked_permissions_after = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_EQ(revoked_permissions_after.size(), 0U); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 0U); |
| EXPECT_EQ( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()).size(), |
| 0U); |
| |
| // Undo reverts the list to its initial state. |
| base::Value::List undo_args; |
| undo_args.Append(revoked_permissions_before.Clone()); |
| handler()->HandleUndoAcknowledgeRevokedUnusedSitePermissionsList(undo_args); |
| EXPECT_EQ(revoked_permissions_before, |
| handler()->PopulateUnusedSitePermissionsData()); |
| EXPECT_EQ(hcsm() |
| ->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS) |
| .size(), |
| 2U); |
| EXPECT_EQ( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()).size(), |
| 2U); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleIgnoreOriginsForNotificationPermissionReview) { |
| HostContentSettingsMap* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| ContentSettingsForOneType ignored_patterns = |
| content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW); |
| ASSERT_EQ(0U, ignored_patterns.size()); |
| |
| base::Value::List args; |
| args.Append(GetOriginList(1)); |
| handler()->HandleIgnoreOriginsForNotificationPermissionReview(args); |
| |
| // Check there is 1 origin in ignore list. |
| ignored_patterns = content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW); |
| ASSERT_EQ(1U, ignored_patterns.size()); |
| |
| ValidateNotificationPermissionUpdate(); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleUndoIgnoreOriginsForNotificationPermissionReview) { |
| base::Value::List args; |
| args.Append(GetOriginList(1)); |
| handler()->HandleIgnoreOriginsForNotificationPermissionReview(args); |
| |
| // Check there is 1 origin in ignore list. |
| HostContentSettingsMap* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| ContentSettingsForOneType ignored_patterns = |
| content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW); |
| ASSERT_EQ(1U, ignored_patterns.size()); |
| |
| // Check there are no origins in ignore list. |
| handler()->HandleUndoIgnoreOriginsForNotificationPermissionReview(args); |
| ignored_patterns = content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATION_PERMISSION_REVIEW); |
| ASSERT_EQ(0U, ignored_patterns.size()); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleAllowNotificationPermissionForOrigins) { |
| base::Value::List args; |
| base::Value::List origins = GetOriginList(2); |
| args.Append(origins.Clone()); |
| handler()->HandleAllowNotificationPermissionForOrigins(args); |
| |
| // Check the permission for the two origins is allow. |
| HostContentSettingsMap* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| ContentSettingsForOneType notification_permissions = |
| content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATIONS); |
| auto type = content_settings->GetContentSetting( |
| GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS); |
| ASSERT_EQ(CONTENT_SETTING_ALLOW, type); |
| |
| type = content_settings->GetContentSetting( |
| GURL(origins[1].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS); |
| ASSERT_EQ(CONTENT_SETTING_ALLOW, type); |
| |
| ValidateNotificationPermissionUpdate(); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleBlockNotificationPermissionForOrigins) { |
| base::Value::List args; |
| base::Value::List origins = GetOriginList(2); |
| args.Append(origins.Clone()); |
| |
| handler()->HandleBlockNotificationPermissionForOrigins(args); |
| |
| // Check the permission for the two origins is block. |
| HostContentSettingsMap* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| ContentSettingsForOneType notification_permissions = |
| content_settings->GetSettingsForOneType( |
| ContentSettingsType::NOTIFICATIONS); |
| auto type = content_settings->GetContentSetting( |
| GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS); |
| ASSERT_EQ(CONTENT_SETTING_BLOCK, type); |
| |
| type = content_settings->GetContentSetting( |
| GURL(origins[1].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS); |
| ASSERT_EQ(CONTENT_SETTING_BLOCK, type); |
| |
| ValidateNotificationPermissionUpdate(); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleResetNotificationPermissionForOrigins) { |
| HostContentSettingsMap* content_settings = |
| HostContentSettingsMapFactory::GetForProfile(profile()); |
| base::Value::List args; |
| base::Value::List origins = GetOriginList(1); |
| args.Append(origins.Clone()); |
| |
| content_settings->SetContentSettingCustomScope( |
| ContentSettingsPattern::FromString(origins[0].GetString()), |
| ContentSettingsPattern::Wildcard(), ContentSettingsType::NOTIFICATIONS, |
| CONTENT_SETTING_ALLOW); |
| |
| handler()->HandleResetNotificationPermissionForOrigins(args); |
| |
| // Check the permission for the origin is reset. |
| auto type = content_settings->GetContentSetting( |
| GURL(origins[0].GetString()), GURL(), ContentSettingsType::NOTIFICATIONS); |
| ASSERT_EQ(CONTENT_SETTING_ASK, type); |
| |
| ValidateNotificationPermissionUpdate(); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingCardData_EnabledEnhanced) { |
| SetPrefsForSafeBrowsing(true, true, SettingManager::USER); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| |
| SetPrefsForSafeBrowsing(true, true, SettingManager::EXTENSION); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| |
| SetPrefsForSafeBrowsing(true, true, SettingManager::ADMIN); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_ENHANCED_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingCardData_EnabledStandard) { |
| SetPrefsForSafeBrowsing(true, false, SettingManager::USER); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| |
| SetPrefsForSafeBrowsing(true, false, SettingManager::EXTENSION); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| |
| SetPrefsForSafeBrowsing(true, false, SettingManager::ADMIN); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_ON_STANDARD_SUBHEADER), |
| SafetyHubCardState::kSafe); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingCardData_DisabledByAdmin) { |
| SetPrefsForSafeBrowsing(false, false, SettingManager::ADMIN); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_OFF_MANAGED_SUBHEADER), |
| SafetyHubCardState::kInfo); |
| |
| SetPrefsForSafeBrowsing(false, true, SettingManager::ADMIN); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_OFF_MANAGED_SUBHEADER), |
| SafetyHubCardState::kInfo); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafeBrowsingCardData_DisabledByExtension) { |
| SetPrefsForSafeBrowsing(false, false, SettingManager::EXTENSION); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_OFF_EXTENSION_SUBHEADER), |
| SafetyHubCardState::kInfo); |
| |
| SetPrefsForSafeBrowsing(false, true, SettingManager::EXTENSION); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SB_OFF_EXTENSION_SUBHEADER), |
| SafetyHubCardState::kInfo); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, HandleGetSafeBrowsingCardData_DisabledByUser) { |
| SetPrefsForSafeBrowsing(false, false, SettingManager::USER); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_USER_SUBHEADER), |
| SafetyHubCardState::kWarning); |
| |
| SetPrefsForSafeBrowsing(false, true, SettingManager::USER); |
| ValidateHandleSafeBrowsingCardData( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_HEADER), |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_SB_OFF_USER_SUBHEADER), |
| SafetyHubCardState::kWarning); |
| } |
| |
| // Test that revocation is happen correctly for all content setting types. |
| TEST_F(SafetyHubHandlerTest, RevokeAllContentSettingTypes) { |
| // TODO(crbug.com/40066645): Remove this after adding names for those |
| // types. |
| static constexpr auto kNoNameTypes = |
| base::MakeFixedFlatSet<ContentSettingsType>({ |
| // clang-format off |
| ContentSettingsType::MIDI, |
| ContentSettingsType::DURABLE_STORAGE, |
| ContentSettingsType::NFC, |
| ContentSettingsType::FILE_SYSTEM_READ_GUARD, |
| ContentSettingsType::CAMERA_PAN_TILT_ZOOM, |
| ContentSettingsType::FILE_SYSTEM_ACCESS_EXTENDED_PERMISSION, |
| ContentSettingsType::POINTER_LOCK, |
| // clang-format on |
| }); |
| |
| // Add all content settings in the content setting registry to revoked |
| // permissions list. |
| auto* content_settings_registry = |
| content_settings::ContentSettingsRegistry::GetInstance(); |
| for (const content_settings::ContentSettingsInfo* info : |
| *content_settings_registry) { |
| ContentSettingsType type = info->website_settings_info()->type(); |
| |
| // If the permission can not be tracked, then also can not be revoked. |
| if (!content_settings::CanTrackLastVisit(type)) { |
| continue; |
| } |
| |
| // If the permission can not set to ALLOW, then also can not be revoked. |
| if (!content_settings_registry->Get(type)->IsSettingValid( |
| ContentSetting::CONTENT_SETTING_ALLOW)) { |
| continue; |
| } |
| |
| // Add the permission to revoked permission list. |
| auto dict = base::Value::Dict().Set( |
| permissions::kRevokedKey, |
| base::Value::List().Append( |
| UnusedSitePermissionsManager::ConvertContentSettingsTypeToKey( |
| type))); |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kUnusedTestSite), GURL(kUnusedTestSite), |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS, |
| base::Value(dict.Clone())); |
| |
| // Unless the permission in kNoNameTypes, it should be shown on the UI. |
| const auto& revoked_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| if (base::Contains(kNoNameTypes, type)) { |
| EXPECT_EQ(revoked_permissions.size(), 0U); |
| } else { |
| EXPECT_EQ(revoked_permissions.size(), 1U); |
| } |
| } |
| } |
| |
| TEST_F(SafetyHubHandlerTest, VersionCardUpToDate) { |
| base::Value::List args; |
| args.Append("getVersionCardData"); |
| handler()->HandleGetVersionCardData(args); |
| |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| ASSERT_TRUE(data.arg3()->is_dict()); |
| |
| EXPECT_EQ(l10n_util::GetStringUTF16( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_UPDATED), |
| base::UTF8ToUTF16(*data.arg3()->GetDict().FindString("header"))); |
| EXPECT_EQ(VersionUI::GetAnnotatedVersionStringForUi(), |
| base::UTF8ToUTF16(*data.arg3()->GetDict().FindString("subheader"))); |
| EXPECT_EQ(static_cast<int>(SafetyHubCardState::kSafe), |
| *data.arg3()->GetDict().FindInt("state")); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, VersionCardOutOfDate) { |
| // An update is available, the version card should let the user know. |
| g_browser_process->GetBuildState()->SetUpdate( |
| BuildState::UpdateType::kNormalUpdate, |
| base::Version({CHROME_VERSION_MAJOR, CHROME_VERSION_MINOR, |
| CHROME_VERSION_BUILD, CHROME_VERSION_PATCH + 1}), |
| std::nullopt); |
| |
| base::Value::List args; |
| args.Append("getVersionCardData"); |
| handler()->HandleGetVersionCardData(args); |
| |
| const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); |
| ASSERT_TRUE(data.arg3()->is_dict()); |
| |
| EXPECT_EQ(l10n_util::GetStringUTF16( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_HEADER_RESTART), |
| base::UTF8ToUTF16(*data.arg3()->GetDict().FindString("header"))); |
| EXPECT_EQ(l10n_util ::GetStringUTF16( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_CARD_SUBHEADER_RESTART), |
| base::UTF8ToUTF16(*data.arg3()->GetDict().FindString("subheader"))); |
| EXPECT_EQ(static_cast<int>(SafetyHubCardState::kWarning), |
| *data.arg3()->GetDict().FindInt("state")); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafetyHubEntryPointData_HasRecommendationsAndHeader) { |
| std::vector<SafetyHubHandler::SafetyHubModule> modules; |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kPasswords); |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kVersion); |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kSafeBrowsing); |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kExtensions); |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kNotifications); |
| modules.push_back(SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions); |
| |
| std::vector<int> masks; |
| for (int i = 0; i < (int)modules.size(); i++) { |
| masks.push_back(pow(2, i)); |
| } |
| |
| // Each module can either have a recommendation or not. To test all possible |
| // combinations of modules, a binary vector and bit masking is used. i-th |
| // element of the vector represents whether the i-th module in modules array |
| // has a recommendation or not. |
| for (int testCase = pow(2, (int)modules.size()) - 1; testCase >= 0; |
| testCase--) { |
| SCOPED_TRACE("testCase: " + std::bitset<8>(testCase).to_string()); |
| std::set<SafetyHubHandler::SafetyHubModule> recommendedModules; |
| |
| for (int i = 0; i < (int)modules.size(); i++) { |
| bool isModuleRecommended = (testCase & masks[i]); |
| SetupTestToShowOrHideRecommendationForModule(modules[i], |
| isModuleRecommended); |
| if (isModuleRecommended) { |
| recommendedModules.insert(modules[i]); |
| } |
| } |
| |
| ValidateEntryPointHasRecommendationsAndHeader(!recommendedModules.empty()); |
| } |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafetyHubEntryPointData_Subheader_NothingToDo) { |
| // Reset unused site permissions data that is set in the test suite setup. |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions, false); |
| ValidateEntryPointSubheader(l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_ENTRY_POINT_NOTHING_TO_DO)); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafetyHubEntryPointData_Subheader_OneModule) { |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions, false); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME), |
| SafetyHubHandler::SafetyHubModule::kPasswords); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_MODULE_UPPERCASE_NAME), |
| SafetyHubHandler::SafetyHubModule::kVersion); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SAFE_BROWSING_MODULE_NAME), |
| SafetyHubHandler::SafetyHubModule::kSafeBrowsing); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MODULE_UPPERCASE_NAME), |
| SafetyHubHandler::SafetyHubModule::kExtensions); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_NOTIFICATIONS_MODULE_UPPERCASE_NAME), |
| SafetyHubHandler::SafetyHubModule::kNotifications); |
| |
| ValidateEntryPointSubheader( |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_PERMISSIONS_MODULE_UPPERCASE_NAME), |
| SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafetyHubEntryPointData_Subheader_TwoModulesWithPassword) { |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions, false); |
| // Passwords module will always be in a subheader string. |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kPasswords, true); |
| std::string expected_subheader = ""; |
| |
| // The expected subheader should be "Passwords, Chrome update" |
| expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_MODULE_LOWERCASE_NAME); |
| ValidateEntryPointSubheader(expected_subheader, |
| SafetyHubHandler::SafetyHubModule::kVersion); |
| |
| // The expected subheader should be "Passwords, Safe Browsing" |
| expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SAFE_BROWSING_MODULE_NAME); |
| ValidateEntryPointSubheader(expected_subheader, |
| SafetyHubHandler::SafetyHubModule::kSafeBrowsing); |
| |
| // The expected subheader should be "Passwords, extensions" |
| expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MODULE_LOWERCASE_NAME); |
| ValidateEntryPointSubheader(expected_subheader, |
| SafetyHubHandler::SafetyHubModule::kExtensions); |
| |
| // The expected subheader should be "Passwords, notifications" |
| expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_NOTIFICATIONS_MODULE_LOWERCASE_NAME); |
| ValidateEntryPointSubheader( |
| expected_subheader, SafetyHubHandler::SafetyHubModule::kNotifications); |
| |
| // The expected subheader should be "Passwords, permissions" |
| expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_PERMISSIONS_MODULE_LOWERCASE_NAME); |
| ValidateEntryPointSubheader( |
| expected_subheader, |
| SafetyHubHandler::SafetyHubModule::kUnusedSitePermissions); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, |
| HandleGetSafetyHubEntryPointData_Subheader_AllModules) { |
| AddUnusedPermission(); |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kPasswords, true); |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kVersion, true); |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kSafeBrowsing, true); |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kExtensions, true); |
| SetupTestToShowOrHideRecommendationForModule( |
| SafetyHubHandler::SafetyHubModule::kNotifications, true); |
| |
| // The expected subheader should be "Passwords, Chrome update, Safe Browsing, |
| // extensions, notifications, permissions" |
| std::string expected_subheader = |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_PASSWORDS_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_VERSION_MODULE_LOWERCASE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_SAFE_BROWSING_MODULE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_EXTENSIONS_MODULE_LOWERCASE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_NOTIFICATIONS_MODULE_LOWERCASE_NAME) + |
| l10n_util::GetStringUTF8(IDS_SETTINGS_SAFETY_HUB_MODULE_NAME_SEPARATOR) + |
| " " + |
| l10n_util::GetStringUTF8( |
| IDS_SETTINGS_SAFETY_HUB_PERMISSIONS_MODULE_LOWERCASE_NAME); |
| ValidateEntryPointSubheader(expected_subheader); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, ExtensionPrefAndInitialization) { |
| // Create fake extensions for our pref service to load and test that |
| // the `web_ui()` has recorded no events |
| AddExtensionsForReview(); |
| EXPECT_EQ(5, handler()->GetNumberOfExtensionsThatNeedReview()); |
| CreatMockCWSInfoService(); |
| EXPECT_EQ(0u, web_ui()->call_data().size()); |
| // After `AcknowledgeSafetyCheckExtensions` one event should have been fired. |
| safety_hub_test_util::AcknowledgeSafetyCheckExtensions( |
| crx_file::id_util::GenerateId("TestExtension5"), profile()); |
| EXPECT_EQ(1u, web_ui()->call_data().size()); |
| // After `RemoveExtension` one additional event should have been fired |
| // making two total events. |
| safety_hub_test_util::AcknowledgeSafetyCheckExtensions( |
| crx_file::id_util::GenerateId("TestExtension4"), profile()); |
| EXPECT_EQ(2u, web_ui()->call_data().size()); |
| // When the same actions are preformed on good extensions, no more |
| // events are fired. |
| safety_hub_test_util::AcknowledgeSafetyCheckExtensions( |
| crx_file::id_util::GenerateId("lgcbbihjdcmnlndohpfhppljiilnkoab"), |
| profile()); |
| EXPECT_EQ(2u, web_ui()->call_data().size()); |
| safety_hub_test_util::RemoveExtension("jadkojfancihcakelhdnpkcidencgdjg", |
| ManifestLocation::kInternal, profile()); |
| EXPECT_EQ(2u, web_ui()->call_data().size()); |
| } |
| |
| TEST_F(SafetyHubHandlerTest, OnSafeBrowsingEnhancedChanged) { |
| auto* mock_service = static_cast<MockIOSPromoTriggerService*>( |
| IOSPromoTriggerServiceFactory::GetInstance()->SetTestingFactoryAndUse( |
| profile(), base::BindRepeating(&BuildMockIOSPromoTriggerService))); |
| |
| // Turn off enhanced safe browsing. The promo should not be triggered. |
| EXPECT_CALL(*mock_service, NotifyPromoShouldBeShown(testing::_)).Times(0); |
| profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false); |
| testing::Mock::VerifyAndClearExpectations(mock_service); |
| |
| // Turn on enhanced safe browsing. The promo should be triggered. |
| EXPECT_CALL(*mock_service, |
| NotifyPromoShouldBeShown(IOSPromoType::kEnhancedBrowsing)); |
| profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true); |
| } |
| |
| class SafetyHubHandlerUnusedPermissionRevocationDisabledTest |
| : public testing::Test, |
| public testing::WithParamInterface<bool> { |
| public: |
| SafetyHubHandlerUnusedPermissionRevocationDisabledTest() { |
| feature_list_.InitWithFeatures( |
| /*enabled_features=*/{}, |
| /*disabled_features=*/ |
| {content_settings::features::kSafetyCheckUnusedSitePermissions, |
| content_settings::features:: |
| kSafetyCheckUnusedSitePermissionsForSupportedChooserPermissions}); |
| } |
| |
| // If the test parameter is true, enable safe browsing and expect that abusive |
| // notifications are auto-revoked. Otherwise, disabling safe browsing should |
| // not have any permissions revoked. |
| bool IsSafeBrowsingEnabled() { return GetParam(); } |
| |
| void SetUp() override { |
| // Set clock for HostContentSettingsMap. |
| base::Time time; |
| ASSERT_TRUE(base::Time::FromString("2022-09-07 13:00", &time)); |
| clock_.SetNow(time); |
| |
| safety_hub_test_util::CreateRevokedPermissionsService(profile()); |
| safety_hub_test_util::CreateNotificationPermissionsReviewService(profile()); |
| |
| hcsm_ = HostContentSettingsMapFactory::GetForProfile(profile()); |
| hcsm_->SetClockForTesting(&clock_); |
| |
| if (IsSafeBrowsingEnabled()) { |
| SetUpSafeBrowsingService(); |
| } |
| |
| handler_ = std::make_unique<SafetyHubHandler>(profile()); |
| handler()->set_web_ui(web_ui()); |
| handler()->AllowJavascript(); |
| |
| if (IsSafeBrowsingEnabled()) { |
| AddAbusiveNotificationPermission(); |
| } |
| } |
| |
| void TearDown() override { |
| if (IsSafeBrowsingEnabled()) { |
| TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr); |
| } |
| } |
| |
| TestingProfile* profile() { return &profile_; } |
| content::TestWebUI* web_ui() { return &web_ui_; } |
| SafetyHubHandler* handler() { return handler_.get(); } |
| HostContentSettingsMap* hcsm() { return hcsm_.get(); } |
| base::SimpleTestClock* clock() { return &clock_; } |
| MockSafeBrowsingDatabaseManager* mock_database_manager() { |
| return mock_database_manager_.get(); |
| } |
| |
| void AddAbusiveNotificationPermission() { |
| mock_database_manager()->SetThreatTypeForUrl( |
| GURL(kAbusiveTestSite), |
| safe_browsing::SBThreatType::SB_THREAT_TYPE_URL_PHISHING); |
| hcsm()->SetWebsiteSettingDefaultScope( |
| GURL(kAbusiveTestSite), GURL(kAbusiveTestSite), |
| ContentSettingsType::REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS, |
| base::Value(base::Value::Dict().Set( |
| safety_hub::kRevokedStatusDictKeyStr, safety_hub::kRevokeStr))); |
| } |
| |
| void ExpectRevokedAbusiveNotificationPermission(const std::string& url) { |
| EXPECT_EQ(ContentSetting::CONTENT_SETTING_ASK, |
| hcsm()->GetContentSetting(GURL(url), GURL(url), |
| ContentSettingsType::NOTIFICATIONS)); |
| } |
| |
| private: |
| void SetUpSafeBrowsingService() { |
| mock_database_manager_ = |
| base::MakeRefCounted<MockSafeBrowsingDatabaseManager>(); |
| safe_browsing_factory_ = |
| std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>(); |
| safe_browsing_factory_->SetTestDatabaseManager( |
| mock_database_manager_.get()); |
| TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService( |
| safe_browsing_factory_->CreateSafeBrowsingService()); |
| } |
| |
| base::test::ScopedFeatureList feature_list_; |
| content::BrowserTaskEnvironment task_environment_; |
| TestingProfile profile_; |
| content::TestWebUI web_ui_; |
| scoped_refptr<HostContentSettingsMap> hcsm_; |
| base::SimpleTestClock clock_; |
| std::unique_ptr<SafetyHubHandler> handler_; |
| scoped_refptr<MockSafeBrowsingDatabaseManager> mock_database_manager_; |
| std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory> |
| safe_browsing_factory_; |
| }; |
| |
| TEST_P(SafetyHubHandlerUnusedPermissionRevocationDisabledTest, |
| PopulateSitePermissionsData) { |
| const auto& revoked_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| if (IsSafeBrowsingEnabled()) { |
| // Revoked permissions list should contain the url. |
| EXPECT_EQ(revoked_permissions.size(), 1UL); |
| EXPECT_EQ(GURL(kAbusiveTestSite), |
| GURL(*revoked_permissions[0].GetDict().FindString( |
| site_settings::kOrigin))); |
| |
| auto* revoked_permission_list = |
| revoked_permissions[0].GetDict().FindList(site_settings::kPermissions); |
| EXPECT_EQ((*revoked_permission_list)[0], "notifications"); |
| // Notifications should not be allowed. |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| } else { |
| EXPECT_EQ(revoked_permissions.size(), 0UL); |
| } |
| } |
| |
| TEST_P(SafetyHubHandlerUnusedPermissionRevocationDisabledTest, |
| HandleAllowPermissionsAgainForSite) { |
| base::Value::List initial_permissions = |
| handler()->PopulateUnusedSitePermissionsData(); |
| if (IsSafeBrowsingEnabled()) { |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| EXPECT_FALSE( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| } else { |
| EXPECT_TRUE( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| } |
| |
| // Allow the revoked permission for the abusive notification site again. |
| base::Value::List args; |
| args.Append(base::Value(kAbusiveTestSite)); |
| handler()->HandleAllowPermissionsAgainForUnusedSite(args); |
| |
| // Check there is no origin in revoked permissions list. |
| EXPECT_TRUE(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| // Check if the permissions of url is regranted. |
| EXPECT_EQ( |
| IsSafeBrowsingEnabled() ? ContentSetting::CONTENT_SETTING_ALLOW |
| : ContentSetting::CONTENT_SETTING_ASK, |
| hcsm()->GetContentSetting(GURL(kAbusiveTestSite), GURL(kAbusiveTestSite), |
| ContentSettingsType::NOTIFICATIONS)); |
| |
| // Check there is no origin in revoked permissions list. |
| ContentSettingsForOneType revoked_permissions_list = |
| hcsm()->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| EXPECT_TRUE(revoked_permissions_list.empty()); |
| |
| // Undoing restores the initial state. |
| if (IsSafeBrowsingEnabled()) { |
| handler()->HandleUndoAllowPermissionsAgainForUnusedSite( |
| std::move(initial_permissions)); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| EXPECT_FALSE( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| } else { |
| EXPECT_TRUE( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| } |
| EXPECT_EQ( |
| ContentSetting::CONTENT_SETTING_ASK, |
| hcsm()->GetContentSetting(GURL(kAbusiveTestSite), GURL(kAbusiveTestSite), |
| ContentSettingsType::NOTIFICATIONS)); |
| } |
| |
| TEST_P(SafetyHubHandlerUnusedPermissionRevocationDisabledTest, |
| HandleAcknowledgeRevokedSitePermissionsList) { |
| const auto& revoked_permissions_before = |
| handler()->PopulateUnusedSitePermissionsData(); |
| if (IsSafeBrowsingEnabled()) { |
| EXPECT_EQ(revoked_permissions_before.size(), 1U); |
| ExpectRevokedAbusiveNotificationPermission(kAbusiveTestSite); |
| EXPECT_EQ(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .size(), |
| 1U); |
| } else { |
| EXPECT_EQ(revoked_permissions_before.size(), 0U); |
| EXPECT_EQ(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .size(), |
| 0U); |
| } |
| ContentSettingsForOneType revoked_permissions_list = |
| hcsm()->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| EXPECT_EQ(0U, revoked_permissions_list.size()); |
| |
| // Acknowledging revoked permissions clears the list. |
| base::Value::List args; |
| handler()->HandleAcknowledgeRevokedUnusedSitePermissionsList(args); |
| const auto& revoked_permissions_after = |
| handler()->PopulateUnusedSitePermissionsData(); |
| EXPECT_TRUE(revoked_permissions_after.empty()); |
| EXPECT_TRUE(safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()) |
| .empty()); |
| revoked_permissions_list = hcsm()->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| EXPECT_TRUE(revoked_permissions_list.empty()); |
| |
| // Undo reverts the list to its initial state. |
| base::Value::List undo_args; |
| undo_args.Append(revoked_permissions_before.Clone()); |
| handler()->HandleUndoAcknowledgeRevokedUnusedSitePermissionsList(undo_args); |
| EXPECT_EQ(revoked_permissions_before, |
| handler()->PopulateUnusedSitePermissionsData()); |
| EXPECT_EQ( |
| safety_hub_util::GetRevokedAbusiveNotificationPermissions(hcsm()).size(), |
| IsSafeBrowsingEnabled() ? 1U : 0U); |
| revoked_permissions_list = hcsm()->GetSettingsForOneType( |
| ContentSettingsType::REVOKED_UNUSED_SITE_PERMISSIONS); |
| EXPECT_EQ(0U, revoked_permissions_list.size()); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| SafetyHubHandlerUnusedPermissionRevocationDisabledTest, |
| testing::Bool()); |