| // Copyright 2015 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/android/remote_database_manager.h" |
| |
| #include <map> |
| #include <memory> |
| |
| #include "base/metrics/field_trial.h" |
| #include "base/metrics/field_trial_params.h" |
| #include "base/run_loop.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 "components/safe_browsing/android/safe_browsing_api_handler_bridge.h" |
| #include "components/safe_browsing/android/safe_browsing_api_handler_test_util.h" |
| #include "components/safe_browsing/core/browser/db/database_manager.h" |
| #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h" |
| #include "components/safe_browsing/core/browser/db/v4_test_util.h" |
| #include "components/safe_browsing/core/common/features.h" |
| #include "components/variations/variations_associated_data.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/network/test/test_url_loader_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace safe_browsing { |
| |
| namespace { |
| |
| // Used to override response from SafeBrowsingApiHandlerBridge. |
| class TestUrlCheckInterceptor : public safe_browsing::UrlCheckInterceptor { |
| public: |
| TestUrlCheckInterceptor() = default; |
| ~TestUrlCheckInterceptor() override = default; |
| |
| // Checks the threat type of |url| previously set by |
| // |SetSafeBrowsingThreatTypeForUrl|. It crashes if the threat type of |url| |
| // is not set in advance. |
| void CheckBySafeBrowsing( |
| SafeBrowsingApiHandlerBridge::ResponseCallback callback, |
| const GURL& gurl) override { |
| std::string url = gurl.spec(); |
| DCHECK(base::Contains(urls_safebrowsing_threat_type_, url)); |
| std::move(callback).Run(urls_safebrowsing_threat_type_[url], |
| ThreatMetadata()); |
| } |
| |
| void SetSafeBrowsingThreatTypeForUrl(const GURL& url, |
| SBThreatType threat_type) { |
| urls_safebrowsing_threat_type_[url.spec()] = threat_type; |
| } |
| |
| private: |
| base::flat_map<std::string, SBThreatType> urls_safebrowsing_threat_type_; |
| }; |
| |
| // Used to verify the result returned from RemoteDatabaseManager is expected. |
| class TestClient : public SafeBrowsingDatabaseManager::Client { |
| public: |
| TestClient(scoped_refptr<RemoteSafeBrowsingDatabaseManager> db, |
| const GURL& expected_url, |
| SBThreatType expected_threat_type) |
| : SafeBrowsingDatabaseManager::Client(GetPassKeyForTesting()), |
| db_(db), |
| expected_url_(expected_url), |
| expected_threat_type_(expected_threat_type) {} |
| |
| ~TestClient() override { db_->CancelCheck(this); } |
| |
| void OnCheckBrowseUrlResult(const GURL& url, |
| SBThreatType threat_type, |
| const ThreatMetadata& metadata) override { |
| EXPECT_EQ(expected_url_, url); |
| EXPECT_EQ(expected_threat_type_, threat_type); |
| is_callback_called_ = true; |
| } |
| |
| bool IsCallbackCalled() { return is_callback_called_; } |
| |
| private: |
| scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_; |
| GURL expected_url_; |
| SBThreatType expected_threat_type_; |
| bool is_callback_called_ = false; |
| }; |
| |
| } // namespace |
| |
| class RemoteDatabaseManagerTest |
| : public testing::Test, |
| public safe_browsing::test::WithMockSafeBrowsingApiHandler { |
| protected: |
| using enum SBThreatType; |
| |
| RemoteDatabaseManagerTest() = default; |
| |
| void SetUp() override { |
| safe_browsing::test::WithMockSafeBrowsingApiHandler::SetUp(); |
| test_shared_loader_factory_ = |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_); |
| db_ = new RemoteSafeBrowsingDatabaseManager(); |
| db_->StartOnUIThread(test_shared_loader_factory_, |
| GetTestV4ProtocolConfig()); |
| |
| url_interceptor_ = std::make_unique<TestUrlCheckInterceptor>(); |
| SafeBrowsingApiHandlerBridge::GetInstance().SetInterceptorForTesting( |
| url_interceptor_.get()); |
| } |
| |
| void TearDown() override { |
| db_->StopOnUIThread(/*shutdown=*/false); |
| db_ = nullptr; |
| safe_browsing::test::WithMockSafeBrowsingApiHandler::TearDown(); |
| } |
| |
| content::BrowserTaskEnvironment task_environment_; |
| std::unique_ptr<TestUrlCheckInterceptor> url_interceptor_; |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; |
| scoped_refptr<RemoteSafeBrowsingDatabaseManager> db_; |
| base::HistogramTester histogram_tester_; |
| }; |
| |
| TEST_F(RemoteDatabaseManagerTest, CheckBrowseUrl_HashDatabase) { |
| GURL url("https://example.com"); |
| url_interceptor_->SetSafeBrowsingThreatTypeForUrl( |
| url, SB_THREAT_TYPE_URL_PHISHING); |
| TestClient client(db_, /*expected_url=*/url, |
| /*expected_threat_type=*/SB_THREAT_TYPE_URL_PHISHING); |
| |
| db_->CheckBrowseUrl(url, {SB_THREAT_TYPE_URL_PHISHING}, &client, |
| CheckBrowseUrlType::kHashDatabase); |
| |
| task_environment_.RunUntilIdle(); |
| EXPECT_TRUE(client.IsCallbackCalled()); |
| histogram_tester_.ExpectUniqueSample("SB2.RemoteCall.CanCheckUrl", |
| /*sample=*/true, |
| /*expected_bucket_count=*/1); |
| histogram_tester_.ExpectUniqueSample( |
| "SB2.RemoteCall.CanCheckUrl.HashDatabase", |
| /*sample=*/true, |
| /*expected_bucket_count=*/1); |
| histogram_tester_.ExpectTotalCount("SB2.RemoteCall.CanCheckUrl.HashRealTime", |
| /*expected_count=*/0); |
| } |
| |
| TEST_F(RemoteDatabaseManagerTest, CheckBrowseUrl_HashRealtime) { |
| GURL url("https://example.com"); |
| url_interceptor_->SetSafeBrowsingThreatTypeForUrl( |
| url, SB_THREAT_TYPE_URL_PHISHING); |
| TestClient client(db_, /*expected_url=*/url, |
| /*expected_threat_type=*/SB_THREAT_TYPE_URL_PHISHING); |
| |
| db_->CheckBrowseUrl(url, {SB_THREAT_TYPE_URL_PHISHING}, &client, |
| CheckBrowseUrlType::kHashRealTime); |
| |
| task_environment_.RunUntilIdle(); |
| EXPECT_TRUE(client.IsCallbackCalled()); |
| histogram_tester_.ExpectUniqueSample("SB2.RemoteCall.CanCheckUrl", |
| /*sample=*/true, |
| /*expected_bucket_count=*/1); |
| histogram_tester_.ExpectUniqueSample( |
| "SB2.RemoteCall.CanCheckUrl.HashRealTime", |
| /*sample=*/true, |
| /*expected_bucket_count=*/1); |
| histogram_tester_.ExpectTotalCount("SB2.RemoteCall.CanCheckUrl.HashDatabase", |
| /*expected_count=*/0); |
| } |
| |
| TEST_F(RemoteDatabaseManagerTest, ThreatSource) { |
| EXPECT_EQ(ThreatSource::ANDROID_SAFEBROWSING, |
| db_->GetBrowseUrlThreatSource(CheckBrowseUrlType::kHashDatabase)); |
| EXPECT_EQ(ThreatSource::ANDROID_SAFEBROWSING_REAL_TIME, |
| db_->GetBrowseUrlThreatSource(CheckBrowseUrlType::kHashRealTime)); |
| } |
| |
| TEST_F(RemoteDatabaseManagerTest, MatchDownloadAllowlistUrl) { |
| GURL allowlisted_url{"https://www.example.test"}; |
| AddLocalAllowlistEntry(allowlisted_url, /*is_download_allowlist=*/true, |
| /*is_match=*/true); |
| |
| // Allowlisted URL should match. |
| { |
| base::test::TestFuture<bool> result_future; |
| db_->MatchDownloadAllowlistUrl(allowlisted_url, |
| result_future.GetCallback()); |
| EXPECT_TRUE(result_future.Get()); |
| } |
| |
| // Not on the allowlist. |
| { |
| GURL url{"https://www.notexample.test"}; |
| base::test::TestFuture<bool> result_future; |
| db_->MatchDownloadAllowlistUrl(url, result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| |
| // Not checked because of URL scheme. |
| { |
| GURL url{"data:,Hello%2C%20World%21"}; |
| base::test::TestFuture<bool> result_future; |
| db_->MatchDownloadAllowlistUrl(url, result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| { |
| GURL url{"file:///usr/home/foo"}; |
| base::test::TestFuture<bool> result_future; |
| db_->MatchDownloadAllowlistUrl(url, result_future.GetCallback()); |
| EXPECT_FALSE(result_future.Get()); |
| } |
| } |
| |
| } // namespace safe_browsing |