1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/permissions/abusive_origin_permission_revocation_request.h"
6
7 #include "base/time/default_clock.h"
8 #include "chrome/browser/browser_process.h"
9 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
10 #include "chrome/browser/permissions/abusive_origin_notifications_permission_revocation_config.h"
11 #include "chrome/browser/permissions/crowd_deny_preload_data.h"
12 #include "chrome/browser/permissions/permission_manager_factory.h"
13 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
14 #include "components/content_settings/core/browser/host_content_settings_map.h"
15 #include "components/permissions/permission_manager.h"
16 #include "components/permissions/permission_result.h"
17 #include "components/permissions/permission_uma_util.h"
18 #include "components/permissions/permissions_client.h"
19 #include "components/prefs/pref_service.h"
20 #include "components/safe_browsing/core/common/safe_browsing_prefs.h"
21 #include "components/safe_browsing/core/db/database_manager.h"
22
23 namespace {
24 constexpr char kExcludedKey[] = "exempted";
25 constexpr char kRevokedKey[] = "revoked";
26 constexpr char kPermissionName[] = "notifications";
27
28 struct OriginStatus {
29 bool is_exempt_from_future_revocations = false;
30 bool has_been_previously_revoked = false;
31 };
32
GetOriginStatus(Profile * profile,const GURL & origin)33 OriginStatus GetOriginStatus(Profile* profile, const GURL& origin) {
34 std::unique_ptr<base::Value> stored_value =
35 permissions::PermissionsClient::Get()
36 ->GetSettingsMap(profile)
37 ->GetWebsiteSetting(
38 origin, GURL(),
39 ContentSettingsType::PERMISSION_AUTOREVOCATION_DATA, nullptr);
40
41 OriginStatus status;
42
43 if (!stored_value || !stored_value->is_dict())
44 return status;
45
46 base::Value* dict = stored_value->FindPath(kPermissionName);
47 if (!dict)
48 return status;
49
50 if (dict->FindBoolPath(kExcludedKey).has_value()) {
51 status.is_exempt_from_future_revocations =
52 dict->FindBoolPath(kExcludedKey).value();
53 }
54 if (dict->FindBoolPath(kRevokedKey).has_value()) {
55 status.has_been_previously_revoked =
56 dict->FindBoolPath(kRevokedKey).value();
57 }
58
59 return status;
60 }
61
SetOriginStatus(Profile * profile,const GURL & origin,const OriginStatus & status)62 void SetOriginStatus(Profile* profile,
63 const GURL& origin,
64 const OriginStatus& status) {
65 base::Value dict(base::Value::Type::DICTIONARY);
66 base::Value permission_dict(base::Value::Type::DICTIONARY);
67 permission_dict.SetKey(kExcludedKey,
68 base::Value(status.is_exempt_from_future_revocations));
69 permission_dict.SetKey(kRevokedKey,
70 base::Value(status.has_been_previously_revoked));
71 dict.SetKey(kPermissionName, std::move(permission_dict));
72
73 permissions::PermissionsClient::Get()
74 ->GetSettingsMap(profile)
75 ->SetWebsiteSettingDefaultScope(
76 origin, GURL(), ContentSettingsType::PERMISSION_AUTOREVOCATION_DATA,
77 base::WrapUnique(dict.DeepCopy()));
78 }
79
RevokePermission(const GURL & origin,Profile * profile)80 void RevokePermission(const GURL& origin, Profile* profile) {
81 permissions::PermissionsClient::Get()
82 ->GetSettingsMap(profile)
83 ->SetContentSettingDefaultScope(origin, GURL(),
84 ContentSettingsType::NOTIFICATIONS,
85 ContentSetting::CONTENT_SETTING_DEFAULT);
86
87 OriginStatus status = GetOriginStatus(profile, origin);
88 status.has_been_previously_revoked = true;
89 SetOriginStatus(profile, origin, status);
90
91 permissions::PermissionUmaUtil::PermissionRevoked(
92 ContentSettingsType::NOTIFICATIONS,
93 permissions::PermissionSourceUI::AUTO_REVOCATION, origin, profile);
94 }
95 } // namespace
96
97 AbusiveOriginPermissionRevocationRequest::
AbusiveOriginPermissionRevocationRequest(Profile * profile,const GURL & origin,OutcomeCallback callback)98 AbusiveOriginPermissionRevocationRequest(Profile* profile,
99 const GURL& origin,
100 OutcomeCallback callback)
101 : profile_(profile), origin_(origin), callback_(std::move(callback)) {
102 base::SequencedTaskRunnerHandle::Get()->PostTask(
103 FROM_HERE,
104 base::BindOnce(
105 &AbusiveOriginPermissionRevocationRequest::CheckAndRevokeIfAbusive,
106 weak_factory_.GetWeakPtr()));
107 }
108
109 AbusiveOriginPermissionRevocationRequest::
110 ~AbusiveOriginPermissionRevocationRequest() = default;
111
CheckAndRevokeIfAbusive()112 void AbusiveOriginPermissionRevocationRequest::CheckAndRevokeIfAbusive() {
113 DCHECK(profile_);
114 DCHECK(callback_);
115
116 if (!AbusiveOriginNotificationsPermissionRevocationConfig::IsEnabled() ||
117 !safe_browsing::IsSafeBrowsingEnabled(*profile_->GetPrefs()) ||
118 IsOriginExemptedFromFutureRevocations(profile_, origin_)) {
119 std::move(callback_).Run(Outcome::PERMISSION_NOT_REVOKED);
120 return;
121 }
122
123 CrowdDenyPreloadData* crowd_deny = CrowdDenyPreloadData::GetInstance();
124 permissions::PermissionUmaUtil::RecordCrowdDenyIsLoadedAtAbuseCheckTime(
125 crowd_deny->is_loaded_from_disk());
126 permissions::PermissionUmaUtil::RecordCrowdDenyVersionAtAbuseCheckTime(
127 crowd_deny->version_on_disk());
128
129 const CrowdDenyPreloadData::SiteReputation* site_reputation =
130 crowd_deny->GetReputationDataForSite(url::Origin::Create(origin_));
131 if (site_reputation && !site_reputation->warning_only() &&
132 (site_reputation->notification_ux_quality() ==
133 CrowdDenyPreloadData::SiteReputation::ABUSIVE_PROMPTS ||
134 site_reputation->notification_ux_quality() ==
135 CrowdDenyPreloadData::SiteReputation::ABUSIVE_CONTENT)) {
136 DCHECK(g_browser_process->safe_browsing_service());
137
138 if (g_browser_process->safe_browsing_service()) {
139 safe_browsing_request_.emplace(
140 g_browser_process->safe_browsing_service()->database_manager(),
141 base::DefaultClock::GetInstance(), url::Origin::Create(origin_),
142 base::BindOnce(&AbusiveOriginPermissionRevocationRequest::
143 OnSafeBrowsingVerdictReceived,
144 weak_factory_.GetWeakPtr()));
145 return;
146 }
147 }
148
149 std::move(callback_).Run(Outcome::PERMISSION_NOT_REVOKED);
150 }
151
OnSafeBrowsingVerdictReceived(CrowdDenySafeBrowsingRequest::Verdict verdict)152 void AbusiveOriginPermissionRevocationRequest::OnSafeBrowsingVerdictReceived(
153 CrowdDenySafeBrowsingRequest::Verdict verdict) {
154 DCHECK(safe_browsing_request_);
155 DCHECK(profile_);
156 DCHECK(callback_);
157
158 if (verdict == CrowdDenySafeBrowsingRequest::Verdict::kUnacceptable) {
159 RevokePermission(origin_, profile_);
160 std::move(callback_).Run(Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
161 } else {
162 std::move(callback_).Run(Outcome::PERMISSION_NOT_REVOKED);
163 }
164 }
165
166 // static
167 bool AbusiveOriginPermissionRevocationRequest::
IsOriginExemptedFromFutureRevocations(Profile * profile,const GURL & origin)168 IsOriginExemptedFromFutureRevocations(Profile* profile,
169 const GURL& origin) {
170 OriginStatus status = GetOriginStatus(profile, origin);
171 return status.is_exempt_from_future_revocations;
172 }
173
174 // static
HasPreviouslyRevokedPermission(Profile * profile,const GURL & origin)175 bool AbusiveOriginPermissionRevocationRequest::HasPreviouslyRevokedPermission(
176 Profile* profile,
177 const GURL& origin) {
178 OriginStatus status = GetOriginStatus(profile, origin);
179 return status.has_been_previously_revoked;
180 }
181
182 // static
183 void AbusiveOriginPermissionRevocationRequest::
ExemptOriginFromFutureRevocations(Profile * profile,const GURL & origin)184 ExemptOriginFromFutureRevocations(Profile* profile, const GURL& origin) {
185 OriginStatus status = GetOriginStatus(profile, origin);
186 status.is_exempt_from_future_revocations = true;
187 SetOriginStatus(profile, origin, status);
188 }
189