blob: e137b42c5a8099cb79d984fa17addc8f6e7500f9 [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/safe_browsing/content/password_protection/password_protection_service.h"
#include <memory>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/bind.h"
#include "base/test/gmock_move_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/null_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "build/build_config.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/password_manager/core/browser/password_manager_metrics_util.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/safe_browsing/content/common/safe_browsing.mojom-forward.h"
#include "components/safe_browsing/content/common/safe_browsing.mojom.h"
#include "components/safe_browsing/content/password_protection/mock_password_protection_service.h"
#include "components/safe_browsing/content/password_protection/password_protection_navigation_throttle.h"
#include "components/safe_browsing/content/password_protection/password_protection_request_content.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "components/safe_browsing/core/db/test_database_manager.h"
#include "components/safe_browsing/core/features.h"
#include "components/safe_browsing/core/password_protection/metrics_util.h"
#include "components/safe_browsing/core/password_protection/password_protection_request.h"
#include "components/safe_browsing/core/proto/csd.pb.h"
#include "components/safe_browsing/core/verdict_cache_manager.h"
#include "components/signin/public/identity_manager/account_info.h"
#include "components/signin/public/identity_manager/identity_test_environment.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_task_environment.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/system/message_pipe.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::ASCIIToUTF16;
using testing::_;
using testing::AnyNumber;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Mock;
using testing::Return;
using testing::StrictMock;
namespace safe_browsing {
namespace {
const char kFormActionUrl[] = "https://form_action.com/";
const char kPasswordFrameUrl[] = "https://password_frame.com/";
const char kSavedDomain[] = "http://saved_domain.com";
const char kSavedDomain2[] = "http://saved_domain2.com";
const char kTargetUrl[] = "http://foo.com/";
const char kUserName[] = "username";
const unsigned int kMinute = 60;
const unsigned int kDay = 24 * 60 * kMinute;
using PasswordReuseEvent = LoginReputationClientRequest::PasswordReuseEvent;
class MockSafeBrowsingTokenFetcher : public SafeBrowsingTokenFetcher {
public:
MockSafeBrowsingTokenFetcher() = default;
~MockSafeBrowsingTokenFetcher() override = default;
MOCK_METHOD1(Start, void(Callback));
MOCK_METHOD1(OnInvalidAccessToken, void(const std::string&));
private:
DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingTokenFetcher);
};
class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
public:
MockSafeBrowsingDatabaseManager() {}
MOCK_METHOD2(CheckCsdAllowlistUrl,
AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
protected:
~MockSafeBrowsingDatabaseManager() override {}
private:
DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
};
// PhishingDetector is not supported on Android.
#if !defined(OS_ANDROID)
class TestPhishingDetector : public mojom::PhishingDetector {
public:
TestPhishingDetector() : should_timeout_(false) {}
~TestPhishingDetector() override {}
void Bind(mojo::ScopedMessagePipeHandle handle) {
receiver_.Bind(
mojo::PendingReceiver<mojom::PhishingDetector>(std::move(handle)));
}
void SetPhishingModel(const std::string& model, base::File file) override {}
void SetPhishingFlatBufferModel(base::ReadOnlySharedMemoryRegion region,
base::File file) override {}
void StartPhishingDetection(
const GURL& url,
StartPhishingDetectionCallback callback) override {
if (should_timeout_) {
deferred_callbacks_.push_back(std::move(callback));
} else {
ReturnFeatures(url, std::move(callback));
}
}
void ReturnFeatures(const GURL& url,
StartPhishingDetectionCallback callback) {
ClientPhishingRequest verdict;
verdict.set_is_phishing(false);
verdict.set_client_score(0.1);
std::move(callback).Run(mojom::PhishingDetectorResult::SUCCESS,
verdict.SerializeAsString());
}
void set_should_timeout(bool timeout) { should_timeout_ = timeout; }
private:
bool should_timeout_;
std::vector<StartPhishingDetectionCallback> deferred_callbacks_;
mojo::Receiver<mojom::PhishingDetector> receiver_{this};
DISALLOW_COPY_AND_ASSIGN(TestPhishingDetector);
};
#endif
class TestPasswordProtectionService : public MockPasswordProtectionService {
public:
TestPasswordProtectionService(
const scoped_refptr<SafeBrowsingDatabaseManager>& database_manager,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
scoped_refptr<HostContentSettingsMap> content_setting_map,
PrefService* pref_service,
std::unique_ptr<SafeBrowsingTokenFetcher> token_fetcher,
bool is_off_the_record,
signin::IdentityManager* identity_manager,
bool try_token_fetch = true)
: MockPasswordProtectionService(database_manager,
url_loader_factory,
nullptr,
pref_service,
std::move(token_fetcher),
is_off_the_record,
identity_manager,
try_token_fetch),
cache_manager_(
std::make_unique<VerdictCacheManager>(nullptr,
content_setting_map.get())) {
cache_manager_->StopCleanUpTimerForTesting();
}
void RequestFinished(
PasswordProtectionRequest* request,
RequestOutcome outcome,
std::unique_ptr<LoginReputationClientResponse> response) override {
latest_request_ = request;
latest_response_ = std::move(response);
run_loop_.Quit();
}
LoginReputationClientResponse* latest_response() {
return latest_response_.get();
}
void WaitForResponse() { run_loop_.Run(); }
~TestPasswordProtectionService() override {}
size_t GetPendingRequestsCount() { return pending_requests_.size(); }
const LoginReputationClientRequest* GetLatestRequestProto() {
return latest_request_ ? latest_request_->request_proto() : nullptr;
}
#if !defined(OS_ANDROID)
void GetPhishingDetector(
service_manager::InterfaceProvider* provider,
mojo::Remote<mojom::PhishingDetector>* phishing_detector) override {
service_manager::InterfaceProvider::TestApi test_api(provider);
test_api.SetBinderForName(
mojom::PhishingDetector::Name_,
base::BindRepeating(&TestPhishingDetector::Bind,
base::Unretained(&test_phishing_detector_)));
provider->GetInterface(phishing_detector->BindNewPipeAndPassReceiver());
test_api.ClearBinderForName(mojom::PhishingDetector::Name_);
}
#endif
void CacheVerdict(const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordAccountType password_type,
const LoginReputationClientResponse& verdict,
const base::Time& receive_time) override {
if (!CanGetReputationOfURL(url) || IsIncognito())
return;
cache_manager_->CachePhishGuardVerdict(trigger_type, password_type, verdict,
receive_time);
}
LoginReputationClientResponse::VerdictType GetCachedVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordAccountType password_type,
LoginReputationClientResponse* out_response) override {
if (!url.is_valid() || !CanGetReputationOfURL(url))
return LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED;
return cache_manager_->GetCachedPhishGuardVerdict(
url, trigger_type, password_type, out_response);
}
int GetStoredVerdictCount(
LoginReputationClientRequest::TriggerType trigger_type) override {
return cache_manager_->GetStoredPhishGuardVerdictCount(trigger_type);
}
#if !defined(OS_ANDROID)
void SetDomFeatureCollectionTimeout(bool should_timeout) {
test_phishing_detector_.set_should_timeout(should_timeout);
}
#endif
private:
PasswordProtectionRequest* latest_request_;
base::RunLoop run_loop_;
std::unique_ptr<LoginReputationClientResponse> latest_response_;
#if !defined(OS_ANDROID)
TestPhishingDetector test_phishing_detector_;
#endif
// The TestPasswordProtectionService manages its own cache, rather than using
// the global one.
std::unique_ptr<VerdictCacheManager> cache_manager_;
DISALLOW_COPY_AND_ASSIGN(TestPasswordProtectionService);
};
} // namespace
class PasswordProtectionServiceTest : public ::testing::Test {
public:
PasswordProtectionServiceTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
protected:
void SetUp() override {
HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
content_setting_map_ = new HostContentSettingsMap(
&test_pref_service_, /*is_off_the_record=*/false,
/*store_last_modified=*/false, /*restore_session=*/false);
database_manager_ = new MockSafeBrowsingDatabaseManager();
password_protection_service_ =
std::make_unique<TestPasswordProtectionService>(
database_manager_,
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
content_setting_map_, nullptr, nullptr, false, nullptr, false);
web_contents_ =
base::WrapUnique(content::WebContentsTester::CreateTestWebContents(
content::WebContents::CreateParams(&browser_context_)));
const std::vector<password_manager::MatchingReusedCredential>
matching_reused_credentials = {};
request_ =
base::MakeRefCounted<safe_browsing::PasswordProtectionRequestContent>(
web_contents_.get(), GURL(kTargetUrl),
/*password_form_action=*/GURL(),
/*password_form_frame_url=*/GURL(),
web_contents_->GetContentsMimeType(), kUserName,
PasswordType::PASSWORD_TYPE_UNKNOWN, matching_reused_credentials,
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
/*password_field_exists=*/true, password_protection_service_.get(),
/*request_timeout_in_ms=*/10000);
}
void TearDown() override {
password_protection_service_.reset();
content_setting_map_->ShutdownOnUIThread();
}
size_t GetNumberOfNavigationThrottles() {
return request_ ? request_->throttles_.size() : 0u;
}
// |task_environment_| is needed here because this test involves both UI and
// IO threads.
content::BrowserTaskEnvironment task_environment_;
scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
sync_preferences::TestingPrefServiceSyncable test_pref_service_;
scoped_refptr<HostContentSettingsMap> content_setting_map_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<TestPasswordProtectionService> password_protection_service_;
content::TestBrowserContext browser_context_;
content::RenderViewHostTestEnabler rvh_test_enabler_;
std::unique_ptr<content::WebContents> web_contents_;
scoped_refptr<PasswordProtectionRequestContent> request_;
};
TEST_F(PasswordProtectionServiceTest,
VerifyNavigationThrottleNotRemovedWhenCanceledOnTimeout) {
request_->Start();
auto throttle = std::make_unique<PasswordProtectionNavigationThrottle>(
nullptr, request_, false);
EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
request_->Cancel(/*timed_out=*/true);
EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
}
TEST_F(PasswordProtectionServiceTest,
VerifyNavigationThrottleRemovedWhenCanceledNotOnTimeout) {
request_->Start();
auto throttle = std::make_unique<PasswordProtectionNavigationThrottle>(
nullptr, request_, false);
EXPECT_EQ(1U, GetNumberOfNavigationThrottles());
request_->Cancel(/*timed_out=*/false);
EXPECT_EQ(0U, GetNumberOfNavigationThrottles());
}
class PasswordProtectionServiceBaseTest
: public ::testing::TestWithParam<bool> {
public:
PasswordProtectionServiceBaseTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
LoginReputationClientResponse CreateVerdictProto(
LoginReputationClientResponse::VerdictType verdict,
int cache_duration_sec,
const std::string& cache_expression) {
LoginReputationClientResponse verdict_proto;
verdict_proto.set_verdict_type(verdict);
verdict_proto.set_cache_duration_sec(cache_duration_sec);
verdict_proto.set_cache_expression(cache_expression);
return verdict_proto;
}
void SetUp() override {
HostContentSettingsMap::RegisterProfilePrefs(test_pref_service_.registry());
safe_browsing::RegisterProfilePrefs(test_pref_service_.registry());
content_setting_map_ = new HostContentSettingsMap(
&test_pref_service_, false /* is_off_the_record */,
false /* store_last_modified */,
false /* restore_session*/);
database_manager_ = new MockSafeBrowsingDatabaseManager();
auto token_fetcher =
std::make_unique<StrictMock<MockSafeBrowsingTokenFetcher>>();
raw_token_fetcher_ = token_fetcher.get();
identity_test_env_.MakePrimaryAccountAvailable("user@gmail.com",
signin::ConsentLevel::kSync);
password_protection_service_ =
std::make_unique<TestPasswordProtectionService>(
database_manager_,
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
content_setting_map_, &test_pref_service_, std::move(token_fetcher),
/*is_off_the_record=*/false, identity_test_env_.identity_manager(),
/*try_token_fetch=*/true);
EXPECT_CALL(*password_protection_service_, IsExtendedReporting())
.WillRepeatedly(Return(GetParam()));
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_,
IsURLAllowlistedForPasswordEntry(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
url_ = PasswordProtectionServiceBase::GetPasswordProtectionRequestUrl();
}
void TearDown() override {
password_protection_service_.reset();
content_setting_map_->ShutdownOnUIThread();
}
// Sets up |database_manager_| and |pending_requests_| as needed.
void InitializeAndStartPasswordOnFocusRequest(
bool match_allowlist,
int timeout_in_ms,
content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdAllowlistUrl(target_url, _))
.WillRepeatedly(
Return(match_allowlist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequestContent(
web_contents, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
web_contents->GetContentsMimeType(), kUserName,
PasswordType::PASSWORD_TYPE_UNKNOWN, {},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true,
password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
void InitializeAndStartPasswordEntryRequest(
PasswordType type,
const std::vector<password_manager::MatchingReusedCredential>&
matching_reused_credentials,
bool match_allowlist,
int timeout_in_ms,
content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdAllowlistUrl(target_url, _))
.WillRepeatedly(
Return(match_allowlist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequestContent(
web_contents, target_url, GURL(), GURL(),
web_contents->GetContentsMimeType(), kUserName, type,
matching_reused_credentials,
LoginReputationClientRequest::PASSWORD_REUSE_EVENT, true,
password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
void CacheVerdict(const GURL& url,
LoginReputationClientRequest::TriggerType trigger,
ReusedPasswordAccountType password_type,
LoginReputationClientResponse::VerdictType verdict,
int cache_duration_sec,
const std::string& cache_expression,
const base::Time& verdict_received_time) {
ASSERT_FALSE(cache_expression.empty());
LoginReputationClientResponse response(
CreateVerdictProto(verdict, cache_duration_sec, cache_expression));
password_protection_service_->CacheVerdict(url, trigger, password_type,
response, verdict_received_time);
}
void CacheInvalidVerdict(ReusedPasswordAccountType password_type) {
GURL invalid_hostname("http://invalid.com");
std::unique_ptr<base::DictionaryValue> verdict_dictionary =
base::DictionaryValue::From(content_setting_map_->GetWebsiteSetting(
invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
nullptr));
if (!verdict_dictionary)
verdict_dictionary = std::make_unique<base::DictionaryValue>();
std::unique_ptr<base::DictionaryValue> invalid_verdict_entry =
std::make_unique<base::DictionaryValue>();
invalid_verdict_entry->SetString("invalid", "invalid_string");
std::unique_ptr<base::DictionaryValue> invalid_cache_expression_entry =
std::make_unique<base::DictionaryValue>();
invalid_cache_expression_entry->SetKey(
"invalid_cache_expression",
base::Value::FromUniquePtrValue(std::move(invalid_verdict_entry)));
verdict_dictionary->SetKey(
base::NumberToString(static_cast<std::underlying_type_t<PasswordType>>(
password_protection_service_
->ConvertReusedPasswordAccountTypeToPasswordType(
password_type))),
base::Value::FromUniquePtrValue(
std::move(invalid_cache_expression_entry)));
content_setting_map_->SetWebsiteSettingDefaultScope(
invalid_hostname, GURL(), ContentSettingsType::PASSWORD_PROTECTION,
std::move(verdict_dictionary));
}
size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
return password_protection_service_->GetStoredVerdictCount(type);
}
std::unique_ptr<content::WebContents> GetWebContents() {
std::unique_ptr<content::WebContents> contents =
base::WrapUnique(content::WebContentsTester::CreateTestWebContents(
content::WebContents::CreateParams(&browser_context_)));
// Initiate the connection to a (pretend) renderer process.
content::WebContentsTester::For(contents.get())
->NavigateAndCommit(GURL("about:blank"));
return contents;
}
void SetFeatures(const std::vector<base::Feature>& enabled_features,
const std::vector<base::Feature>& disabled_features) {
feature_list_.InitWithFeatures(enabled_features, disabled_features);
}
// Visual features are not supported on Android.
#if !defined(OS_ANDROID)
void VerifyContentAreaSizeCollection(
const LoginReputationClientRequest& request) {
bool should_report_content_size =
password_protection_service_->IsExtendedReporting() &&
!password_protection_service_->IsIncognito();
EXPECT_EQ(should_report_content_size, request.has_content_area_height());
EXPECT_EQ(should_report_content_size, request.has_content_area_width());
}
#endif
protected:
// |task_environment_| is needed here because this test involves both UI and
// IO threads.
content::BrowserTaskEnvironment task_environment_;
scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
sync_preferences::TestingPrefServiceSyncable test_pref_service_;
scoped_refptr<HostContentSettingsMap> content_setting_map_;
GURL url_;
network::TestURLLoaderFactory test_url_loader_factory_;
std::unique_ptr<TestPasswordProtectionService> password_protection_service_;
scoped_refptr<PasswordProtectionRequest> request_;
base::HistogramTester histograms_;
content::TestBrowserContext browser_context_;
content::RenderViewHostTestEnabler rvh_test_enabler_;
StrictMock<MockSafeBrowsingTokenFetcher>* raw_token_fetcher_ = nullptr;
base::test::ScopedFeatureList feature_list_;
signin::IdentityTestEnvironment identity_test_env_;
};
TEST_P(PasswordProtectionServiceBaseTest, TestCachePasswordReuseVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
.WillRepeatedly(Return(true));
// Assume each verdict has a TTL of 10 minutes.
// Cache a verdict for http://www.test.com/foo/index.html
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
reused_password_account_type.set_is_account_syncing(true);
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Cache another verdict with the some origin and cache_expression should
// override the cache.
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
LoginReputationClientResponse out_verdict;
EXPECT_EQ(LoginReputationClientResponse::PHISHING,
password_protection_service_->GetCachedVerdict(
GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &out_verdict));
// Cache a password reuse verdict with a different password type but same
// origin and cache expression should add a new entry.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(2U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_EQ(LoginReputationClientResponse::PHISHING,
password_protection_service_->GetCachedVerdict(
GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &out_verdict));
// Cache another verdict with the same origin but different cache_expression
// will not increase setting count, but will increase the number of verdicts
// in the given origin.
CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/", base::Time::Now());
EXPECT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Now cache a UNFAMILIAR_LOGIN_PAGE verdict, stored verdict count for
// PASSWORD_REUSE_EVENT should be the same.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
EXPECT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
}
TEST_P(PasswordProtectionServiceBaseTest,
TestCachePasswordReuseVerdictsIncognito) {
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(true));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
reused_password_account_type.set_is_account_syncing(true);
// No verdict will be cached for incognito profile.
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Try cache another verdict with the some origin and cache_expression.
// Verdict count should not increase.
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Now cache a UNFAMILIAR_LOGIN_PAGE verdict, verdict count should not
// increase.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
}
TEST_P(PasswordProtectionServiceBaseTest, TestCacheUnfamiliarLoginVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
reused_password_account_type.set_is_account_syncing(true);
// Assume each verdict has a TTL of 10 minutes.
// Cache a verdict for http://www.test.com/foo/index.html
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Cache another verdict with the same origin but different cache_expression
// will not increase setting count, but will increase the number of verdicts
// in the given origin.
CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/", base::Time::Now());
EXPECT_EQ(2U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Now cache a PASSWORD_REUSE_EVENT verdict, stored verdict count for
// UNFAMILIAR_LOGIN_PAGE should be the same.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
EXPECT_EQ(2U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
EXPECT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
}
TEST_P(PasswordProtectionServiceBaseTest,
TestCacheUnfamiliarLoginVerdictsIncognito) {
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(true));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
reused_password_account_type.set_is_account_syncing(true);
// No verdict will be cached for incognito profile.
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foo/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
CacheVerdict(GURL("http://www.test.com/bar/index2.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Now cache a PASSWORD_REUSE_EVENT verdict. Verdict count should not
// increase.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
reused_password_account_type.set_is_account_syncing(true);
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/foobar/", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
}
TEST_P(PasswordProtectionServiceBaseTest, TestGetCachedVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
reused_password_account_type.set_is_account_syncing(true);
// Prepare 4 verdicts of the same origin with different cache expressions,
// or password type, one is expired, one is not, one is of a different
// trigger type, and the other is with a different password type.
base::Time now = base::Time::Now();
CacheVerdict(GURL("http://test.com/login.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
now);
CacheVerdict(
GURL("http://test.com/def/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING,
10 * kMinute, "test.com/def/",
base::Time::FromDoubleT(now.ToDoubleT() - kDay)); // Yesterday, expired.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
CacheVerdict(GURL("http://test.com/bar/login.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/bar/", now);
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
CacheVerdict(GURL("http://test.com/login.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
now);
ASSERT_EQ(3U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(1U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// Return VERDICT_TYPE_UNSPECIFIED if look up for a URL with unknown origin.
LoginReputationClientResponse actual_verdict;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://www.unknown.com/"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &actual_verdict));
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://www.unknown.com/"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &actual_verdict));
// Return SAFE if look up for a URL that matches "test.com" cache expression.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/xyz/foo.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &actual_verdict));
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/xyz/foo.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &actual_verdict));
// Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
// test.com/def, but the corresponding verdict is expired.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/def/ghi/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, &actual_verdict));
// Return PHISHING. Matches "test.com/bar/" cache expression.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
EXPECT_EQ(LoginReputationClientResponse::PHISHING,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type, &actual_verdict));
// Now cache SAFE verdict for the full path.
CacheVerdict(GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::SAFE, 10 * kMinute,
"test.com/bar/foo.jsp", now);
// Return SAFE now. Matches the full cache expression.
EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type, &actual_verdict));
}
TEST_P(PasswordProtectionServiceBaseTest, TestDoesNotCacheAboutBlank) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
// Should not actually cache, since about:blank is not valid for reputation
// computing.
CacheVerdict(
GURL("about:blank"), LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type, LoginReputationClientResponse::SAFE,
10 * kMinute, "about:blank", base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
}
TEST_P(PasswordProtectionServiceBaseTest, VerifyCanGetReputationOfURL) {
// Invalid main frame URL.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(GURL()));
// Main frame URL scheme is not HTTP or HTTPS.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("data:text/html, <p>hellow")));
// Main frame URL is a local host.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://localhost:80")));
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://127.0.0.1")));
// Main frame URL is a private IP address or anything in an IANA-reserved
// range.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://192.168.1.0/")));
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://10.0.1.0/")));
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://[FEED::BEEF]")));
// Main frame URL is a no-yet-assigned y ICANN gTLD.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://intranet")));
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://host.intranet.example")));
// Main frame URL is a dotless domain.
EXPECT_FALSE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://go/example")));
// Main frame URL is anything else.
EXPECT_TRUE(PasswordProtectionService::CanGetReputationOfURL(
GURL("http://www.chromium.org")));
}
TEST_P(PasswordProtectionServiceBaseTest, TestNoRequestSentForAllowlistedURL) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
content::WebContentsTester::For(web_contents.get())
->SetLastCommittedURL(GURL("http://safe.com/"));
InitializeAndStartPasswordOnFocusRequest(true /* match allowlist */,
10000 /* timeout in ms */,
web_contents.get());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(4 /* MATCHED_ALLOWLIST */, 1)));
}
// crbug.com/1010007: crashes on win
#if defined(OS_WIN)
#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
DISABLED_TestNoRequestSentIfVerdictAlreadyCached
#else
#define MAYBE_TestNoRequestSentIfVerdictAlreadyCached \
TestNoRequestSentIfVerdictAlreadyCached
#endif
TEST_P(PasswordProtectionServiceBaseTest,
MAYBE_TestNoRequestSentIfVerdictAlreadyCached) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
CacheVerdict(GURL(kTargetUrl),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
GURL(kTargetUrl).host().append("/"), base::Time::Now());
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
base::RunLoop().RunUntilIdle();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(5 /* RESPONSE_ALREADY_CACHED */, 1)));
EXPECT_EQ(LoginReputationClientResponse::LOW_REPUTATION,
password_protection_service_->latest_response()->verdict_type());
}
TEST_P(PasswordProtectionServiceBaseTest, TestResponseFetchFailed) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
// Set up failed response.
network::URLLoaderCompletionStatus status(net::ERR_FAILED);
test_url_loader_factory_.AddResponse(
url_, network::mojom::URLResponseHead::New(), std::string(), status);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
password_protection_service_->WaitForResponse();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(9 /* FETCH_FAILED */, 1)));
}
TEST_P(PasswordProtectionServiceBaseTest, TestMalformedResponse) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
// Set up malformed response.
test_url_loader_factory_.AddResponse(url_.spec(), "invalid response");
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
password_protection_service_->WaitForResponse();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(10 /* RESPONSE_MALFORMED */, 1)));
}
TEST_P(PasswordProtectionServiceBaseTest, TestRequestTimedout) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/0,
web_contents.get());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
}
TEST_P(PasswordProtectionServiceBaseTest,
TestPasswordOnFocusRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
password_protection_service_->WaitForResponse();
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
EXPECT_THAT(histograms_.GetAllSamples(kPasswordOnFocusVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
LoginReputationClientResponse* actual_response =
password_protection_service_->latest_response();
EXPECT_EQ(expected_response.verdict_type(), actual_response->verdict_type());
EXPECT_EQ(expected_response.cache_expression(),
actual_response->cache_expression());
EXPECT_EQ(expected_response.cache_duration_sec(),
actual_response->cache_duration_sec());
}
TEST_P(PasswordProtectionServiceBaseTest,
TestPasswordOnFocusRequestEnhancedProtectionShouldHaveToken) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestWithTokenHistogram, 0);
SetEnhancedProtectionPrefForTests(&test_pref_service_, true);
SetFeatures(
/*enable_features*/ {kPasswordProtectionWithToken},
/*disable_features*/ {});
std::string access_token = "fake access token";
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::string out;
EXPECT_TRUE(request.headers.GetHeader(
net::HttpRequestHeaders::kAuthorization, &out));
EXPECT_EQ(out, kAuthHeaderBearer + access_token);
}));
// Set up mock call to token fetcher.
SafeBrowsingTokenFetcher::Callback cb;
EXPECT_CALL(*raw_token_fetcher_, Start(_)).WillOnce(MoveArg<0>(&cb));
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
// Wait for token fetcher to be called.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(Mock::VerifyAndClear(raw_token_fetcher_));
ASSERT_FALSE(cb.is_null());
std::move(cb).Run(access_token);
histograms_.ExpectUniqueSample(kPasswordOnFocusRequestWithTokenHistogram,
1 /* Attached token */, 1);
}
TEST_P(PasswordProtectionServiceBaseTest,
TestPasswordOnFocusRequestNoEnhancedProtectionShouldNotHaveToken) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestWithTokenHistogram, 0);
SetFeatures(
/*enable_features*/ {kPasswordProtectionWithToken},
/*disable_features*/ {});
std::string access_token = "fake access token";
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::string out;
EXPECT_FALSE(request.headers.GetHeader(
net::HttpRequestHeaders::kAuthorization, &out));
}));
// Never call token fetcher
EXPECT_CALL(*raw_token_fetcher_, Start(_)).Times(0);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
base::RunLoop().RunUntilIdle();
histograms_.ExpectUniqueSample(kPasswordOnFocusRequestWithTokenHistogram,
0 /* No attached token */, 1);
}
TEST_P(PasswordProtectionServiceBaseTest,
TestPasswordOnFocusRequestDisabledFeatureShouldNotHaveToken) {
SetEnhancedProtectionPrefForTests(&test_pref_service_, true);
SetFeatures(
/*enable_features*/ {},
/*disable_features*/ {kPasswordProtectionWithToken});
std::string access_token = "fake access token";
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::string out;
EXPECT_FALSE(request.headers.GetHeader(
net::HttpRequestHeaders::kAuthorization, &out));
}));
// Never call token fetcher
EXPECT_CALL(*raw_token_fetcher_, Start(_)).Times(0);
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
}
TEST_P(PasswordProtectionServiceBaseTest,
TestProtectedPasswordEntryRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
// Initiate a saved password entry request (w/ no sync password).
AccountInfo account_info;
account_info.account_id = CoreAccountId("account_id");
account_info.email = "email";
account_info.gaia = "gaia";
EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
.WillRepeatedly(Return(account_info));
InitializeAndStartPasswordEntryRequest(
PasswordType::OTHER_GAIA_PASSWORD, {{"gmail.com", u"username"}},
/*match_allowlist=*/false,
/*timeout_in_ms=*/10000, web_contents.get());
password_protection_service_->WaitForResponse();
// UMA: request outcomes
EXPECT_THAT(
histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
EXPECT_THAT(
histograms_.GetAllSamples(kNonSyncPasswordEntryRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
// UMA: verdicts
EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
histograms_.ExpectTotalCount(kSyncPasswordEntryVerdictHistogram, 0);
EXPECT_THAT(histograms_.GetAllSamples(kNonSyncPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
}
TEST_P(PasswordProtectionServiceBaseTest,
TestSyncPasswordEntryRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSyncing())
.WillRepeatedly(Return(true));
EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
.WillRepeatedly(Return(true));
// Initiate a sync password entry request (w/ no saved password).
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordEntryRequest(
PasswordType::PRIMARY_ACCOUNT_PASSWORD, {},
/*match_allowlist=*/false,
/*timeout_in_ms=*/10000, web_contents.get());
password_protection_service_->WaitForResponse();
// UMA: request outcomes
EXPECT_THAT(
histograms_.GetAllSamples(kAnyPasswordEntryRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
EXPECT_THAT(
histograms_.GetAllSamples(kSyncPasswordEntryRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
histograms_.ExpectTotalCount(kNonSyncPasswordEntryRequestOutcomeHistogram, 0);
// UMA: verdicts
EXPECT_THAT(histograms_.GetAllSamples(kAnyPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
EXPECT_THAT(histograms_.GetAllSamples(kSyncPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
histograms_.ExpectTotalCount(kNonSyncPasswordEntryVerdictHistogram, 0);
}
TEST_P(PasswordProtectionServiceBaseTest, TestTearDownWithPendingRequests) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdAllowlistUrl(target_url, _))
.WillRepeatedly(Return(AsyncMatch::NO_MATCH));
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
password_protection_service_->StartRequest(
web_contents.get(), target_url, GURL("http://foo.com/submit"),
GURL("http://foo.com/frame"), "username", PasswordType::SAVED_PASSWORD,
{}, LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
// Destroy password_protection_service_ while there is one request pending.
password_protection_service_.reset();
base::RunLoop().RunUntilIdle();
// We should not log on TearDown, since that can dispatch calls to pure
// virtual methods.
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
IsEmpty());
}
TEST_P(PasswordProtectionServiceBaseTest, VerifyPasswordOnFocusRequestProto) {
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/false,
/*timeout_in_ms=*/10000,
web_contents.get());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
EXPECT_EQ(kTargetUrl, actual_request->page_url());
EXPECT_EQ(LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
actual_request->trigger_type());
ASSERT_EQ(2, actual_request->frames_size());
EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
EXPECT_EQ(kPasswordFrameUrl, actual_request->frames(1).url());
EXPECT_EQ(true, actual_request->frames(1).has_password_field());
ASSERT_EQ(1, actual_request->frames(1).forms_size());
EXPECT_EQ(kFormActionUrl, actual_request->frames(1).forms(0).action_url());
#if !defined(OS_ANDROID)
VerifyContentAreaSizeCollection(*actual_request);
#endif
}
TEST_P(PasswordProtectionServiceBaseTest,
VerifyPasswordOnFocusRequestProtoForAllowlistMatch) {
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
EXPECT_CALL(*password_protection_service_, CanSendSamplePing())
.WillRepeatedly(Return(true));
InitializeAndStartPasswordOnFocusRequest(/*match_allowlist=*/true,
/*timeout_in_ms=*/10000,
web_contents.get());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
EXPECT_EQ(kTargetUrl, actual_request->page_url());
ASSERT_EQ(1, actual_request->frames_size());
EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
}
TEST_P(PasswordProtectionServiceBaseTest,
VerifySyncPasswordProtectionRequestProto) {
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
// Initialize request triggered by chrome sync password reuse.
InitializeAndStartPasswordEntryRequest(
PasswordType::PRIMARY_ACCOUNT_PASSWORD, {}, false /* match allowlist */,
100000 /* timeout in ms*/, web_contents.get());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
EXPECT_EQ(kTargetUrl, actual_request->page_url());
EXPECT_EQ(LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
actual_request->trigger_type());
EXPECT_EQ(1, actual_request->frames_size());
EXPECT_EQ(kTargetUrl, actual_request->frames(0).url());
EXPECT_TRUE(actual_request->frames(0).has_password_field());
ASSERT_TRUE(actual_request->has_password_reuse_event());
const auto& reuse_event = actual_request->password_reuse_event();
EXPECT_TRUE(reuse_event.is_chrome_signin_password());
EXPECT_EQ(0, reuse_event.domains_matching_password_size());
#if !defined(OS_ANDROID)
VerifyContentAreaSizeCollection(*actual_request);
#endif
}
TEST_P(PasswordProtectionServiceBaseTest,
VerifySavePasswordProtectionRequestProto) {
// Set up valid response.
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
// Initialize request triggered by saved password reuse.
InitializeAndStartPasswordEntryRequest(
PasswordType::SAVED_PASSWORD,
{{kSavedDomain, u"username"},
{kSavedDomain2, u"username"},
{"http://localhost:8080", u"username"}},
false /* match allowlist */, 100000 /* timeout in ms*/,
web_contents.get());
password_protection_service_->WaitForResponse();
const LoginReputationClientRequest* actual_request =
password_protection_service_->GetLatestRequestProto();
ASSERT_TRUE(actual_request->has_password_reuse_event());
const auto& reuse_event = actual_request->password_reuse_event();
EXPECT_FALSE(reuse_event.is_chrome_signin_password());
if (password_protection_service_->IsExtendedReporting() &&
!password_protection_service_->IsIncognito()) {
ASSERT_EQ(3, reuse_event.domains_matching_password_size());
EXPECT_EQ("localhost:8080", reuse_event.domains_matching_password(0));
EXPECT_EQ("saved_domain.com", reuse_event.domains_matching_password(1));
EXPECT_EQ("saved_domain2.com", reuse_event.domains_matching_password(2));
} else {
EXPECT_EQ(0, reuse_event.domains_matching_password_size());
}
#if !defined(OS_ANDROID)
VerifyContentAreaSizeCollection(*actual_request);
#endif
}
TEST_P(PasswordProtectionServiceBaseTest, VerifyShouldShowModalWarning) {
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PHISHING_REUSE));
EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
.WillRepeatedly(Return(true));
AccountInfo account_info;
account_info.account_id = CoreAccountId("account_id");
account_info.email = "email";
account_info.gaia = "gaia";
EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
.WillRepeatedly(Return(account_info));
// Don't show modal warning if it is not a password reuse ping.
ReusedPasswordAccountType reused_password_account_type;
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
reused_password_account_type.set_is_account_syncing(true);
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::SAVED_PASSWORD);
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::LOW_REPUTATION));
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
safe_browsing::kPasswordProtectionForSignedInUsers);
// Don't show modal warning if non-sync gaia account experiment is not
// on.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GMAIL);
reused_password_account_type.set_is_account_syncing(false);
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
}
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
safe_browsing::kPasswordProtectionForSignedInUsers);
// Show modal warning if non-sync gaia account experiment is on.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GMAIL);
reused_password_account_type.set_is_account_syncing(false);
// Currently password reuse warnings are only supported for saved passwords on
// Android.
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
#else
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
#endif
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
}
// Don't show modal warning if reused password type unknown.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::UNKNOWN);
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GMAIL);
reused_password_account_type.set_is_account_syncing(true);
// Currently password reuse warnings are only supported for saved passwords on
// Android.
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
#else
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
#endif
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
// For a GSUITE account, don't show warning if password protection is set to
// off by enterprise policy.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::GSUITE);
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
// For a GSUITE account, show warning if password protection is set to
// PHISHING_REUSE.
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PHISHING_REUSE));
EXPECT_EQ(
PHISHING_REUSE,
password_protection_service_->GetPasswordProtectionWarningTriggerPref(
reused_password_account_type));
// Currently password reuse warnings are only supported for saved passwords on
// Android.
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
#else
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
#endif
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
// Modal dialog warning is also shown on LOW_REPUTATION verdict.
// Currently password reuse warnings are only supported for saved passwords on
// Android.
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
#else
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
#endif
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type,
LoginReputationClientResponse::LOW_REPUTATION));
// Modal dialog warning should not be shown for enterprise password reuse
// if it is turned off by policy.
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
// Show modal warning for enterprise password reuse if the trigger is
// configured to PHISHING_REUSE.
reused_password_account_type.set_account_type(
ReusedPasswordAccountType::NON_GAIA_ENTERPRISE);
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref(_))
.WillRepeatedly(Return(PHISHING_REUSE));
// Currently password reuse warnings are only supported for saved passwords on
// Android.
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
#else
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
#endif
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
reused_password_account_type, LoginReputationClientResponse::PHISHING));
}
TEST_P(PasswordProtectionServiceBaseTest, VerifyContentTypeIsPopulated) {
LoginReputationClientResponse response =
CreateVerdictProto(LoginReputationClientResponse::SAFE, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
content::WebContentsTester::For(web_contents.get())
->SetMainFrameMimeType("application/pdf");
InitializeAndStartPasswordOnFocusRequest(false /* match allowlist */,
10000 /* timeout in ms */,
web_contents.get());
password_protection_service_->WaitForResponse();
EXPECT_EQ(
"application/pdf",
password_protection_service_->GetLatestRequestProto()->content_type());
}
TEST_P(PasswordProtectionServiceBaseTest,
VerifyIsSupportedPasswordTypeForPinging) {
EXPECT_CALL(*password_protection_service_, IsPrimaryAccountSignedIn())
.WillRepeatedly(Return(true));
AccountInfo account_info;
account_info.account_id = CoreAccountId("account_id");
account_info.email = "email";
account_info.gaia = "gaia";
EXPECT_CALL(*password_protection_service_, GetSignedInNonSyncAccount(_))
.WillRepeatedly(Return(account_info));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::PRIMARY_ACCOUNT_PASSWORD));
#if defined(OS_ANDROID)
EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::OTHER_GAIA_PASSWORD));
#else
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::OTHER_GAIA_PASSWORD));
#endif
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::ENTERPRISE_PASSWORD));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::SAVED_PASSWORD));
{
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
safe_browsing::kPasswordProtectionForSignedInUsers);
EXPECT_FALSE(
password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordType::OTHER_GAIA_PASSWORD));
}
}
TEST_P(PasswordProtectionServiceBaseTest, TestPingsForAboutBlank) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL("about:blank").host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
password_protection_service_->StartRequest(
web_contents.get(), GURL("about:blank"), GURL(), GURL(), "username",
PasswordType::SAVED_PASSWORD, {{"example1.com", u"username"}},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
base::RunLoop().RunUntilIdle();
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 1);
}
// DOM features and visual features are not supported on Android.
#if !defined(OS_ANDROID)
TEST_P(PasswordProtectionServiceBaseTest,
TestVisualFeaturesPopulatedInOnFocusPing) {
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL("about:blank").host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
.Times(AnyNumber())
.WillOnce(Return(gfx::Size(1000, 1000)));
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
password_protection_service_->StartRequest(
web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
PasswordType::SAVED_PASSWORD, {{"example.com", u"username"}},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
base::RunLoop().RunUntilIdle();
bool is_sber = GetParam();
if (is_sber) {
password_protection_service_->WaitForResponse();
ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
->has_visual_features());
}
}
TEST_P(PasswordProtectionServiceBaseTest, TestDomFeaturesPopulated) {
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL("about:blank").host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
.Times(AnyNumber())
.WillOnce(Return(gfx::Size(1000, 1000)));
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
password_protection_service_->StartRequest(
web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
PasswordType::SAVED_PASSWORD, {{"example.com", u"username"}},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
base::RunLoop().RunUntilIdle();
password_protection_service_->WaitForResponse();
ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
EXPECT_TRUE(password_protection_service_->GetLatestRequestProto()
->has_dom_features());
}
TEST_P(PasswordProtectionServiceBaseTest, TestDomFeaturesTimeout) {
password_protection_service_->SetDomFeatureCollectionTimeout(true);
LoginReputationClientResponse expected_response =
CreateVerdictProto(LoginReputationClientResponse::PHISHING, 10 * kMinute,
GURL("about:blank").host());
test_url_loader_factory_.AddResponse(url_.spec(),
expected_response.SerializeAsString());
EXPECT_CALL(*password_protection_service_, GetCurrentContentAreaSize())
.Times(AnyNumber())
.WillOnce(Return(gfx::Size(1000, 1000)));
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
password_protection_service_->StartRequest(
web_contents.get(), GURL("about:blank"), GURL(), GURL(), kUserName,
PasswordType::SAVED_PASSWORD, {{"example.com", u"username"}},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
task_environment_.RunUntilIdle();
password_protection_service_->WaitForResponse();
ASSERT_NE(nullptr, password_protection_service_->GetLatestRequestProto());
EXPECT_FALSE(password_protection_service_->GetLatestRequestProto()
->has_dom_features());
}
#endif
TEST_P(PasswordProtectionServiceBaseTest, TestWebContentsDestroyed) {
std::unique_ptr<content::WebContents> web_contents = GetWebContents();
InitializeAndStartPasswordOnFocusRequest(false /* match allowlist */,
10000 /* timeout in ms */,
web_contents.get());
web_contents.reset();
task_environment_.RunUntilIdle();
}
INSTANTIATE_TEST_SUITE_P(Regular,
PasswordProtectionServiceBaseTest,
::testing::Values(false));
INSTANTIATE_TEST_SUITE_P(SBER,
PasswordProtectionServiceBaseTest,
::testing::Values(true));
} // namespace safe_browsing