| // Copyright 2016 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/db/v4_local_database_manager.h" |
| |
| #include <utility> |
| |
| #include "base/command_line.h" |
| #include "base/containers/fixed_flat_map.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/run_loop.h" |
| #include "base/strings/string_tokenizer.h" |
| #include "base/task/sequenced_task_runner.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/task_environment.h" |
| #include "base/test/test_simple_task_runner.h" |
| #include "build/branding_buildflags.h" |
| #include "build/build_config.h" |
| #include "components/safe_browsing/core/browser/db/v4_database.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 "crypto/sha2.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/platform_test.h" |
| |
| namespace safe_browsing { |
| |
| namespace { |
| |
| typedef std::vector<FullHashInfo> FullHashInfos; |
| |
| // Utility function for populating hashes. |
| FullHashStr HashForUrl(const GURL& url) { |
| std::vector<FullHashStr> full_hashes; |
| V4ProtocolManagerUtil::UrlToFullHashes(url, &full_hashes); |
| // ASSERT_GE(full_hashes.size(), 1u); |
| return full_hashes[0]; |
| } |
| |
| const int kDefaultStoreFileSizeInBytes = 320000; |
| |
| // Use this if you want GetFullHashes() to always return prescribed results. |
| class FakeGetHashProtocolManager : public V4GetHashProtocolManager { |
| public: |
| FakeGetHashProtocolManager( |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| const StoresToCheck& stores_to_check, |
| const V4ProtocolConfig& config, |
| const FullHashInfos& full_hash_infos) |
| : V4GetHashProtocolManager(url_loader_factory, stores_to_check, config), |
| full_hash_infos_(full_hash_infos) {} |
| |
| void GetFullHashes(const FullHashToStoreAndHashPrefixesMap, |
| const std::vector<std::string>&, |
| FullHashCallback callback) override { |
| // Async, since the real manager might use a fetcher. |
| base::SequencedTaskRunner::GetCurrentDefault()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(callback), full_hash_infos_)); |
| } |
| |
| private: |
| FullHashInfos full_hash_infos_; |
| }; |
| |
| class FakeGetHashProtocolManagerFactory |
| : public V4GetHashProtocolManagerFactory { |
| public: |
| FakeGetHashProtocolManagerFactory(const FullHashInfos& full_hash_infos) |
| : full_hash_infos_(full_hash_infos) {} |
| |
| std::unique_ptr<V4GetHashProtocolManager> CreateProtocolManager( |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, |
| const StoresToCheck& stores_to_check, |
| const V4ProtocolConfig& config) override { |
| return std::make_unique<FakeGetHashProtocolManager>( |
| url_loader_factory, stores_to_check, config, full_hash_infos_); |
| } |
| |
| private: |
| FullHashInfos full_hash_infos_; |
| }; |
| |
| // Use FakeGetHashProtocolManagerFactory in scope, then reset. |
| // You should make sure the DatabaseManager is created _after_ this. |
| class ScopedFakeGetHashProtocolManagerFactory { |
| public: |
| ScopedFakeGetHashProtocolManagerFactory( |
| const FullHashInfos& full_hash_infos) { |
| V4GetHashProtocolManager::RegisterFactory( |
| std::make_unique<FakeGetHashProtocolManagerFactory>(full_hash_infos)); |
| } |
| ~ScopedFakeGetHashProtocolManagerFactory() { |
| V4GetHashProtocolManager::RegisterFactory(nullptr); |
| } |
| }; |
| |
| } // namespace |
| |
| class FakeV4Database : public V4Database { |
| public: |
| static void Create( |
| const scoped_refptr<base::SequencedTaskRunner>& db_task_runner, |
| std::unique_ptr<StoreMap> store_map, |
| const StoreAndHashPrefixes& store_and_hash_prefixes, |
| NewDatabaseReadyCallback new_db_callback, |
| bool stores_available, |
| int64_t store_file_size) { |
| // Mimics V4Database::Create |
| const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner = |
| base::SequencedTaskRunner::GetCurrentDefault(); |
| db_task_runner->PostTask( |
| FROM_HERE, |
| base::BindOnce(&FakeV4Database::CreateOnTaskRunner, db_task_runner, |
| std::move(store_map), store_and_hash_prefixes, |
| callback_task_runner, std::move(new_db_callback), |
| stores_available, store_file_size)); |
| } |
| |
| // V4Database implementation |
| void GetStoresMatchingFullHash( |
| const FullHashStr& full_hash, |
| const StoresToCheck& stores_to_check, |
| StoreAndHashPrefixes* store_and_hash_prefixes) override { |
| store_and_hash_prefixes->clear(); |
| for (const StoreAndHashPrefix& stored_sahp : store_and_hash_prefixes_) { |
| if (stores_to_check.count(stored_sahp.list_id) == 0) |
| continue; |
| const PrefixSize& prefix_size = stored_sahp.hash_prefix.size(); |
| if (!full_hash.compare(0, prefix_size, stored_sahp.hash_prefix)) { |
| store_and_hash_prefixes->push_back(stored_sahp); |
| } |
| } |
| } |
| |
| // V4Database implementation |
| int64_t GetStoreSizeInBytes(const ListIdentifier& store) const override { |
| return store_file_size_; |
| } |
| |
| bool AreAllStoresAvailable( |
| const StoresToCheck& stores_to_check) const override { |
| return stores_available_; |
| } |
| |
| bool AreAnyStoresAvailable( |
| const StoresToCheck& stores_to_check) const override { |
| return stores_available_; |
| } |
| |
| private: |
| static void CreateOnTaskRunner( |
| const scoped_refptr<base::SequencedTaskRunner>& db_task_runner, |
| std::unique_ptr<StoreMap> store_map, |
| const StoreAndHashPrefixes& store_and_hash_prefixes, |
| const scoped_refptr<base::SequencedTaskRunner>& callback_task_runner, |
| NewDatabaseReadyCallback new_db_callback, |
| bool stores_available, |
| int64_t store_file_size) { |
| // Mimics the semantics of V4Database::CreateOnTaskRunner |
| std::unique_ptr<FakeV4Database, base::OnTaskRunnerDeleter> fake_v4_database( |
| new FakeV4Database(db_task_runner, std::move(store_map), |
| store_and_hash_prefixes, stores_available, |
| store_file_size), |
| base::OnTaskRunnerDeleter(db_task_runner)); |
| callback_task_runner->PostTask(FROM_HERE, |
| base::BindOnce(std::move(new_db_callback), |
| std::move(fake_v4_database))); |
| } |
| |
| FakeV4Database(const scoped_refptr<base::SequencedTaskRunner>& db_task_runner, |
| std::unique_ptr<StoreMap> store_map, |
| const StoreAndHashPrefixes& store_and_hash_prefixes, |
| bool stores_available, |
| int64_t store_file_size) |
| : V4Database(db_task_runner, std::move(store_map)), |
| store_and_hash_prefixes_(store_and_hash_prefixes), |
| stores_available_(stores_available), |
| store_file_size_(store_file_size) {} |
| |
| const StoreAndHashPrefixes store_and_hash_prefixes_; |
| const bool stores_available_; |
| int64_t store_file_size_; |
| }; |
| |
| // TODO(nparker): This might be simpler with a mock and EXPECT calls. |
| // That would also catch unexpected calls. |
| class TestClient : public SafeBrowsingDatabaseManager::Client { |
| public: |
| TestClient(SBThreatType sb_threat_type, |
| const GURL& url, |
| V4LocalDatabaseManager* manager_to_cancel = nullptr) |
| : expected_sb_threat_type_(sb_threat_type), |
| expected_urls_(1, url), |
| manager_to_cancel_(manager_to_cancel) {} |
| |
| TestClient(SBThreatType sb_threat_type, const std::vector<GURL>& url_chain) |
| : expected_sb_threat_type_(sb_threat_type), expected_urls_(url_chain) {} |
| |
| void OnCheckBrowseUrlResult(const GURL& url, |
| SBThreatType threat_type, |
| const ThreatMetadata& metadata) override { |
| ASSERT_EQ(expected_urls_[0], url); |
| ASSERT_EQ(expected_sb_threat_type_, threat_type); |
| on_check_browse_url_result_called_ = true; |
| if (manager_to_cancel_) { |
| manager_to_cancel_->CancelCheck(this); |
| } |
| } |
| |
| void OnCheckResourceUrlResult(const GURL& url, |
| SBThreatType threat_type, |
| const std::string& threat_hash) override { |
| ASSERT_EQ(expected_urls_[0], url); |
| ASSERT_EQ(expected_sb_threat_type_, threat_type); |
| ASSERT_EQ(threat_type == SB_THREAT_TYPE_SAFE, threat_hash.empty()); |
| on_check_resource_url_result_called_ = true; |
| } |
| |
| void OnCheckDownloadUrlResult(const std::vector<GURL>& url_chain, |
| SBThreatType threat_type) override { |
| ASSERT_EQ(expected_urls_, url_chain); |
| ASSERT_EQ(expected_sb_threat_type_, threat_type); |
| on_check_download_urls_result_called_ = true; |
| } |
| |
| std::vector<GURL>* mutable_expected_urls() { return &expected_urls_; } |
| |
| bool on_check_browse_url_result_called() { |
| return on_check_browse_url_result_called_; |
| } |
| bool on_check_download_urls_result_called() { |
| return on_check_download_urls_result_called_; |
| } |
| bool on_check_resource_url_result_called() { |
| return on_check_resource_url_result_called_; |
| } |
| |
| private: |
| const SBThreatType expected_sb_threat_type_; |
| std::vector<GURL> expected_urls_; |
| bool on_check_browse_url_result_called_ = false; |
| bool on_check_download_urls_result_called_ = false; |
| bool on_check_resource_url_result_called_ = false; |
| raw_ptr<V4LocalDatabaseManager> manager_to_cancel_; |
| }; |
| |
| class TestAllowlistClient : public SafeBrowsingDatabaseManager::Client { |
| public: |
| // |match_expected| specifies whether a full hash match is expected. |
| // |expected_sb_threat_type| identifies which callback method to expect to get |
| // called. |
| explicit TestAllowlistClient(bool match_expected, |
| SBThreatType expected_sb_threat_type) |
| : expected_sb_threat_type_(expected_sb_threat_type), |
| match_expected_(match_expected) {} |
| |
| void OnCheckAllowlistUrlResult(bool is_allowlisted) override { |
| EXPECT_EQ(match_expected_, is_allowlisted); |
| EXPECT_EQ(SB_THREAT_TYPE_CSD_ALLOWLIST, expected_sb_threat_type_); |
| callback_called_ = true; |
| } |
| |
| bool callback_called() { return callback_called_; } |
| |
| private: |
| const SBThreatType expected_sb_threat_type_; |
| const bool match_expected_; |
| bool callback_called_ = false; |
| }; |
| |
| class TestExtensionClient : public SafeBrowsingDatabaseManager::Client { |
| public: |
| TestExtensionClient(const std::set<FullHashStr>& expected_bad_crxs) |
| : expected_bad_crxs_(expected_bad_crxs), |
| on_check_extensions_result_called_(false) {} |
| |
| void OnCheckExtensionsResult(const std::set<FullHashStr>& bad_crxs) override { |
| EXPECT_EQ(expected_bad_crxs_, bad_crxs); |
| on_check_extensions_result_called_ = true; |
| } |
| |
| bool on_check_extensions_result_called() { |
| return on_check_extensions_result_called_; |
| } |
| |
| private: |
| const std::set<FullHashStr> expected_bad_crxs_; |
| bool on_check_extensions_result_called_; |
| }; |
| |
| class FakeV4LocalDatabaseManager : public V4LocalDatabaseManager { |
| public: |
| FakeV4LocalDatabaseManager( |
| const base::FilePath& base_path, |
| ExtendedReportingLevelCallback extended_reporting_level_callback, |
| scoped_refptr<base::SequencedTaskRunner> task_runner) |
| : V4LocalDatabaseManager(base_path, |
| extended_reporting_level_callback, |
| base::SequencedTaskRunner::GetCurrentDefault(), |
| base::SequencedTaskRunner::GetCurrentDefault(), |
| task_runner), |
| perform_full_hash_check_called_(false) {} |
| |
| // V4LocalDatabaseManager impl: |
| void PerformFullHashCheck(std::unique_ptr<PendingCheck> check) override { |
| perform_full_hash_check_called_ = true; |
| RemovePendingCheck(pending_checks_.find(check.get())); |
| } |
| |
| static bool PerformFullHashCheckCalled( |
| scoped_refptr<safe_browsing::V4LocalDatabaseManager>& v4_ldbm) { |
| FakeV4LocalDatabaseManager* fake = |
| static_cast<FakeV4LocalDatabaseManager*>(v4_ldbm.get()); |
| return fake->perform_full_hash_check_called_; |
| } |
| |
| void OnFullHashResponse( |
| std::unique_ptr<PendingCheck> check, |
| const std::vector<FullHashInfo>& full_hash_infos) override { |
| RemovePendingCheck(pending_checks_.find(check.get())); |
| } |
| |
| private: |
| ~FakeV4LocalDatabaseManager() override {} |
| |
| bool perform_full_hash_check_called_; |
| }; |
| |
| class V4LocalDatabaseManagerTest : public PlatformTest { |
| public: |
| V4LocalDatabaseManagerTest() : task_runner_(new base::TestSimpleTaskRunner) {} |
| |
| void SetUp() override { |
| PlatformTest::SetUp(); |
| |
| test_shared_loader_factory_ = |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| &test_url_loader_factory_); |
| |
| ASSERT_TRUE(base_dir_.CreateUniqueTempDir()); |
| DVLOG(1) << "base_dir_: " << base_dir_.GetPath().value(); |
| |
| extended_reporting_level_ = SBER_LEVEL_OFF; |
| erl_callback_ = base::BindRepeating( |
| &V4LocalDatabaseManagerTest::GetExtendedReportingLevel, |
| base::Unretained(this)); |
| |
| v4_local_database_manager_ = |
| base::WrapRefCounted(new V4LocalDatabaseManager( |
| base_dir_.GetPath(), erl_callback_, |
| base::SequencedTaskRunner::GetCurrentDefault(), |
| base::SequencedTaskRunner::GetCurrentDefault(), task_runner_)); |
| |
| StartLocalDatabaseManager(); |
| } |
| |
| void TearDown() override { |
| StopLocalDatabaseManager(); |
| |
| PlatformTest::TearDown(); |
| } |
| |
| void ForceDisableLocalDatabaseManager() { |
| v4_local_database_manager_->enabled_ = false; |
| } |
| |
| void ForceEnableLocalDatabaseManager() { |
| v4_local_database_manager_->enabled_ = true; |
| } |
| |
| const V4LocalDatabaseManager::QueuedChecks& GetQueuedChecks() { |
| return v4_local_database_manager_->queued_checks_; |
| } |
| |
| const V4LocalDatabaseManager::PendingChecks& GetPendingChecks() { |
| return v4_local_database_manager_->pending_checks_; |
| } |
| |
| ExtendedReportingLevel GetExtendedReportingLevel() { |
| return extended_reporting_level_; |
| } |
| |
| void PopulateArtificialDatabase() { |
| v4_local_database_manager_->PopulateArtificialDatabase(); |
| } |
| |
| void ReplaceV4Database( |
| const StoreAndHashPrefixes& store_and_hash_prefixes, |
| bool stores_available = false, |
| int64_t store_file_size = kDefaultStoreFileSizeInBytes) { |
| // Disable the V4LocalDatabaseManager first so that if the callback to |
| // verify checksum has been scheduled, then it doesn't do anything when it |
| // is called back. |
| ForceDisableLocalDatabaseManager(); |
| // Wait to make sure that the callback gets executed if it has already been |
| // scheduled. |
| WaitForTasksOnTaskRunner(); |
| // Re-enable the V4LocalDatabaseManager otherwise the checks won't work and |
| // the fake database won't be set either. |
| ForceEnableLocalDatabaseManager(); |
| |
| NewDatabaseReadyCallback db_ready_callback = |
| base::BindOnce(&V4LocalDatabaseManager::DatabaseReadyForChecks, |
| base::Unretained(v4_local_database_manager_.get())); |
| FakeV4Database::Create( |
| task_runner_, std::make_unique<StoreMap>(), store_and_hash_prefixes, |
| std::move(db_ready_callback), stores_available, store_file_size); |
| WaitForTasksOnTaskRunner(); |
| } |
| |
| void ResetLocalDatabaseManager() { |
| StopLocalDatabaseManager(); |
| v4_local_database_manager_ = |
| base::WrapRefCounted(new V4LocalDatabaseManager( |
| base_dir_.GetPath(), erl_callback_, |
| base::SequencedTaskRunner::GetCurrentDefault(), |
| base::SequencedTaskRunner::GetCurrentDefault(), task_runner_)); |
| StartLocalDatabaseManager(); |
| } |
| |
| void ResetV4Database() { v4_local_database_manager_->v4_database_.reset(); } |
| |
| void StartLocalDatabaseManager() { |
| v4_local_database_manager_->StartOnIOThread(test_shared_loader_factory_, |
| GetTestV4ProtocolConfig()); |
| } |
| |
| void StopLocalDatabaseManager() { |
| if (v4_local_database_manager_) { |
| v4_local_database_manager_->StopOnIOThread(true); |
| } |
| |
| // Force destruction of the database. |
| WaitForTasksOnTaskRunner(); |
| } |
| |
| void WaitForTasksOnTaskRunner() { |
| // Wait for tasks on the task runner so we're sure that the |
| // V4LocalDatabaseManager has read the data from disk. |
| task_runner_->RunPendingTasks(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| // For those tests that need the fake manager |
| void SetupFakeManager() { |
| // StopLocalDatabaseManager before resetting it because that's what |
| // ~V4LocalDatabaseManager expects. |
| StopLocalDatabaseManager(); |
| v4_local_database_manager_ = |
| base::WrapRefCounted(new FakeV4LocalDatabaseManager( |
| base_dir_.GetPath(), erl_callback_, task_runner_)); |
| StartLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| } |
| |
| const SBThreatTypeSet usual_threat_types_ = CreateSBThreatTypeSet( |
| {SB_THREAT_TYPE_URL_PHISHING, SB_THREAT_TYPE_URL_MALWARE, |
| SB_THREAT_TYPE_URL_UNWANTED}); |
| |
| network::TestURLLoaderFactory test_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_; |
| base::ScopedTempDir base_dir_; |
| ExtendedReportingLevel extended_reporting_level_; |
| ExtendedReportingLevelCallback erl_callback_; |
| scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
| base::test::TaskEnvironment task_environment_; |
| scoped_refptr<V4LocalDatabaseManager> v4_local_database_manager_; |
| }; |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestGetThreatSource) { |
| WaitForTasksOnTaskRunner(); |
| EXPECT_EQ(ThreatSource::LOCAL_PVER4, |
| v4_local_database_manager_->GetThreatSource()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCanCheckUrl) { |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE( |
| v4_local_database_manager_->CanCheckUrl(GURL("http://example.com/a/"))); |
| EXPECT_TRUE( |
| v4_local_database_manager_->CanCheckUrl(GURL("https://example.com/a/"))); |
| EXPECT_TRUE( |
| v4_local_database_manager_->CanCheckUrl(GURL("ftp://example.com/a/"))); |
| EXPECT_FALSE( |
| v4_local_database_manager_->CanCheckUrl(GURL("adp://example.com/a/"))); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, |
| TestCheckBrowseUrlWithEmptyStoresReturnsNoMatch) { |
| WaitForTasksOnTaskRunner(); |
| // Both the stores are empty right now so CheckBrowseUrl should return true. |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| GURL("http://example.com/a/"), usual_threat_types_, nullptr)); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithFakeDbReturnsMatch) { |
| // Setup to receive full-hash misses. We won't make URL requests. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, &client)); |
| |
| // Wait for PerformFullHashCheck to complete. |
| WaitForTasksOnTaskRunner(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdAllowlistWithPrefixMatch) { |
| // Setup to receive full-hash misses. We won't make URL requests. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| const HashPrefixStr safe_hash_prefix(safe_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlCsdAllowlistId(), |
| safe_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| TestAllowlistClient client( |
| /* match_expected= */ false, |
| /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_ALLOWLIST); |
| const GURL url_check("https://" + url_safe_no_scheme); |
| EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdAllowlistUrl( |
| url_check, &client)); |
| |
| EXPECT_FALSE(client.callback_called()); |
| |
| // Wait for PerformFullHashCheck to complete. |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.callback_called()); |
| } |
| |
| // This is like CsdAllowlistWithPrefixMatch, but we also verify the |
| // full-hash-match results in an appropriate callback value. |
| TEST_F(V4LocalDatabaseManagerTest, |
| TestCheckCsdAllowlistWithPrefixTheFullMatch) { |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| |
| // Setup to receive full-hash hit. We won't make URL requests. |
| FullHashInfos infos( |
| {{safe_full_hash, GetUrlCsdAllowlistId(), base::Time::Now()}}); |
| ScopedFakeGetHashProtocolManagerFactory pin(infos); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| const HashPrefixStr safe_hash_prefix(safe_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlCsdAllowlistId(), |
| safe_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| TestAllowlistClient client( |
| /* match_expected= */ true, |
| /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_ALLOWLIST); |
| const GURL url_check("https://" + url_safe_no_scheme); |
| EXPECT_EQ(AsyncMatch::ASYNC, v4_local_database_manager_->CheckCsdAllowlistUrl( |
| url_check, &client)); |
| |
| EXPECT_FALSE(client.callback_called()); |
| |
| // Wait for PerformFullHashCheck to complete. |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.callback_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdAllowlistWithFullMatch) { |
| // Setup to receive full-hash misses. We won't make URL requests. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlCsdAllowlistId(), safe_full_hash); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| TestAllowlistClient client( |
| /* match_expected= */ false, |
| /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_ALLOWLIST); |
| const GURL url_check("https://" + url_safe_no_scheme); |
| EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdAllowlistUrl( |
| url_check, &client)); |
| |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(client.callback_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdAllowlistWithNoMatch) { |
| // Setup to receive full-hash misses. We won't make URL requests. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // Add a full hash that won't match the URL we check. |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), safe_full_hash); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| TestAllowlistClient client( |
| /* match_expected= */ true, |
| /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_ALLOWLIST); |
| const GURL url_check("https://other.com/"); |
| EXPECT_EQ( |
| AsyncMatch::NO_MATCH, |
| v4_local_database_manager_->CheckCsdAllowlistUrl(url_check, &client)); |
| |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(client.callback_called()); |
| } |
| |
| // When allowlist is unavailable, all URLS should be allowed. |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckCsdAllowlistUnavailable) { |
| // Setup to receive full-hash misses. We won't make URL requests. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false); |
| |
| TestAllowlistClient client( |
| /* match_expected= */ false, |
| /* expected_sb_threat_type= */ SB_THREAT_TYPE_CSD_ALLOWLIST); |
| const GURL url_check("https://other.com/"); |
| EXPECT_EQ(AsyncMatch::MATCH, v4_local_database_manager_->CheckCsdAllowlistUrl( |
| url_check, &client)); |
| |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(client.callback_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, |
| TestCheckBrowseUrlReturnsNoMatchWhenDisabled) { |
| WaitForTasksOnTaskRunner(); |
| |
| // The same URL returns |false| in the previous test because |
| // v4_local_database_manager_ is enabled. |
| ForceDisableLocalDatabaseManager(); |
| |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| GURL("http://example.com/a/"), usual_threat_types_, nullptr)); |
| } |
| |
| // Hash prefix matches on the high confidence allowlist, but not a full hash |
| // match, so it says there is no match and does not perform a full hash check. |
| // This can only happen with an invalid db setup. |
| TEST_F(V4LocalDatabaseManagerTest, |
| TestCheckUrlForHCAllowlistWithPrefixMatchButNoLocalFullHashMatch) { |
| SetupFakeManager(); |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| |
| // Setup to match hash prefix in the local database. |
| const HashPrefixStr safe_hash_prefix(safe_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(), |
| safe_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| // Confirm there is no match and the full hash check is not performed. |
| const GURL url_check("https://" + url_safe_no_scheme); |
| EXPECT_FALSE(v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist( |
| url_check)); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| // Full hash match on the high confidence allowlist. Returns true |
| // synchronously and the full hash check is not performed. |
| TEST_F(V4LocalDatabaseManagerTest, |
| TestCheckUrlForHCAllowlistWithLocalFullHashMatch) { |
| SetupFakeManager(); |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| |
| // Setup to match full hash in the local database. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(), |
| safe_full_hash); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| // Confirm there is a match and the full hash check is not performed. |
| const GURL url_check("https://" + url_safe_no_scheme); |
| EXPECT_TRUE(v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist( |
| url_check)); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| // Hash prefix has no match on the high confidence allowlist. Returns false |
| // synchronously and the full hash check is not performed. |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistWithNoMatch) { |
| SetupFakeManager(); |
| std::string url_safe_no_scheme("example.com/safe/"); |
| FullHashStr safe_full_hash(crypto::SHA256HashString(url_safe_no_scheme)); |
| |
| // Add a full hash that won't match the URL we check. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlHighConfidenceAllowlistId(), |
| safe_full_hash); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| // Confirm there is no match and the full hash check is not performed. |
| const GURL url_check("https://example.com/other/"); |
| EXPECT_FALSE(v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist( |
| url_check)); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| // When allowlist is unavailable, all URLs should be considered as matches. |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistUnavailable) { |
| SetupFakeManager(); |
| |
| // Setup local database as unavailable. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ false); |
| |
| // Confirm there is a match and the full hash check is not performed. |
| const GURL url_check("https://example.com/safe"); |
| EXPECT_TRUE(v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist( |
| url_check)); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| // When allowlist is available but the size is too small, all URLs should be |
| // considered as matches. |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckUrlForHCAllowlistSmallSize) { |
| SetupFakeManager(); |
| |
| // Setup the size of the allowlist to be smaller than the threshold. (10 |
| // entries) |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true, |
| /* store_file_size= */ 32 * 10); |
| |
| // Confirm there is a match and the full hash check is not performed. |
| const GURL url_check("https://example.com/safe"); |
| EXPECT_TRUE(v4_local_database_manager_->CheckUrlForHighConfidenceAllowlist( |
| url_check)); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestGetSeverestThreatTypeAndMetadata) { |
| base::HistogramTester histograms; |
| WaitForTasksOnTaskRunner(); |
| |
| FullHashStr fh_malware("Malware"); |
| FullHashInfo fhi_malware(fh_malware, GetUrlMalwareId(), base::Time::Now()); |
| fhi_malware.metadata.population_id = "malware_popid"; |
| |
| FullHashStr fh_api("api"); |
| FullHashInfo fhi_api(fh_api, GetChromeUrlApiId(), base::Time::Now()); |
| fhi_api.metadata.population_id = "api_popid"; |
| |
| FullHashStr fh_example("example"); |
| std::vector<FullHashInfo> fhis({fhi_malware, fhi_api}); |
| std::vector<FullHashStr> full_hashes({fh_malware, fh_example, fh_api}); |
| |
| std::vector<SBThreatType> full_hash_threat_types(full_hashes.size(), |
| SB_THREAT_TYPE_SAFE); |
| SBThreatType result_threat_type; |
| ThreatMetadata metadata; |
| FullHashStr matching_full_hash; |
| |
| const std::vector<SBThreatType> expected_full_hash_threat_types( |
| {SB_THREAT_TYPE_URL_MALWARE, SB_THREAT_TYPE_SAFE, |
| SB_THREAT_TYPE_API_ABUSE}); |
| |
| v4_local_database_manager_->GetSeverestThreatTypeAndMetadata( |
| fhis, full_hashes, &full_hash_threat_types, &result_threat_type, |
| &metadata, &matching_full_hash); |
| EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types); |
| |
| EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type); |
| EXPECT_EQ("malware_popid", metadata.population_id); |
| EXPECT_EQ(fh_malware, matching_full_hash); |
| |
| // Reversing the list has no effect. |
| std::reverse(std::begin(fhis), std::end(fhis)); |
| full_hash_threat_types.assign(full_hashes.size(), SB_THREAT_TYPE_SAFE); |
| |
| v4_local_database_manager_->GetSeverestThreatTypeAndMetadata( |
| fhis, full_hashes, &full_hash_threat_types, &result_threat_type, |
| &metadata, &matching_full_hash); |
| EXPECT_EQ(expected_full_hash_threat_types, full_hash_threat_types); |
| EXPECT_EQ(SB_THREAT_TYPE_URL_MALWARE, result_threat_type); |
| EXPECT_EQ("malware_popid", metadata.population_id); |
| EXPECT_EQ(fh_malware, matching_full_hash); |
| |
| histograms.ExpectUniqueSample( |
| "SafeBrowsing.V4LocalDatabaseManager.ThreatInfoSize", |
| /* sample */ 2, /* expected_count */ 2); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestChecksAreQueued) { |
| const GURL url("https://www.example.com/"); |
| TestClient client(SB_THREAT_TYPE_SAFE, url); |
| EXPECT_TRUE(GetQueuedChecks().empty()); |
| v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client); |
| // The database is unavailable so the check should get queued. |
| EXPECT_EQ(1ul, GetQueuedChecks().size()); |
| |
| // The following function waits for the DB to load. |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(GetQueuedChecks().empty()); |
| |
| ResetV4Database(); |
| v4_local_database_manager_->CheckBrowseUrl(url, usual_threat_types_, &client); |
| // The database is unavailable so the check should get queued. |
| EXPECT_EQ(1ul, GetQueuedChecks().size()); |
| |
| StopLocalDatabaseManager(); |
| EXPECT_TRUE(GetQueuedChecks().empty()); |
| } |
| |
| // Verify that a window where checks cannot be cancelled is closed. |
| TEST_F(V4LocalDatabaseManagerTest, CancelPending) { |
| // Setup to receive full-hash misses. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| // Test that a request flows through to the callback. |
| { |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, &client)); |
| EXPECT_FALSE(client.on_check_browse_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_browse_url_result_called()); |
| } |
| |
| // Test that cancel prevents the callback from being called. |
| { |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, &client)); |
| v4_local_database_manager_->CancelCheck(&client); |
| EXPECT_FALSE(client.on_check_browse_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_FALSE(client.on_check_browse_url_result_called()); |
| } |
| |
| // Test that the client gets a safe response for a pending check when safe |
| // browsing is stopped. |
| { |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, &client)); |
| EXPECT_EQ(1ul, GetPendingChecks().size()); |
| EXPECT_FALSE(client.on_check_browse_url_result_called()); |
| EXPECT_TRUE((*GetPendingChecks().begin())->is_in_pending_checks); |
| StopLocalDatabaseManager(); |
| EXPECT_TRUE(GetPendingChecks().empty()); |
| EXPECT_TRUE(client.on_check_browse_url_result_called()); |
| } |
| } |
| |
| // When the database load flushes the queued requests, make sure that |
| // CancelCheck() is not fatal in the client callback. |
| TEST_F(V4LocalDatabaseManagerTest, CancelQueued) { |
| const GURL url("http://example.com/a/"); |
| |
| TestClient client1(SB_THREAT_TYPE_SAFE, url, |
| v4_local_database_manager_.get()); |
| TestClient client2(SB_THREAT_TYPE_SAFE, url); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url, usual_threat_types_, &client1)); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url, usual_threat_types_, &client2)); |
| EXPECT_EQ(2ul, GetQueuedChecks().size()); |
| EXPECT_FALSE(client1.on_check_browse_url_result_called()); |
| EXPECT_FALSE(client2.on_check_browse_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client1.on_check_browse_url_result_called()); |
| EXPECT_TRUE(client2.on_check_browse_url_result_called()); |
| } |
| |
| // This test is somewhat similar to TestCheckBrowseUrlWithFakeDbReturnsMatch but |
| // it uses a fake V4LocalDatabaseManager to assert that PerformFullHashCheck is |
| // called async. |
| TEST_F(V4LocalDatabaseManagerTest, PerformFullHashCheckCalledAsync) { |
| SetupFakeManager(); |
| |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| // The fake database returns a matched hash prefix. |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, nullptr)); |
| |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| |
| // Wait for PerformFullHashCheck to complete. |
| WaitForTasksOnTaskRunner(); |
| |
| EXPECT_TRUE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, UsingWeakPtrDropsCallback) { |
| SetupFakeManager(); |
| |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, &client)); |
| v4_local_database_manager_->StopOnIOThread(true); |
| |
| // Release the V4LocalDatabaseManager object right away before the callback |
| // gets called. When the callback gets called, without using a weak-ptr |
| // factory, this leads to a use after free. However, using the weak-ptr means |
| // that the callback is simply dropped. |
| v4_local_database_manager_ = nullptr; |
| |
| // Wait for the tasks scheduled by StopOnIOThread to complete. |
| WaitForTasksOnTaskRunner(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestMatchDownloadAllowlistUrl) { |
| SetupFakeManager(); |
| GURL good_url("http://safe.com"); |
| GURL other_url("http://iffy.com"); |
| |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlCsdDownloadAllowlistId(), |
| HashForUrl(good_url)); |
| |
| ReplaceV4Database(store_and_hash_prefixes, false /* not available */); |
| // Verify it defaults to false when DB is not available. |
| EXPECT_FALSE(v4_local_database_manager_->MatchDownloadAllowlistUrl(good_url)); |
| |
| ReplaceV4Database(store_and_hash_prefixes, true /* available */); |
| // Not allowlisted. |
| EXPECT_FALSE( |
| v4_local_database_manager_->MatchDownloadAllowlistUrl(other_url)); |
| // Allowlisted. |
| EXPECT_TRUE(v4_local_database_manager_->MatchDownloadAllowlistUrl(good_url)); |
| |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestMatchMalwareIP) { |
| SetupFakeManager(); |
| |
| // >>> hashlib.sha1(socket.inet_pton(socket.AF_INET6, |
| // '::ffff:192.168.1.2')).digest() + chr(128) |
| // '\xb3\xe0z\xafAv#h\x9a\xcf<\xf3ee\x94\xda\xf6y\xb1\xad\x80' |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back( |
| GetIpMalwareId(), FullHashStr("\xB3\xE0z\xAF" |
| "Av#h\x9A\xCF<\xF3" |
| "ee\x94\xDA\xF6y\xB1\xAD\x80")); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP("")); |
| // Not blocklisted. |
| EXPECT_FALSE(v4_local_database_manager_->MatchMalwareIP("192.168.1.1")); |
| // Blocklisted. |
| EXPECT_TRUE(v4_local_database_manager_->MatchMalwareIP("192.168.1.2")); |
| |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| } |
| |
| // This verifies the fix for race in http://crbug.com/660293 |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckBrowseUrlWithSameClientAndCancel) { |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalwareId(), |
| HashPrefixStr("sÙ†\340\t\006_")); |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| GURL first_url("http://example.com/a"); |
| GURL second_url("http://example.com/"); |
| TestClient client(SB_THREAT_TYPE_SAFE, first_url); |
| // The fake database returns a matched hash prefix. |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| first_url, usual_threat_types_, &client)); |
| |
| // That check gets queued. Now, let's cancel the check. After this, we should |
| // not receive a call for |OnCheckBrowseUrlResult| with |first_url|. |
| v4_local_database_manager_->CancelCheck(&client); |
| |
| // Now, re-use that client but for |second_url|. |
| client.mutable_expected_urls()->assign(1, second_url); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| second_url, usual_threat_types_, &client)); |
| |
| // Wait for PerformFullHashCheck to complete. |
| WaitForTasksOnTaskRunner(); |
| // |on_check_browse_url_result_called_| is true only if OnCheckBrowseUrlResult |
| // gets called with the |url| equal to |expected_url|, which is |second_url| |
| // in |
| // this test. |
| EXPECT_TRUE(client.on_check_browse_url_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrl) { |
| // Setup to receive full-hash misses. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(), |
| bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client)); |
| EXPECT_FALSE(client.on_check_resource_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_resource_url_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestSubresourceFilterCallback) { |
| // Setup to receive full-hash misses. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlSubresourceFilterId(), |
| bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| // Test that a request flows through to the callback. |
| { |
| TestClient client(SB_THREAT_TYPE_SAFE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckUrlForSubresourceFilter( |
| url_bad, &client)); |
| EXPECT_FALSE(client.on_check_browse_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_browse_url_result_called()); |
| } |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckResourceUrlReturnsBad) { |
| // Setup to receive full-hash hit. |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| FullHashInfo fhi(bad_full_hash, GetChromeUrlClientIncidentId(), base::Time()); |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetChromeUrlClientIncidentId(), |
| bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme); |
| TestClient client(SB_THREAT_TYPE_BLOCKLISTED_RESOURCE, url_bad); |
| EXPECT_FALSE(v4_local_database_manager_->CheckResourceUrl(url_bad, &client)); |
| EXPECT_FALSE(client.on_check_resource_url_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_resource_url_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsNothingBlocklisted) { |
| // Setup to receive full-hash misses. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // bad_extension_id is in the local DB but the full hash won't match. |
| const FullHashStr bad_extension_id("aaaabbbbccccdddd"), |
| good_extension_id("ddddccccbbbbaaaa"); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(), |
| bad_extension_id); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const std::set<FullHashStr> expected_bad_crxs({}); |
| const std::set<FullHashStr> extension_ids( |
| {good_extension_id, bad_extension_id}); |
| TestExtensionClient client(expected_bad_crxs); |
| EXPECT_FALSE( |
| v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client)); |
| EXPECT_FALSE(client.on_check_extensions_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_extensions_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckExtensionIDsOneIsBlocklisted) { |
| // bad_extension_id is in the local DB and the full hash will match. |
| const FullHashStr bad_extension_id("aaaabbbbccccdddd"), |
| good_extension_id("ddddccccbbbbaaaa"); |
| FullHashInfo fhi(bad_extension_id, GetChromeExtMalwareId(), base::Time()); |
| |
| // Setup to receive full-hash hit. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetChromeExtMalwareId(), |
| bad_extension_id); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const std::set<FullHashStr> expected_bad_crxs({bad_extension_id}); |
| const std::set<FullHashStr> extension_ids( |
| {good_extension_id, bad_extension_id}); |
| TestExtensionClient client(expected_bad_crxs); |
| EXPECT_FALSE( |
| v4_local_database_manager_->CheckExtensionIDs(extension_ids, &client)); |
| EXPECT_FALSE(client.on_check_extensions_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_extensions_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlNothingBlocklisted) { |
| // Setup to receive full-hash misses. |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme), |
| url_good("https://example.com/good/"); |
| const std::vector<GURL> url_chain({url_good, url_bad}); |
| |
| TestClient client(SB_THREAT_TYPE_SAFE, url_chain); |
| EXPECT_FALSE( |
| v4_local_database_manager_->CheckDownloadUrl(url_chain, &client)); |
| EXPECT_FALSE(client.on_check_download_urls_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_download_urls_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, TestCheckDownloadUrlWithOneBlocklisted) { |
| // Setup to receive full-hash hit. |
| std::string url_bad_no_scheme("example.com/bad/"); |
| FullHashStr bad_full_hash(crypto::SHA256HashString(url_bad_no_scheme)); |
| FullHashInfo fhi(bad_full_hash, GetUrlMalBinId(), base::Time()); |
| ScopedFakeGetHashProtocolManagerFactory pin(FullHashInfos({fhi})); |
| |
| // Reset the database manager so it picks up the replacement protocol manager. |
| ResetLocalDatabaseManager(); |
| WaitForTasksOnTaskRunner(); |
| |
| const GURL url_bad("https://" + url_bad_no_scheme), |
| url_good("https://example.com/good/"); |
| const std::vector<GURL> url_chain({url_good, url_bad}); |
| |
| // Put a match in the db that will cause a protocol-manager request. |
| const HashPrefixStr bad_hash_prefix(bad_full_hash.substr(0, 5)); |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| store_and_hash_prefixes.emplace_back(GetUrlMalBinId(), bad_hash_prefix); |
| ReplaceV4Database(store_and_hash_prefixes, /* stores_available= */ true); |
| |
| TestClient client(SB_THREAT_TYPE_URL_BINARY_MALWARE, url_chain); |
| EXPECT_FALSE( |
| v4_local_database_manager_->CheckDownloadUrl(url_chain, &client)); |
| EXPECT_FALSE(client.on_check_download_urls_result_called()); |
| WaitForTasksOnTaskRunner(); |
| EXPECT_TRUE(client.on_check_download_urls_result_called()); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, NotificationOnUpdate) { |
| base::RunLoop run_loop; |
| auto callback_subscription = |
| v4_local_database_manager_->RegisterDatabaseUpdatedCallback( |
| run_loop.QuitClosure()); |
| |
| // Creates and associates a V4Database instance. |
| StoreAndHashPrefixes store_and_hash_prefixes; |
| ReplaceV4Database(store_and_hash_prefixes); |
| |
| v4_local_database_manager_->DatabaseUpdated(); |
| |
| run_loop.Run(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsPhishing) { |
| SetupFakeManager(); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_phishing", "https://example.com/1/"); |
| PopulateArtificialDatabase(); |
| |
| const GURL url_bad("https://example.com/1/"); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, nullptr)); |
| // PerformFullHashCheck will not be called if there is a match within the |
| // artificial database |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| |
| const GURL url_good("https://other.example.com"); |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| url_good, usual_threat_types_, nullptr)); |
| |
| WaitForTasksOnTaskRunner(); |
| StopLocalDatabaseManager(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsMalware) { |
| SetupFakeManager(); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_malware", "https://example.com/1/"); |
| PopulateArtificialDatabase(); |
| |
| const GURL url_bad("https://example.com/1/"); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, nullptr)); |
| // PerformFullHashCheck will not be called if there is a match within the |
| // artificial database |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| |
| const GURL url_good("https://other.example.com"); |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| url_good, usual_threat_types_, nullptr)); |
| |
| WaitForTasksOnTaskRunner(); |
| StopLocalDatabaseManager(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, FlagOneUrlAsUWS) { |
| SetupFakeManager(); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_uws", "https://example.com/1/"); |
| PopulateArtificialDatabase(); |
| |
| const GURL url_bad("https://example.com/1/"); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_bad, usual_threat_types_, nullptr)); |
| // PerformFullHashCheck will not be called if there is a match within the |
| // artificial database |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| |
| const GURL url_good("https://other.example.com"); |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| url_good, usual_threat_types_, nullptr)); |
| |
| WaitForTasksOnTaskRunner(); |
| StopLocalDatabaseManager(); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, FlagMultipleUrls) { |
| SetupFakeManager(); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_phishing", "https://example.com/1/"); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_malware", "https://2.example.com"); |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| "mark_as_uws", "https://example.test.com"); |
| PopulateArtificialDatabase(); |
| |
| const GURL url_phishing("https://example.com/1/"); |
| TestClient client_phishing(SB_THREAT_TYPE_SAFE, url_phishing); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_phishing, usual_threat_types_, &client_phishing)); |
| const GURL url_malware("https://2.example.com"); |
| TestClient client_malware(SB_THREAT_TYPE_SAFE, url_malware); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_malware, usual_threat_types_, &client_malware)); |
| const GURL url_uws("https://example.test.com"); |
| TestClient client_uws(SB_THREAT_TYPE_SAFE, url_uws); |
| EXPECT_FALSE(v4_local_database_manager_->CheckBrowseUrl( |
| url_uws, usual_threat_types_, &client_uws)); |
| // PerformFullHashCheck will not be called if there is a match within the |
| // artificial database |
| EXPECT_FALSE(FakeV4LocalDatabaseManager::PerformFullHashCheckCalled( |
| v4_local_database_manager_)); |
| |
| const GURL url_good("https://other.example.com"); |
| TestClient client_good(SB_THREAT_TYPE_SAFE, url_good); |
| EXPECT_TRUE(v4_local_database_manager_->CheckBrowseUrl( |
| url_good, usual_threat_types_, &client_good)); |
| |
| StopLocalDatabaseManager(); |
| } |
| |
| // Verify that the correct set of lists is synced on each platform: iOS, |
| // Chrome-branded desktop, and non-Chrome-branded desktop. |
| TEST_F(V4LocalDatabaseManagerTest, SyncedLists) { |
| WaitForTasksOnTaskRunner(); |
| |
| #if BUILDFLAG(IS_IOS) |
| std::vector<ListIdentifier> expected_lists{ |
| GetUrlSocEngId(), GetUrlMalwareId(), GetUrlBillingId(), |
| GetUrlCsdAllowlistId(), GetUrlHighConfidenceAllowlistId()}; |
| #elif BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| std::vector<ListIdentifier> expected_lists{GetIpMalwareId(), |
| GetUrlSocEngId(), |
| GetUrlMalwareId(), |
| GetUrlUwsId(), |
| GetUrlMalBinId(), |
| GetChromeExtMalwareId(), |
| GetChromeUrlClientIncidentId(), |
| GetUrlBillingId(), |
| GetUrlCsdDownloadAllowlistId(), |
| GetUrlCsdAllowlistId(), |
| GetUrlSubresourceFilterId(), |
| GetUrlSuspiciousSiteId(), |
| GetUrlHighConfidenceAllowlistId()}; |
| #else |
| std::vector<ListIdentifier> expected_lists{ |
| GetIpMalwareId(), GetUrlSocEngId(), GetUrlMalwareId(), |
| GetUrlUwsId(), GetUrlMalBinId(), GetChromeExtMalwareId(), |
| GetUrlBillingId()}; |
| #endif |
| |
| std::vector<ListIdentifier> synced_lists; |
| for (const auto& info : v4_local_database_manager_->list_infos_) { |
| if (info.fetch_updates()) |
| synced_lists.push_back(info.list_id()); |
| } |
| EXPECT_EQ(expected_lists, synced_lists); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, RenameStoreFile_RenameSuccess) { |
| const std::string old_store_name = "UrlCsdWhitelist"; |
| const std::string old_name_in_use_histogram = |
| "SafeBrowsing.V4Store.OldFileNameInUse." + old_store_name; |
| const std::string old_name_exists_histogram = |
| "SafeBrowsing.V4Store.OldFileNameExists." + old_store_name; |
| const std::string new_store_name = "UrlCsdAllowlist"; |
| const std::string new_name_exists_histogram = |
| "SafeBrowsing.V4Store.NewFileNameExists." + new_store_name; |
| const std::string rename_status_histogram = |
| "SafeBrowsing.V4Store.RenameStatus." + new_store_name; |
| |
| base::HistogramTester histograms; |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 0); |
| histograms.ExpectTotalCount(old_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(new_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| auto old_store_path = |
| base_dir_.GetPath().AppendASCII(old_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| // Now write an empty file at |old_store_path|. |
| base::WriteFile(old_store_path, "", 0); |
| ASSERT_TRUE(base::PathExists(old_store_path)); |
| |
| WaitForTasksOnTaskRunner(); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| auto new_store_path = |
| base_dir_.GetPath().AppendASCII(new_store_name + ".store"); |
| ASSERT_TRUE(base::PathExists(new_store_path)); |
| |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 1); |
| histograms.ExpectBucketCount(old_name_in_use_histogram, false, 1); |
| |
| histograms.ExpectTotalCount(old_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(old_name_exists_histogram, true, 1); |
| |
| histograms.ExpectTotalCount(new_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(new_name_exists_histogram, false, 1); |
| |
| histograms.ExpectTotalCount(rename_status_histogram, 1); |
| histograms.ExpectBucketCount(rename_status_histogram, 0, 1); |
| |
| // Cleanup |
| base::DeleteFile(new_store_path); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, RenameStoreFile_RenameSuccessMultiple) { |
| const std::string old_name_in_use = "SafeBrowsing.V4Store.OldFileNameInUse."; |
| const std::string old_name_exists = "SafeBrowsing.V4Store.OldFileNameExists."; |
| const std::string new_name_exists = "SafeBrowsing.V4Store.NewFileNameExists."; |
| const std::string rename_status = "SafeBrowsing.V4Store.RenameStatus."; |
| |
| const auto kStoreFilesToRename = |
| base::MakeFixedFlatMap<std::string, std::string>({ |
| {"UrlCsdDownloadWhitelist", "UrlCsdDownloadAllowlist"}, |
| {"UrlCsdWhitelist", "UrlCsdAllowlist"}, |
| }); |
| |
| base::HistogramTester histograms; |
| for (auto const& pair : kStoreFilesToRename) { |
| const std::string& old_store_name = pair.first; |
| const std::string& new_store_name = pair.second; |
| |
| std::string old_name_in_use_histogram = old_name_in_use + old_store_name; |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 0); |
| std::string old_name_exists_histogram = old_name_exists + old_store_name; |
| histograms.ExpectTotalCount(old_name_exists_histogram, 0); |
| |
| std::string new_name_exists_histogram = new_name_exists + new_store_name; |
| histograms.ExpectTotalCount(new_name_exists_histogram, 0); |
| std::string rename_status_histogram = rename_status + new_store_name; |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| auto old_store_path = |
| base_dir_.GetPath().AppendASCII(old_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| auto new_store_path = |
| base_dir_.GetPath().AppendASCII(new_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(new_store_path)); |
| |
| // Now write an empty file at |old_store_path|. |
| base::WriteFile(old_store_path, "", 0); |
| ASSERT_TRUE(base::PathExists(old_store_path)); |
| } |
| |
| WaitForTasksOnTaskRunner(); |
| for (auto const& pair : kStoreFilesToRename) { |
| const std::string& old_store_name = pair.first; |
| const std::string& new_store_name = pair.second; |
| |
| auto old_store_path = |
| base_dir_.GetPath().AppendASCII(old_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| auto new_store_path = |
| base_dir_.GetPath().AppendASCII(new_store_name + ".store"); |
| ASSERT_TRUE(base::PathExists(new_store_path)); |
| |
| std::string old_name_in_use_histogram = old_name_in_use + old_store_name; |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 1); |
| histograms.ExpectBucketCount(old_name_in_use_histogram, false, 1); |
| |
| std::string old_name_exists_histogram = old_name_exists + old_store_name; |
| histograms.ExpectTotalCount(old_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(old_name_exists_histogram, true, 1); |
| |
| std::string new_name_exists_histogram = new_name_exists + new_store_name; |
| histograms.ExpectTotalCount(new_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(new_name_exists_histogram, false, 1); |
| |
| std::string rename_status_histogram = rename_status + new_store_name; |
| histograms.ExpectTotalCount(rename_status_histogram, 1); |
| histograms.ExpectBucketCount(rename_status_histogram, 0, 1); |
| |
| // Cleanup |
| base::DeleteFile(new_store_path); |
| } |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, |
| RenameStoreOldFileDoesNotExist_DoesNotRename) { |
| const std::string old_store_name = "UrlCsdWhitelist"; |
| const std::string old_name_in_use_histogram = |
| "SafeBrowsing.V4Store.OldFileNameInUse." + old_store_name; |
| const std::string old_name_exists_histogram = |
| "SafeBrowsing.V4Store.OldFileNameExists." + old_store_name; |
| const std::string new_store_name = "UrlCsdAllowlist"; |
| const std::string new_name_exists_histogram = |
| "SafeBrowsing.V4Store.NewFileNameExists." + new_store_name; |
| const std::string rename_status_histogram = |
| "SafeBrowsing.V4Store.RenameStatus." + new_store_name; |
| |
| base::HistogramTester histograms; |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 0); |
| histograms.ExpectTotalCount(old_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(new_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| auto old_store_path = |
| base_dir_.GetPath().AppendASCII(old_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| WaitForTasksOnTaskRunner(); |
| |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 1); |
| histograms.ExpectBucketCount(old_name_in_use_histogram, false, 1); |
| |
| histograms.ExpectTotalCount(old_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(old_name_exists_histogram, false, 1); |
| |
| histograms.ExpectTotalCount(new_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| // Cleanup |
| base::DeleteFile(old_store_path); |
| } |
| |
| TEST_F(V4LocalDatabaseManagerTest, RenameStoreNewFileExists_DoesNotRename) { |
| const std::string old_store_name = "UrlCsdWhitelist"; |
| const std::string old_name_in_use_histogram = |
| "SafeBrowsing.V4Store.OldFileNameInUse." + old_store_name; |
| const std::string old_name_exists_histogram = |
| "SafeBrowsing.V4Store.OldFileNameExists." + old_store_name; |
| const std::string new_store_name = "UrlCsdAllowlist"; |
| const std::string new_name_exists_histogram = |
| "SafeBrowsing.V4Store.NewFileNameExists." + new_store_name; |
| const std::string rename_status_histogram = |
| "SafeBrowsing.V4Store.RenameStatus." + new_store_name; |
| |
| base::HistogramTester histograms; |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 0); |
| histograms.ExpectTotalCount(old_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(new_name_exists_histogram, 0); |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| auto old_store_path = |
| base_dir_.GetPath().AppendASCII(old_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(old_store_path)); |
| |
| // Now write an empty old file. |
| base::WriteFile(old_store_path, "", 0); |
| ASSERT_TRUE(base::PathExists(old_store_path)); |
| |
| auto new_store_path = |
| base_dir_.GetPath().AppendASCII(new_store_name + ".store"); |
| ASSERT_FALSE(base::PathExists(new_store_path)); |
| |
| // Now write an empty new file. |
| base::WriteFile(new_store_path, "", 0); |
| ASSERT_TRUE(base::PathExists(new_store_path)); |
| |
| WaitForTasksOnTaskRunner(); |
| |
| histograms.ExpectTotalCount(old_name_in_use_histogram, 1); |
| histograms.ExpectBucketCount(old_name_in_use_histogram, false, 1); |
| |
| histograms.ExpectTotalCount(old_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(old_name_exists_histogram, true, 1); |
| |
| histograms.ExpectTotalCount(new_name_exists_histogram, 1); |
| histograms.ExpectBucketCount(new_name_exists_histogram, true, 1); |
| |
| histograms.ExpectTotalCount(rename_status_histogram, 0); |
| |
| // Cleanup |
| base::DeleteFile(old_store_path); |
| base::DeleteFile(new_store_path); |
| } |
| |
| } // namespace safe_browsing |