1 // Copyright 2017 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 #include "chrome/browser/safe_browsing/chrome_password_protection_service.h"
5
6 #include <memory>
7
8 #include "base/bind.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/run_loop.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/test/bind.h"
14 #include "base/test/scoped_feature_list.h"
15 #include "build/build_config.h"
16 #include "chrome/browser/password_manager/account_password_store_factory.h"
17 #include "chrome/browser/password_manager/password_store_factory.h"
18 #include "chrome/browser/safe_browsing/ui_manager.h"
19 #include "chrome/browser/signin/chrome_signin_client_factory.h"
20 #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
21 #include "chrome/browser/signin/test_signin_client_builder.h"
22 #include "chrome/browser/sync/user_event_service_factory.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
25 #include "chrome/test/base/scoped_testing_local_state.h"
26 #include "chrome/test/base/testing_browser_process.h"
27 #include "chrome/test/base/testing_profile.h"
28 #include "components/content_settings/core/browser/host_content_settings_map.h"
29 #include "components/password_manager/core/browser/compromised_credentials_table.h"
30 #include "components/password_manager/core/browser/hash_password_manager.h"
31 #include "components/password_manager/core/browser/mock_password_store.h"
32 #include "components/password_manager/core/browser/password_manager_metrics_util.h"
33 #include "components/password_manager/core/browser/password_manager_test_utils.h"
34 #include "components/password_manager/core/common/password_manager_features.h"
35 #include "components/prefs/pref_service.h"
36 #include "components/prefs/scoped_user_pref_update.h"
37 #include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
38 #include "components/safe_browsing/content/password_protection/password_protection_request.h"
39 #include "components/safe_browsing/core/common/utils.h"
40 #include "components/safe_browsing/core/db/v4_protocol_manager_util.h"
41 #include "components/safe_browsing/core/features.h"
42 #include "components/safe_browsing/core/verdict_cache_manager.h"
43 #include "components/signin/public/identity_manager/account_info.h"
44 #include "components/strings/grit/components_strings.h"
45 #include "components/sync/model/model_type_controller_delegate.h"
46 #include "components/sync_preferences/testing_pref_service_syncable.h"
47 #include "components/sync_user_events/fake_user_event_service.h"
48 #include "content/public/browser/navigation_handle.h"
49 #include "content/public/browser/web_contents.h"
50 #include "content/public/test/mock_navigation_handle.h"
51 #include "net/http/http_util.h"
52 #include "testing/gmock/include/gmock/gmock.h"
53 #include "testing/gtest/include/gtest/gtest.h"
54 #include "ui/base/l10n/l10n_util.h"
55
56 // All tests related to extension is disabled on Android, because enterprise
57 // reporting extension is not supported.
58 #if !defined(OS_ANDROID)
59 #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h"
60 #include "chrome/browser/safe_browsing/test_extension_event_observer.h"
61 #include "chrome/common/extensions/api/safe_browsing_private.h"
62 #include "extensions/browser/test_event_router.h"
63 #endif
64
65 using sync_pb::UserEventSpecifics;
66 using GaiaPasswordReuse = sync_pb::GaiaPasswordReuse;
67 using GaiaPasswordCaptured = UserEventSpecifics::GaiaPasswordCaptured;
68 using PasswordReuseDialogInteraction =
69 GaiaPasswordReuse::PasswordReuseDialogInteraction;
70 using PasswordReuseEvent =
71 safe_browsing::LoginReputationClientRequest::PasswordReuseEvent;
72 using PasswordReuseLookup = GaiaPasswordReuse::PasswordReuseLookup;
73 using ::testing::_;
74 using ::testing::Return;
75 using ::testing::WithArg;
76
77 #if !defined(OS_ANDROID)
78 namespace OnPolicySpecifiedPasswordReuseDetected = extensions::api::
79 safe_browsing_private::OnPolicySpecifiedPasswordReuseDetected;
80 namespace OnPolicySpecifiedPasswordChanged =
81 extensions::api::safe_browsing_private::OnPolicySpecifiedPasswordChanged;
82 #endif
83
84 class MockSecurityEventRecorder : public SecurityEventRecorder {
85 public:
86 MockSecurityEventRecorder() = default;
87 ~MockSecurityEventRecorder() override = default;
88
Create()89 static BrowserContextKeyedServiceFactory::TestingFactory Create() {
90 return base::BindRepeating(
91 [](content::BrowserContext* context) -> std::unique_ptr<KeyedService> {
92 return std::make_unique<MockSecurityEventRecorder>();
93 });
94 }
95
96 MOCK_METHOD0(GetControllerDelegate,
97 base::WeakPtr<syncer::ModelTypeControllerDelegate>());
98 MOCK_METHOD1(RecordGaiaPasswordReuse, void(const GaiaPasswordReuse&));
99 };
100
101 namespace safe_browsing {
102
103 namespace {
104
105 const char kPhishingURL[] = "http://phishing.com/";
106 const char kTestEmail[] = "foo@example.com";
107 const char kUserName[] = "username";
108 const char kRedirectURL[] = "http://redirect.com";
109 #if !defined(OS_ANDROID)
110 const char kPasswordReuseURL[] = "http://login.example.com/";
111 const char kTestGmail[] = "foo@gmail.com";
112 #endif
113
114 BrowserContextKeyedServiceFactory::TestingFactory
GetFakeUserEventServiceFactory()115 GetFakeUserEventServiceFactory() {
116 return base::BindRepeating(
117 [](content::BrowserContext* context) -> std::unique_ptr<KeyedService> {
118 return std::make_unique<syncer::FakeUserEventService>();
119 });
120 }
121
122 constexpr struct {
123 // The response from the password protection service.
124 RequestOutcome request_outcome;
125 // The enum to log in the user event for that response.
126 PasswordReuseLookup::LookupResult lookup_result;
127 } kTestCasesWithoutVerdict[]{
128 {RequestOutcome::MATCHED_WHITELIST, PasswordReuseLookup::WHITELIST_HIT},
129 {RequestOutcome::URL_NOT_VALID_FOR_REPUTATION_COMPUTING,
130 PasswordReuseLookup::URL_UNSUPPORTED},
131 {RequestOutcome::CANCELED, PasswordReuseLookup::REQUEST_FAILURE},
132 {RequestOutcome::TIMEDOUT, PasswordReuseLookup::REQUEST_FAILURE},
133 {RequestOutcome::DISABLED_DUE_TO_INCOGNITO,
134 PasswordReuseLookup::REQUEST_FAILURE},
135 {RequestOutcome::REQUEST_MALFORMED, PasswordReuseLookup::REQUEST_FAILURE},
136 {RequestOutcome::FETCH_FAILED, PasswordReuseLookup::REQUEST_FAILURE},
137 {RequestOutcome::RESPONSE_MALFORMED, PasswordReuseLookup::REQUEST_FAILURE},
138 {RequestOutcome::SERVICE_DESTROYED, PasswordReuseLookup::REQUEST_FAILURE},
139 {RequestOutcome::DISABLED_DUE_TO_FEATURE_DISABLED,
140 PasswordReuseLookup::REQUEST_FAILURE},
141 {RequestOutcome::DISABLED_DUE_TO_USER_POPULATION,
142 PasswordReuseLookup::REQUEST_FAILURE}};
143
144 } // namespace
145
146 class MockChromePasswordProtectionService
147 : public ChromePasswordProtectionService {
148 public:
MockChromePasswordProtectionService(Profile * profile,scoped_refptr<SafeBrowsingUIManager> ui_manager,StringProvider sync_password_hash_provider,VerdictCacheManager * cache_manager)149 explicit MockChromePasswordProtectionService(
150 Profile* profile,
151 scoped_refptr<SafeBrowsingUIManager> ui_manager,
152 StringProvider sync_password_hash_provider,
153 VerdictCacheManager* cache_manager)
154 : ChromePasswordProtectionService(profile,
155 ui_manager,
156 sync_password_hash_provider,
157 cache_manager),
158 is_incognito_(false),
159 is_extended_reporting_(false),
160 is_syncing_(false),
161 is_no_hosted_domain_found_(false),
162 is_account_signed_in_(false) {}
IsExtendedReporting()163 bool IsExtendedReporting() override { return is_extended_reporting_; }
IsIncognito()164 bool IsIncognito() override { return is_incognito_; }
IsPrimaryAccountSyncing() const165 bool IsPrimaryAccountSyncing() const override { return is_syncing_; }
IsPrimaryAccountSignedIn() const166 bool IsPrimaryAccountSignedIn() const override {
167 return is_account_signed_in_;
168 }
IsPrimaryAccountGmail() const169 bool IsPrimaryAccountGmail() const override {
170 return is_no_hosted_domain_found_;
171 }
GetSignedInNonSyncAccount(const std::string & username) const172 AccountInfo GetSignedInNonSyncAccount(
173 const std::string& username) const override {
174 return account_info_;
175 }
176
177 safe_browsing::LoginReputationClientRequest::UrlDisplayExperiment
GetUrlDisplayExperiment() const178 GetUrlDisplayExperiment() const override {
179 return safe_browsing::LoginReputationClientRequest::UrlDisplayExperiment();
180 }
181
182 // Configures the results returned by IsExtendedReporting(), IsIncognito(),
183 // and IsHistorySyncEnabled().
ConfigService(bool is_incognito,bool is_extended_reporting)184 void ConfigService(bool is_incognito, bool is_extended_reporting) {
185 is_incognito_ = is_incognito;
186 is_extended_reporting_ = is_extended_reporting;
187 }
188
SetIsSyncing(bool is_syncing)189 void SetIsSyncing(bool is_syncing) { is_syncing_ = is_syncing; }
SetIsNoHostedDomainFound(bool is_no_hosted_domain_found)190 void SetIsNoHostedDomainFound(bool is_no_hosted_domain_found) {
191 is_no_hosted_domain_found_ = is_no_hosted_domain_found;
192 }
SetIsAccountSignedIn(bool is_account_signed_in)193 void SetIsAccountSignedIn(bool is_account_signed_in) {
194 is_account_signed_in_ = is_account_signed_in;
195 }
SetAccountInfo(const std::string & username)196 void SetAccountInfo(const std::string& username) {
197 AccountInfo account_info;
198 account_info.account_id = CoreAccountId("account_id");
199 account_info.email = username;
200 account_info.gaia = "gaia";
201 account_info_ = account_info;
202 }
203
ui_manager()204 SafeBrowsingUIManager* ui_manager() { return ui_manager_.get(); }
205
206 protected:
207 friend class ChromePasswordProtectionServiceTest;
208
209 private:
210 bool is_incognito_;
211 bool is_extended_reporting_;
212 bool is_syncing_;
213 bool is_no_hosted_domain_found_;
214 bool is_account_signed_in_;
215 AccountInfo account_info_;
216 std::string mocked_sync_password_hash_;
217 };
218
219 class ChromePasswordProtectionServiceTest
220 : public ChromeRenderViewHostTestHarness {
221 public:
ChromePasswordProtectionServiceTest()222 ChromePasswordProtectionServiceTest()
223 : local_state_(TestingBrowserProcess::GetGlobal()) {}
224
SetUp()225 void SetUp() override {
226 ChromeRenderViewHostTestHarness::SetUp();
227
228 password_store_ =
229 base::WrapRefCounted(static_cast<password_manager::MockPasswordStore*>(
230 PasswordStoreFactory::GetInstance()
231 ->SetTestingFactoryAndUse(
232 profile(),
233 base::BindRepeating(&password_manager::BuildPasswordStore<
234 content::BrowserContext,
235 password_manager::MockPasswordStore>))
236 .get()));
237
238 if (base::FeatureList::IsEnabled(
239 password_manager::features::kEnablePasswordsAccountStorage)) {
240 account_password_store_ = base::WrapRefCounted(
241 static_cast<password_manager::MockPasswordStore*>(
242 AccountPasswordStoreFactory::GetInstance()
243 ->SetTestingFactoryAndUse(
244 profile(),
245 base::BindRepeating(&password_manager::BuildPasswordStore<
246 content::BrowserContext,
247 password_manager::MockPasswordStore>))
248 .get()));
249 }
250
251 profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
252 profile()->GetPrefs()->SetInteger(
253 prefs::kPasswordProtectionWarningTrigger,
254 PasswordProtectionTrigger::PHISHING_REUSE);
255 HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
256 content_setting_map_ = new HostContentSettingsMap(
257 &test_pref_service_, /*is_off_the_record=*/false,
258 /*store_last_modified=*/false, /*restore_session=*/false);
259
260 cache_manager_ = std::make_unique<VerdictCacheManager>(
261 nullptr, content_setting_map_.get());
262
263 service_ = NewMockPasswordProtectionService();
264 fake_user_event_service_ = static_cast<syncer::FakeUserEventService*>(
265 browser_sync::UserEventServiceFactory::GetInstance()
266 ->SetTestingFactoryAndUse(browser_context(),
267 GetFakeUserEventServiceFactory()));
268 #if !defined(OS_ANDROID)
269 test_event_router_ =
270 extensions::CreateAndUseTestEventRouter(browser_context());
271 extensions::SafeBrowsingPrivateEventRouterFactory::GetInstance()
272 ->SetTestingFactory(
273 browser_context(),
274 base::BindRepeating(&BuildSafeBrowsingPrivateEventRouter));
275 #endif
276
277 identity_test_env_profile_adaptor_ =
278 std::make_unique<IdentityTestEnvironmentProfileAdaptor>(profile());
279
280 security_event_recorder_ = static_cast<MockSecurityEventRecorder*>(
281 SecurityEventRecorderFactory::GetInstance()->SetTestingFactoryAndUse(
282 browser_context(), MockSecurityEventRecorder::Create()));
283 // To make sure we are not accidentally calling the SecurityEventRecorder.
284 EXPECT_CALL(*security_event_recorder_, RecordGaiaPasswordReuse(_)).Times(0);
285 }
286
TearDown()287 void TearDown() override {
288 base::RunLoop().RunUntilIdle();
289 service_.reset();
290 request_ = nullptr;
291 if (account_password_store_)
292 account_password_store_->ShutdownOnUIThread();
293 password_store_->ShutdownOnUIThread();
294 identity_test_env_profile_adaptor_.reset();
295 cache_manager_.reset();
296 content_setting_map_->ShutdownOnUIThread();
297 ChromeRenderViewHostTestHarness::TearDown();
298 }
299
300 // This can be called whenever to reset service_, if initialition
301 // conditions have changed.
302 std::unique_ptr<MockChromePasswordProtectionService>
NewMockPasswordProtectionService(const std::string & sync_password_hash=std::string ())303 NewMockPasswordProtectionService(
304 const std::string& sync_password_hash = std::string()) {
305 StringProvider sync_password_hash_provider =
306 base::BindLambdaForTesting([=] { return sync_password_hash; });
307
308 // TODO(crbug/925153): Port consumers of the SafeBrowsingService
309 // to use the interface in components/safe_browsing, and remove this
310 // cast.
311 return std::make_unique<MockChromePasswordProtectionService>(
312 profile(),
313 new SafeBrowsingUIManager(
314 static_cast<safe_browsing::SafeBrowsingService*>(
315 SafeBrowsingService::CreateSafeBrowsingService())),
316 sync_password_hash_provider, cache_manager_.get());
317 }
318
GetTestingFactories() const319 TestingProfile::TestingFactories GetTestingFactories() const override {
320 return IdentityTestEnvironmentProfileAdaptor::
321 GetIdentityTestEnvironmentFactories();
322 }
323
GetUserEventService()324 syncer::FakeUserEventService* GetUserEventService() {
325 return fake_user_event_service_;
326 }
327
InitializeRequest(LoginReputationClientRequest::TriggerType trigger_type,PasswordType reused_password_type)328 void InitializeRequest(LoginReputationClientRequest::TriggerType trigger_type,
329 PasswordType reused_password_type) {
330 std::vector<password_manager::MatchingReusedCredential> credentials = {
331 {"somedomain.com"}};
332 if (trigger_type == LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE) {
333 request_ = new PasswordProtectionRequest(
334 web_contents(), GURL(kPhishingURL), GURL(), GURL(), kUserName,
335 PasswordType::PASSWORD_TYPE_UNKNOWN, credentials, trigger_type, true,
336 service_.get(), 0);
337 } else {
338 ASSERT_EQ(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
339 trigger_type);
340 request_ = new PasswordProtectionRequest(
341 web_contents(), GURL(kPhishingURL), GURL(), GURL(), kUserName,
342 reused_password_type, credentials, trigger_type,
343 /* password_field_exists*/ true, service_.get(),
344 /*request_timeout_in_ms=*/0);
345 }
346 }
347
InitializeVerdict(LoginReputationClientResponse::VerdictType type)348 void InitializeVerdict(LoginReputationClientResponse::VerdictType type) {
349 verdict_ = std::make_unique<LoginReputationClientResponse>();
350 verdict_->set_verdict_type(type);
351 }
352
SimulateRequestFinished(LoginReputationClientResponse::VerdictType verdict_type)353 void SimulateRequestFinished(
354 LoginReputationClientResponse::VerdictType verdict_type) {
355 std::unique_ptr<LoginReputationClientResponse> verdict =
356 std::make_unique<LoginReputationClientResponse>();
357 verdict->set_verdict_type(verdict_type);
358 service_->RequestFinished(request_.get(), RequestOutcome::SUCCEEDED,
359 std::move(verdict));
360 }
361
SetPrimaryAccount(const std::string & email)362 CoreAccountInfo SetPrimaryAccount(const std::string& email) {
363 identity_test_env()->MakeAccountAvailable(email);
364 return identity_test_env()->SetPrimaryAccount(email);
365 }
366
SetUpSyncAccount(const std::string & hosted_domain,const CoreAccountInfo & account_info)367 void SetUpSyncAccount(const std::string& hosted_domain,
368 const CoreAccountInfo& account_info) {
369 identity_test_env()->SimulateSuccessfulFetchOfAccountInfo(
370 account_info.account_id, account_info.email, account_info.gaia,
371 hosted_domain, "full_name", "given_name", "locale",
372 "http://picture.example.com/picture.jpg");
373 }
374
PrepareRequest(LoginReputationClientRequest::TriggerType trigger_type,PasswordType reused_password_type,bool is_warning_showing)375 void PrepareRequest(LoginReputationClientRequest::TriggerType trigger_type,
376 PasswordType reused_password_type,
377 bool is_warning_showing) {
378 InitializeRequest(trigger_type, reused_password_type);
379 request_->set_is_modal_warning_showing(is_warning_showing);
380 service_->pending_requests_.insert(request_);
381 }
382
GetSizeofUnhandledSyncPasswordReuses()383 int GetSizeofUnhandledSyncPasswordReuses() {
384 DictionaryPrefUpdate unhandled_sync_password_reuses(
385 profile()->GetPrefs(), prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
386 return unhandled_sync_password_reuses->size();
387 }
388
GetNumberOfNavigationThrottles()389 size_t GetNumberOfNavigationThrottles() {
390 return request_ ? request_->throttles_.size() : 0u;
391 }
392
identity_test_env()393 signin::IdentityTestEnvironment* identity_test_env() {
394 return identity_test_env_profile_adaptor_->identity_test_env();
395 }
396
397 protected:
398 sync_preferences::TestingPrefServiceSyncable test_pref_service_;
399 scoped_refptr<HostContentSettingsMap> content_setting_map_;
400 std::unique_ptr<MockChromePasswordProtectionService> service_;
401 scoped_refptr<PasswordProtectionRequest> request_;
402 std::unique_ptr<LoginReputationClientResponse> verdict_;
403 std::unique_ptr<IdentityTestEnvironmentProfileAdaptor>
404 identity_test_env_profile_adaptor_;
405 MockSecurityEventRecorder* security_event_recorder_;
406 scoped_refptr<password_manager::MockPasswordStore> password_store_;
407 scoped_refptr<password_manager::MockPasswordStore> account_password_store_;
408 // Owned by KeyedServiceFactory.
409 syncer::FakeUserEventService* fake_user_event_service_;
410 #if !defined(OS_ANDROID)
411 extensions::TestEventRouter* test_event_router_;
412 #endif
413 std::unique_ptr<VerdictCacheManager> cache_manager_;
414 ScopedTestingLocalState local_state_;
415 };
416
TEST_F(ChromePasswordProtectionServiceTest,VerifyUserPopulationForPasswordOnFocusPing)417 TEST_F(ChromePasswordProtectionServiceTest,
418 VerifyUserPopulationForPasswordOnFocusPing) {
419 ReusedPasswordAccountType reused_password_type;
420 reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
421
422 // Password field on focus pinging is enabled on !incognito && (SBER ||
423 // enhanced protection).
424 service_->ConfigService(false /*incognito*/, false /*SBER*/);
425 EXPECT_FALSE(service_->IsPingingEnabled(
426 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
427 reused_password_type));
428
429 service_->ConfigService(false /*incognito*/, true /*SBER*/);
430 EXPECT_TRUE(service_->IsPingingEnabled(
431 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
432 reused_password_type));
433
434 service_->ConfigService(true /*incognito*/, false /*SBER*/);
435 EXPECT_FALSE(service_->IsPingingEnabled(
436 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
437 reused_password_type));
438
439 service_->ConfigService(true /*incognito*/, true /*SBER*/);
440 EXPECT_FALSE(service_->IsPingingEnabled(
441 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
442 reused_password_type));
443 }
444
TEST_F(ChromePasswordProtectionServiceTest,VerifyUserPopulationForSavedPasswordEntryPing)445 TEST_F(ChromePasswordProtectionServiceTest,
446 VerifyUserPopulationForSavedPasswordEntryPing) {
447 base::test::ScopedFeatureList feature_list;
448
449 ReusedPasswordAccountType reused_password_type;
450 reused_password_type.set_account_type(
451 ReusedPasswordAccountType::SAVED_PASSWORD);
452
453 service_->ConfigService(false /*incognito*/, false /*SBER*/);
454 EXPECT_TRUE(service_->IsPingingEnabled(
455 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
456 reused_password_type));
457
458 service_->ConfigService(false /*incognito*/, true /*SBER*/);
459 EXPECT_TRUE(service_->IsPingingEnabled(
460 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
461 reused_password_type));
462
463 service_->ConfigService(true /*incognito*/, false /*SBER*/);
464 EXPECT_TRUE(service_->IsPingingEnabled(
465 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
466 reused_password_type));
467
468 service_->ConfigService(true /*incognito*/, true /*SBER*/);
469 EXPECT_TRUE(service_->IsPingingEnabled(
470 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
471 reused_password_type));
472
473 service_->ConfigService(false /*incognito*/, false /*SBER*/);
474 reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
475 EXPECT_FALSE(service_->IsPingingEnabled(
476 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
477 reused_password_type));
478 }
479
TEST_F(ChromePasswordProtectionServiceTest,VerifyUserPopulationForSyncPasswordEntryPing)480 TEST_F(ChromePasswordProtectionServiceTest,
481 VerifyUserPopulationForSyncPasswordEntryPing) {
482 // Sets up the account as a gmail account as there is no hosted domain.
483 ReusedPasswordAccountType reused_password_type;
484 reused_password_type.set_account_type(ReusedPasswordAccountType::GMAIL);
485 reused_password_type.set_is_account_syncing(true);
486
487 // Sync password entry pinging is enabled by default.
488 service_->ConfigService(false /*incognito*/, false /*SBER*/);
489 // Sync password pings are gated by SBER on Android, because warnings are
490 // disabled.
491 #if defined(OS_ANDROID)
492 EXPECT_FALSE(service_->IsPingingEnabled(
493 #else
494 EXPECT_TRUE(service_->IsPingingEnabled(
495 #endif
496 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
497 reused_password_type));
498
499 service_->ConfigService(false /*incognito*/, true /*SBER*/);
500 EXPECT_TRUE(service_->IsPingingEnabled(
501 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
502 reused_password_type));
503
504 service_->ConfigService(true /*incognito*/, false /*SBER*/);
505 // Sync password pings are gated by SBER on Android, because warnings are
506 // disabled.
507 #if defined(OS_ANDROID)
508 EXPECT_FALSE(service_->IsPingingEnabled(
509 #else
510 EXPECT_TRUE(service_->IsPingingEnabled(
511 #endif
512 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
513 reused_password_type));
514
515 // Even if sync password entry pinging is disabled by policy,
516 // |IsPingingEnabled(..)| should still default to true if the
517 // the password reuse type is syncing Gmail account.
518 service_->ConfigService(true /*incognito*/, true /*SBER*/);
519 service_->SetIsNoHostedDomainFound(true);
520 EXPECT_TRUE(service_->IsPingingEnabled(
521 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
522 reused_password_type));
523
524 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
525 PASSWORD_PROTECTION_OFF);
526 service_->ConfigService(false /*incognito*/, false /*SBER*/);
527 // Sync password pings are gated by SBER on Android, because warnings are
528 // disabled.
529 #if defined(OS_ANDROID)
530 EXPECT_FALSE(service_->IsPingingEnabled(
531 #else
532 EXPECT_TRUE(service_->IsPingingEnabled(
533 #endif
534 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
535 reused_password_type));
536
537 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
538 PASSWORD_REUSE);
539 // Sync password pings are gated by SBER on Android, because warnings are
540 // disabled.
541 #if defined(OS_ANDROID)
542 EXPECT_FALSE(service_->IsPingingEnabled(
543 #else
544 EXPECT_TRUE(service_->IsPingingEnabled(
545 #endif
546 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
547 reused_password_type));
548 }
549
TEST_F(ChromePasswordProtectionServiceTest,VerifyPingingIsSkippedIfMatchEnterpriseWhitelist)550 TEST_F(ChromePasswordProtectionServiceTest,
551 VerifyPingingIsSkippedIfMatchEnterpriseWhitelist) {
552 ASSERT_FALSE(
553 profile()->GetPrefs()->HasPrefPath(prefs::kSafeBrowsingWhitelistDomains));
554
555 // If there's no whitelist, IsURLWhitelistedForPasswordEntry(_) should
556 // return false.
557 EXPECT_FALSE(service_->IsURLWhitelistedForPasswordEntry(
558 GURL("https://www.mydomain.com")));
559
560 // Verify if match enterprise whitelist.
561 base::ListValue whitelist;
562 whitelist.AppendString("mydomain.com");
563 whitelist.AppendString("mydomain.net");
564 profile()->GetPrefs()->Set(prefs::kSafeBrowsingWhitelistDomains, whitelist);
565 EXPECT_TRUE(service_->IsURLWhitelistedForPasswordEntry(
566 GURL("https://www.mydomain.com")));
567
568 // Verify if matches enterprise change password url.
569 profile()->GetPrefs()->ClearPref(prefs::kSafeBrowsingWhitelistDomains);
570 EXPECT_FALSE(service_->IsURLWhitelistedForPasswordEntry(
571 GURL("https://www.mydomain.com")));
572
573 profile()->GetPrefs()->SetString(prefs::kPasswordProtectionChangePasswordURL,
574 "https://mydomain.com/change_password.html");
575 EXPECT_TRUE(service_->IsURLWhitelistedForPasswordEntry(
576 GURL("https://mydomain.com/change_password.html#ref?user_name=alice")));
577
578 // Verify if matches enterprise login url.
579 profile()->GetPrefs()->ClearPref(prefs::kSafeBrowsingWhitelistDomains);
580 profile()->GetPrefs()->ClearPref(prefs::kPasswordProtectionChangePasswordURL);
581 EXPECT_FALSE(service_->IsURLWhitelistedForPasswordEntry(
582 GURL("https://www.mydomain.com")));
583 base::ListValue login_urls;
584 login_urls.AppendString("https://mydomain.com/login.html");
585 profile()->GetPrefs()->Set(prefs::kPasswordProtectionLoginURLs, login_urls);
586 EXPECT_TRUE(service_->IsURLWhitelistedForPasswordEntry(
587 GURL("https://mydomain.com/login.html#ref?user_name=alice")));
588 }
589
TEST_F(ChromePasswordProtectionServiceTest,VerifyPersistPhishedSavedPasswordCredential)590 TEST_F(ChromePasswordProtectionServiceTest,
591 VerifyPersistPhishedSavedPasswordCredential) {
592 service_->ConfigService(/*is_incognito=*/false,
593 /*is_extended_reporting=*/true);
594 std::vector<password_manager::MatchingReusedCredential> credentials = {
595 {"http://example.test"}, {"http://2.example.com"}};
596
597 EXPECT_CALL(*password_store_, AddCompromisedCredentialsImpl(_)).Times(2);
598 service_->PersistPhishedSavedPasswordCredential(credentials);
599 }
600
TEST_F(ChromePasswordProtectionServiceTest,VerifyRemovePhishedSavedPasswordCredential)601 TEST_F(ChromePasswordProtectionServiceTest,
602 VerifyRemovePhishedSavedPasswordCredential) {
603 service_->ConfigService(/*is_incognito=*/false,
604 /*is_extended_reporting=*/true);
605 std::vector<password_manager::MatchingReusedCredential> credentials = {
606 {"http://example.test", base::ASCIIToUTF16("username1")},
607 {"http://2.example.test", base::ASCIIToUTF16("username2")}};
608
609 EXPECT_CALL(*password_store_,
610 RemoveCompromisedCredentialsImpl(
611 _, _,
612 password_manager::RemoveCompromisedCredentialsReason::
613 kMarkSiteAsLegitimate))
614 .Times(2);
615 service_->RemovePhishedSavedPasswordCredential(credentials);
616 }
617
TEST_F(ChromePasswordProtectionServiceTest,VerifyCanSendSamplePing)618 TEST_F(ChromePasswordProtectionServiceTest, VerifyCanSendSamplePing) {
619 // Experiment is on by default.
620 service_->ConfigService(/*is_incognito=*/false,
621 /*is_extended_reporting=*/true);
622 service_->set_bypass_probability_for_tests(true);
623 EXPECT_TRUE(service_->CanSendSamplePing());
624
625 // If not SBER, do not send sample ping.
626 service_->ConfigService(/*is_incognito=*/false,
627 /*is_extended_reporting=*/false);
628 EXPECT_FALSE(service_->CanSendSamplePing());
629
630 // If incognito, do not send sample ping.
631 service_->ConfigService(/*is_incognito=*/true,
632 /*is_extended_reporting=*/true);
633 EXPECT_FALSE(service_->CanSendSamplePing());
634
635 service_->ConfigService(/*is_incognito=*/true,
636 /*is_extended_reporting=*/false);
637 EXPECT_FALSE(service_->CanSendSamplePing());
638
639 service_->ConfigService(/*is_incognito=*/false,
640 /*is_extended_reporting=*/false);
641 }
642
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetOrganizationTypeGmail)643 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetOrganizationTypeGmail) {
644 ReusedPasswordAccountType reused_password_type;
645 reused_password_type.set_account_type(ReusedPasswordAccountType::GMAIL);
646 reused_password_type.set_is_account_syncing(true);
647 EXPECT_TRUE(service_->GetOrganizationName(reused_password_type).empty());
648 EXPECT_EQ("", service_->GetOrganizationName(reused_password_type));
649 }
650
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetOrganizationTypeGSuite)651 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetOrganizationTypeGSuite) {
652 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
653 SetUpSyncAccount("example.com", account_info);
654 ReusedPasswordAccountType reused_password_type;
655 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
656 reused_password_type.set_is_account_syncing(true);
657 EXPECT_EQ("example.com", service_->GetOrganizationName(reused_password_type));
658 }
659
TEST_F(ChromePasswordProtectionServiceTest,VerifyUpdateSecurityState)660 TEST_F(ChromePasswordProtectionServiceTest, VerifyUpdateSecurityState) {
661 GURL url("http://password_reuse_url.com");
662 NavigateAndCommit(url);
663 SBThreatType current_threat_type = SB_THREAT_TYPE_UNUSED;
664 ASSERT_FALSE(service_->ui_manager()->IsUrlWhitelistedOrPendingForWebContents(
665 url, false, web_contents()->GetController().GetLastCommittedEntry(),
666 web_contents(), false, ¤t_threat_type));
667 EXPECT_EQ(SB_THREAT_TYPE_UNUSED, current_threat_type);
668
669 // Cache a verdict for this URL.
670 LoginReputationClientResponse verdict_proto;
671 verdict_proto.set_verdict_type(LoginReputationClientResponse::PHISHING);
672 verdict_proto.set_cache_duration_sec(600);
673 verdict_proto.set_cache_expression("password_reuse_url.com/");
674 ReusedPasswordAccountType reused_password_type;
675 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
676 reused_password_type.set_is_account_syncing(true);
677 service_->CacheVerdict(
678 url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
679 reused_password_type, verdict_proto, base::Time::Now());
680
681 service_->UpdateSecurityState(SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE,
682 reused_password_type, web_contents());
683 ASSERT_TRUE(service_->ui_manager()->IsUrlWhitelistedOrPendingForWebContents(
684 url, false, web_contents()->GetController().GetLastCommittedEntry(),
685 web_contents(), false, ¤t_threat_type));
686 EXPECT_EQ(SB_THREAT_TYPE_SIGNED_IN_SYNC_PASSWORD_REUSE, current_threat_type);
687
688 service_->UpdateSecurityState(safe_browsing::SB_THREAT_TYPE_SAFE,
689 reused_password_type, web_contents());
690 current_threat_type = SB_THREAT_TYPE_UNUSED;
691 service_->ui_manager()->IsUrlWhitelistedOrPendingForWebContents(
692 url, false, web_contents()->GetController().GetLastCommittedEntry(),
693 web_contents(), false, ¤t_threat_type);
694 EXPECT_EQ(SB_THREAT_TYPE_UNUSED, current_threat_type);
695 LoginReputationClientResponse verdict;
696 EXPECT_EQ(LoginReputationClientResponse::SAFE,
697 service_->GetCachedVerdict(
698 url, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
699 reused_password_type, &verdict));
700 }
701
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordReuseUserEventNotRecordedDueToIncognito)702 TEST_F(ChromePasswordProtectionServiceTest,
703 VerifyPasswordReuseUserEventNotRecordedDueToIncognito) {
704 // Configure sync account type to GMAIL.
705 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
706 SetUpSyncAccount(kNoHostedDomainFound, account_info);
707 service_->ConfigService(true /*is_incognito*/,
708 false /*is_extended_reporting*/);
709 ASSERT_TRUE(service_->IsIncognito());
710
711 // Nothing should be logged because of incognito.
712 NavigateAndCommit(GURL("https:www.example.com/"));
713
714 // PasswordReuseDetected
715 service_->MaybeLogPasswordReuseDetectedEvent(web_contents());
716 EXPECT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty());
717 service_->MaybeLogPasswordReuseLookupEvent(
718 web_contents(), RequestOutcome::MATCHED_WHITELIST,
719 PasswordType::PRIMARY_ACCOUNT_PASSWORD, nullptr);
720 EXPECT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty());
721
722 // PasswordReuseLookup
723 unsigned long t = 0;
724 for (const auto& it : kTestCasesWithoutVerdict) {
725 service_->MaybeLogPasswordReuseLookupEvent(
726 web_contents(), it.request_outcome,
727 PasswordType::PRIMARY_ACCOUNT_PASSWORD, nullptr);
728 ASSERT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty()) << t;
729 t++;
730 }
731
732 // PasswordReuseDialogInteraction
733 service_->MaybeLogPasswordReuseDialogInteraction(
734 1000 /* navigation_id */,
735 PasswordReuseDialogInteraction::WARNING_ACTION_TAKEN);
736 ASSERT_TRUE(GetUserEventService()->GetRecordedUserEvents().empty());
737 }
738
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordReuseDetectedUserEventRecorded)739 TEST_F(ChromePasswordProtectionServiceTest,
740 VerifyPasswordReuseDetectedUserEventRecorded) {
741 // Configure sync account type to GMAIL.
742 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
743 SetUpSyncAccount(kNoHostedDomainFound, account_info);
744 service_->SetIsAccountSignedIn(true);
745 NavigateAndCommit(GURL("https://www.example.com/"));
746
747 // Case 1: safe_browsing_enabled = true
748 profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
749 service_->MaybeLogPasswordReuseDetectedEvent(web_contents());
750 ASSERT_EQ(1ul, GetUserEventService()->GetRecordedUserEvents().size());
751 GaiaPasswordReuse event = GetUserEventService()
752 ->GetRecordedUserEvents()[0]
753 .gaia_password_reuse_event();
754 EXPECT_TRUE(event.reuse_detected().status().enabled());
755
756 // Case 2: safe_browsing_enabled = false
757 profile()->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
758 service_->MaybeLogPasswordReuseDetectedEvent(web_contents());
759 ASSERT_EQ(2ul, GetUserEventService()->GetRecordedUserEvents().size());
760 event = GetUserEventService()
761 ->GetRecordedUserEvents()[1]
762 .gaia_password_reuse_event();
763 EXPECT_FALSE(event.reuse_detected().status().enabled());
764
765 // Not checking for the extended_reporting_level since that requires setting
766 // multiple prefs and doesn't add much verification value.
767 }
768
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordReuseDetectedSecurityEventRecorded)769 TEST_F(ChromePasswordProtectionServiceTest,
770 VerifyPasswordReuseDetectedSecurityEventRecorded) {
771 identity_test_env()->SetPrimaryAccount(kTestEmail);
772 service_->set_username_for_last_shown_warning(kTestEmail);
773 EXPECT_CALL(*security_event_recorder_, RecordGaiaPasswordReuse(_))
774 .WillOnce(WithArg<0>([&](const auto& message) {
775 EXPECT_EQ(PasswordReuseLookup::REQUEST_SUCCESS,
776 message.reuse_lookup().lookup_result());
777 EXPECT_EQ(PasswordReuseLookup::SAFE, message.reuse_lookup().verdict());
778 EXPECT_EQ("verdict_token", message.reuse_lookup().verdict_token());
779 }));
780 service_->MaybeLogPasswordReuseLookupResultWithVerdict(
781 web_contents(), PasswordType::OTHER_GAIA_PASSWORD,
782 PasswordReuseLookup::REQUEST_SUCCESS, PasswordReuseLookup::SAFE,
783 "verdict_token");
784 }
785
786 // The following tests are disabled on Android, because password capture events
787 // are not enabled on Android.
788 #if !defined(OS_ANDROID)
789 // Check that the PasswordCapturedEvent timer is set for 1 min if password
790 // hash is saved and no timer pref is set yet.
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordCaptureEventScheduledOnStartup)791 TEST_F(ChromePasswordProtectionServiceTest,
792 VerifyPasswordCaptureEventScheduledOnStartup) {
793 // Configure sync account type to GMAIL.
794 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
795 SetUpSyncAccount(kNoHostedDomainFound, account_info);
796
797 // Case 1: Check that the timer is not set in the ctor if no password hash is
798 // saved.
799 service_ = NewMockPasswordProtectionService(
800 /*sync_password_hash=*/"");
801 EXPECT_FALSE(service_->log_password_capture_timer_.IsRunning());
802
803 // Case 1: Timer is set to 60 sec by default.
804 service_ = NewMockPasswordProtectionService(
805 /*sync_password_hash=*/"some-hash-value");
806 EXPECT_TRUE(service_->log_password_capture_timer_.IsRunning());
807 EXPECT_EQ(
808 60, service_->log_password_capture_timer_.GetCurrentDelay().InSeconds());
809 }
810
811 // Check that the timer is set for prescribed time based on pref.
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordCaptureEventScheduledFromPref)812 TEST_F(ChromePasswordProtectionServiceTest,
813 VerifyPasswordCaptureEventScheduledFromPref) {
814 // Pick a delay and store the deadline in the pref
815 base::TimeDelta delay = base::TimeDelta::FromDays(13);
816 SetDelayInPref(profile()->GetPrefs(),
817 prefs::kSafeBrowsingNextPasswordCaptureEventLogTime, delay);
818
819 // Configure sync account type to GMAIL.
820 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
821 SetUpSyncAccount(kNoHostedDomainFound, account_info);
822
823 service_ = NewMockPasswordProtectionService(
824 /*sync_password_hash=*/"");
825 // Check that the timer is not set if no password hash is saved.
826 EXPECT_EQ(
827 0, service_->log_password_capture_timer_.GetCurrentDelay().InSeconds());
828
829 // Save a password hash
830 service_ = NewMockPasswordProtectionService(
831 /*sync_password_hash=*/"some-hash-value");
832
833 // Verify the delay is approx correct (not exact since we're not controlling
834 // the clock).
835 base::TimeDelta cur_delay =
836 service_->log_password_capture_timer_.GetCurrentDelay();
837 EXPECT_GE(14, cur_delay.InDays());
838 EXPECT_LE(12, cur_delay.InDays());
839 }
840
841 // Check that we do log the event
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordCaptureEventRecorded)842 TEST_F(ChromePasswordProtectionServiceTest,
843 VerifyPasswordCaptureEventRecorded) {
844 // Configure sync account type to GMAIL.
845 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
846 SetUpSyncAccount(kNoHostedDomainFound, account_info);
847
848 // Case 1: Default service_ ctor has an empty password hash. Should not log.
849 service_->MaybeLogPasswordCapture(/*did_log_in=*/false);
850 ASSERT_EQ(0ul, GetUserEventService()->GetRecordedUserEvents().size());
851
852 // Cases 2 and 3: With a password hash. Should log.
853 service_ = NewMockPasswordProtectionService(
854 /*sync_password_hash=*/"some-hash-value");
855 service_->SetIsAccountSignedIn(true);
856 service_->MaybeLogPasswordCapture(/*did_log_in=*/false);
857 ASSERT_EQ(1ul, GetUserEventService()->GetRecordedUserEvents().size());
858 GaiaPasswordCaptured event = GetUserEventService()
859 ->GetRecordedUserEvents()[0]
860 .gaia_password_captured_event();
861 EXPECT_EQ(event.event_trigger(), GaiaPasswordCaptured::EXPIRED_28D_TIMER);
862
863 service_->MaybeLogPasswordCapture(/*did_log_in=*/true);
864 ASSERT_EQ(2ul, GetUserEventService()->GetRecordedUserEvents().size());
865 event = GetUserEventService()
866 ->GetRecordedUserEvents()[1]
867 .gaia_password_captured_event();
868 EXPECT_EQ(event.event_trigger(), GaiaPasswordCaptured::USER_LOGGED_IN);
869 }
870
871 // Check that we reschedule after logging.
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordCaptureEventReschedules)872 TEST_F(ChromePasswordProtectionServiceTest,
873 VerifyPasswordCaptureEventReschedules) {
874 // Configure sync account type to GMAIL.
875 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
876 SetUpSyncAccount(kNoHostedDomainFound, account_info);
877
878 // Case 1: Default service_ ctor has an empty password hash, so we don't log
879 // or reschedule the logging.
880 service_->MaybeLogPasswordCapture(/*did_log_in=*/false);
881 EXPECT_FALSE(service_->log_password_capture_timer_.IsRunning());
882
883 // Case 2: A non-empty password hash.
884 service_ = NewMockPasswordProtectionService(
885 /*sync_password_hash=*/"some-hash-value");
886 service_->SetIsAccountSignedIn(true);
887
888 service_->MaybeLogPasswordCapture(/*did_log_in=*/false);
889
890 // Verify the delay is approx correct. Will be 24-28 days, +- clock drift.
891 EXPECT_TRUE(service_->log_password_capture_timer_.IsRunning());
892 base::TimeDelta cur_delay =
893 service_->log_password_capture_timer_.GetCurrentDelay();
894 EXPECT_GT(29, cur_delay.InDays());
895 EXPECT_LT(23, cur_delay.InDays());
896 }
897 #endif
898
TEST_F(ChromePasswordProtectionServiceTest,VerifyPasswordReuseLookupUserEventRecorded)899 TEST_F(ChromePasswordProtectionServiceTest,
900 VerifyPasswordReuseLookupUserEventRecorded) {
901 // Configure sync account type to GMAIL.
902 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
903 SetUpSyncAccount(kNoHostedDomainFound, account_info);
904
905 NavigateAndCommit(GURL("https://www.example.com/"));
906
907 unsigned long t = 0;
908 for (const auto& it : kTestCasesWithoutVerdict) {
909 service_->MaybeLogPasswordReuseLookupEvent(
910 web_contents(), it.request_outcome,
911 PasswordType::PRIMARY_ACCOUNT_PASSWORD, nullptr);
912 ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size())
913 << t;
914 PasswordReuseLookup reuse_lookup = GetUserEventService()
915 ->GetRecordedUserEvents()[t]
916 .gaia_password_reuse_event()
917 .reuse_lookup();
918 EXPECT_EQ(it.lookup_result, reuse_lookup.lookup_result()) << t;
919 t++;
920 }
921
922 {
923 auto response = std::make_unique<LoginReputationClientResponse>();
924 response->set_verdict_token("token1");
925 response->set_verdict_type(LoginReputationClientResponse::LOW_REPUTATION);
926 service_->MaybeLogPasswordReuseLookupEvent(
927 web_contents(), RequestOutcome::RESPONSE_ALREADY_CACHED,
928 PasswordType::PRIMARY_ACCOUNT_PASSWORD, response.get());
929 ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size())
930 << t;
931 PasswordReuseLookup reuse_lookup = GetUserEventService()
932 ->GetRecordedUserEvents()[t]
933 .gaia_password_reuse_event()
934 .reuse_lookup();
935 EXPECT_EQ(PasswordReuseLookup::CACHE_HIT, reuse_lookup.lookup_result())
936 << t;
937 EXPECT_EQ(PasswordReuseLookup::LOW_REPUTATION, reuse_lookup.verdict()) << t;
938 EXPECT_EQ("token1", reuse_lookup.verdict_token()) << t;
939 t++;
940 }
941
942 {
943 auto response = std::make_unique<LoginReputationClientResponse>();
944 response->set_verdict_token("token2");
945 response->set_verdict_type(LoginReputationClientResponse::SAFE);
946 service_->MaybeLogPasswordReuseLookupEvent(
947 web_contents(), RequestOutcome::SUCCEEDED,
948 PasswordType::PRIMARY_ACCOUNT_PASSWORD, response.get());
949 ASSERT_EQ(t + 1, GetUserEventService()->GetRecordedUserEvents().size())
950 << t;
951 PasswordReuseLookup reuse_lookup = GetUserEventService()
952 ->GetRecordedUserEvents()[t]
953 .gaia_password_reuse_event()
954 .reuse_lookup();
955 EXPECT_EQ(PasswordReuseLookup::REQUEST_SUCCESS,
956 reuse_lookup.lookup_result())
957 << t;
958 EXPECT_EQ(PasswordReuseLookup::SAFE, reuse_lookup.verdict()) << t;
959 EXPECT_EQ("token2", reuse_lookup.verdict_token()) << t;
960 t++;
961 }
962 }
963
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetDefaultChangePasswordURL)964 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetDefaultChangePasswordURL) {
965 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
966 SetUpSyncAccount("example.com", account_info);
967 EXPECT_EQ(GURL("https://accounts.google.com/"
968 "AccountChooser?Email=foo%40example.com&continue=https%3A%2F%"
969 "2Fmyaccount.google.com%2Fsigninoptions%2Fpassword%3Futm_"
970 "source%3DGoogle%26utm_campaign%3DPhishGuard&hl=en"),
971 service_->GetDefaultChangePasswordURL());
972 }
973
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetEnterprisePasswordURL)974 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetEnterprisePasswordURL) {
975 // If enterprise change password url is not set. This will return the default
976 // GAIA change password url.
977 EXPECT_TRUE(service_->GetEnterpriseChangePasswordURL().DomainIs(
978 "accounts.google.com"));
979
980 // Sets enterprise change password url.
981 GURL enterprise_change_password_url("https://changepassword.example.com");
982 profile()->GetPrefs()->SetString(prefs::kPasswordProtectionChangePasswordURL,
983 enterprise_change_password_url.spec());
984 EXPECT_EQ(enterprise_change_password_url,
985 service_->GetEnterpriseChangePasswordURL());
986 }
987
TEST_F(ChromePasswordProtectionServiceTest,VerifyNavigationDuringPasswordOnFocusPingNotBlocked)988 TEST_F(ChromePasswordProtectionServiceTest,
989 VerifyNavigationDuringPasswordOnFocusPingNotBlocked) {
990 GURL trigger_url(kPhishingURL);
991 NavigateAndCommit(trigger_url);
992 PrepareRequest(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
993 PasswordType::PASSWORD_TYPE_UNKNOWN,
994 /*is_warning_showing=*/false);
995 GURL redirect_url(kRedirectURL);
996 content::MockNavigationHandle test_handle(redirect_url, main_rfh());
997 std::unique_ptr<PasswordProtectionNavigationThrottle> throttle =
998 service_->MaybeCreateNavigationThrottle(&test_handle);
999 EXPECT_EQ(nullptr, throttle);
1000 }
1001
TEST_F(ChromePasswordProtectionServiceTest,VerifyNavigationDuringPasswordReusePingDeferred)1002 TEST_F(ChromePasswordProtectionServiceTest,
1003 VerifyNavigationDuringPasswordReusePingDeferred) {
1004 GURL trigger_url(kPhishingURL);
1005 NavigateAndCommit(trigger_url);
1006 service_->SetIsSyncing(true);
1007 service_->SetIsAccountSignedIn(true);
1008
1009 // Simulate a on-going password reuse request that hasn't received
1010 // verdict yet.
1011 PrepareRequest(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1012 PasswordType::SAVED_PASSWORD,
1013 /*is_warning_showing=*/false);
1014
1015 GURL redirect_url(kRedirectURL);
1016 bool was_navigation_resumed = false;
1017 content::MockNavigationHandle test_handle(redirect_url, main_rfh());
1018 std::unique_ptr<PasswordProtectionNavigationThrottle> throttle =
1019 service_->MaybeCreateNavigationThrottle(&test_handle);
1020 ASSERT_NE(nullptr, throttle);
1021 throttle->set_resume_callback_for_testing(
1022 base::BindLambdaForTesting([&]() { was_navigation_resumed = true; }));
1023
1024 // Verify navigation get deferred.
1025 EXPECT_EQ(content::NavigationThrottle::DEFER, throttle->WillStartRequest());
1026 base::RunLoop().RunUntilIdle();
1027
1028 // Simulate receiving a SAFE verdict.
1029 SimulateRequestFinished(LoginReputationClientResponse::SAFE);
1030 base::RunLoop().RunUntilIdle();
1031 EXPECT_EQ(true, was_navigation_resumed);
1032
1033 // Verify that navigation can be resumed.
1034 EXPECT_EQ(content::NavigationThrottle::PROCEED,
1035 throttle->WillProcessResponse());
1036 }
1037
TEST_F(ChromePasswordProtectionServiceTest,VerifyNavigationDuringModalWarningCanceled)1038 TEST_F(ChromePasswordProtectionServiceTest,
1039 VerifyNavigationDuringModalWarningCanceled) {
1040 GURL trigger_url(kPhishingURL);
1041 NavigateAndCommit(trigger_url);
1042 // Simulate a password reuse request, whose verdict is triggering a modal
1043 // warning.
1044 PrepareRequest(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1045 PasswordType::PRIMARY_ACCOUNT_PASSWORD,
1046 /*is_warning_showing=*/true);
1047 base::RunLoop().RunUntilIdle();
1048
1049 // Simulate receiving a phishing verdict.
1050 SimulateRequestFinished(LoginReputationClientResponse::PHISHING);
1051 base::RunLoop().RunUntilIdle();
1052
1053 GURL redirect_url(kRedirectURL);
1054 content::MockNavigationHandle test_handle(redirect_url, main_rfh());
1055 std::unique_ptr<PasswordProtectionNavigationThrottle> throttle =
1056 service_->MaybeCreateNavigationThrottle(&test_handle);
1057
1058 // Verify that navigation gets canceled.
1059 EXPECT_EQ(content::NavigationThrottle::CANCEL, throttle->WillStartRequest());
1060 }
1061
TEST_F(ChromePasswordProtectionServiceTest,VerifyNavigationThrottleRemovedWhenNavigationHandleIsGone)1062 TEST_F(ChromePasswordProtectionServiceTest,
1063 VerifyNavigationThrottleRemovedWhenNavigationHandleIsGone) {
1064 GURL trigger_url(kPhishingURL);
1065 NavigateAndCommit(trigger_url);
1066 service_->SetIsSyncing(true);
1067 service_->SetIsAccountSignedIn(true);
1068 // Simulate a on-going password reuse request that hasn't received
1069 // verdict yet.
1070 PrepareRequest(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1071 PasswordType::SAVED_PASSWORD,
1072 /*is_warning_showing=*/false);
1073
1074 GURL redirect_url(kRedirectURL);
1075 content::MockNavigationHandle test_handle(redirect_url, main_rfh());
1076 std::unique_ptr<PasswordProtectionNavigationThrottle> throttle =
1077 service_->MaybeCreateNavigationThrottle(&test_handle);
1078
1079 // Verify navigation get deferred.
1080 EXPECT_EQ(content::NavigationThrottle::DEFER, throttle->WillStartRequest());
1081
1082 EXPECT_EQ(1u, GetNumberOfNavigationThrottles());
1083
1084 // Simulate the deletion of the PasswordProtectionNavigationThrottle.
1085 throttle.reset();
1086 base::RunLoop().RunUntilIdle();
1087
1088 // Expect no navigation throttle kept by |request_|.
1089 EXPECT_EQ(0u, GetNumberOfNavigationThrottles());
1090
1091 // Simulate receiving a SAFE verdict.
1092 SimulateRequestFinished(LoginReputationClientResponse::SAFE);
1093 base::RunLoop().RunUntilIdle();
1094 }
1095
TEST_F(ChromePasswordProtectionServiceTest,VerifyUnhandledSyncPasswordReuseUponClearHistoryDeletion)1096 TEST_F(ChromePasswordProtectionServiceTest,
1097 VerifyUnhandledSyncPasswordReuseUponClearHistoryDeletion) {
1098 ASSERT_EQ(0, GetSizeofUnhandledSyncPasswordReuses());
1099 GURL url_a("https://www.phishinga.com");
1100 GURL url_b("https://www.phishingb.com");
1101 GURL url_c("https://www.phishingc.com");
1102
1103 DictionaryPrefUpdate update(profile()->GetPrefs(),
1104 prefs::kSafeBrowsingUnhandledGaiaPasswordReuses);
1105 update->SetKey(Origin::Create(url_a).Serialize(),
1106 base::Value("navigation_id_a"));
1107 update->SetKey(Origin::Create(url_b).Serialize(),
1108 base::Value("navigation_id_b"));
1109 update->SetKey(Origin::Create(url_c).Serialize(),
1110 base::Value("navigation_id_c"));
1111
1112 // Delete a https://www.phishinga.com URL.
1113 history::URLRows deleted_urls;
1114 deleted_urls.push_back(history::URLRow(url_a));
1115 deleted_urls.push_back(history::URLRow(GURL("https://www.notinlist.com")));
1116
1117 service_->RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
1118 /*all_history=*/false, deleted_urls);
1119 base::RunLoop().RunUntilIdle();
1120 EXPECT_EQ(2, GetSizeofUnhandledSyncPasswordReuses());
1121
1122 service_->RemoveUnhandledSyncPasswordReuseOnURLsDeleted(
1123 /*all_history=*/true, {});
1124 EXPECT_EQ(0, GetSizeofUnhandledSyncPasswordReuses());
1125 }
1126
1127 // The following tests are disabled on Android, because enterprise reporting
1128 // extension is not supported.
1129 #if !defined(OS_ANDROID)
TEST_F(ChromePasswordProtectionServiceTest,VerifyOnPolicySpecifiedPasswordChangedEvent)1130 TEST_F(ChromePasswordProtectionServiceTest,
1131 VerifyOnPolicySpecifiedPasswordChangedEvent) {
1132 TestExtensionEventObserver event_observer(test_event_router_);
1133
1134 // Preparing sync account.
1135 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
1136 SetUpSyncAccount("example.com", account_info);
1137 service_->SetIsAccountSignedIn(true);
1138
1139 // Simulates change password.
1140 service_->OnGaiaPasswordChanged("foo@example.com", false);
1141 base::RunLoop().RunUntilIdle();
1142
1143 ASSERT_EQ(1, test_event_router_->GetEventCount(
1144 OnPolicySpecifiedPasswordChanged::kEventName));
1145
1146 auto captured_args = event_observer.PassEventArgs().GetList()[0].Clone();
1147 EXPECT_EQ("foo@example.com", captured_args.GetString());
1148
1149 // If user is in incognito mode, no event should be sent.
1150 service_->ConfigService(true /*incognito*/, false /*SBER*/);
1151 service_->OnGaiaPasswordChanged("foo@example.com", false);
1152 base::RunLoop().RunUntilIdle();
1153 // Event count should be unchanged.
1154 EXPECT_EQ(1, test_event_router_->GetEventCount(
1155 OnPolicySpecifiedPasswordChanged::kEventName));
1156 }
1157
TEST_F(ChromePasswordProtectionServiceTest,VerifyTriggerOnPolicySpecifiedPasswordReuseDetectedForGsuiteUser)1158 TEST_F(ChromePasswordProtectionServiceTest,
1159 VerifyTriggerOnPolicySpecifiedPasswordReuseDetectedForGsuiteUser) {
1160 TestExtensionEventObserver event_observer(test_event_router_);
1161 CoreAccountInfo account_info = SetPrimaryAccount(kTestEmail);
1162 SetUpSyncAccount("example.com", account_info);
1163 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1164 PASSWORD_REUSE);
1165 NavigateAndCommit(GURL(kPasswordReuseURL));
1166
1167 service_->MaybeReportPasswordReuseDetected(web_contents(), kUserName,
1168 PasswordType::ENTERPRISE_PASSWORD,
1169 /*is_phishing_url =*/true);
1170 base::RunLoop().RunUntilIdle();
1171
1172 ASSERT_EQ(1, test_event_router_->GetEventCount(
1173 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1174 auto captured_args = event_observer.PassEventArgs().GetList()[0].Clone();
1175 EXPECT_EQ(kPasswordReuseURL, captured_args.FindKey("url")->GetString());
1176 EXPECT_EQ(kUserName, captured_args.FindKey("userName")->GetString());
1177 EXPECT_TRUE(captured_args.FindKey("isPhishingUrl")->GetBool());
1178
1179 // If the reused password is not Enterprise password but the account is
1180 // GSuite, event should be sent.
1181 service_->SetAccountInfo(kUserName);
1182 service_->SetIsAccountSignedIn(true);
1183 service_->MaybeReportPasswordReuseDetected(web_contents(), kUserName,
1184 PasswordType::OTHER_GAIA_PASSWORD,
1185 /*is_phishing_url =*/true);
1186 base::RunLoop().RunUntilIdle();
1187
1188 ASSERT_EQ(2, test_event_router_->GetEventCount(
1189 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1190
1191 // If no password is used , no event should be sent.
1192 service_->MaybeReportPasswordReuseDetected(
1193 web_contents(), kUserName, PasswordType::PASSWORD_TYPE_UNKNOWN,
1194 /*is_phishing_url =*/true);
1195 base::RunLoop().RunUntilIdle();
1196 EXPECT_EQ(2, test_event_router_->GetEventCount(
1197 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1198
1199 // If user is in incognito mode, no event should be sent.
1200 service_->ConfigService(true /*incognito*/, false /*SBER*/);
1201 service_->MaybeReportPasswordReuseDetected(web_contents(), kUserName,
1202 PasswordType::ENTERPRISE_PASSWORD,
1203 /*is_phishing_url =*/true);
1204 base::RunLoop().RunUntilIdle();
1205 EXPECT_EQ(2, test_event_router_->GetEventCount(
1206 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1207 }
1208
TEST_F(ChromePasswordProtectionServiceTest,VerifyTriggerOnPolicySpecifiedPasswordReuseDetectedForGmailUser)1209 TEST_F(ChromePasswordProtectionServiceTest,
1210 VerifyTriggerOnPolicySpecifiedPasswordReuseDetectedForGmailUser) {
1211 TestExtensionEventObserver event_observer(test_event_router_);
1212
1213 // If user is a Gmail user and enterprise password is used, event should be
1214 // sent.
1215 CoreAccountInfo gmail_account_info = SetPrimaryAccount(kTestGmail);
1216 SetUpSyncAccount(kNoHostedDomainFound, gmail_account_info);
1217 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1218 PASSWORD_REUSE);
1219 NavigateAndCommit(GURL(kPasswordReuseURL));
1220
1221 service_->MaybeReportPasswordReuseDetected(web_contents(), kUserName,
1222 PasswordType::ENTERPRISE_PASSWORD,
1223 /*is_phishing_url =*/true);
1224 base::RunLoop().RunUntilIdle();
1225 EXPECT_EQ(1, test_event_router_->GetEventCount(
1226 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1227
1228 // If user is a Gmail user and not an enterprise password is used , no event
1229 // should be sent.
1230 service_->MaybeReportPasswordReuseDetected(web_contents(), kUserName,
1231 PasswordType::OTHER_GAIA_PASSWORD,
1232 /*is_phishing_url =*/true);
1233 base::RunLoop().RunUntilIdle();
1234 EXPECT_EQ(1, test_event_router_->GetEventCount(
1235 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1236
1237 // If user is a Gmail user and no password is used , no event should be sent.
1238 service_->MaybeReportPasswordReuseDetected(
1239 web_contents(), kUserName, PasswordType::PASSWORD_TYPE_UNKNOWN,
1240 /*is_phishing_url =*/true);
1241 base::RunLoop().RunUntilIdle();
1242 EXPECT_EQ(1, test_event_router_->GetEventCount(
1243 OnPolicySpecifiedPasswordReuseDetected::kEventName));
1244 }
1245 #endif
1246
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetWarningDetailTextSaved)1247 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetWarningDetailTextSaved) {
1248 base::string16 warning_text =
1249 l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED);
1250 ReusedPasswordAccountType reused_password_type;
1251 reused_password_type.set_account_type(
1252 ReusedPasswordAccountType::SAVED_PASSWORD);
1253 std::vector<size_t> placeholder_offsets;
1254 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1255 &placeholder_offsets));
1256 }
1257
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetWarningDetailTextSavedDomains)1258 TEST_F(ChromePasswordProtectionServiceTest,
1259 VerifyGetWarningDetailTextSavedDomains) {
1260 base::test::ScopedFeatureList feature_list;
1261 feature_list.InitWithFeaturesAndParameters(
1262 {}, {password_manager::features::kPasswordCheck});
1263 ReusedPasswordAccountType reused_password_type;
1264 reused_password_type.set_account_type(
1265 ReusedPasswordAccountType::SAVED_PASSWORD);
1266 std::vector<std::string> domains{"www.example.com"};
1267 service_->set_saved_passwords_matching_domains(domains);
1268 base::string16 warning_text = l10n_util::GetStringFUTF16(
1269 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED_1_DOMAIN,
1270 base::UTF8ToUTF16(domains[0]));
1271 std::vector<size_t> placeholder_offsets;
1272 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1273 &placeholder_offsets));
1274 // GetWarningDetailText shouldCall GetWarningDetailTextForSavedPasswords, so
1275 // we can check to see if the placeholder offset values are the same.
1276 // Hardcoding the offset in the expected value cannot be done as it's
1277 // different for different OS.
1278 std::vector<size_t> expected_placeholder_offsets;
1279 service_->GetWarningDetailTextForSavedPasswords(
1280 &expected_placeholder_offsets);
1281 EXPECT_EQ(expected_placeholder_offsets, placeholder_offsets);
1282
1283 placeholder_offsets.clear();
1284 domains.push_back("www.2.example.com");
1285 service_->set_saved_passwords_matching_domains(domains);
1286 warning_text = l10n_util::GetStringFUTF16(
1287 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED_2_DOMAINS,
1288 base::UTF8ToUTF16(domains[0]), base::UTF8ToUTF16(domains[1]));
1289 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1290 &placeholder_offsets));
1291 expected_placeholder_offsets.clear();
1292 service_->GetWarningDetailTextForSavedPasswords(
1293 &expected_placeholder_offsets);
1294 EXPECT_EQ(expected_placeholder_offsets, placeholder_offsets);
1295
1296 placeholder_offsets.clear();
1297 domains.push_back("www.3.example.com");
1298 service_->set_saved_passwords_matching_domains(domains);
1299 warning_text = l10n_util::GetStringFUTF16(
1300 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED_3_DOMAINS,
1301 base::UTF8ToUTF16(domains[0]), base::UTF8ToUTF16(domains[1]),
1302 base::UTF8ToUTF16(domains[2]));
1303 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1304 &placeholder_offsets));
1305 expected_placeholder_offsets.clear();
1306 service_->GetWarningDetailTextForSavedPasswords(
1307 &expected_placeholder_offsets);
1308 EXPECT_EQ(expected_placeholder_offsets, placeholder_offsets);
1309
1310 // Default domains should be prioritzed over other domains.
1311 placeholder_offsets.clear();
1312 domains.push_back("yahoo.com");
1313 service_->set_saved_passwords_matching_domains(domains);
1314 warning_text = l10n_util::GetStringFUTF16(
1315 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED_3_DOMAINS,
1316 base::UTF8ToUTF16("yahoo.com"), base::UTF8ToUTF16(domains[0]),
1317 base::UTF8ToUTF16(domains[1]));
1318 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1319 &placeholder_offsets));
1320 expected_placeholder_offsets.clear();
1321 service_->GetWarningDetailTextForSavedPasswords(
1322 &expected_placeholder_offsets);
1323 EXPECT_EQ(expected_placeholder_offsets, placeholder_offsets);
1324
1325 // Matching domains that have a suffix of a default domains should be
1326 // prioritzed over other non common spoofed domains.
1327 placeholder_offsets.clear();
1328 domains.push_back("login.amazon.com");
1329 service_->set_saved_passwords_matching_domains(domains);
1330 warning_text = l10n_util::GetStringFUTF16(
1331 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SAVED_3_DOMAINS,
1332 base::UTF8ToUTF16("yahoo.com"), base::UTF8ToUTF16("login.amazon.com"),
1333 base::UTF8ToUTF16(domains[0]));
1334 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1335 &placeholder_offsets));
1336 expected_placeholder_offsets.clear();
1337 service_->GetWarningDetailTextForSavedPasswords(
1338 &expected_placeholder_offsets);
1339 EXPECT_EQ(expected_placeholder_offsets, placeholder_offsets);
1340 }
1341
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetWarningDetailTextCheckSavedDomains)1342 TEST_F(ChromePasswordProtectionServiceTest,
1343 VerifyGetWarningDetailTextCheckSavedDomains) {
1344 base::test::ScopedFeatureList feature_list;
1345 feature_list.InitAndEnableFeature(password_manager::features::kPasswordCheck);
1346 ReusedPasswordAccountType reused_password_type;
1347 reused_password_type.set_account_type(
1348 ReusedPasswordAccountType::SAVED_PASSWORD);
1349 std::vector<std::string> domains{"www.example.com"};
1350 service_->set_saved_passwords_matching_domains(domains);
1351 base::string16 warning_text = l10n_util::GetStringFUTF16(
1352 IDS_PAGE_INFO_CHECK_PASSWORD_DETAILS_SAVED_1_DOMAIN,
1353 base::UTF8ToUTF16(domains[0]));
1354 std::vector<size_t> placeholder_offsets;
1355 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1356 &placeholder_offsets));
1357
1358 placeholder_offsets.clear();
1359 domains.push_back("www.2.example.com");
1360 service_->set_saved_passwords_matching_domains(domains);
1361 warning_text = l10n_util::GetStringFUTF16(
1362 IDS_PAGE_INFO_CHECK_PASSWORD_DETAILS_SAVED_2_DOMAIN,
1363 base::UTF8ToUTF16(domains[0]), base::UTF8ToUTF16(domains[1]));
1364 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1365 &placeholder_offsets));
1366
1367 placeholder_offsets.clear();
1368 domains.push_back("www.3.example.com");
1369 service_->set_saved_passwords_matching_domains(domains);
1370 warning_text = l10n_util::GetStringFUTF16(
1371 IDS_PAGE_INFO_CHECK_PASSWORD_DETAILS_SAVED_3_DOMAIN,
1372 base::UTF8ToUTF16(domains[0]), base::UTF8ToUTF16(domains[1]),
1373 base::UTF8ToUTF16(domains[2]));
1374 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1375 &placeholder_offsets));
1376 // Default domains should be prioritzed over other domains.
1377 placeholder_offsets.clear();
1378 domains.push_back("amazon.com");
1379 service_->set_saved_passwords_matching_domains(domains);
1380 warning_text = l10n_util::GetStringFUTF16(
1381 IDS_PAGE_INFO_CHECK_PASSWORD_DETAILS_SAVED_3_DOMAIN,
1382 base::UTF8ToUTF16("amazon.com"), base::UTF8ToUTF16(domains[0]),
1383 base::UTF8ToUTF16(domains[1]));
1384 EXPECT_EQ(warning_text, service_->GetWarningDetailText(reused_password_type,
1385 &placeholder_offsets));
1386 }
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetPlaceholdersForSavedPasswordWarningText)1387 TEST_F(ChromePasswordProtectionServiceTest,
1388 VerifyGetPlaceholdersForSavedPasswordWarningText) {
1389 std::vector<std::string> domains{"www.example.com"};
1390 domains.push_back("www.2.example.com");
1391 domains.push_back("www.3.example.com");
1392 domains.push_back("amazon.com");
1393 service_->set_saved_passwords_matching_domains(domains);
1394 // Default domains should be prioritzed over other domains.
1395 std::vector<base::string16> expected_placeholders{
1396 base::UTF8ToUTF16("amazon.com"), base::UTF8ToUTF16(domains[0]),
1397 base::UTF8ToUTF16(domains[1])};
1398 EXPECT_EQ(expected_placeholders,
1399 service_->GetPlaceholdersForSavedPasswordWarningText());
1400 }
1401
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetWarningDetailTextEnterprise)1402 TEST_F(ChromePasswordProtectionServiceTest,
1403 VerifyGetWarningDetailTextEnterprise) {
1404 base::string16 warning_text_non_sync = l10n_util::GetStringUTF16(
1405 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SIGNED_IN_NON_SYNC);
1406 base::string16 generic_enterprise_warning_text = l10n_util::GetStringUTF16(
1407 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_ENTERPRISE);
1408 base::string16 warning_text_with_org_name = l10n_util::GetStringFUTF16(
1409 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_ENTERPRISE_WITH_ORG_NAME,
1410 base::UTF8ToUTF16("example.com"));
1411 base::string16 warning_text_sync =
1412 l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SYNC);
1413
1414 ReusedPasswordAccountType reused_password_type;
1415 reused_password_type.set_account_type(ReusedPasswordAccountType::GMAIL);
1416 reused_password_type.set_is_account_syncing(true);
1417 std::vector<size_t> placeholder_offsets;
1418 EXPECT_EQ(warning_text_sync, service_->GetWarningDetailText(
1419 reused_password_type, &placeholder_offsets));
1420 reused_password_type.set_account_type(
1421 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
1422 reused_password_type.set_is_account_syncing(false);
1423 EXPECT_EQ(generic_enterprise_warning_text,
1424 service_->GetWarningDetailText(reused_password_type,
1425 &placeholder_offsets));
1426 {
1427 base::test::ScopedFeatureList feature_list;
1428 feature_list.InitAndEnableFeature(
1429 safe_browsing::kPasswordProtectionForSignedInUsers);
1430 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1431 EXPECT_EQ(warning_text_non_sync,
1432 service_->GetWarningDetailText(reused_password_type,
1433 &placeholder_offsets));
1434 }
1435
1436 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1437 reused_password_type.set_is_account_syncing(true);
1438 CoreAccountInfo core_account_info = SetPrimaryAccount(kTestEmail);
1439 SetUpSyncAccount(std::string("example.com"), core_account_info);
1440 EXPECT_EQ(warning_text_with_org_name,
1441 service_->GetWarningDetailText(reused_password_type,
1442 &placeholder_offsets));
1443 reused_password_type.set_account_type(
1444 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
1445 EXPECT_EQ(generic_enterprise_warning_text,
1446 service_->GetWarningDetailText(reused_password_type,
1447 &placeholder_offsets));
1448 }
1449
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetWarningDetailTextGmail)1450 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetWarningDetailTextGmail) {
1451 base::string16 warning_text_non_sync = l10n_util::GetStringUTF16(
1452 IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SIGNED_IN_NON_SYNC);
1453 base::string16 warning_text_sync =
1454 l10n_util::GetStringUTF16(IDS_PAGE_INFO_CHANGE_PASSWORD_DETAILS_SYNC);
1455
1456 base::test::ScopedFeatureList feature_list;
1457 feature_list.InitAndEnableFeature(
1458 safe_browsing::kPasswordProtectionForSignedInUsers);
1459 ReusedPasswordAccountType reused_password_type;
1460 reused_password_type.set_account_type(ReusedPasswordAccountType::GMAIL);
1461 std::vector<size_t> placeholder_offsets;
1462 EXPECT_EQ(warning_text_non_sync,
1463 service_->GetWarningDetailText(reused_password_type,
1464 &placeholder_offsets));
1465 reused_password_type.set_is_account_syncing(true);
1466 EXPECT_EQ(warning_text_sync, service_->GetWarningDetailText(
1467 reused_password_type, &placeholder_offsets));
1468 }
1469
TEST_F(ChromePasswordProtectionServiceTest,VerifyCanShowInterstitial)1470 TEST_F(ChromePasswordProtectionServiceTest, VerifyCanShowInterstitial) {
1471 // Do not show interstitial if policy not set for password_alert.
1472 ASSERT_FALSE(
1473 profile()->GetPrefs()->HasPrefPath(prefs::kSafeBrowsingWhitelistDomains));
1474 GURL trigger_url = GURL(kPhishingURL);
1475 ReusedPasswordAccountType reused_password_type;
1476 reused_password_type.set_account_type(
1477 ReusedPasswordAccountType::SAVED_PASSWORD);
1478 EXPECT_FALSE(
1479 service_->CanShowInterstitial(reused_password_type, trigger_url));
1480 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1481 reused_password_type.set_is_account_syncing(true);
1482 EXPECT_FALSE(
1483 service_->CanShowInterstitial(reused_password_type, trigger_url));
1484 {
1485 base::test::ScopedFeatureList feature_list;
1486 feature_list.InitAndEnableFeature(
1487 safe_browsing::kPasswordProtectionForSignedInUsers);
1488 service_->SetAccountInfo(kUserName);
1489 reused_password_type.set_is_account_syncing(false);
1490 EXPECT_FALSE(
1491 service_->CanShowInterstitial(reused_password_type, trigger_url));
1492 }
1493
1494 reused_password_type.set_account_type(
1495 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
1496 reused_password_type.set_is_account_syncing(false);
1497 EXPECT_FALSE(
1498 service_->CanShowInterstitial(reused_password_type, trigger_url));
1499 reused_password_type.set_account_type(
1500 ReusedPasswordAccountType::SAVED_PASSWORD);
1501 EXPECT_FALSE(
1502 service_->CanShowInterstitial(reused_password_type, trigger_url));
1503 // Show interstitial if user is a syncing GSuite user and the policy is set to
1504 // password_alert.
1505 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1506 reused_password_type.set_is_account_syncing(true);
1507 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1508 PASSWORD_REUSE);
1509 EXPECT_TRUE(service_->CanShowInterstitial(reused_password_type, trigger_url));
1510 {
1511 base::test::ScopedFeatureList feature_list;
1512 feature_list.InitAndEnableFeature(
1513 safe_browsing::kPasswordProtectionForSignedInUsers);
1514 service_->SetAccountInfo(kUserName);
1515 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1516 reused_password_type.set_is_account_syncing(false);
1517 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1518 PASSWORD_REUSE);
1519 EXPECT_TRUE(
1520 service_->CanShowInterstitial(reused_password_type, trigger_url));
1521 }
1522 // Show interstitial if user is a Enterprise user and the policy is set to
1523 // password_alert.
1524 reused_password_type.set_account_type(
1525 ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
1526 reused_password_type.set_is_account_syncing(false);
1527 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1528 PASSWORD_REUSE);
1529 EXPECT_TRUE(service_->CanShowInterstitial(reused_password_type, trigger_url));
1530
1531 // Add |trigger_url| to enterprise whitelist.
1532 base::ListValue whitelisted_domains;
1533 whitelisted_domains.AppendString(trigger_url.host());
1534 profile()->GetPrefs()->Set(prefs::kSafeBrowsingWhitelistDomains,
1535 whitelisted_domains);
1536 reused_password_type.set_account_type(
1537 ReusedPasswordAccountType::SAVED_PASSWORD);
1538 reused_password_type.set_is_account_syncing(false);
1539 EXPECT_FALSE(
1540 service_->CanShowInterstitial(reused_password_type, trigger_url));
1541 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1542 reused_password_type.set_is_account_syncing(true);
1543 EXPECT_FALSE(
1544 service_->CanShowInterstitial(reused_password_type, trigger_url));
1545 }
1546
TEST_F(ChromePasswordProtectionServiceTest,VerifySendsPingForAboutBlank)1547 TEST_F(ChromePasswordProtectionServiceTest, VerifySendsPingForAboutBlank) {
1548 ReusedPasswordAccountType reused_password_type;
1549 reused_password_type.set_account_type(
1550 ReusedPasswordAccountType::SAVED_PASSWORD);
1551 service_->ConfigService(false /*incognito*/, true /*SBER*/);
1552 EXPECT_TRUE(
1553 service_->CanSendPing(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1554 GURL("about:blank"), reused_password_type));
1555 }
1556
TEST_F(ChromePasswordProtectionServiceTest,VerifyGetPingNotSentReason)1557 TEST_F(ChromePasswordProtectionServiceTest, VerifyGetPingNotSentReason) {
1558 {
1559 // SBER disabled.
1560 ReusedPasswordAccountType reused_password_type;
1561 service_->ConfigService(false /*incognito*/, false /*SBER*/);
1562 EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_USER_POPULATION,
1563 service_->GetPingNotSentReason(
1564 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
1565 GURL("about:blank"), reused_password_type));
1566 reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
1567 EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_USER_POPULATION,
1568 service_->GetPingNotSentReason(
1569 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1570 GURL("about:blank"), reused_password_type));
1571 }
1572 {
1573 // In Incognito.
1574 ReusedPasswordAccountType reused_password_type;
1575 service_->ConfigService(true /*incognito*/, true /*SBER*/);
1576 EXPECT_EQ(RequestOutcome::DISABLED_DUE_TO_INCOGNITO,
1577 service_->GetPingNotSentReason(
1578 LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
1579 GURL("about:blank"), reused_password_type));
1580 }
1581 {
1582 // Turned off by admin.
1583 ReusedPasswordAccountType reused_password_type;
1584 service_->ConfigService(false /*incognito*/, false /*SBER*/);
1585 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1586 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1587 PASSWORD_PROTECTION_OFF);
1588 EXPECT_EQ(RequestOutcome::TURNED_OFF_BY_ADMIN,
1589 service_->GetPingNotSentReason(
1590 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1591 GURL("about:blank"), reused_password_type));
1592 }
1593 {
1594 // Whitelisted by policy.
1595 ReusedPasswordAccountType reused_password_type;
1596 service_->ConfigService(false /*incognito*/, false /*SBER*/);
1597 reused_password_type.set_account_type(ReusedPasswordAccountType::GSUITE);
1598 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1599 PHISHING_REUSE);
1600 base::ListValue whitelist;
1601 whitelist.AppendString("mydomain.com");
1602 whitelist.AppendString("mydomain.net");
1603 profile()->GetPrefs()->Set(prefs::kSafeBrowsingWhitelistDomains, whitelist);
1604 EXPECT_EQ(RequestOutcome::MATCHED_ENTERPRISE_WHITELIST,
1605 service_->GetPingNotSentReason(
1606 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1607 GURL("https://www.mydomain.com"), reused_password_type));
1608 }
1609 {
1610 // Password alert mode.
1611 ReusedPasswordAccountType reused_password_type;
1612 service_->ConfigService(false /*incognito*/, false /*SBER*/);
1613 reused_password_type.set_account_type(ReusedPasswordAccountType::UNKNOWN);
1614 profile()->GetPrefs()->SetInteger(prefs::kPasswordProtectionWarningTrigger,
1615 PASSWORD_REUSE);
1616 EXPECT_EQ(RequestOutcome::PASSWORD_ALERT_MODE,
1617 service_->GetPingNotSentReason(
1618 LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
1619 GURL("about:blank"), reused_password_type));
1620 }
1621 }
1622
1623 namespace {
1624
1625 class ChromePasswordProtectionServiceWithAccountPasswordStoreTest
1626 : public ChromePasswordProtectionServiceTest {
1627 public:
ChromePasswordProtectionServiceWithAccountPasswordStoreTest()1628 ChromePasswordProtectionServiceWithAccountPasswordStoreTest() {
1629 feature_list_.InitAndEnableFeature(
1630 password_manager::features::kEnablePasswordsAccountStorage);
1631 }
1632
1633 private:
1634 base::test::ScopedFeatureList feature_list_;
1635 };
1636
TEST_F(ChromePasswordProtectionServiceWithAccountPasswordStoreTest,VerifyPersistPhishedSavedPasswordCredential)1637 TEST_F(ChromePasswordProtectionServiceWithAccountPasswordStoreTest,
1638 VerifyPersistPhishedSavedPasswordCredential) {
1639 service_->ConfigService(/*is_incognito=*/false,
1640 /*is_extended_reporting=*/true);
1641 std::vector<password_manager::MatchingReusedCredential> credentials = {
1642 {.signon_realm = "http://example.test",
1643 .in_store = password_manager::PasswordForm::Store::kAccountStore},
1644 {.signon_realm = "http://2.example.test",
1645 .in_store = password_manager::PasswordForm::Store::kAccountStore}};
1646
1647 EXPECT_CALL(*account_password_store_, AddCompromisedCredentialsImpl(_))
1648 .Times(2);
1649 service_->PersistPhishedSavedPasswordCredential(credentials);
1650 }
1651
TEST_F(ChromePasswordProtectionServiceWithAccountPasswordStoreTest,VerifyRemovePhishedSavedPasswordCredential)1652 TEST_F(ChromePasswordProtectionServiceWithAccountPasswordStoreTest,
1653 VerifyRemovePhishedSavedPasswordCredential) {
1654 service_->ConfigService(/*is_incognito=*/false,
1655 /*is_extended_reporting=*/true);
1656 std::vector<password_manager::MatchingReusedCredential> credentials = {
1657 {"http://example.test", base::ASCIIToUTF16("username1"),
1658 password_manager::PasswordForm::Store::kAccountStore},
1659 {"http://2.example.test", base::ASCIIToUTF16("username2"),
1660 password_manager::PasswordForm::Store::kAccountStore}};
1661
1662 EXPECT_CALL(*account_password_store_,
1663 RemoveCompromisedCredentialsImpl(
1664 _, _,
1665 password_manager::RemoveCompromisedCredentialsReason::
1666 kMarkSiteAsLegitimate))
1667 .Times(2);
1668 service_->RemovePhishedSavedPasswordCredential(credentials);
1669 }
1670
1671 } // namespace
1672
1673 } // namespace safe_browsing
1674