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/files/scoped_temp_dir.h"
8 #include "base/test/mock_callback.h"
9 #include "base/test/scoped_feature_list.h"
10 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
11 #include "chrome/browser/history/history_service_factory.h"
12 #include "chrome/browser/permissions/abusive_origin_notifications_permission_revocation_config.h"
13 #include "chrome/browser/permissions/crowd_deny_fake_safe_browsing_database_manager.h"
14 #include "chrome/browser/permissions/crowd_deny_preload_data.h"
15 #include "chrome/browser/safe_browsing/test_safe_browsing_service.h"
16 #include "chrome/common/chrome_features.h"
17 #include "chrome/test/base/testing_browser_process.h"
18 #include "chrome/test/base/testing_profile.h"
19 #include "components/content_settings/core/browser/host_content_settings_map.h"
20 #include "components/prefs/pref_service.h"
21 #include "content/public/test/browser_task_environment.h"
22 
23 class AbusiveOriginPermissionRevocationRequestTestBase : public testing::Test {
24  public:
25   using Outcome = AbusiveOriginPermissionRevocationRequest::Outcome;
26   using SiteReputation = CrowdDenyPreloadData::SiteReputation;
27 
28   AbusiveOriginPermissionRevocationRequestTestBase() = default;
29 
30   ~AbusiveOriginPermissionRevocationRequestTestBase() override = default;
31 
32  protected:
SetUp()33   void SetUp() override {
34     testing::Test::SetUp();
35 
36     DCHECK(profile_dir_.CreateUniqueTempDir());
37     TestingProfile::Builder profile_builder;
38     profile_builder.SetPath(profile_dir_.GetPath());
39     profile_builder.AddTestingFactory(
40         HistoryServiceFactory::GetInstance(),
41         HistoryServiceFactory::GetDefaultFactory());
42     testing_profile_ = profile_builder.Build();
43 
44     fake_database_manager_ =
45         base::MakeRefCounted<CrowdDenyFakeSafeBrowsingDatabaseManager>();
46     safe_browsing_factory_ =
47         std::make_unique<safe_browsing::TestSafeBrowsingServiceFactory>();
48     safe_browsing_factory_->SetTestDatabaseManager(
49         fake_database_manager_.get());
50     TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(
51         safe_browsing_factory_->CreateSafeBrowsingService());
52   }
53 
TearDown()54   void TearDown() override {
55     TestingBrowserProcess::GetGlobal()->SetSafeBrowsingService(nullptr);
56     testing::Test::TearDown();
57   }
58 
AddToSafeBrowsingBlocklist(const GURL & url)59   void AddToSafeBrowsingBlocklist(const GURL& url) {
60     safe_browsing::ThreatMetadata test_metadata;
61     test_metadata.api_permissions.emplace("NOTIFICATIONS");
62     fake_database_manager_->SetSimulatedMetadataForUrl(url, test_metadata);
63   }
64 
ClearSafeBrowsingBlocklist()65   void ClearSafeBrowsingBlocklist() {
66     fake_database_manager_->RemoveAllBlacklistedUrls();
67   }
68 
AddToPreloadDataBlocklist(const GURL & origin,chrome_browser_crowd_deny::SiteReputation_NotificationUserExperienceQuality reputation_type,bool has_warning)69   void AddToPreloadDataBlocklist(
70       const GURL& origin,
71       chrome_browser_crowd_deny::
72           SiteReputation_NotificationUserExperienceQuality reputation_type,
73       bool has_warning) {
74     SiteReputation reputation;
75     reputation.set_notification_ux_quality(reputation_type);
76     reputation.set_warning_only(has_warning);
77     testing_preload_data_.SetOriginReputation(url::Origin::Create(origin),
78                                               std::move(reputation));
79   }
80 
QueryAndExpectDecisionForUrl(const GURL & origin,Outcome expected_result)81   void QueryAndExpectDecisionForUrl(const GURL& origin,
82                                     Outcome expected_result) {
83     base::MockOnceCallback<void(Outcome)> mock_callback_receiver;
84     permission_revocation_ =
85         std::make_unique<AbusiveOriginPermissionRevocationRequest>(
86             testing_profile_.get(), origin, mock_callback_receiver.Get());
87     EXPECT_CALL(mock_callback_receiver, Run(expected_result));
88     task_environment_.RunUntilIdle();
89     permission_revocation_.reset();
90   }
91 
SetPermission(const GURL & origin,const ContentSetting value)92   void SetPermission(const GURL& origin, const ContentSetting value) {
93     HostContentSettingsMap* host_content_settings_map =
94         HostContentSettingsMapFactory::GetForProfile(testing_profile_.get());
95     host_content_settings_map->SetContentSettingDefaultScope(
96         origin, GURL(), ContentSettingsType::NOTIFICATIONS, value);
97   }
98 
VerifyNotificationsPermission(const GURL & origin,const ContentSetting value)99   void VerifyNotificationsPermission(const GURL& origin,
100                                      const ContentSetting value) {
101     HostContentSettingsMap* host_content_settings_map =
102         HostContentSettingsMapFactory::GetForProfile(testing_profile_.get());
103 
104     ContentSetting result = host_content_settings_map->GetContentSetting(
105         origin, GURL(), ContentSettingsType::NOTIFICATIONS);
106 
107     EXPECT_EQ(value, result);
108   }
109 
GetTestingProfile()110   TestingProfile* GetTestingProfile() { return testing_profile_.get(); }
111 
112  private:
113   base::ScopedTempDir profile_dir_;
114   content::BrowserTaskEnvironment task_environment_;
115   testing::ScopedCrowdDenyPreloadDataOverride testing_preload_data_;
116   std::unique_ptr<TestingProfile> testing_profile_;
117   std::unique_ptr<AbusiveOriginPermissionRevocationRequest>
118       permission_revocation_;
119   scoped_refptr<CrowdDenyFakeSafeBrowsingDatabaseManager>
120       fake_database_manager_;
121   std::unique_ptr<safe_browsing::TestSafeBrowsingServiceFactory>
122       safe_browsing_factory_;
123 
124   DISALLOW_COPY_AND_ASSIGN(AbusiveOriginPermissionRevocationRequestTestBase);
125 };
126 
127 class AbusiveOriginPermissionRevocationRequestTest
128     : public AbusiveOriginPermissionRevocationRequestTestBase {
129  public:
AbusiveOriginPermissionRevocationRequestTest()130   AbusiveOriginPermissionRevocationRequestTest() {
131     feature_list_.InitAndEnableFeature(
132         features::kAbusiveNotificationPermissionRevocation);
133   }
134 
135   ~AbusiveOriginPermissionRevocationRequestTest() override = default;
136 
137  private:
138   base::test::ScopedFeatureList feature_list_;
139 };
140 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,OriginIsNotOnBlockingLists)141 TEST_F(AbusiveOriginPermissionRevocationRequestTest,
142        OriginIsNotOnBlockingLists) {
143   const GURL origin_to_revoke = GURL("https://origin.com/");
144 
145   SetPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
146 
147   QueryAndExpectDecisionForUrl(origin_to_revoke,
148                                Outcome::PERMISSION_NOT_REVOKED);
149   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
150 }
151 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,SafeBrowsingTest)152 TEST_F(AbusiveOriginPermissionRevocationRequestTest, SafeBrowsingTest) {
153   const GURL origin_to_revoke = GURL("https://origin.com/");
154 
155   SetPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
156 
157   // The origin is not on any blocking lists. Notifications permission is not
158   // revoked.
159   QueryAndExpectDecisionForUrl(origin_to_revoke,
160                                Outcome::PERMISSION_NOT_REVOKED);
161 
162   AddToSafeBrowsingBlocklist(origin_to_revoke);
163   // Origin is not on CrowdDeny blocking lists.
164   QueryAndExpectDecisionForUrl(origin_to_revoke,
165                                Outcome::PERMISSION_NOT_REVOKED);
166   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
167   EXPECT_FALSE(
168       AbusiveOriginPermissionRevocationRequest::HasPreviouslyRevokedPermission(
169           GetTestingProfile(), origin_to_revoke));
170 
171   AddToPreloadDataBlocklist(origin_to_revoke, SiteReputation::ABUSIVE_CONTENT,
172                             /*has_warning=*/false);
173   QueryAndExpectDecisionForUrl(origin_to_revoke,
174                                Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
175   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ASK);
176   EXPECT_TRUE(
177       AbusiveOriginPermissionRevocationRequest::HasPreviouslyRevokedPermission(
178           GetTestingProfile(), origin_to_revoke));
179 }
180 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,PreloadDataTest)181 TEST_F(AbusiveOriginPermissionRevocationRequestTest, PreloadDataTest) {
182   const GURL abusive_content_origin_to_revoke =
183       GURL("https://abusive-content.com/");
184   const GURL abusive_prompts_origin_to_revoke =
185       GURL("https://abusive-prompts.com/");
186   const GURL unsolicited_prompts_origin =
187       GURL("https://unsolicited-prompts.com/");
188   const GURL acceptable_origin = GURL("https://acceptable-origin.com/");
189   const GURL unknown_origin = GURL("https://unknown-origin.com/");
190 
191   auto origins = {abusive_content_origin_to_revoke,
192                   abusive_prompts_origin_to_revoke, unsolicited_prompts_origin,
193                   acceptable_origin, unknown_origin};
194 
195   for (auto origin : origins)
196     SetPermission(origin, CONTENT_SETTING_ALLOW);
197 
198   // The origins are not on any blocking lists.
199   for (auto origin : origins)
200     QueryAndExpectDecisionForUrl(origin, Outcome::PERMISSION_NOT_REVOKED);
201 
202   AddToPreloadDataBlocklist(abusive_content_origin_to_revoke,
203                             SiteReputation::ABUSIVE_CONTENT,
204                             /*has_warning=*/false);
205   AddToPreloadDataBlocklist(abusive_prompts_origin_to_revoke,
206                             SiteReputation::ABUSIVE_PROMPTS,
207                             /*has_warning=*/false);
208   AddToPreloadDataBlocklist(unsolicited_prompts_origin,
209                             SiteReputation::UNSOLICITED_PROMPTS,
210                             /*has_warning=*/false);
211   AddToPreloadDataBlocklist(acceptable_origin, SiteReputation::ACCEPTABLE,
212                             /*has_warning=*/false);
213   AddToPreloadDataBlocklist(unknown_origin, SiteReputation::UNKNOWN,
214                             /*has_warning=*/false);
215 
216   // The origins are on CrowdDeny blocking lists, but not on SafeBrowsing.
217   for (auto origin : origins)
218     QueryAndExpectDecisionForUrl(origin, Outcome::PERMISSION_NOT_REVOKED);
219 
220   for (auto origin : origins)
221     AddToSafeBrowsingBlocklist(origin);
222 
223   QueryAndExpectDecisionForUrl(abusive_content_origin_to_revoke,
224                                Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
225   QueryAndExpectDecisionForUrl(abusive_prompts_origin_to_revoke,
226                                Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
227   QueryAndExpectDecisionForUrl(unsolicited_prompts_origin,
228                                Outcome::PERMISSION_NOT_REVOKED);
229   QueryAndExpectDecisionForUrl(acceptable_origin,
230                                Outcome::PERMISSION_NOT_REVOKED);
231   QueryAndExpectDecisionForUrl(unknown_origin, Outcome::PERMISSION_NOT_REVOKED);
232 }
233 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,PreloadDataTestWithWarning)234 TEST_F(AbusiveOriginPermissionRevocationRequestTest,
235        PreloadDataTestWithWarning) {
236   const GURL abusive_content_origin_to_revoke =
237       GURL("https://abusive-content.com/");
238   const GURL abusive_prompts_origin_to_revoke =
239       GURL("https://abusive-prompts.com/");
240   const GURL unsolicited_prompts_origin =
241       GURL("https://unsolicited-prompts.com/");
242   const GURL acceptable_origin = GURL("https://acceptable-origin.com/");
243   const GURL unknown_origin = GURL("https://unknown-origin.com/");
244 
245   auto origins = {abusive_content_origin_to_revoke,
246                   abusive_prompts_origin_to_revoke, unsolicited_prompts_origin,
247                   acceptable_origin, unknown_origin};
248 
249   for (auto origin : origins)
250     SetPermission(origin, CONTENT_SETTING_ALLOW);
251 
252   // The origins are not on any blocking lists.
253   for (auto origin : origins)
254     QueryAndExpectDecisionForUrl(origin, Outcome::PERMISSION_NOT_REVOKED);
255 
256   AddToPreloadDataBlocklist(abusive_content_origin_to_revoke,
257                             SiteReputation::ABUSIVE_CONTENT,
258                             /*has_warning=*/true);
259   AddToPreloadDataBlocklist(abusive_prompts_origin_to_revoke,
260                             SiteReputation::ABUSIVE_PROMPTS,
261                             /*has_warning=*/true);
262   AddToPreloadDataBlocklist(unsolicited_prompts_origin,
263                             SiteReputation::UNSOLICITED_PROMPTS,
264                             /*has_warning=*/true);
265   AddToPreloadDataBlocklist(acceptable_origin, SiteReputation::ACCEPTABLE,
266                             /*has_warning=*/true);
267   AddToPreloadDataBlocklist(unknown_origin, SiteReputation::UNKNOWN,
268                             /*has_warning=*/true);
269 
270   // The origins are on CrowdDeny blocking lists, but not on SafeBrowsing.
271   for (auto origin : origins)
272     QueryAndExpectDecisionForUrl(origin, Outcome::PERMISSION_NOT_REVOKED);
273 
274   for (auto origin : origins)
275     AddToSafeBrowsingBlocklist(origin);
276 
277   // The warning is enabled for all origin, permission should not be revoked.
278   for (auto origin : origins)
279     QueryAndExpectDecisionForUrl(origin, Outcome::PERMISSION_NOT_REVOKED);
280 }
281 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,ExemptAbusiveOriginTest)282 TEST_F(AbusiveOriginPermissionRevocationRequestTest, ExemptAbusiveOriginTest) {
283   const GURL origin_to_exempt = GURL("https://origin-allow.com/");
284   const GURL origin_to_revoke = GURL("https://origin.com/");
285 
286   AbusiveOriginPermissionRevocationRequest::ExemptOriginFromFutureRevocations(
287       GetTestingProfile(), origin_to_exempt);
288 
289   SetPermission(origin_to_exempt, CONTENT_SETTING_ALLOW);
290 
291   AddToPreloadDataBlocklist(origin_to_exempt, SiteReputation::ABUSIVE_CONTENT,
292                             /*has_warning=*/false);
293   AddToSafeBrowsingBlocklist(origin_to_exempt);
294 
295   SetPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
296   AddToPreloadDataBlocklist(origin_to_revoke, SiteReputation::ABUSIVE_CONTENT,
297                             /*has_warning=*/false);
298   AddToSafeBrowsingBlocklist(origin_to_revoke);
299 
300   // The origin added to the exempt list will not be revoked.
301   QueryAndExpectDecisionForUrl(origin_to_exempt,
302                                Outcome::PERMISSION_NOT_REVOKED);
303   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
304 
305   QueryAndExpectDecisionForUrl(origin_to_revoke,
306                                Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
307   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ASK);
308 }
309 
TEST_F(AbusiveOriginPermissionRevocationRequestTest,SafeBrowsingDisabledTest)310 TEST_F(AbusiveOriginPermissionRevocationRequestTest, SafeBrowsingDisabledTest) {
311   const GURL origin_to_revoke = GURL("https://origin.com/");
312 
313   SetPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
314 
315   AddToSafeBrowsingBlocklist(origin_to_revoke);
316   AddToPreloadDataBlocklist(origin_to_revoke, SiteReputation::ABUSIVE_CONTENT,
317                             /*has_warning=*/false);
318   QueryAndExpectDecisionForUrl(origin_to_revoke,
319                                Outcome::PERMISSION_REVOKED_DUE_TO_ABUSE);
320 
321   GetTestingProfile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled,
322                                               false);
323 
324   // Permission should not be revoked because Safe Browsing is disabled.
325   const GURL origin_to_not_revoke = GURL("https://origin-not_revoked.com/");
326 
327   SetPermission(origin_to_not_revoke, CONTENT_SETTING_ALLOW);
328 
329   AddToSafeBrowsingBlocklist(origin_to_not_revoke);
330   AddToPreloadDataBlocklist(origin_to_not_revoke,
331                             SiteReputation::ABUSIVE_CONTENT,
332                             /*has_warning=*/false);
333 
334   QueryAndExpectDecisionForUrl(origin_to_not_revoke,
335                                Outcome::PERMISSION_NOT_REVOKED);
336   VerifyNotificationsPermission(origin_to_not_revoke, CONTENT_SETTING_ALLOW);
337 }
338 
339 class AbusiveOriginPermissionRevocationRequestDisabledTest
340     : public AbusiveOriginPermissionRevocationRequestTestBase {
341  public:
342   AbusiveOriginPermissionRevocationRequestDisabledTest() = default;
343   ~AbusiveOriginPermissionRevocationRequestDisabledTest() override = default;
344 };
345 
TEST_F(AbusiveOriginPermissionRevocationRequestDisabledTest,PermissionRevocationFeatureDisabled)346 TEST_F(AbusiveOriginPermissionRevocationRequestDisabledTest,
347        PermissionRevocationFeatureDisabled) {
348   const GURL origin_to_revoke = GURL("https://origin.com/");
349 
350   SetPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
351   QueryAndExpectDecisionForUrl(origin_to_revoke,
352                                Outcome::PERMISSION_NOT_REVOKED);
353   VerifyNotificationsPermission(origin_to_revoke, CONTENT_SETTING_ALLOW);
354 }
355