blob: 5c7c62212289f87e95c25f45ead034ac57bc0533 [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_SAFETY_HUB_ABUSIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_
#define CHROME_BROWSER_UI_SAFETY_HUB_ABUSIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ui/safety_hub/safety_hub_constants.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/safe_browsing/core/browser/db/database_manager.h"
#include "components/safe_browsing/core/browser/safe_browsing_metrics_collector.h"
class GURL;
class Profile;
namespace {
// Maximum time in milliseconds to wait for the Safe Browsing service reputation
// check. After this amount of time the outstanding check will be aborted, and
// the resource will be treated as if it were safe.
inline constexpr int kCheckUrlTimeoutMs = 5000;
// Key of the base::Value dictionary we assign to the
// REVOKED_ABUSIVE_NOTIFICATION_PERMISSION to specify revocation reason.
inline constexpr char kAbusiveRevocationSourceKeyStr[] = "revocation_source";
inline constexpr char kSocialEngineeringBlocklistStr[] = "social_engineering";
inline constexpr char kSafeBrowsingUnwantedRevocationStr[] = "manual";
inline constexpr char kSuspiciousContentAutoRevocationStr[] =
"suspicious_content";
} // namespace
namespace safe_browsing {
struct ThreatMetadata;
} // namespace safe_browsing
// This class keeps track of abusive notification permissions by checking URLs
// against the Safe Browsing social engineering blocklist. This also handles
// automatic revocation and responding to user decisions in Safety Hub.
class AbusiveNotificationPermissionsManager {
public:
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
//
// LINT.IfChange(AbusiveNotificationPermissionsInteractions)
enum class AbusiveNotificationPermissionsInteractions {
kOpenReviewUI = 0,
kAllowAgain = 1,
kAcknowledgeAll = 2,
kUndoAllowAgain = 3,
kUndoAcknowledgeAll = 4,
kMinimize = 5,
kGoToSettings = 6,
kMaxValue = kGoToSettings,
};
// LINT.ThenChange(//tools/metrics/histograms/metadata/settings/enums.xml:SafetyCheckUnusedSitePermissionsModuleInteractions)
explicit AbusiveNotificationPermissionsManager(
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager>
database_manager,
scoped_refptr<HostContentSettingsMap> hcsm,
PrefService* pref_service);
AbusiveNotificationPermissionsManager(
const AbusiveNotificationPermissionsManager&) = delete;
AbusiveNotificationPermissionsManager& operator=(
const AbusiveNotificationPermissionsManager&) = delete;
~AbusiveNotificationPermissionsManager();
// Handles the auto-revocation of abusive notification permissions for both
// social engineering blocklist and manual enforcement. This includes updating
// the settings with the correct expiration, and logging metrics.
static void ExecuteAbusiveNotificationAutoRevocation(
HostContentSettingsMap* hcsm,
GURL url,
safe_browsing::NotificationRevocationSource revocation_source,
const raw_ptr<const base::Clock> clock);
// Sets the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` value for a url,
// revocation source, given the constraints, and whether the user wants to
// ignore future auto-revocation.
static void SetRevokedAbusiveNotificationPermission(
HostContentSettingsMap* hcsm,
GURL url,
bool is_ignored,
safe_browsing::NotificationRevocationSource revocation_source,
const content_settings::ContentSettingConstraints& constraints = {});
// Perform `SetRevokedAbusiveNotificationPermission`, preserving revocation
// source if exists.
static void SetRevokedAbusiveNotificationPermission(
HostContentSettingsMap* hcsm,
GURL url,
bool is_ignored,
const content_settings::ContentSettingConstraints& constraints = {});
// Revoke notification permission for `url` if suspicious notification
// criteria are met, setting `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS`.
// Return true if the notification has been revoked.
static bool MaybeRevokeSuspiciousNotificationPermission(Profile* profile,
GURL url);
// Return `NotificationRevocationSource` if there is a
// `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` setting value for the
// `setting_url` with the `safety_hub::kAbusiveRevocationSourceKeyStr`.
// Returns `NotificationRevocationSource::kUnknown` if the setting does not
// exist, the key does not exist, or the value is invalid.
static safe_browsing::NotificationRevocationSource
GetRevokedAbusiveNotificationRevocationSource(HostContentSettingsMap* hcsm,
GURL url);
// Returns true if `url` belongs to a site with revoked abusive notifications
// with `NotificationRevocationSource::kSuspiciousContentAutoRevocation` as
// notification revocation source.
static bool IsUrlRevokedDueToSuspiciousContent(HostContentSettingsMap* hcsm,
GURL url);
// Calls `PerformSafeBrowsingChecks` on URLs which have notifications
// enabled and haven't been marked as a URL to be ignored.
void CheckNotificationPermissionOrigins();
// If the url has a revoked abusive notification permission, this method
// allows notification permissions again and adds a constraint so that this
// permission is not auto-revoked during future Safety Hub checks.
void RegrantPermissionForOriginIfNecessary(const GURL& url);
// If `permission_types` includes notifications, undo the actions from
// `RegrantPermissionForOrigin` by changing the `NOTIFICATIONS` setting back
// to `CONTENT_SETTING_ASK` and the
// `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` setting back to a dictionary
// with `safety_hub::kRevokedStatusDictKeyStr` set to
// `safety_hub::kRevokeStr`.
void UndoRegrantPermissionForOriginIfNecessary(
const GURL& url,
std::set<ContentSettingsType> permission_types,
content_settings::ContentSettingConstraints constraints);
// Clear the list of abusive notification permissions so they will no longer
// be shown to the user. Does not change permissions themselves.
void ClearRevokedPermissionsList();
// Remove the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` setting for the given
// pattern pairs.
void DeletePatternFromRevokedAbusiveNotificationList(
const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern);
// Log metric for permission changed and remove
// `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` setting for the given pattern
// pairs.
void OnPermissionChanged(const ContentSettingsPattern& primary_pattern,
const ContentSettingsPattern& secondary_pattern);
// Restores REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS entry for the
// primary_pattern after it was deleted after user
// has accepted the revocation (via `ClearRevokedPermissionsList()`).
void RestoreDeletedRevokedPermission(
const ContentSettingsPattern& primary_pattern,
content_settings::ContentSettingConstraints constraints);
// If there's a clock for testing, return that. Otherwise, return an instance
// of a default clock.
const base::Clock* GetClock();
// Returns true if settings are being changed by
// `AbusiveNotificationPermissionManager` activities and should be ignored by
// `RevokedPermissionsService::OnContentSettingChanged`.
bool IsRevocationRunning();
// Helper method for logging the
// `AbusiveNotificationPermissionRevocation.Interactions` UKM.
void LogAbusiveNotificationPermissionRevocationUKM(
const GURL& origin,
AbusiveNotificationPermissionsInteractions interaction,
safe_browsing::NotificationRevocationSource source);
// Test support:
// TODO(crbug/342210522): Use a unique_ptr here if possible.
void SetClockForTesting(base::Clock* clock) { clock_for_testing_ = clock; }
private:
friend class AbusiveNotificationPermissionsManagerTest;
friend class AbusiveNotificationPermissionsRevocationTest;
FRIEND_TEST_ALL_PREFIXES(
AbusiveNotificationPermissionsManagerTest,
AddAllowedAbusiveNotificationSitesToRevokedOriginSet);
FRIEND_TEST_ALL_PREFIXES(
AbusiveNotificationPermissionsManagerTest,
DoesNotAddSafeAbusiveNotificationSitesToRevokedOriginSet);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
DoesNotAddBlockedSettingToRevokedList);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
DoesNotAddIgnoredSettingToRevokedList);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
DoesNotAddAbusiveNotificationSitesOnTimeout);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
RegrantedPermissionShouldNotBeChecked);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
ClearRevokedPermissionsList);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
SetRevokedAbusiveNotificationPermission);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
SetIgnoreRevokedAbusiveNotificationPermission);
FRIEND_TEST_ALL_PREFIXES(AbusiveNotificationPermissionsManagerTest,
UndoRegrantPermissionForOriginIfNecessary);
// On object creation, checks the Safe Browsing blocklist for `url_`
// and revokes notification permissions if blocklisted.
// This subclass is necessary to avoid concurrency issues - each
// request needs its own Client and the AbusiveNotificationPermissionsManager
// makes multiple Safe Browsing requests.
class SafeBrowsingCheckClient
: safe_browsing::SafeBrowsingDatabaseManager::Client {
public:
SafeBrowsingCheckClient(
base::PassKey<safe_browsing::SafeBrowsingDatabaseManager::Client>
pass_key,
safe_browsing::SafeBrowsingDatabaseManager* database_manager,
raw_ptr<std::map<SafeBrowsingCheckClient*,
std::unique_ptr<SafeBrowsingCheckClient>>>
safe_browsing_request_clients,
raw_ptr<HostContentSettingsMap> hcsm,
PrefService* pref_service,
GURL url,
int safe_browsing_check_delay,
const base::Clock* clock);
~SafeBrowsingCheckClient() override;
// Trigger the call to check the Safe Browsing social engineering blocklist.
void CheckSocialEngineeringBlocklist();
private:
// safe_browsing::SafeBrowsingDatabaseManager::Client:
void OnCheckBrowseUrlResult(
const GURL& url,
safe_browsing::SBThreatType threat_type,
const safe_browsing::ThreatMetadata& metadata) override;
// Callback to be run if a Safe Browsing blocklist request does not return
// a response within `kCheckUrlTimeoutMs` time.
void OnCheckBlocklistTimeout();
// A pointer to the `database_manager_` of the
// `AbusiveNotificationPermissionsManager`.
raw_ptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
// A pointer to the `safe_browsing_request_clients_`
// of the `AbusiveNotificationPermissionsManager`.
raw_ptr<std::map<SafeBrowsingCheckClient*,
std::unique_ptr<SafeBrowsingCheckClient>>>
safe_browsing_request_clients_;
// A pointer to the `hcsm_` of the `AbusiveNotificationPermissionsManager`.
raw_ptr<HostContentSettingsMap> hcsm_;
// A pointer to the `pref_service_` of the
// `AbusiveNotificationPermissionsManager`.
raw_ptr<PrefService> pref_service_;
// The URL that is being checked against the Safe Browsing blocklist.
GURL url_;
// Delay amount allowed for blocklist checks.
int safe_browsing_check_delay_;
// Timer for running Safe Browsing blocklist checks. If `kCheckUrlTimeoutMs`
// time has passed, run `OnCheckBlocklistTimeout`.
base::OneShotTimer timer_;
// To enable automatic cleanup after the threshold has passed, this is used
// to set the lifetime of the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS`
// value.
const raw_ptr<const base::Clock> clock_;
base::WeakPtrFactory<
AbusiveNotificationPermissionsManager::SafeBrowsingCheckClient>
weak_factory_{this};
};
// Primarily used for tests.
void SetNullSBCheckDelayForTesting() { safe_browsing_check_delay_ = 0; }
// Create a `SafeBrowsingCheckClient` object, triggering a blocklist check,
// and add it to `safe_browsing_request_clients_`.
void PerformSafeBrowsingChecks(GURL url);
// Returns true if the notification permission is allowed and the setting
// does not indicate "ignore".
bool ShouldCheckOrigin(const ContentSettingPatternSource& setting) const;
// Clears any helper members that stored state from the previous safety check.
// Called each time Safe Browsing checks are performed on a set of URLs.
void ResetSafeBrowsingCheckHelpers();
// Update Notification permission setting in response to Safety Hub actions
// (e.g. re-grant and undo re-grant). This will set
// `is_abusive_site_revocation_running_` to signal
// `RevokedPermissionsService::OnContentSettingChanged` to ignore the setting
// change.
void UpdateNotificationPermissionForSafetyHubAction(
HostContentSettingsMap* hcsm,
GURL url,
ContentSetting setting_value);
// Convert `NotificationRevocationSource` to its string representation for
// storing in `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS`.
static std::optional<std::string> GetRevocationSourceString(
safe_browsing::NotificationRevocationSource source);
// Used for interactions with the local database, when checking the blocklist.
scoped_refptr<safe_browsing::SafeBrowsingDatabaseManager> database_manager_;
// Object that allows us to manage site permissions.
scoped_refptr<HostContentSettingsMap> hcsm_;
// Used for updating prefs after performing blocklist checks.
raw_ptr<PrefService> pref_service_;
// Safe Browsing blocklist check clients. Each object is responsible for a
// single Safe Browsing check, given a URL. Stored this way so that the object
// can delete itself from this map within the SafeBrowsingCheckClient.
std::map<SafeBrowsingCheckClient*, std::unique_ptr<SafeBrowsingCheckClient>>
safe_browsing_request_clients_;
// Length of time allowed for Safe Browsing check before timeout. This allows
// us to test timeout behavior.
int safe_browsing_check_delay_;
// To enable automatic cleanup after the threshold has passed, this is used to
// set the lifetime of the `REVOKED_ABUSIVE_NOTIFICATION_PERMISSIONS` value.
// Pass this into each instance of the SafeBrowsingCheckClient class.
raw_ptr<base::Clock> clock_for_testing_;
// True if automatic check is occurring or
// `AbusiveNotificationPermissionManager` is changing notification setting.
// This value is used to help decide whether to handle setting change inside
// `OnContentSettingChanged`.
bool is_abusive_site_revocation_running_ = false;
};
#endif // CHROME_BROWSER_UI_SAFETY_HUB_ABUSIVE_NOTIFICATION_PERMISSIONS_MANAGER_H_