diff options
Diffstat (limited to 'chrome/browser')
6 files changed, 453 insertions, 34 deletions
diff --git a/chrome/browser/safe_browsing/protocol_manager.cc b/chrome/browser/safe_browsing/protocol_manager.cc index 16e4d28..8e29489 100644 --- a/chrome/browser/safe_browsing/protocol_manager.cc +++ b/chrome/browser/safe_browsing/protocol_manager.cc @@ -37,9 +37,53 @@ static const int kSbMaxUpdateWaitSec = 10; // Maximum back off multiplier. static const int kSbMaxBackOff = 8; +// The default SBProtocolManagerFactory. +class SBProtocolManagerFactoryImpl : public SBProtocolManagerFactory { + public: + SBProtocolManagerFactoryImpl() { } + virtual ~SBProtocolManagerFactoryImpl() { } + virtual SafeBrowsingProtocolManager* CreateProtocolManager( + SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update) { + return new SafeBrowsingProtocolManager( + sb_service, client_name, client_key, wrapped_key, + request_context_getter, info_url_prefix, mackey_url_prefix, + disable_auto_update); + } + private: + DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactoryImpl); +}; // SafeBrowsingProtocolManager implementation ---------------------------------- +// static +SBProtocolManagerFactory* SafeBrowsingProtocolManager::factory_ = NULL; + +// static +SafeBrowsingProtocolManager* SafeBrowsingProtocolManager::Create( + SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (!factory_) + factory_ = new SBProtocolManagerFactoryImpl(); + return factory_->CreateProtocolManager(sb_service, client_name, client_key, + wrapped_key, request_context_getter, + info_url_prefix, mackey_url_prefix, + disable_auto_update); +} + SafeBrowsingProtocolManager::SafeBrowsingProtocolManager( SafeBrowsingService* sb_service, const std::string& client_name, @@ -206,11 +250,11 @@ void SafeBrowsingProtocolManager::OnURLFetchComplete( } else { HandleGetHashError(Time::Now()); if (status.status() == URLRequestStatus::FAILED) { - VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() - << " failed with os error: " << status.os_error(); + VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() + << " failed with os error: " << status.os_error(); } else { - VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() - << " failed with error: " << response_code; + VLOG(1) << "SafeBrowsing GetHash request for: " << source->url() + << " failed with error: " << response_code; } } diff --git a/chrome/browser/safe_browsing/protocol_manager.h b/chrome/browser/safe_browsing/protocol_manager.h index 7d5ba50..7869e0f4 100644 --- a/chrome/browser/safe_browsing/protocol_manager.h +++ b/chrome/browser/safe_browsing/protocol_manager.h @@ -42,6 +42,25 @@ struct hash<const URLFetcher*> { } #endif +class SafeBrowsingProtocolManager; +// Interface of a factory to create ProtocolManager. Useful for tests. +class SBProtocolManagerFactory { + public: + SBProtocolManagerFactory() {} + virtual ~SBProtocolManagerFactory() {} + virtual SafeBrowsingProtocolManager* CreateProtocolManager( + SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update) = 0; + private: + DISALLOW_COPY_AND_ASSIGN(SBProtocolManagerFactory); +}; + class SafeBrowsingProtocolManager : public URLFetcher::Delegate { FRIEND_TEST_ALL_PREFIXES(SafeBrowsingProtocolManagerTest, TestBackOffTimes); FRIEND_TEST_ALL_PREFIXES(SafeBrowsingProtocolManagerTest, TestChunkStrings); @@ -58,23 +77,28 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { friend class SafeBrowsingServiceTest; public: - // Constructs a SafeBrowsingProtocolManager for |sb_service| that issues - // network requests using |request_context_getter|. When |disable_auto_update| - // is true, protocol manager won't schedule next update until - // ForceScheduleNextUpdate is called. - SafeBrowsingProtocolManager(SafeBrowsingService* sb_service, - const std::string& client_name, - const std::string& client_key, - const std::string& wrapped_key, - URLRequestContextGetter* request_context_getter, - const std::string& http_url_prefix, - const std::string& https_url_prefix, - bool disable_auto_update); virtual ~SafeBrowsingProtocolManager(); + // Makes the passed |factory| the factory used to instantiate + // a SafeBrowsingService. Useful for tests. + static void RegisterFactory(SBProtocolManagerFactory* factory) { + factory_ = factory; + } + + // Create an instance of the safe browsing service. + static SafeBrowsingProtocolManager* Create( + SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update); + // Sets up the update schedule and internal state for making periodic requests // of the SafeBrowsing service. - void Initialize(); + virtual void Initialize(); // URLFetcher::Delegate interface. virtual void OnURLFetchComplete(const URLFetcher* source, @@ -86,14 +110,12 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { // API used by the SafeBrowsingService for issuing queries. When the results // are available, SafeBrowsingService::HandleGetHashResults is called. - void GetFullHash(SafeBrowsingService::SafeBrowsingCheck* check, - const std::vector<SBPrefix>& prefixes); + virtual void GetFullHash(SafeBrowsingService::SafeBrowsingCheck* check, + const std::vector<SBPrefix>& prefixes); // Forces the start of next update after |next_update_msec| in msec. void ForceScheduleNextUpdate(int next_update_msec); - bool is_initial_request() const { return initial_request_; } - // Scheduled update callback. void GetNextUpdate(); @@ -107,9 +129,6 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { // Called after the chunks that were parsed were inserted in the database. void OnChunkInserted(); - // The last time we received an update. - base::Time last_update() const { return last_update_; } - // For UMA users we report to Google when a SafeBrowsing interstitial is shown // to the user. We assume that the threat type is either URL_MALWARE or // URL_PHISHING. @@ -123,6 +142,11 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { // malware reports. |report| is the serialized report. void ReportMalwareDetails(const std::string& report); + bool is_initial_request() const { return initial_request_; } + + // The last time we received an update. + base::Time last_update() const { return last_update_; } + // Setter for additional_query_. To make sure the additional_query_ won't // be changed in the middle of an update, caller (e.g.: SafeBrowsingService) // should call this after callbacks triggered in UpdateFinished() or before @@ -134,7 +158,22 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { return additional_query_; } + protected: + // Constructs a SafeBrowsingProtocolManager for |sb_service| that issues + // network requests using |request_context_getter|. When |disable_auto_update| + // is true, protocol manager won't schedule next update until + // ForceScheduleNextUpdate is called. + SafeBrowsingProtocolManager(SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& http_url_prefix, + const std::string& https_url_prefix, + bool disable_auto_update); private: + friend class SBProtocolManagerFactoryImpl; + // Internal API for fetching information from the SafeBrowsing servers. The // GetHash requests are higher priority since they can block user requests // so are handled separately. @@ -230,6 +269,10 @@ class SafeBrowsingProtocolManager : public URLFetcher::Delegate { void UpdateResponseTimeout(); private: + // The factory that controls the creation of SafeBrowsingProtocolManager. + // This is used by tests. + static SBProtocolManagerFactory* factory_; + // Main SafeBrowsing interface object. SafeBrowsingService* sb_service_; diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc index 49c8101..590be6b 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc @@ -135,14 +135,33 @@ bool SBAddFullHashPrefixLess(const SBAddFullHash& a, const SBAddFullHash& b) { } // namespace -// Factory method. +// The default SafeBrowsingDatabaseFactory. +class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory { + public: + virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase() { + return new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile); + } + + SafeBrowsingDatabaseFactoryImpl() { } + + private: + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactoryImpl); +}; + +// static +SafeBrowsingDatabaseFactory* SafeBrowsingDatabase::factory_ = NULL; + +// Factory method, non-thread safe. Caller has to make sure this s called +// on SafeBrowsing Thread. // TODO(shess): Milestone-7 is converting from SQLite-based // SafeBrowsingDatabaseBloom to the new file format with // SafeBrowsingDatabaseNew. Once that conversion is too far along to // consider reversing, circle back and lift SafeBrowsingDatabaseNew up // to SafeBrowsingDatabase and get rid of the abstract class. SafeBrowsingDatabase* SafeBrowsingDatabase::Create() { - return new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile); + if (!factory_) + factory_ = new SafeBrowsingDatabaseFactoryImpl(); + return factory_->CreateSafeBrowsingDatabase(); } SafeBrowsingDatabase::~SafeBrowsingDatabase() { diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index 9ca53f7..6dbd6e7 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h @@ -24,6 +24,18 @@ namespace base { class BloomFilter; class GURL; class MessageLoop; +class SafeBrowsingDatabase; + +// Factory for creating SafeBrowsingDatabase. Tests implement this factory +// to create fake Databases for testing. +class SafeBrowsingDatabaseFactory { + public: + SafeBrowsingDatabaseFactory() { } + virtual ~SafeBrowsingDatabaseFactory() { } + virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase() = 0; + private: + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactory); +}; // Encapsulates the database that stores information about phishing // and malware sites. There is one on-disk database for all profiles, @@ -34,7 +46,15 @@ class MessageLoop; class SafeBrowsingDatabase { public: // Factory method for obtaining a SafeBrowsingDatabase implementation. + // It is not thread safe. static SafeBrowsingDatabase* Create(); + + // Makes the passed |factory| the factory used to instantiate + // a SafeBrowsingDatabase. This is used for tests. + static void RegisterFactory(SafeBrowsingDatabaseFactory* factory) { + factory_ = factory; + } + virtual ~SafeBrowsingDatabase(); // Initializes the database with the given filename. @@ -112,6 +132,12 @@ class SafeBrowsingDatabase { }; static void RecordFailure(FailureType failure_type); + + private: + // The factory used to instantiate a SafeBrowsingDatabase object. + // Useful for tests, so they can provide their own implementation of + // SafeBrowsingDatabase. + static SafeBrowsingDatabaseFactory* factory_; }; class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index 38ec170..94b97fa 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -433,14 +433,16 @@ void SafeBrowsingService::OnIOInitialize( cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) : kSbDefaultMacKeyURLPrefix; - protocol_manager_ = new SafeBrowsingProtocolManager(this, - client_name, - client_key, - wrapped_key, - request_context_getter, - info_url_prefix, - mackey_url_prefix, - disable_auto_update); + DCHECK(!protocol_manager_); + protocol_manager_ = + SafeBrowsingProtocolManager::Create(this, + client_name, + client_key, + wrapped_key, + request_context_getter, + info_url_prefix, + mackey_url_prefix, + disable_auto_update); protocol_manager_->Initialize(); } diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc new file mode 100644 index 0000000..cbc0b5f --- /dev/null +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc @@ -0,0 +1,285 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This test creates a safebrowsing service using test safebrowsing database +// and a test protocol manager. It is used to test logics in safebrowsing +// service. + +#include "base/command_line.h" +#include "base/sha2.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/browser/safe_browsing/protocol_manager.h" +#include "chrome/browser/safe_browsing/safe_browsing_database.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/safe_browsing/safe_browsing_util.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" + +namespace { + +// A SafeBrowingDatabase class that allows us to inject the malicious URLs. +class TestSafeBrowsingDatabase : public SafeBrowsingDatabase { + public: + TestSafeBrowsingDatabase() {} + + virtual ~TestSafeBrowsingDatabase() {} + + // Initializes the database with the given filename. + virtual void Init(const FilePath& filename) {} + + // Deletes the current database and creates a new one. + virtual bool ResetDatabase() { + badurls_.clear(); + return true; + } + + // Called on the IO thread to check if the given URL is safe or not. If we + // can synchronously determine that the URL is safe, CheckUrl returns true, + // otherwise it returns false. + virtual bool ContainsUrl(const GURL& url, + std::string* matching_list, + std::vector<SBPrefix>* prefix_hits, + std::vector<SBFullHashResult>* full_hits, + base::Time last_update) { + base::hash_map<std::string, Hits>::const_iterator + badurls_it = badurls_.find(url.spec()); + if (badurls_it == badurls_.end()) + return false; + *prefix_hits = badurls_it->second.prefix_hits; + *full_hits = badurls_it->second.full_hits; + return true; + } + + virtual bool UpdateStarted(std::vector<SBListChunkRanges>* lists) { + ADD_FAILURE() << "Not implemented."; + return false; + } + virtual void InsertChunks(const std::string& list_name, + const SBChunkList& chunks) { + ADD_FAILURE() << "Not implemented."; + } + virtual void DeleteChunks(const std::vector<SBChunkDelete>& chunk_deletes) { + ADD_FAILURE() << "Not implemented."; + } + virtual void UpdateFinished(bool update_succeeded) { + ADD_FAILURE() << "Not implemented."; + } + virtual void CacheHashResults(const std::vector<SBPrefix>& prefixes, + const std::vector<SBFullHashResult>& full_hits) { + // Do nothing for the cache. + return; + } + + // Fill up the database with test URL. + void AddUrl(const GURL& url, + const std::vector<SBPrefix>& prefix_hits, + const std::vector<SBFullHashResult>& full_hits) { + badurls_[url.spec()].prefix_hits = prefix_hits; + badurls_[url.spec()].full_hits = full_hits; + } + private: + struct Hits { + std::vector<SBPrefix> prefix_hits; + std::vector<SBFullHashResult> full_hits; + }; + base::hash_map<std::string, Hits> badurls_; +}; + +// Factory that creates TestSafeBrowsingDatabase instances. +class TestSafeBrowsingDatabaseFactory : public SafeBrowsingDatabaseFactory { + public: + TestSafeBrowsingDatabaseFactory() : db_(NULL) {} + virtual ~TestSafeBrowsingDatabaseFactory() {} + + virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase() { + db_ = new TestSafeBrowsingDatabase(); + return db_; + } + TestSafeBrowsingDatabase* GetDb() { + return db_; + } + private: + // Owned by the SafebrowsingService. + TestSafeBrowsingDatabase* db_; +}; + + +// A TestProtocolManager that could return fixed responses from +// safebrowsing server for testing purpose. +class TestProtocolManager : public SafeBrowsingProtocolManager { + public: + TestProtocolManager(SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update) + : SafeBrowsingProtocolManager(sb_service, client_name, client_key, + wrapped_key, request_context_getter, + info_url_prefix, mackey_url_prefix, + disable_auto_update), + sb_service_(sb_service) { + } + + // This function is called when there is a prefix hit in local safebrowsing + // database and safebrowsing service issues a get hash request to backends. + // We return a result from the prefilled full_hashes_ hash_map to simulate + // server's response. + virtual void GetFullHash(SafeBrowsingService::SafeBrowsingCheck* check, + const std::vector<SBPrefix>& prefixes) { + // The hash result should be inserted to the full_hashes_. + ASSERT_TRUE(full_hashes_.find(check->url.spec()) != full_hashes_.end()); + // When we get a valid response, always cache the result. + bool cancache = true; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod( + sb_service_, &SafeBrowsingService::HandleGetHashResults, + check, full_hashes_[check->url.spec()], cancache)); + } + + // Prepare the GetFullHash results for |url|. + void SetGetFullHashResponse(const GURL& url, + const SBFullHashResult& full_hash_result) { + full_hashes_[url.spec()].push_back(full_hash_result); + } + + private: + base::hash_map<std::string, std::vector<SBFullHashResult> > full_hashes_; + SafeBrowsingService* sb_service_; +}; + +// Factory that creates TestProtocolManager instances. +class TestSBProtocolManagerFactory : public SBProtocolManagerFactory { + public: + TestSBProtocolManagerFactory() : pm_(NULL) {} + virtual ~TestSBProtocolManagerFactory() {} + + virtual SafeBrowsingProtocolManager* CreateProtocolManager( + SafeBrowsingService* sb_service, + const std::string& client_name, + const std::string& client_key, + const std::string& wrapped_key, + URLRequestContextGetter* request_context_getter, + const std::string& info_url_prefix, + const std::string& mackey_url_prefix, + bool disable_auto_update) { + pm_ = new TestProtocolManager( + sb_service, client_name, client_key, wrapped_key, + request_context_getter, info_url_prefix, mackey_url_prefix, + disable_auto_update); + return pm_; + } + TestProtocolManager* GetProtocolManager() { + return pm_; + } + private: + // Owned by the SafebrowsingService. + TestProtocolManager* pm_; +}; + + +// Tests the safe browsing blocking page in a browser. +class SafeBrowsingServiceTest : public InProcessBrowserTest { + public: + SafeBrowsingServiceTest() { + } + + static void GenerateFullhashResult(const GURL& url, + const std::string& list_name, + int add_chunk_id, + SBFullHashResult* full_hash) { + std::string host; + std::string path; + safe_browsing_util::CanonicalizeUrl(url, &host, &path, NULL); + base::SHA256HashString(host + path, &full_hash->hash, + sizeof(SBFullHash)); + full_hash->list_name = list_name; + full_hash->add_chunk_id = add_chunk_id; + } + + virtual void SetUp() { + // InProcessBrowserTest::SetUp() intantiates SafebrowsingService and + // RegisterFactory has to be called before SafeBrowsingService is created. + SafeBrowsingDatabase::RegisterFactory(&db_factory_); + SafeBrowsingProtocolManager::RegisterFactory(&pm_factory_); + + InProcessBrowserTest::SetUp(); + } + + virtual void TearDown() { + InProcessBrowserTest::TearDown(); + + // Unregister test factories after InProcessBrowserTest::TearDown + // (which destructs SafeBrowsingService). + SafeBrowsingDatabase::RegisterFactory(NULL); + SafeBrowsingProtocolManager::RegisterFactory(NULL); + } + + virtual void SetUpCommandLine(CommandLine* command_line) { + // Makes sure the auto update is not triggered during the test. + // This test will fill up the database using testing prefixes + // and urls. + command_line->AppendSwitch(switches::kSbDisableAutoUpdate); + } + + virtual void SetUpInProcessBrowserTestFixture() { + ASSERT_TRUE(test_server()->Start()); + } + + // This will setup the prefix in database and prepare protocol manager + // to response with |full_hash| for get full hash request. + void SetupResponseForUrl(const GURL& url, + const SBFullHashResult& full_hash) { + std::vector<SBPrefix> prefix_hits; + prefix_hits.push_back(full_hash.hash.prefix); + + // Make sure the full hits is empty unless we need to test the + // full hash is hit in database's local cache. + std::vector<SBFullHashResult> empty_full_hits; + TestSafeBrowsingDatabase* db = db_factory_.GetDb(); + db->AddUrl(url, prefix_hits, empty_full_hits); + + TestProtocolManager* pm = pm_factory_.GetProtocolManager(); + pm->SetGetFullHashResponse(url, full_hash); + } + + bool ShowingInterstitialPage() { + TabContents* contents = browser()->GetSelectedTabContents(); + InterstitialPage* interstitial_page = contents->interstitial_page(); + return interstitial_page != NULL; + } + + private: + TestSafeBrowsingDatabaseFactory db_factory_; + TestSBProtocolManagerFactory pm_factory_; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceTest); +}; + +const char kEmptyPage[] = "files/empty.html"; +const char kMalwarePage[] = "files/safe_browsing/malware.html"; +const char kMalwareIframe[] = "files/safe_browsing/malware_iframe.html"; + +IN_PROC_BROWSER_TEST_F(SafeBrowsingServiceTest, Malware) { + GURL url = test_server()->GetURL(kEmptyPage); + // After adding the url to safebrowsing database and getfullhash result, + // we should see the interstitial page. + SBFullHashResult malware_full_hash; + int chunk_id = 0; + GenerateFullhashResult(url, safe_browsing_util::kMalwareList, chunk_id, + &malware_full_hash); + SetupResponseForUrl(url, malware_full_hash); + ui_test_utils::NavigateToURL(browser(), url); + EXPECT_TRUE(ShowingInterstitialPage()); +} + +} // namespace |