blob: a6eb0bf081a378685abeef7f2b810d47bf01e600 [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/password_protection/password_protection_service.h"
#include <memory>
#include "base/bind.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/null_task_runner.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/password_manager/core/browser/password_reuse_detector.h"
#include "components/safe_browsing/common/safe_browsing.mojom-forward.h"
#include "components/safe_browsing/common/safe_browsing.mojom.h"
#include "components/safe_browsing/db/test_database_manager.h"
#include "components/safe_browsing/password_protection/metrics_util.h"
#include "components/safe_browsing/password_protection/mock_password_protection_service.h"
#include "components/safe_browsing/password_protection/password_protection_request.h"
#include "components/safe_browsing/proto/csd.pb.h"
#include "components/safe_browsing/verdict_cache_manager.h"
#include "components/sync_preferences/testing_pref_service_syncable.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/web_contents_tester.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 testing::_;
using testing::AnyNumber;
using testing::ElementsAre;
using testing::IsEmpty;
using testing::Return;
namespace {
const char kFormActionUrl[] = "https://form_action.com/";
const char kPasswordFrameUrl[] = "https://password_frame.com/";
const char kSavedDomain[] = "saved_domain.com";
const char kSavedDomain2[] = "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;
} // namespace
namespace safe_browsing {
class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
public:
MockSafeBrowsingDatabaseManager() {}
MOCK_METHOD2(CheckCsdWhitelistUrl,
AsyncMatch(const GURL&, SafeBrowsingDatabaseManager::Client*));
protected:
~MockSafeBrowsingDatabaseManager() override {}
private:
DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
};
class TestPhishingDetector : public mojom::PhishingDetector {
public:
TestPhishingDetector() : binding_(this) {}
~TestPhishingDetector() override {}
void Bind(mojo::ScopedMessagePipeHandle handle) {
binding_.Bind(mojom::PhishingDetectorRequest(std::move(handle)));
}
void StartPhishingDetection(
const GURL& url,
StartPhishingDetectionCallback callback) override {
ClientPhishingRequest verdict;
verdict.set_is_phishing(false);
verdict.set_client_score(0.1);
std::move(callback).Run(verdict.SerializeAsString());
}
private:
mojo::Binding<mojom::PhishingDetector> binding_;
DISALLOW_COPY_AND_ASSIGN(TestPhishingDetector);
};
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)
: MockPasswordProtectionService(database_manager,
url_loader_factory,
nullptr),
cache_manager_(
std::make_unique<VerdictCacheManager>(nullptr,
content_setting_map.get())) {}
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;
}
void GetPhishingDetector(
service_manager::InterfaceProvider* provider,
mojom::PhishingDetectorPtr* 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);
test_api.ClearBinderForName(mojom::PhishingDetector::Name_);
}
void CacheVerdict(const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
ReusedPasswordType password_type,
const LoginReputationClientResponse& verdict,
const base::Time& receive_time) override {
if (!CanGetReputationOfURL(url) || IsIncognito())
return;
cache_manager_->CachePhishGuardVerdict(url, trigger_type, password_type,
verdict, receive_time);
}
LoginReputationClientResponse::VerdictType GetCachedVerdict(
const GURL& url,
LoginReputationClientRequest::TriggerType trigger_type,
LoginReputationClientRequest::PasswordReuseEvent::ReusedPasswordType
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);
}
private:
PasswordProtectionRequest* latest_request_;
base::RunLoop run_loop_;
std::unique_ptr<LoginReputationClientResponse> latest_response_;
TestPhishingDetector test_phishing_detector_;
// The TestPasswordProtectionService manages its own cache, rather than using
// the global one.
std::unique_ptr<VerdictCacheManager> cache_manager_;
DISALLOW_COPY_AND_ASSIGN(TestPasswordProtectionService);
};
class PasswordProtectionServiceTest : public ::testing::TestWithParam<bool> {
public:
PasswordProtectionServiceTest() {}
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());
content_setting_map_ = new HostContentSettingsMap(
&test_pref_service_, false /* is_off_the_record */,
false /* store_last_modified */,
false /* migrate_requesting_and_top_level_origin_settings */);
database_manager_ = new MockSafeBrowsingDatabaseManager();
password_protection_service_ =
std::make_unique<TestPasswordProtectionService>(
database_manager_,
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_),
content_setting_map_);
EXPECT_CALL(*password_protection_service_, IsExtendedReporting())
.WillRepeatedly(Return(GetParam()));
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
EXPECT_CALL(*password_protection_service_,
IsURLWhitelistedForPasswordEntry(_, _))
.WillRepeatedly(Return(false));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
url_ = PasswordProtectionService::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_whitelist,
int timeout_in_ms,
content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
.WillRepeatedly(
Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
web_contents, target_url, GURL(kFormActionUrl), GURL(kPasswordFrameUrl),
kUserName, PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, {},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true,
password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
void InitializeAndStartPasswordEntryRequest(
PasswordReuseEvent::ReusedPasswordType type,
const std::vector<std::string>& matching_domains,
bool match_whitelist,
int timeout_in_ms,
content::WebContents* web_contents) {
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
.WillRepeatedly(
Return(match_whitelist ? AsyncMatch::MATCH : AsyncMatch::NO_MATCH));
request_ = new PasswordProtectionRequest(
web_contents, target_url, GURL(), GURL(), kUserName, type,
matching_domains, LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
true, password_protection_service_.get(), timeout_in_ms);
request_->Start();
}
void CacheVerdict(const GURL& url,
LoginReputationClientRequest::TriggerType trigger,
ReusedPasswordType 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(ReusedPasswordType 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(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
std::string(), 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->SetWithoutPathExpansion(
"invalid_cache_expression", std::move(invalid_verdict_entry));
verdict_dictionary->SetWithoutPathExpansion(
base::NumberToString(password_type),
std::move(invalid_cache_expression_entry));
content_setting_map_->SetWebsiteSettingDefaultScope(
invalid_hostname, GURL(), CONTENT_SETTINGS_TYPE_PASSWORD_PROTECTION,
std::string(), std::move(verdict_dictionary));
}
size_t GetStoredVerdictCount(LoginReputationClientRequest::TriggerType type) {
return password_protection_service_->GetStoredVerdictCount(type);
}
content::WebContents* GetWebContents() {
return content::WebContentsTester::CreateTestWebContents(
content::WebContents::CreateParams(&browser_context_));
}
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());
}
protected:
// |thread_bundle_| is needed here because this test involves both UI and IO
// threads.
content::TestBrowserThreadBundle thread_bundle_;
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_;
};
TEST_P(PasswordProtectionServiceTest, TestCachePasswordReuseVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// 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::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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,
PasswordReuseEvent::SIGN_IN_PASSWORD, &out_verdict));
// Cache a password reuse verdict with a different password type but same
// origin and cache expression should add a new entry.
CacheVerdict(GURL("http://www.test.com/foo/index2.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD,
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,
PasswordReuseEvent::ENTERPRISE_PASSWORD, &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,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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(PasswordProtectionServiceTest, TestCachePasswordReuseVerdictsIncognito) {
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(true));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// No verdict will be cached for incognito profile.
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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(PasswordProtectionServiceTest, TestCacheUnfamiliarLoginVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// 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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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(PasswordProtectionServiceTest,
TestCacheUnfamiliarLoginVerdictsIncognito) {
EXPECT_CALL(*password_protection_service_, IsIncognito())
.WillRepeatedly(Return(true));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// No verdict will be cached for incognito profile.
CacheVerdict(GURL("http://www.test.com/foo/index.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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.
CacheVerdict(GURL("http://www.test.com/foobar/index3.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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(PasswordProtectionServiceTest, TestGetCachedVerdicts) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE));
// 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,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::SAFE, 10 * kMinute, "test.com/",
now);
CacheVerdict(
GURL("http://test.com/def/index.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING, 10 * kMinute, "test.com/def/",
base::Time::FromDoubleT(now.ToDoubleT() - kDay)); // Yesterday, expired.
CacheVerdict(GURL("http://test.com/bar/login.html"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::PHISHING, 10 * kMinute,
"test.com/bar/", now);
CacheVerdict(GURL("http://test.com/login.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD,
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;
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://www.unknown.com/"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://www.unknown.com/"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD, &actual_verdict));
// Return SAFE if look up for a URL that matches "test.com" cache expression.
EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/xyz/foo.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
EXPECT_EQ(LoginReputationClientResponse::SAFE,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/xyz/foo.jsp"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD, &actual_verdict));
// Return VERDICT_TYPE_UNSPECIFIED if look up for a URL whose variants match
// test.com/def, but the corresponding verdict is expired.
EXPECT_EQ(LoginReputationClientResponse::VERDICT_TYPE_UNSPECIFIED,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/def/ghi/index.html"),
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD, &actual_verdict));
// Return PHISHING. Matches "test.com/bar/" cache expression.
EXPECT_EQ(
LoginReputationClientResponse::PHISHING,
password_protection_service_->GetCachedVerdict(
GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
// Now cache SAFE verdict for the full path.
CacheVerdict(GURL("http://test.com/bar/foo.jsp"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
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,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN, &actual_verdict));
}
TEST_P(PasswordProtectionServiceTest, TestDoesNotCacheAboutBlank) {
ASSERT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
// Should not actually cache, since about:blank is not valid for reputation
// computing.
CacheVerdict(GURL("about:blank"),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::SAFE, 10 * kMinute, "about:blank",
base::Time::Now());
EXPECT_EQ(0U, GetStoredVerdictCount(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT));
}
TEST_P(PasswordProtectionServiceTest, 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(PasswordProtectionServiceTest, TestNoRequestSentForWhitelistedURL) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
content::WebContents* web_contents = GetWebContents();
content::WebContentsTester::For(web_contents)
->SetLastCommittedURL(GURL("http://safe.com/"));
InitializeAndStartPasswordOnFocusRequest(
true /* match whitelist */, 10000 /* timeout in ms */, web_contents);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(4 /* MATCHED_WHITELIST */, 1)));
}
TEST_P(PasswordProtectionServiceTest, TestNoRequestSentIfVerdictAlreadyCached) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
CacheVerdict(GURL(kTargetUrl),
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::LOW_REPUTATION, 10 * kMinute,
GURL(kTargetUrl).host().append("/"), base::Time::Now());
InitializeAndStartPasswordOnFocusRequest(
false /* match whitelist */, 10000 /* timeout in ms*/, GetWebContents());
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(PasswordProtectionServiceTest, TestResponseFetchFailed) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
// Set up failed response.
network::ResourceResponseHead head;
network::URLLoaderCompletionStatus status(net::ERR_FAILED);
test_url_loader_factory_.AddResponse(url_, head, std::string(), status);
InitializeAndStartPasswordOnFocusRequest(
false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
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(PasswordProtectionServiceTest, TestMalformedResponse) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
// Set up malformed response.
test_url_loader_factory_.AddResponse(url_.spec(), "invalid response");
InitializeAndStartPasswordOnFocusRequest(
false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
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(PasswordProtectionServiceTest, TestRequestTimedout) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
0 /* timeout immediately */,
GetWebContents());
base::RunLoop().RunUntilIdle();
EXPECT_EQ(nullptr, password_protection_service_->latest_response());
EXPECT_THAT(
histograms_.GetAllSamples(kPasswordOnFocusRequestOutcomeHistogram),
ElementsAre(base::Bucket(3 /* TIMEDOUT */, 1)));
}
TEST_P(PasswordProtectionServiceTest,
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());
InitializeAndStartPasswordOnFocusRequest(
false /* match whitelist */, 10000 /* timeout in ms */, GetWebContents());
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(PasswordProtectionServiceTest,
TestProtectedPasswordEntryRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kProtectedPasswordEntryRequestOutcomeHistogram,
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());
// Initiate a saved password entry request (w/ no sync password).
InitializeAndStartPasswordEntryRequest(
PasswordReuseEvent::SAVED_PASSWORD, {"example.com"},
false /* match whitelist */, 10000 /* timeout in ms*/, GetWebContents());
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(kProtectedPasswordEntryRequestOutcomeHistogram),
ElementsAre(base::Bucket(1 /* SUCCEEDED */, 1)));
EXPECT_THAT(
histograms_.GetAllSamples(kProtectedPasswordEntryVerdictHistogram),
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(kProtectedPasswordEntryVerdictHistogram),
ElementsAre(base::Bucket(3 /* PHISHING */, 1)));
}
TEST_P(PasswordProtectionServiceTest,
TestSyncPasswordEntryRequestAndResponseSuccessfull) {
histograms_.ExpectTotalCount(kAnyPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kSyncPasswordEntryRequestOutcomeHistogram, 0);
histograms_.ExpectTotalCount(kProtectedPasswordEntryRequestOutcomeHistogram,
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());
// Initiate a sync password entry request (w/ no saved password).
InitializeAndStartPasswordEntryRequest(
PasswordReuseEvent::SIGN_IN_PASSWORD, {}, false /* match whitelist */,
10000 /* timeout in ms*/, GetWebContents());
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(kProtectedPasswordEntryRequestOutcomeHistogram,
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(kProtectedPasswordEntryVerdictHistogram, 0);
}
TEST_P(PasswordProtectionServiceTest, TestTearDownWithPendingRequests) {
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 0);
GURL target_url(kTargetUrl);
EXPECT_CALL(*database_manager_, CheckCsdWhitelistUrl(target_url, _))
.WillRepeatedly(Return(AsyncMatch::NO_MATCH));
password_protection_service_->StartRequest(
GetWebContents(), target_url, GURL("http://foo.com/submit"),
GURL("http://foo.com/frame"), "username",
PasswordReuseEvent::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(PasswordProtectionServiceTest, 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());
InitializeAndStartPasswordOnFocusRequest(false /* match whitelist */,
100000 /* timeout in ms */,
GetWebContents());
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());
VerifyContentAreaSizeCollection(*actual_request);
}
TEST_P(PasswordProtectionServiceTest,
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());
// Initialize request triggered by chrome sync password reuse.
InitializeAndStartPasswordEntryRequest(
PasswordReuseEvent::SIGN_IN_PASSWORD, {}, false /* match whitelist */,
100000 /* timeout in ms*/, GetWebContents());
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());
VerifyContentAreaSizeCollection(*actual_request);
}
TEST_P(PasswordProtectionServiceTest,
VerifyNonSyncPasswordProtectionRequestProto) {
// 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());
// Initialize request triggered by saved password reuse.
InitializeAndStartPasswordEntryRequest(
PasswordReuseEvent::SAVED_PASSWORD, {kSavedDomain, kSavedDomain2},
false /* match whitelist */, 100000 /* timeout in ms*/, GetWebContents());
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(2, reuse_event.domains_matching_password_size());
EXPECT_EQ(kSavedDomain, reuse_event.domains_matching_password(0));
EXPECT_EQ(kSavedDomain2, reuse_event.domains_matching_password(1));
} else {
EXPECT_EQ(0, reuse_event.domains_matching_password_size());
}
VerifyContentAreaSizeCollection(*actual_request);
}
TEST_P(PasswordProtectionServiceTest, VerifyShouldShowModalWarning) {
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PHISHING_REUSE));
// Don't show modal warning if it is not a password reuse ping.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Don't show modal warning if it is a saved password reuse.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SAVED_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Don't show modal warning if it is a non-sync gaia password reuse.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::OTHER_GAIA_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Don't show modal warning if reused password type unknown.
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::REUSED_PASSWORD_TYPE_UNKNOWN,
LoginReputationClientResponse::PHISHING));
// Don't show modal warning if it is a sync password reuse but user is not
// signed in.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Show warning if it is a sync password reuse and user is signed in and
// is not manged by enterprise.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING));
// For a GSUITE account, don't show warning if password protection is set to
// off by enterprise policy.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GSUITE));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
EXPECT_EQ(
PASSWORD_PROTECTION_OFF,
password_protection_service_->GetPasswordProtectionWarningTriggerPref());
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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());
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Modal dialog warning is also shown on LOW_REPUTATION verdict.
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::SIGN_IN_PASSWORD,
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_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PASSWORD_PROTECTION_OFF));
EXPECT_FALSE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD,
LoginReputationClientResponse::PHISHING));
// Show modal warning for enterprise password reuse if the trigger is
// configured to PHISHING_REUSE.
EXPECT_CALL(*password_protection_service_,
GetPasswordProtectionWarningTriggerPref())
.WillRepeatedly(Return(PHISHING_REUSE));
EXPECT_TRUE(password_protection_service_->ShouldShowModalWarning(
LoginReputationClientRequest::PASSWORD_REUSE_EVENT,
PasswordReuseEvent::ENTERPRISE_PASSWORD,
LoginReputationClientResponse::PHISHING));
}
TEST_P(PasswordProtectionServiceTest, VerifyIsEventLoggingEnabled) {
// For user who is not signed-in, event logging should be disabled.
EXPECT_EQ(PasswordReuseEvent::NOT_SIGNED_IN,
password_protection_service_->GetSyncAccountType());
EXPECT_FALSE(password_protection_service_->IsEventLoggingEnabled());
// Event logging should be enable for all signed-in users..
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_EQ(PasswordReuseEvent::GMAIL,
password_protection_service_->GetSyncAccountType());
EXPECT_TRUE(password_protection_service_->IsEventLoggingEnabled());
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GSUITE));
EXPECT_EQ(PasswordReuseEvent::GSUITE,
password_protection_service_->GetSyncAccountType());
EXPECT_TRUE(password_protection_service_->IsEventLoggingEnabled());
}
TEST_P(PasswordProtectionServiceTest, VerifyContentTypeIsPopulated) {
LoginReputationClientResponse response =
CreateVerdictProto(LoginReputationClientResponse::SAFE, 10 * kMinute,
GURL(kTargetUrl).host());
test_url_loader_factory_.AddResponse(url_.spec(),
response.SerializeAsString());
content::WebContents* web_contents = GetWebContents();
content::WebContentsTester::For(web_contents)
->SetMainFrameMimeType("application/pdf");
InitializeAndStartPasswordOnFocusRequest(
false /* match whitelist */, 10000 /* timeout in ms */, web_contents);
password_protection_service_->WaitForResponse();
EXPECT_EQ(
"application/pdf",
password_protection_service_->GetLatestRequestProto()->content_type());
}
TEST_P(PasswordProtectionServiceTest, VerifyIsSupportedPasswordTypeForPinging) {
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::NOT_SIGNED_IN));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::SAVED_PASSWORD));
EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::SIGN_IN_PASSWORD));
EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::OTHER_GAIA_PASSWORD));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::ENTERPRISE_PASSWORD));
EXPECT_CALL(*password_protection_service_, GetSyncAccountType())
.WillRepeatedly(Return(PasswordReuseEvent::GMAIL));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::SAVED_PASSWORD));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::SIGN_IN_PASSWORD));
EXPECT_FALSE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::OTHER_GAIA_PASSWORD));
EXPECT_TRUE(password_protection_service_->IsSupportedPasswordTypeForPinging(
PasswordReuseEvent::ENTERPRISE_PASSWORD));
}
TEST_P(PasswordProtectionServiceTest, 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());
password_protection_service_->StartRequest(
GetWebContents(), GURL("about:blank"), GURL(), GURL(), "username",
PasswordReuseEvent::SAVED_PASSWORD, {"example.com"},
LoginReputationClientRequest::UNFAMILIAR_LOGIN_PAGE, true);
base::RunLoop().RunUntilIdle();
histograms_.ExpectTotalCount(kPasswordOnFocusRequestOutcomeHistogram, 1);
}
TEST_P(PasswordProtectionServiceTest,
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)));
password_protection_service_->StartRequest(
GetWebContents(), GURL("about:blank"), GURL(), GURL(), kUserName,
PasswordReuseEvent::SAVED_PASSWORD, {"example.com"},
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(PasswordProtectionServiceTest, 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)));
password_protection_service_->StartRequest(
GetWebContents(), GURL("about:blank"), GURL(), GURL(), kUserName,
PasswordReuseEvent::SAVED_PASSWORD, {"example.com"},
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());
}
INSTANTIATE_TEST_SUITE_P(Regular,
PasswordProtectionServiceTest,
::testing::Values(false));
INSTANTIATE_TEST_SUITE_P(SBER,
PasswordProtectionServiceTest,
::testing::Values(true));
} // namespace safe_browsing