blob: 1324ff8f2244f9e12cfe140629474a68d22492cc [file] [log] [blame]
// Copyright 2012 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/content/browser/client_side_detection_service.h"
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/field_trial.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chrome/browser/safe_browsing/chrome_client_side_detection_service_delegate.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/base/testing_profile_manager.h"
#include "components/optimization_guide/core/delivery/test_model_info_builder.h"
#include "components/optimization_guide/core/delivery/test_optimization_guide_model_provider.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/client_model.pb.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/safe_browsing/core/common/safebrowsing_constants.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/test/browser_task_environment.h"
#include "crypto/sha2.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
using content::BrowserThread;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::NiceMock;
using ::testing::StrictMock;
namespace safe_browsing {
class ClientSidePhishingModelObserverTracker
: public optimization_guide::TestOptimizationGuideModelProvider {
public:
void AddObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget optimization_target,
const std::optional<optimization_guide::proto::Any>& model_metadata,
optimization_guide::OptimizationTargetModelObserver* observer) override {
if (optimization_target ==
optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING) {
EXPECT_FALSE(model_observer_);
model_observer_ = observer;
}
}
void RemoveObserverForOptimizationTargetModel(
optimization_guide::proto::OptimizationTarget optimization_target,
optimization_guide::OptimizationTargetModelObserver* observer) override {
if (optimization_target ==
optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING) {
EXPECT_EQ(observer, model_observer_);
model_observer_ = nullptr;
}
}
// Notifies the model validation observer about the model file update.
void NotifyModelFileUpdate(
optimization_guide::proto::OptimizationTarget optimization_target,
const base::FilePath& model_file_path,
const base::flat_set<base::FilePath>& additional_files_path) {
if (optimization_target ==
optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING) {
auto model_metadata = optimization_guide::TestModelInfoBuilder()
.SetModelFilePath(model_file_path)
.SetAdditionalFiles(additional_files_path)
.Build();
model_observer_->OnModelUpdated(optimization_target, *model_metadata);
}
}
private:
// The observer that is registered to receive model validation optimzation
// target events.
raw_ptr<optimization_guide::OptimizationTargetModelObserver> model_observer_;
};
class ClientSideDetectionServiceTest
: public testing::Test,
public testing::WithParamInterface<bool> {
public:
ClientSideDetectionServiceTest()
: profile_manager_(TestingBrowserProcess::GetGlobal()) {
EXPECT_TRUE(profile_manager_.SetUp());
profile_ = profile_manager_.CreateTestingProfile("test-user");
std::vector<base::test::FeatureRefAndParams> enabled_features = {};
if (ShouldEnableESBDailyPhishingLimit()) {
base::FieldTrialParams params;
params["kMaxReportsPerIntervalESB"] = "10";
enabled_features.push_back(
{kSafeBrowsingDailyPhishingReportsLimit, params});
}
feature_list_.InitWithFeaturesAndParameters(enabled_features, {});
}
bool ShouldEnableESBDailyPhishingLimit() { return GetParam(); }
protected:
void SetUp() override {
test_shared_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
model_observer_tracker_ =
std::make_unique<ClientSidePhishingModelObserverTracker>();
}
void TearDown() override {
base::RunLoop().RunUntilIdle();
csd_service_.reset();
}
void ValidateModel(
const base::FilePath& model_file_path,
const base::flat_set<base::FilePath>& additional_file_path) {
model_observer_tracker_->NotifyModelFileUpdate(
optimization_guide::proto::OPTIMIZATION_TARGET_CLIENT_SIDE_PHISHING,
model_file_path, additional_file_path);
task_environment_.RunUntilIdle();
}
void ReadModelAndTfLiteFiles() {
base::FilePath model_file_path;
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &model_file_path);
model_file_path = model_file_path.AppendASCII("components")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("safe_browsing")
.AppendASCII("client_model.pb");
base::FilePath additional_files_path;
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT,
&additional_files_path);
additional_files_path = additional_files_path.AppendASCII("components")
.AppendASCII("test")
.AppendASCII("data")
.AppendASCII("safe_browsing");
#if BUILDFLAG(IS_ANDROID)
additional_files_path =
additional_files_path.AppendASCII("visual_model_android.tflite");
#else
additional_files_path =
additional_files_path.AppendASCII("visual_model_desktop.tflite");
#endif
ValidateModel(model_file_path, {additional_files_path});
}
bool SendClientReportPhishingRequest(const GURL& phishing_url,
float score,
const std::string& access_token) {
std::unique_ptr<ClientPhishingRequest> request =
std::make_unique<ClientPhishingRequest>(ClientPhishingRequest());
request->set_url(phishing_url.spec());
request->set_client_score(score);
request->set_is_phishing(true); // client thinks the URL is phishing.
base::RunLoop run_loop;
csd_service_->SendClientReportPhishingRequest(
std::move(request),
base::BindOnce(&ClientSideDetectionServiceTest::SendRequestDone,
base::Unretained(this), run_loop.QuitWhenIdleClosure()),
access_token);
phishing_url_ = phishing_url;
run_loop.Run(); // Waits until callback is called.
return is_phishing_;
}
void SetResponse(const GURL& url,
const std::string& response_data,
int net_error) {
if (net_error != net::OK) {
test_url_loader_factory_.AddResponse(
url, network::mojom::URLResponseHead::New(), std::string(),
network::URLLoaderCompletionStatus(net_error));
return;
}
test_url_loader_factory_.AddResponse(url.spec(), response_data);
}
void SetClientReportPhishingResponse(const std::string& response_data,
int net_error) {
SetResponse(ClientSideDetectionService::GetClientReportUrl(
ClientSideDetectionService::kClientReportPhishingUrl),
response_data, net_error);
}
bool AtPhishingReportLimit() { return csd_service_->AtPhishingReportLimit(); }
std::deque<base::Time>& GetPhishingReportTimes() {
return csd_service_->phishing_report_times_;
}
void TestCache() {
auto& cache = csd_service_->cache_;
EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) == cache.end());
base::Time now = base::Time::Now();
base::Time time =
now -
base::Days(ClientSideDetectionService::kNegativeCacheIntervalDays) +
base::Minutes(5);
cache[GURL("http://first.url.com/")] =
std::make_unique<ClientSideDetectionService::CacheState>(false, time);
time = now -
base::Days(ClientSideDetectionService::kNegativeCacheIntervalDays) -
base::Hours(1);
cache[GURL("http://second.url.com/")] =
std::make_unique<ClientSideDetectionService::CacheState>(false, time);
time = now -
base::Minutes(
ClientSideDetectionService::kPositiveCacheIntervalMinutes) -
base::Minutes(5);
cache[GURL("http://third.url.com/")] =
std::make_unique<ClientSideDetectionService::CacheState>(true, time);
time = now -
base::Minutes(
ClientSideDetectionService::kPositiveCacheIntervalMinutes) +
base::Minutes(5);
cache[GURL("http://fourth.url.com/")] =
std::make_unique<ClientSideDetectionService::CacheState>(true, time);
csd_service_->UpdateCache();
// 3 elements should be in the cache, the first, third, and fourth.
EXPECT_EQ(3U, cache.size());
EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) != cache.end());
EXPECT_TRUE(cache.find(GURL("http://third.url.com/")) != cache.end());
EXPECT_TRUE(cache.find(GURL("http://fourth.url.com/")) != cache.end());
// While 3 elements remain, only the first and the fourth are actually
// valid.
bool is_phishing;
EXPECT_TRUE(csd_service_->GetValidCachedResult(GURL("http://first.url.com"),
&is_phishing));
EXPECT_FALSE(is_phishing);
EXPECT_FALSE(csd_service_->GetValidCachedResult(
GURL("http://third.url.com"), &is_phishing));
EXPECT_TRUE(csd_service_->GetValidCachedResult(
GURL("http://fourth.url.com"), &is_phishing));
EXPECT_TRUE(is_phishing);
}
protected:
content::BrowserTaskEnvironment task_environment_;
TestingProfileManager profile_manager_;
raw_ptr<TestingProfile> profile_;
std::unique_ptr<ClientSideDetectionService> csd_service_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<ClientSidePhishingModelObserverTracker>
model_observer_tracker_;
private:
void SendRequestDone(
base::OnceClosure continuation_callback,
GURL phishing_url,
bool is_phishing,
std::optional<net::HttpStatusCode> response_code,
std::optional<IntelligentScanVerdict> intelligent_scan_verdict) {
ASSERT_EQ(phishing_url, phishing_url_);
is_phishing_ = is_phishing;
std::move(continuation_callback).Run();
}
std::unique_ptr<base::FieldTrialList> field_trials_;
GURL phishing_url_;
bool is_phishing_;
};
INSTANTIATE_TEST_SUITE_P(All, ClientSideDetectionServiceTest, testing::Bool());
TEST_P(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
EXPECT_NE(csd_service_.get(), nullptr);
// We delete the client-side detection service class even though the callbacks
// haven't run yet.
csd_service_.reset();
// Waiting for the callbacks to run should not crash even if the service
// object is gone.
base::RunLoop().RunUntilIdle();
}
TEST_P(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
csd_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
GURL url("http://a.com/");
float score = 0.4f; // Some random client score.
std::string access_token;
// Safe browsing is not enabled.
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
EXPECT_FALSE(SendClientReportPhishingRequest(url, score, access_token));
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
base::Time before = base::Time::Now();
// Invalid response body from the server, but we will still track it as a
// ping count.
auto histogram_tester = std::make_unique<base::HistogramTester>();
SetClientReportPhishingResponse("invalid proto response", net::OK);
EXPECT_FALSE(SendClientReportPhishingRequest(url, score, access_token));
histogram_tester->ExpectUniqueSample(
/*name=*/"SBClientPhishing.NetworkResult2",
/*sample=*/net::HTTP_OK,
/*expected_bucket_count=*/1);
// Normal behavior with no access token.
histogram_tester = std::make_unique<base::HistogramTester>();
ClientPhishingResponse response;
response.set_phishy(true);
SetClientReportPhishingResponse(response.SerializeAsString(), net::OK);
EXPECT_TRUE(SendClientReportPhishingRequest(url, score, access_token));
histogram_tester->ExpectUniqueSample(
/*name=*/"SBClientPhishing.NetworkResult2",
/*sample=*/net::HTTP_OK,
/*expected_bucket_count=*/1);
// This request will fail, but not because of the cap, but because the network
// failed, but we will still log the number of pings sent.
histogram_tester = std::make_unique<base::HistogramTester>();
EXPECT_FALSE(AtPhishingReportLimit());
GURL second_url("http://b.com/");
response.set_phishy(false);
SetClientReportPhishingResponse(response.SerializeAsString(),
net::ERR_FAILED);
EXPECT_FALSE(
SendClientReportPhishingRequest(second_url, score, access_token));
histogram_tester->ExpectUniqueSample(
/*name=*/"SBClientPhishing.NetworkResult2",
/*sample=*/net::ERR_FAILED,
/*expected_bucket_count=*/1);
// We have sent 3 pings so far, which is the cap.
EXPECT_TRUE(AtPhishingReportLimit());
GURL third_url("http://c.com/");
response.set_phishy(true);
SetClientReportPhishingResponse(response.SerializeAsString(), net::OK);
// Although this is a normal behavior, we are capped in the number of pings,
// so this will expect false.
EXPECT_FALSE(SendClientReportPhishingRequest(third_url, score, access_token));
base::Time after = base::Time::Now();
// Check that we have recorded 3 requests within the correct time range. The
// third_url is not recorded because the send was attempted while we are at
// the limit.
std::deque<base::Time>& report_times = GetPhishingReportTimes();
EXPECT_EQ(3U, report_times.size());
EXPECT_TRUE(AtPhishingReportLimit());
while (!report_times.empty()) {
base::Time time = report_times.back();
report_times.pop_back();
EXPECT_LE(before, time);
EXPECT_GE(after, time);
}
// Only the first url should be in the cache.
bool is_phishing;
EXPECT_TRUE(csd_service_->GetValidCachedResult(url, &is_phishing));
EXPECT_TRUE(is_phishing);
bool is_second_url_phishing = false;
EXPECT_FALSE(
csd_service_->GetValidCachedResult(second_url, &is_second_url_phishing));
EXPECT_FALSE(is_second_url_phishing);
bool is_third_url_phishing = false;
EXPECT_FALSE(
csd_service_->GetValidCachedResult(third_url, &is_third_url_phishing));
EXPECT_FALSE(is_third_url_phishing);
}
TEST_P(ClientSideDetectionServiceTest,
SendClientReportPhishingRequestWithToken) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
csd_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
GURL url("http://a.com/");
float score = 0.4f; // Some random client score.
std::string access_token = "fake access token";
ClientPhishingResponse response;
response.set_phishy(true);
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
EXPECT_THAT(
request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
testing::Optional("Bearer " + access_token));
// Cookies should still be included when token is set.
EXPECT_EQ(request.credentials_mode,
network::mojom::CredentialsMode::kInclude);
}));
SetClientReportPhishingResponse(response.SerializeAsString(), net::OK);
EXPECT_TRUE(SendClientReportPhishingRequest(url, score, access_token));
}
TEST_P(ClientSideDetectionServiceTest,
SendClientReportPhishingRequestWithoutToken) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
csd_service_->SetURLLoaderFactoryForTesting(test_shared_loader_factory_);
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
GURL url("http://a.com/");
float score = 0.4f; // Some random client score.
std::string access_token = "";
ClientPhishingResponse response;
response.set_phishy(true);
test_url_loader_factory_.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
EXPECT_EQ(
request.headers.GetHeader(net::HttpRequestHeaders::kAuthorization),
std::nullopt);
// Cookies should be attached when token is empty.
EXPECT_EQ(request.credentials_mode,
network::mojom::CredentialsMode::kInclude);
}));
SetClientReportPhishingResponse(response.SerializeAsString(), net::OK);
EXPECT_TRUE(SendClientReportPhishingRequest(url, score, access_token));
}
TEST_P(ClientSideDetectionServiceTest, GetNumReportTest) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
base::Time now = base::Time::Now();
base::TimeDelta twenty_five_hours = base::Hours(25);
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_EQ(2, csd_service_->GetPhishingNumReports());
EXPECT_FALSE(AtPhishingReportLimit());
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_EQ(3, csd_service_->GetPhishingNumReports());
EXPECT_TRUE(AtPhishingReportLimit());
}
TEST_P(ClientSideDetectionServiceTest,
GetNumReportTestWhenPrefsPreloadedAndOverLimit) {
// The current report limit is 3 as per
// ClientSideDetectionService::kMaxReportsPerInterval.
base::Value::List time_list;
time_list.Append(base::Value(base::Time::Now().InSecondsFSinceUnixEpoch()));
time_list.Append(base::Value(base::Time::Now().InSecondsFSinceUnixEpoch()));
time_list.Append(base::Value(base::Time::Now().InSecondsFSinceUnixEpoch()));
profile_->GetPrefs()->SetList(prefs::kSafeBrowsingCsdPingTimestamps,
std::move(time_list));
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
EXPECT_TRUE(AtPhishingReportLimit());
}
TEST_P(ClientSideDetectionServiceTest,
GetNumReportTestWhenPrefsPreloadedNotOverLimit) {
// The current report limit is 3 as per
// ClientSideDetectionService::kMaxReportsPerInterval.
base::Value::List time_list;
time_list.Append(base::Value(base::Time::Now().InSecondsFSinceUnixEpoch()));
time_list.Append(base::Value(base::Time::Now().InSecondsFSinceUnixEpoch()));
profile_->GetPrefs()->SetList(prefs::kSafeBrowsingCsdPingTimestamps,
std::move(time_list));
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
EXPECT_FALSE(AtPhishingReportLimit());
}
TEST_P(ClientSideDetectionServiceTest, GetNumReportTestESB) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
base::Time now = base::Time::Now();
base::TimeDelta twenty_five_hours = base::Hours(25);
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now - twenty_five_hours));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_EQ(2, csd_service_->GetPhishingNumReports());
// We have not quite hit the limit for both ESB and SSB users.
EXPECT_FALSE(AtPhishingReportLimit());
// Adding one more will hit the limit just for SSB users.
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_EQ(3, csd_service_->GetPhishingNumReports());
if (base::FeatureList::IsEnabled(kSafeBrowsingDailyPhishingReportsLimit)) {
EXPECT_FALSE(AtPhishingReportLimit());
} else {
EXPECT_TRUE(AtPhishingReportLimit());
}
// Adding 7 more to 10 reports total will hit the limit for ESB users as the
// limit is predefined in this class.
if (base::FeatureList::IsEnabled(kSafeBrowsingDailyPhishingReportsLimit)) {
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
EXPECT_TRUE(csd_service_->AddPhishingReport(now));
} else {
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
EXPECT_FALSE(csd_service_->AddPhishingReport(now));
}
EXPECT_TRUE(AtPhishingReportLimit());
}
TEST_P(ClientSideDetectionServiceTest, CacheTest) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
ReadModelAndTfLiteFiles();
TestCache();
}
TEST_P(ClientSideDetectionServiceTest, IsPrivateIPAddress) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
net::IPAddress address;
EXPECT_TRUE(address.AssignFromIPLiteral("10.1.2.3"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("127.0.0.1"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("172.24.3.4"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("192.168.1.1"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("fc00::"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("fec0::"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("fec0:1:2::3"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("::1"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("::ffff:192.168.1.1"));
EXPECT_TRUE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("1.2.3.4"));
EXPECT_FALSE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("200.1.1.1"));
EXPECT_FALSE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("2001:0db8:ac10:fe01::"));
EXPECT_FALSE(csd_service_->IsPrivateIPAddress(address));
EXPECT_TRUE(address.AssignFromIPLiteral("::ffff:23c5:281b"));
EXPECT_FALSE(csd_service_->IsPrivateIPAddress(address));
}
TEST_P(ClientSideDetectionServiceTest, IsLocalResource) {
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
net::IPAddress address;
EXPECT_TRUE(csd_service_->IsLocalResource(address));
// Create an IP address of invalid length
uint8_t addr[5] = {0xFE, 0xDC, 0xBA, 0x98};
address = net::IPAddress(addr);
EXPECT_TRUE(csd_service_->IsLocalResource(address));
EXPECT_TRUE(address.AssignFromIPLiteral("1.2.3.4"));
EXPECT_FALSE(csd_service_->IsLocalResource(address));
}
TEST_P(ClientSideDetectionServiceTest, TestModelFollowsPrefs) {
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, false);
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingScoutReportingEnabled,
false);
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
// Safe Browsing is not enabled.
EXPECT_FALSE(csd_service_->enabled());
// Safe Browsing is enabled.
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
EXPECT_TRUE(csd_service_->enabled());
}
TEST_P(ClientSideDetectionServiceTest,
TestReceivingImageEmbedderUpdatesAfterResubscription) {
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnabled, true);
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
csd_service_ = std::make_unique<ClientSideDetectionService>(
std::make_unique<ChromeClientSideDetectionServiceDelegate>(profile_),
model_observer_tracker_.get());
EXPECT_TRUE(csd_service_->IsSubscribedToImageEmbeddingModelUpdates());
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, false);
EXPECT_FALSE(csd_service_->IsSubscribedToImageEmbeddingModelUpdates());
profile_->GetPrefs()->SetBoolean(prefs::kSafeBrowsingEnhanced, true);
EXPECT_TRUE(csd_service_->IsSubscribedToImageEmbeddingModelUpdates());
}
} // namespace safe_browsing