blob: b32c5622f0d1cf1c0d907fecadd02df602ec7c83 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/safe_browsing/core/browser/safe_browsing_url_checker_impl.h"
#include <memory>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/timer/mock_timer.h"
#include "components/safe_browsing/core/browser/db/test_database_manager.h"
#include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
#include "components/safe_browsing/core/browser/realtime/url_lookup_service.h"
#include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
#include "components/safe_browsing/core/browser/url_checker_delegate.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/security_interstitials/core/unsafe_resource.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
using security_interstitials::UnsafeResource;
using ::testing::_;
namespace safe_browsing {
namespace {
// A matcher for threat source in UnsafeResource.
MATCHER_P(IsSameThreatSource, threatSource, "") {
return arg.threat_source == threatSource;
}
class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
public:
MockSafeBrowsingDatabaseManager()
: TestSafeBrowsingDatabaseManager(
base::SequencedTaskRunner::GetCurrentDefault(),
base::SequencedTaskRunner::GetCurrentDefault()) {}
// SafeBrowsingDatabaseManager implementation.
// Checks the threat type of |gurl| previously set by |SetThreatTypeForUrl|.
// It crashes if the threat type of |gurl| is not set in advance.
bool CheckBrowseUrl(const GURL& gurl,
const safe_browsing::SBThreatTypeSet& threat_types,
Client* client) override {
std::string url = gurl.spec();
DCHECK(base::Contains(urls_threat_type_, url));
DCHECK(base::Contains(urls_delayed_callback_, url));
if (urls_threat_type_[url] == SB_THREAT_TYPE_SAFE) {
return true;
}
if (!urls_delayed_callback_[url]) {
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&MockSafeBrowsingDatabaseManager::OnCheckBrowseURLDone,
this, gurl, client));
} else {
// If delayed callback is set to true, store the client in |urls_client_|.
// The callback can be triggered by |RestartDelayedCallback|.
urls_client_[url] = client;
}
return false;
}
bool CanCheckRequestDestination(
network::mojom::RequestDestination request_destination) const override {
return true;
}
bool ChecksAreAlwaysAsync() const override { return false; }
ThreatSource GetThreatSource() const override {
return ThreatSource::UNKNOWN;
}
// Returns the allowlist match result previously set by
// |SetAllowlistResultForUrl|. It crashes if the allowlist match result for
// the |gurl| is not set in advance.
bool CheckUrlForHighConfidenceAllowlist(const GURL& gurl) override {
std::string url = gurl.spec();
DCHECK(base::Contains(urls_allowlist_match_, url));
return urls_allowlist_match_[url];
}
// Helper functions.
// Restart the previous delayed callback for |gurl|. This is useful to test
// the asynchronous URL check, i.e. the database manager is still checking the
// previous URL and the new redirect URL arrives.
void RestartDelayedCallback(const GURL& gurl) {
std::string url = gurl.spec();
DCHECK(base::Contains(urls_delayed_callback_, url));
DCHECK_EQ(true, urls_delayed_callback_[url]);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&MockSafeBrowsingDatabaseManager::OnCheckBrowseURLDone,
this, gurl, urls_client_[url]));
}
void SetThreatTypeForUrl(const GURL& gurl,
SBThreatType threat_type,
bool delayed_callback) {
std::string url = gurl.spec();
urls_threat_type_[url] = threat_type;
urls_delayed_callback_[url] = delayed_callback;
}
void SetAllowlistResultForUrl(const GURL& gurl, bool match) {
std::string url = gurl.spec();
urls_allowlist_match_[url] = match;
}
void CancelCheck(Client* client) override { called_cancel_check_ = true; }
bool HasCalledCancelCheck() { return called_cancel_check_; }
protected:
~MockSafeBrowsingDatabaseManager() override = default;
private:
void OnCheckBrowseURLDone(const GURL& gurl, Client* client) {
if (called_cancel_check_) {
return;
}
std::string url = gurl.spec();
DCHECK(base::Contains(urls_threat_type_, url));
ThreatMetadata metadata;
client->OnCheckBrowseUrlResult(gurl, urls_threat_type_[url], metadata);
}
base::flat_map<std::string, SBThreatType> urls_threat_type_;
base::flat_map<std::string, bool> urls_delayed_callback_;
base::flat_map<std::string, Client*> urls_client_;
base::flat_map<std::string, bool> urls_allowlist_match_;
bool called_cancel_check_ = false;
};
class MockUrlCheckerDelegate : public UrlCheckerDelegate {
public:
explicit MockUrlCheckerDelegate(SafeBrowsingDatabaseManager* database_manager)
: database_manager_(database_manager),
threat_types_(
SBThreatTypeSet({safe_browsing::SB_THREAT_TYPE_URL_PHISHING})) {}
MOCK_METHOD1(MaybeDestroyNoStatePrefetchContents,
void(base::OnceCallback<content::WebContents*()>));
MOCK_METHOD5(StartDisplayingBlockingPageHelper,
void(const security_interstitials::UnsafeResource&,
const std::string&,
const net::HttpRequestHeaders&,
bool,
bool));
MOCK_METHOD2(StartObservingInteractionsForDelayedBlockingPageHelper,
void(const security_interstitials::UnsafeResource&, bool));
MOCK_METHOD5(ShouldSkipRequestCheck, bool(const GURL&, int, int, int, bool));
MOCK_METHOD1(NotifySuspiciousSiteDetected,
void(const base::RepeatingCallback<content::WebContents*()>&));
MOCK_METHOD0(GetUIManager, BaseUIManager*());
bool IsUrlAllowlisted(const GURL& url) override { return false; }
void SetPolicyAllowlistDomains(
const std::vector<std::string>& allowlist_domains) override {}
const SBThreatTypeSet& GetThreatTypes() override { return threat_types_; }
SafeBrowsingDatabaseManager* GetDatabaseManager() override {
return database_manager_;
}
protected:
~MockUrlCheckerDelegate() override = default;
private:
raw_ptr<SafeBrowsingDatabaseManager> database_manager_;
SBThreatTypeSet threat_types_;
};
class MockRealTimeUrlLookupService : public RealTimeUrlLookupServiceBase {
public:
MockRealTimeUrlLookupService()
: RealTimeUrlLookupServiceBase(
/*url_loader_factory=*/nullptr,
/*cache_manager=*/nullptr,
/*get_user_population_callback=*/base::BindRepeating([]() {
return ChromeUserPopulation();
}),
/*referrer_chain_provider=*/nullptr) {}
// Returns the threat type previously set by |SetThreatTypeForUrl|. It crashes
// if the threat type for the |gurl| is not set in advance.
void StartLookup(
const GURL& gurl,
const GURL& last_committed_url,
bool is_mainframe,
RTLookupRequestCallback request_callback,
RTLookupResponseCallback response_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {
std::string url = gurl.spec();
DCHECK(base::Contains(url_details_, url));
auto response = std::make_unique<RTLookupResponse>();
RTLookupResponse::ThreatInfo* new_threat_info = response->add_threat_info();
RTLookupResponse::ThreatInfo threat_info;
RTLookupResponse::ThreatInfo::ThreatType threat_type;
RTLookupResponse::ThreatInfo::VerdictType verdict_type;
SBThreatType sb_threat_type = url_details_[url].threat_type;
switch (sb_threat_type) {
case SB_THREAT_TYPE_URL_PHISHING:
threat_type = RTLookupResponse::ThreatInfo::SOCIAL_ENGINEERING;
verdict_type = RTLookupResponse::ThreatInfo::DANGEROUS;
break;
case SB_THREAT_TYPE_SAFE:
threat_type = RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED;
verdict_type = RTLookupResponse::ThreatInfo::SAFE;
break;
case SB_THREAT_TYPE_MANAGED_POLICY_BLOCK:
threat_type = RTLookupResponse::ThreatInfo::MANAGED_POLICY;
verdict_type = RTLookupResponse::ThreatInfo::DANGEROUS;
break;
case SB_THREAT_TYPE_MANAGED_POLICY_WARN:
threat_type = RTLookupResponse::ThreatInfo::MANAGED_POLICY;
verdict_type = RTLookupResponse::ThreatInfo::WARN;
break;
default:
NOTREACHED();
threat_type = RTLookupResponse::ThreatInfo::THREAT_TYPE_UNSPECIFIED;
verdict_type = RTLookupResponse::ThreatInfo::SAFE;
}
threat_info.set_threat_type(threat_type);
threat_info.set_verdict_type(verdict_type);
*new_threat_info = threat_info;
if (url_details_[url].should_complete_lookup) {
callback_task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(response_callback),
/*is_rt_lookup_successful=*/true,
/*is_cached_response=*/is_cached_response_,
std::move(response)));
}
}
void SendSampledRequest(
const GURL& gurl,
const GURL& last_committed_url,
bool is_mainframe,
RTLookupRequestCallback request_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
// |should_complete_lookup| should generally be true, unless you specifically
// want to test time-sensitive things like timeouts. Setting it to false will
// avoid calling into |response_callback| in |StartLookup|.
void SetThreatTypeForUrl(const GURL& gurl,
SBThreatType threat_type,
bool should_complete_lookup) {
url_details_[gurl.spec()].threat_type = threat_type;
url_details_[gurl.spec()].should_complete_lookup = should_complete_lookup;
}
void SetIsCachedResponse(bool is_cached_response) {
is_cached_response_ = is_cached_response;
}
// RealTimeUrlLookupServiceBase:
bool CanPerformFullURLLookup() const override { return true; }
bool CanCheckSubresourceURL() const override { return false; }
bool CanCheckSafeBrowsingDb() const override { return true; }
bool CanCheckSafeBrowsingHighConfidenceAllowlist() const override {
return true;
}
bool CanSendRTSampleRequest() const override { return true; }
private:
struct UrlDetail {
SBThreatType threat_type;
bool should_complete_lookup;
};
// RealTimeUrlLookupServiceBase:
GURL GetRealTimeLookupUrl() const override { return GURL(); }
net::NetworkTrafficAnnotationTag GetTrafficAnnotationTag() const override {
return TRAFFIC_ANNOTATION_FOR_TESTS;
}
bool CanPerformFullURLLookupWithToken() const override { return false; }
int GetReferrerUserGestureLimit() const override { return 0; }
bool CanSendPageLoadToken() const override { return false; }
void GetAccessToken(
const GURL& url,
const GURL& last_committed_url,
bool is_mainframe,
RTLookupRequestCallback request_callback,
RTLookupResponseCallback response_callback,
scoped_refptr<base::SequencedTaskRunner> callback_task_runner) override {}
absl::optional<std::string> GetDMTokenString() const override {
return absl::nullopt;
}
std::string GetMetricSuffix() const override { return ""; }
bool ShouldIncludeCredentials() const override { return false; }
double GetMinAllowedTimestampForReferrerChains() const override { return 0; }
base::flat_map<std::string, UrlDetail> url_details_;
bool is_cached_response_ = false;
};
} // namespace
class SafeBrowsingUrlCheckerTest : public PlatformTest {
public:
SafeBrowsingUrlCheckerTest() = default;
void SetUp() override {
PlatformTest::SetUp();
database_manager_ = new MockSafeBrowsingDatabaseManager();
url_checker_delegate_ = new MockUrlCheckerDelegate(database_manager_.get());
url_lookup_service_ = std::make_unique<MockRealTimeUrlLookupService>();
}
std::unique_ptr<SafeBrowsingUrlCheckerImpl> CreateSafeBrowsingUrlChecker(
bool real_time_lookup_enabled,
bool can_check_safe_browsing_db) {
base::MockCallback<base::RepeatingCallback<content::WebContents*()>>
mock_web_contents_getter;
return std::make_unique<SafeBrowsingUrlCheckerImpl>(
net::HttpRequestHeaders(), /*load_flags=*/0,
network::mojom::RequestDestination::kDocument,
/*has_user_gesture=*/false, url_checker_delegate_,
mock_web_contents_getter.Get(), UnsafeResource::kNoRenderProcessId,
UnsafeResource::kNoRenderFrameId, UnsafeResource::kNoFrameTreeNodeId,
real_time_lookup_enabled,
/*can_rt_check_subresource_url=*/false, can_check_safe_browsing_db,
/*can_check_high_confidence_allowlist=*/true,
/*url_lookup_service_metric_suffix=*/
real_time_lookup_enabled ? ".Enterprise" : ".None",
/*last_committed_url=*/GURL(),
base::SequencedTaskRunner::GetCurrentDefault(),
real_time_lookup_enabled ? url_lookup_service_->GetWeakPtr() : nullptr,
/*webui_delegate_=*/nullptr);
}
// This can be used as the CheckUrl callback in cases where it's a local check
// but it is not safe synchronously. Since the database manager is mocked out,
// this is only relevant for unsafe URL checks that are interrupted by a
// timeout, which ends up making them conclude the check is safe and call the
// slow check notifier callback.
void OnCheckUrlCallbackSettingSlowCheckNotifier(
SafeBrowsingUrlCheckerImpl::NativeUrlCheckNotifier* slow_check_notifier,
bool proceed,
bool showed_interstitial,
bool did_perform_real_time_check,
bool did_check_allowlist) {
*slow_check_notifier = slow_check_notifier_callback_.Get();
}
protected:
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
scoped_refptr<MockSafeBrowsingDatabaseManager> database_manager_;
scoped_refptr<MockUrlCheckerDelegate> url_checker_delegate_;
std::unique_ptr<MockRealTimeUrlLookupService> url_lookup_service_;
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeUrlCheckNotifier>
slow_check_notifier_callback_;
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_SafeUrl) {
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(callback,
Run(nullptr, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.CheckUrl.Timeout",
/*sample=*/false,
/*expected_bucket_count=*/1);
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_DangerousUrl) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(callback, Run(_, /*proceed=*/false, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::UNKNOWN), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RedirectUrlsSafe) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL origin_url("https://example.test/");
database_manager_->SetThreatTypeForUrl(origin_url, SB_THREAT_TYPE_SAFE,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
origin_callback;
EXPECT_CALL(origin_callback,
Run(_, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(origin_url, "GET", origin_callback.Get());
GURL redirect_url("https://example.redirect.test/");
database_manager_->SetThreatTypeForUrl(redirect_url, SB_THREAT_TYPE_SAFE,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
redirect_callback;
EXPECT_CALL(redirect_callback,
Run(_, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
safe_browsing_url_checker->CheckUrl(redirect_url, "GET",
redirect_callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest,
CheckUrl_RedirectUrlsOriginDangerousRedirectSafe) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false, /*can_check_safe_browsing_db=*/true);
GURL origin_url("https://example.test/");
database_manager_->SetThreatTypeForUrl(
origin_url, SB_THREAT_TYPE_URL_PHISHING, /*delayed_callback=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
origin_callback;
EXPECT_CALL(origin_callback,
Run(_, /*proceed=*/false, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
// Not displayed yet, because the callback is not returned.
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(origin_url, "GET", origin_callback.Get());
task_environment_.RunUntilIdle();
GURL redirect_url("https://example.redirect.test/");
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
redirect_callback;
// Not called because it is blocked by the first URL.
EXPECT_CALL(redirect_callback, Run(_, _, _, _, _)).Times(0);
safe_browsing_url_checker->CheckUrl(redirect_url, "GET",
redirect_callback.Get());
// The blocking page should be displayed when the origin callback is returned.
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(1);
database_manager_->RestartDelayedCallback(origin_url);
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledAllowlistMatch) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, true);
// To make sure hash based check is not skipped when the URL is in the
// allowlist, set threat type to phishing for hash based check.
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Note that the callback is not called, because resource fetch is not blocked
// while we perform a real time URL check.
EXPECT_CALL(callback, Run(_, _, _, _, _)).Times(0);
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::UNKNOWN), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledSafeUrl) {
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, false);
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(callback, Run(_, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/true,
/*did_check_allowlist=*/true));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.CheckUrl.Timeout",
/*sample=*/false,
/*expected_bucket_count=*/1);
// The false positive metric should not be logged, because the
// verdict is not from cache.
histograms.ExpectTotalCount("SafeBrowsing.RT.GetCache.FallbackThreatType",
/*count=*/0);
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_RealTimeEnabledSafeUrlFromCache) {
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, false);
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*delayed_callback=*/false);
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*should_complete_lookup=*/true);
url_lookup_service_->SetIsCachedResponse(true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(callback, Run(_, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/true,
/*did_check_allowlist=*/true));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.RT.GetCache.FallbackThreatType",
/*sample=*/SB_THREAT_TYPE_SAFE,
/*expected_bucket_count=*/1);
}
TEST_F(SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeUrlFromCacheFalsePositive) {
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, false);
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*delayed_callback=*/false);
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*should_complete_lookup=*/true);
url_lookup_service_->SetIsCachedResponse(true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::UNKNOWN), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.RT.GetCache.FallbackThreatType",
/*sample=*/SB_THREAT_TYPE_URL_PHISHING,
/*expected_bucket_count=*/1);
}
TEST_F(
SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_ManagedWarn_FeatureEnabled) {
scoped_feature_list_.InitAndEnableFeature(kRealTimeUrlFilteringForEnterprise);
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(
url, SB_THREAT_TYPE_MANAGED_POLICY_WARN, /*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Should still show warning page because real time lookup is enabled.
EXPECT_CALL(
*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::REAL_TIME_CHECK), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(
SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_ManagedWarn_FeatureNotEnabled) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(
url, SB_THREAT_TYPE_MANAGED_POLICY_WARN, /*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Should not show warning page because feature is not enabled.
EXPECT_CALL(
*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::REAL_TIME_CHECK), _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(
SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_ManagedBlock_FeatureEnabled) {
scoped_feature_list_.InitAndEnableFeature(kRealTimeUrlFilteringForEnterprise);
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(url,
SB_THREAT_TYPE_MANAGED_POLICY_BLOCK,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Should still show blocking page because real time lookup is enabled.
EXPECT_CALL(
*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::REAL_TIME_CHECK), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(
SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_ManagedBlock_FeatureNotEnabled) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(url,
SB_THREAT_TYPE_MANAGED_POLICY_BLOCK,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Should not show blocking page because the feature is not enabled.
EXPECT_CALL(
*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::REAL_TIME_CHECK), _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_Dangerous) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
// Should still show blocking page because real time lookup is enabled.
EXPECT_CALL(
*url_checker_delegate_,
StartDisplayingBlockingPageHelper(
IsSameThreatSource(ThreatSource::REAL_TIME_CHECK), _, _, _, _))
.Times(1);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest,
CheckUrl_RealTimeEnabledSafeBrowsingDisabled_Safe) {
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/false);
GURL url("https://example.test/");
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_SAFE,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback>
callback;
EXPECT_CALL(callback, Run(_, /*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/true,
/*did_check_allowlist=*/false));
EXPECT_CALL(*url_checker_delegate_,
StartDisplayingBlockingPageHelper(_, _, _, _, _))
.Times(0);
safe_browsing_url_checker->CheckUrl(url, "GET", callback.Get());
task_environment_.RunUntilIdle();
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_CancelCheckOnDestruct) {
// Do not cancel check for real-time checks.
{
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true, /*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, false);
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*should_complete_lookup=*/true);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback> cb;
safe_browsing_url_checker->CheckUrl(url, "GET", cb.Get());
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
safe_browsing_url_checker.reset();
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
task_environment_.RunUntilIdle();
}
// Do cancel check for local checks.
{
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false,
/*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*delayed_callback=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback> cb;
EXPECT_CALL(cb, Run(_, /*proceed=*/false, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
safe_browsing_url_checker->CheckUrl(url, "GET", cb.Get());
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
safe_browsing_url_checker.reset();
EXPECT_TRUE(database_manager_->HasCalledCancelCheck());
task_environment_.RunUntilIdle();
}
}
TEST_F(SafeBrowsingUrlCheckerTest, CheckUrl_CancelCheckOnTimeout) {
// Timeout for real-time checks should not cancel the database check.
{
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/true,
/*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetAllowlistResultForUrl(url, false);
url_lookup_service_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*should_complete_lookup=*/false);
base::MockCallback<SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback> cb;
EXPECT_CALL(cb, Run(_, /*proceed=*/true,
/*showed_interstitial=*/false,
/*did_perform_real_time_check=*/true,
/*did_check_allowlist=*/true));
safe_browsing_url_checker->CheckUrl(url, "GET", cb.Get());
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
task_environment_.FastForwardBy(base::Seconds(5));
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.CheckUrl.Timeout",
/*sample=*/true,
/*expected_bucket_count=*/1);
}
// Timeout for local checks should cancel the database check.
{
base::HistogramTester histograms;
auto safe_browsing_url_checker = CreateSafeBrowsingUrlChecker(
/*real_time_lookup_enabled=*/false,
/*can_check_safe_browsing_db=*/true);
GURL url("https://example.test/");
database_manager_->SetThreatTypeForUrl(url, SB_THREAT_TYPE_URL_PHISHING,
/*delayed_callback=*/true);
SafeBrowsingUrlCheckerImpl::NativeCheckUrlCallback callback =
base::BindOnce(&SafeBrowsingUrlCheckerTest::
OnCheckUrlCallbackSettingSlowCheckNotifier,
base::Unretained(this));
EXPECT_CALL(slow_check_notifier_callback_,
Run(/*proceed=*/true, /*showed_interstitial=*/false,
/*did_perform_real_time_check=*/false,
/*did_check_allowlist=*/false));
safe_browsing_url_checker->CheckUrl(url, "GET", std::move(callback));
EXPECT_FALSE(database_manager_->HasCalledCancelCheck());
task_environment_.FastForwardBy(base::Seconds(5));
EXPECT_TRUE(database_manager_->HasCalledCancelCheck());
task_environment_.RunUntilIdle();
histograms.ExpectUniqueSample("SafeBrowsing.CheckUrl.Timeout",
/*sample=*/true,
/*expected_bucket_count=*/1);
}
}
} // namespace safe_browsing