blob: cfb98491c04a648a39e897ffcb4abeb8804f854d [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include <memory>
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/browser/download/download_item_warning_data.h"
#include "chrome/browser/safe_browsing/chrome_ping_manager_factory.h"
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#include "chrome/test/base/testing_browser_process.h"
#include "chrome/test/base/testing_profile.h"
#include "components/download/public/common/download_danger_type.h"
#include "components/download/public/common/mock_download_item.h"
#include "components/safe_browsing/content/browser/safe_browsing_service_interface.h"
#include "components/safe_browsing/core/browser/ping_manager.h"
#include "components/safe_browsing/core/common/features.h"
#include "components/safe_browsing/core/common/proto/csd.pb.h"
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "content/public/browser/download_item_utils.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "services/network/test/test_utils.h"
using network::GetUploadData;
using testing::Return;
using testing::ReturnRef;
using WarningSurface = DownloadItemWarningData::WarningSurface;
using WarningAction = DownloadItemWarningData::WarningAction;
using WarningActionEvent = DownloadItemWarningData::WarningActionEvent;
using DownloadWarningAction =
safe_browsing::ClientSafeBrowsingReportRequest::DownloadWarningAction;
namespace safe_browsing {
namespace {
const char kTestDownloadUrl[] = "https://example.com";
}
class SafeBrowsingServiceTest : public testing::Test {
public:
SafeBrowsingServiceTest() {
feature_list_.InitAndEnableFeature(
safe_browsing::kSafeBrowsingCsbrrNewDownloadTrigger);
}
void SetUp() override {
browser_process_ = TestingBrowserProcess::GetGlobal();
safe_browsing::SafeBrowsingServiceInterface::RegisterFactory(
GetSafeBrowsingServiceFactory());
// TODO(crbug/925153): Port consumers of the |sb_service_| to use
// the interface in components/safe_browsing, and remove this cast.
sb_service_ = static_cast<SafeBrowsingService*>(
safe_browsing::SafeBrowsingService::CreateSafeBrowsingService());
browser_process_->SetSafeBrowsingService(sb_service_.get());
sb_service_->Initialize();
base::RunLoop().RunUntilIdle();
profile_ = std::make_unique<TestingProfile>();
#if BUILDFLAG(IS_CHROMEOS_ASH)
// Local state is needed to construct ProxyConfigService, which is a
// dependency of PingManager on ChromeOS.
TestingBrowserProcess::GetGlobal()->SetLocalState(profile_->GetPrefs());
#endif
}
void TearDown() override {
browser_process_->safe_browsing_service()->ShutDown();
browser_process_->SetSafeBrowsingService(nullptr);
safe_browsing::SafeBrowsingServiceInterface::RegisterFactory(nullptr);
#if BUILDFLAG(IS_CHROMEOS_ASH)
TestingBrowserProcess::GetGlobal()->SetLocalState(nullptr);
#endif
base::RunLoop().RunUntilIdle();
}
Profile* profile() { return profile_.get(); }
protected:
void SetUpDownload() {
content::DownloadItemUtils::AttachInfoForTesting(&download_item_, profile(),
/*web_contents=*/nullptr);
EXPECT_CALL(download_item_, GetDangerType())
.WillRepeatedly(Return(download::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST));
EXPECT_CALL(download_item_, GetURL())
.WillRepeatedly(ReturnRef(download_url_));
DownloadProtectionService::SetDownloadProtectionData(
&download_item_, "download_token",
ClientDownloadResponse::DANGEROUS_HOST,
ClientDownloadResponse::TailoredVerdict());
DownloadItemWarningData::AddWarningActionEvent(
&download_item_, WarningSurface::BUBBLE_MAINPAGE, WarningAction::SHOWN);
DownloadItemWarningData::AddWarningActionEvent(
&download_item_, WarningSurface::BUBBLE_SUBPAGE, WarningAction::CLOSE);
DownloadItemWarningData::AddWarningActionEvent(
&download_item_, WarningSurface::DOWNLOADS_PAGE,
WarningAction::DISCARD);
}
std::unique_ptr<ClientSafeBrowsingReportRequest> GetActualRequest(
const network::ResourceRequest& request) {
std::string request_string = GetUploadData(request);
auto actual_request = std::make_unique<ClientSafeBrowsingReportRequest>();
actual_request->ParseFromString(request_string);
return actual_request;
}
void VerifyDownloadReportRequest(
ClientSafeBrowsingReportRequest* actual_request) {
EXPECT_EQ(actual_request->type(),
ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_OPENED);
EXPECT_EQ(actual_request->download_verdict(),
ClientDownloadResponse::DANGEROUS_HOST);
EXPECT_EQ(actual_request->url(), download_url_.spec());
EXPECT_TRUE(actual_request->did_proceed());
EXPECT_TRUE(actual_request->show_download_in_folder());
EXPECT_EQ(actual_request->token(), "download_token");
}
void VerifyInteractionOccurrenceCount(
const ClientSafeBrowsingReportRequest& report,
const ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PhishySiteInteractionType& expected_interaction_type,
const int& expected_occurrence_count) {
// Find the interaction within the report by comparing
// security_interstitial_interaction.
for (auto interaction : report.phishy_site_interactions()) {
if (interaction.phishy_site_interaction_type() ==
expected_interaction_type) {
EXPECT_EQ(interaction.occurrence_count(), expected_occurrence_count);
break;
}
}
}
void VerifyPhishySiteReportRequest(
ClientSafeBrowsingReportRequest* actual_request,
GURL& expected_url,
int expected_interaction_size,
int expected_click_occurrences,
int expected_key_occurrences,
int expected_paste_occurrences) {
EXPECT_EQ(actual_request->type(),
ClientSafeBrowsingReportRequest::PHISHY_SITE_INTERACTIONS);
EXPECT_EQ(actual_request->url(), expected_url.spec());
EXPECT_EQ(actual_request->phishy_site_interactions_size(),
expected_interaction_size);
VerifyInteractionOccurrenceCount(
*actual_request,
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_CLICK_EVENT,
expected_click_occurrences);
VerifyInteractionOccurrenceCount(
*actual_request,
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_KEY_EVENT,
expected_key_occurrences);
VerifyInteractionOccurrenceCount(
*actual_request,
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_PASTE_EVENT,
expected_paste_occurrences);
}
void VerifyDownloadWarningAction(
const ClientSafeBrowsingReportRequest::DownloadWarningAction&
warning_action,
DownloadWarningAction::Surface surface,
DownloadWarningAction::Action action,
bool is_terminal_action,
int64_t interval_msec) {
EXPECT_EQ(warning_action.surface(), surface);
EXPECT_EQ(warning_action.action(), action);
EXPECT_EQ(warning_action.is_terminal_action(), is_terminal_action);
EXPECT_EQ(warning_action.interval_msec(), interval_msec);
}
content::BrowserTaskEnvironment task_environment_;
raw_ptr<TestingBrowserProcess> browser_process_;
scoped_refptr<SafeBrowsingService> sb_service_;
std::unique_ptr<TestingProfile> profile_;
::testing::NiceMock<download::MockDownloadItem> download_item_;
GURL download_url_ = GURL(kTestDownloadUrl);
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(SafeBrowsingServiceTest, SendDownloadReport_Success) {
SetUpDownload();
SetExtendedReportingPrefForTests(profile_->GetPrefs(), true);
auto* ping_manager =
ChromePingManagerFactory::GetForBrowserContext(profile());
network::TestURLLoaderFactory test_url_loader_factory;
test_url_loader_factory.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::unique_ptr<ClientSafeBrowsingReportRequest> actual_request =
GetActualRequest(request);
VerifyDownloadReportRequest(actual_request.get());
ASSERT_EQ(actual_request->download_warning_actions().size(), 2);
VerifyDownloadWarningAction(
actual_request->download_warning_actions().Get(0),
DownloadWarningAction::BUBBLE_SUBPAGE, DownloadWarningAction::CLOSE,
/*is_terminal_action=*/false, /*interval_msec=*/0);
VerifyDownloadWarningAction(
actual_request->download_warning_actions().Get(1),
DownloadWarningAction::DOWNLOADS_PAGE,
DownloadWarningAction::DISCARD,
/*is_terminal_action=*/true, /*interval_msec=*/0);
}));
ping_manager->SetURLLoaderFactoryForTesting(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
EXPECT_TRUE(sb_service_->SendDownloadReport(
&download_item_,
ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_OPENED,
/*did_proceed=*/true,
/*show_download_in_folder=*/true));
}
TEST_F(
SafeBrowsingServiceTest,
SendDownloadReport_NoDownloadWarningActionWhenExtendedReportingDisabled) {
SetUpDownload();
SetExtendedReportingPrefForTests(profile_->GetPrefs(), false);
auto* ping_manager =
ChromePingManagerFactory::GetForBrowserContext(profile());
network::TestURLLoaderFactory test_url_loader_factory;
test_url_loader_factory.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::unique_ptr<ClientSafeBrowsingReportRequest> actual_request =
GetActualRequest(request);
EXPECT_TRUE(actual_request->download_warning_actions().empty());
}));
ping_manager->SetURLLoaderFactoryForTesting(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
EXPECT_TRUE(sb_service_->SendDownloadReport(
&download_item_,
ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_OPENED,
/*did_proceed=*/true,
/*show_download_in_folder=*/true));
}
TEST_F(SafeBrowsingServiceTest, SendPhishyInteractionsReport_Disabled) {
SetExtendedReportingPrefForTests(profile_->GetPrefs(), true);
GURL test_url("http://phishing.com");
GURL test_page_url("http://page_url.com");
PhishySiteInteractionMap test_map = PhishySiteInteractionMap();
EXPECT_FALSE(sb_service_->SendPhishyInteractionsReport(
profile(), test_url, test_page_url, test_map));
}
class SafeBrowsingServiceTestWithCsbrrNewTriggerDisabled
: public SafeBrowsingServiceTest {
public:
SafeBrowsingServiceTestWithCsbrrNewTriggerDisabled() {
feature_list_.InitAndDisableFeature(
safe_browsing::kSafeBrowsingCsbrrNewDownloadTrigger);
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(SafeBrowsingServiceTestWithCsbrrNewTriggerDisabled,
SendDownloadReport_NoDownloadWarningActionWhenFeatureFlagDisabled) {
SetUpDownload();
SetExtendedReportingPrefForTests(profile_->GetPrefs(), true);
auto* ping_manager =
ChromePingManagerFactory::GetForBrowserContext(profile());
network::TestURLLoaderFactory test_url_loader_factory;
test_url_loader_factory.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::unique_ptr<ClientSafeBrowsingReportRequest> actual_request =
GetActualRequest(request);
EXPECT_TRUE(actual_request->download_warning_actions().empty());
}));
ping_manager->SetURLLoaderFactoryForTesting(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
EXPECT_TRUE(sb_service_->SendDownloadReport(
&download_item_,
ClientSafeBrowsingReportRequest::DANGEROUS_DOWNLOAD_OPENED,
/*did_proceed=*/true,
/*show_download_in_folder=*/true));
}
class SafeBrowsingServiceTestWithAntiPhishingTelemetryEnabled
: public SafeBrowsingServiceTest {
public:
SafeBrowsingServiceTestWithAntiPhishingTelemetryEnabled() {
feature_list_.InitAndEnableFeature(safe_browsing::kAntiPhishingTelemetry);
}
protected:
PhishySiteInteractionMap SetUpPhishyInteractionMap(
int expected_click_occurrences,
int expected_key_occurrences,
int expected_paste_occurrences) {
PhishySiteInteractionMap new_map;
if (expected_click_occurrences > 0) {
new_map.insert_or_assign(
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_CLICK_EVENT,
PhishyPageInteractionDetails(expected_click_occurrences,
base::Time::Now().ToJavaTime(),
base::Time::Now().ToJavaTime()));
}
if (expected_key_occurrences > 0) {
new_map.insert_or_assign(
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_KEY_EVENT,
PhishyPageInteractionDetails(expected_key_occurrences,
base::Time::Now().ToJavaTime(),
base::Time::Now().ToJavaTime()));
}
if (expected_paste_occurrences > 0) {
new_map.insert_or_assign(
ClientSafeBrowsingReportRequest::PhishySiteInteraction::
PHISHY_PASTE_EVENT,
PhishyPageInteractionDetails(expected_paste_occurrences,
base::Time::Now().ToJavaTime(),
base::Time::Now().ToJavaTime()));
}
return new_map;
}
private:
base::test::ScopedFeatureList feature_list_;
};
TEST_F(SafeBrowsingServiceTestWithAntiPhishingTelemetryEnabled,
SendPhishyInteractionsReport_Success) {
const int kExpectedClickEventCount = 5;
const int kExpectedKeyEventCount = 2;
const int kExpectedPasteEventCount = 0;
SetExtendedReportingPrefForTests(profile_->GetPrefs(), true);
GURL test_url("http://phishing.com");
GURL test_page_url("http://page_url.com");
PhishySiteInteractionMap test_map = SetUpPhishyInteractionMap(
kExpectedClickEventCount, kExpectedKeyEventCount,
kExpectedPasteEventCount);
auto* ping_manager =
ChromePingManagerFactory::GetForBrowserContext(profile());
network::TestURLLoaderFactory test_url_loader_factory;
test_url_loader_factory.SetInterceptor(
base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
std::unique_ptr<ClientSafeBrowsingReportRequest> actual_request =
GetActualRequest(request);
VerifyPhishySiteReportRequest(
actual_request.get(), test_url, 2, kExpectedClickEventCount,
kExpectedKeyEventCount, kExpectedPasteEventCount);
}));
ping_manager->SetURLLoaderFactoryForTesting(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory));
EXPECT_TRUE(sb_service_->SendPhishyInteractionsReport(
profile(), test_url, test_page_url, test_map));
}
} // namespace safe_browsing