diff options
author | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-02 00:47:47 +0000 |
---|---|---|
committer | kalman@chromium.org <kalman@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-02-02 00:47:47 +0000 |
commit | 3e72ed759ddc158877986c87cb8aeb7fb20a6fcb (patch) | |
tree | 43a45da6171450a4f741740074664185d1025609 /chrome | |
parent | bc2a400794707a885c1c7247199d8ca1cd7be5ca (diff) | |
download | chromium_src-3e72ed759ddc158877986c87cb8aeb7fb20a6fcb.zip chromium_src-3e72ed759ddc158877986c87cb8aeb7fb20a6fcb.tar.gz chromium_src-3e72ed759ddc158877986c87cb8aeb7fb20a6fcb.tar.bz2 |
Add an extension blacklist safebrowsing store and use it in
extensions::Blacklist.
BUG=154149
TBR=ben@chromium.org
Review URL: https://codereview.chromium.org/11943016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@180219 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
20 files changed, 731 insertions, 63 deletions
diff --git a/chrome/browser/extensions/blacklist.cc b/chrome/browser/extensions/blacklist.cc index 3d745a6..12e5a7a 100644 --- a/chrome/browser/extensions/blacklist.cc +++ b/chrome/browser/extensions/blacklist.cc @@ -7,13 +7,112 @@ #include <algorithm> #include "base/bind.h" -#include "base/message_loop.h" +#include "base/lazy_instance.h" +#include "base/memory/ref_counted.h" +#include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/safe_browsing/database_manager.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/safe_browsing/safe_browsing_util.h" +#include "chrome/common/chrome_notification_types.h" #include "chrome/common/pref_names.h" +#include "content/public/browser/notification_details.h" +#include "content/public/browser/notification_source.h" + +using content::BrowserThread; namespace extensions { +namespace { + +// The safe browsing database manager to use. Make this a global/static variable +// rather than a member of Blacklist because Blacklist accesses the real +// database manager before it has a chance to get a fake one. +class LazySafeBrowsingDatabaseManager { + public: + LazySafeBrowsingDatabaseManager() { + if (g_browser_process && g_browser_process->safe_browsing_service()) { + instance_ = + g_browser_process->safe_browsing_service()->database_manager(); + } + } + + scoped_refptr<SafeBrowsingDatabaseManager> get() { + return instance_; + } + + void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) { + instance_ = instance; + } + + private: + scoped_refptr<SafeBrowsingDatabaseManager> instance_; +}; + +static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager = + LAZY_INSTANCE_INITIALIZER; + +// Implementation of SafeBrowsingDatabaseManager::Client, the class which is +// called back from safebrowsing queries. +// +// Constructed on any thread but lives on the IO from then on. +class SafeBrowsingClientImpl + : public SafeBrowsingDatabaseManager::Client, + public base::RefCountedThreadSafe<SafeBrowsingClientImpl> { + public: + typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback; + + // Constructs a client to query the database manager for |extension_ids| and + // run |callback| with the IDs of those which have been blacklisted. + SafeBrowsingClientImpl( + const std::set<std::string>& extension_ids, + const OnResultCallback& callback) + : callback_message_loop_(base::MessageLoopProxy::current()), + callback_(callback) { + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&SafeBrowsingClientImpl::StartCheck, this, + g_database_manager.Get().get(), + extension_ids)); + } + + private: + friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>; + virtual ~SafeBrowsingClientImpl() {} + + // Pass |database_manager| as a parameter to avoid touching + // SafeBrowsingService on the IO thread. + void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager, + const std::set<std::string>& extension_ids) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (database_manager->CheckExtensionIDs(extension_ids, this)) { + // Definitely not blacklisted. Callback immediately. + callback_message_loop_->PostTask( + FROM_HERE, + base::Bind(callback_, std::set<std::string>())); + return; + } + // Something might be blacklisted, response will come in + // OnCheckExtensionsResult. + AddRef(); // Balanced in OnCheckExtensionsResult + } + + void OnCheckExtensionsResult(const std::set<std::string>& hits) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + callback_message_loop_->PostTask(FROM_HERE, base::Bind(callback_, hits)); + Release(); // Balanced in StartCheck. + } + + scoped_refptr<base::MessageLoopProxy> callback_message_loop_; + OnResultCallback callback_; + + DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl); +}; + +} // namespace + Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) { blacklist_->AddObserver(this); } @@ -22,7 +121,32 @@ Blacklist::Observer::~Observer() { blacklist_->RemoveObserver(this); } +Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest( + scoped_refptr<SafeBrowsingDatabaseManager> database_manager) + : original_(GetDatabaseManager()) { + SetDatabaseManager(database_manager); +} + +Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() { + SetDatabaseManager(original_); +} + Blacklist::Blacklist(ExtensionPrefs* prefs) : prefs_(prefs) { + scoped_refptr<SafeBrowsingDatabaseManager> database_manager = + g_database_manager.Get().get(); + if (database_manager) { + registrar_.Add(this, + chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, + content::Source<SafeBrowsingDatabaseManager>( + database_manager)); + } + + // TODO(kalman): Delete anything from the pref blacklist that is in the + // safebrowsing blacklist (of course, only entries for which the extension + // hasn't been installed). + // + // Or maybe just wait until we're able to delete the pref blacklist + // altogether (when we're sure it's a strict subset of the safebrowsing one). } Blacklist::~Blacklist() { @@ -30,20 +154,37 @@ Blacklist::~Blacklist() { void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids, const GetBlacklistedIDsCallback& callback) { - // TODO(kalman): Get the blacklisted IDs from the safebrowsing list. - // This will require going to the IO thread and back. - std::set<std::string> blacklisted_ids; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // The blacklisted IDs are the union of those blacklisted in prefs and + // those blacklisted from safe browsing. + std::set<std::string> pref_blacklisted_ids; for (std::set<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { if (prefs_->IsExtensionBlacklisted(*it)) - blacklisted_ids.insert(*it); + pref_blacklisted_ids.insert(*it); } - MessageLoop::current()->PostTask(FROM_HERE, - base::Bind(callback, blacklisted_ids)); + + if (!g_database_manager.Get().get()) { + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(callback, pref_blacklisted_ids)); + return; + } + + // Constructing the SafeBrowsingClientImpl begins the process of asking + // safebrowsing for the blacklisted extensions. + new SafeBrowsingClientImpl( + ids, + base::Bind(&Blacklist::OnSafeBrowsingResponse, AsWeakPtr(), + pref_blacklisted_ids, + callback)); } void Blacklist::SetFromUpdater(const std::vector<std::string>& ids, const std::string& version) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + std::set<std::string> ids_as_set; for (std::vector<std::string>::const_iterator it = ids.begin(); it != ids.end(); ++it) { @@ -82,11 +223,44 @@ void Blacklist::SetFromUpdater(const std::vector<std::string>& ids, } void Blacklist::AddObserver(Observer* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); observers_.AddObserver(observer); } void Blacklist::RemoveObserver(Observer* observer) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); observers_.RemoveObserver(observer); } +// static +void Blacklist::SetDatabaseManager( + scoped_refptr<SafeBrowsingDatabaseManager> database_manager) { + g_database_manager.Get().set(database_manager); +} + +// static +scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() { + return g_database_manager.Get().get(); +} + +void Blacklist::OnSafeBrowsingResponse( + const std::set<std::string>& pref_blacklisted_ids, + const GetBlacklistedIDsCallback& callback, + const std::set<std::string>& safebrowsing_blacklisted_ids) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + std::set<std::string> blacklist = pref_blacklisted_ids; + blacklist.insert(safebrowsing_blacklisted_ids.begin(), + safebrowsing_blacklisted_ids.end()); + + callback.Run(blacklist); +} + +void Blacklist::Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type); + FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated()); +} + } // namespace extensions diff --git a/chrome/browser/extensions/blacklist.h b/chrome/browser/extensions/blacklist.h index 89705a5..d5cdce5 100644 --- a/chrome/browser/extensions/blacklist.h +++ b/chrome/browser/extensions/blacklist.h @@ -10,7 +10,11 @@ #include <vector> #include "base/callback.h" +#include "base/memory/weak_ptr.h" #include "base/observer_list.h" +#include "chrome/browser/safe_browsing/database_manager.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" namespace extensions { @@ -18,7 +22,8 @@ class Extension; class ExtensionPrefs; // A blacklist of extensions. -class Blacklist { +class Blacklist : public content::NotificationObserver, + public base::SupportsWeakPtr<Blacklist> { public: class Observer { public: @@ -34,17 +39,33 @@ class Blacklist { Blacklist* blacklist_; }; + class ScopedDatabaseManagerForTest { + public: + explicit ScopedDatabaseManagerForTest( + scoped_refptr<SafeBrowsingDatabaseManager> database_manager); + + ~ScopedDatabaseManagerForTest(); + + private: + scoped_refptr<SafeBrowsingDatabaseManager> original_; + + DISALLOW_COPY_AND_ASSIGN(ScopedDatabaseManagerForTest); + }; + typedef base::Callback<void(const std::set<std::string>&)> GetBlacklistedIDsCallback; // |prefs_| must outlive this. explicit Blacklist(ExtensionPrefs* prefs); - ~Blacklist(); + virtual ~Blacklist(); // From the set of extension IDs passed in via |ids|, asynchronously checks // which are blacklisted and includes them in the resulting set passed // via |callback|, which will be sent on the caller's message loop. + // + // For a synchronous version which ONLY CHECKS CURRENTLY INSTALLED EXTENSIONS + // see ExtensionPrefs::IsExtensionBlacklisted. void GetBlacklistedIDs(const std::set<std::string>& ids, const GetBlacklistedIDsCallback& callback); @@ -57,12 +78,32 @@ class Blacklist { void RemoveObserver(Observer* observer); private: + // Use via ScopedDatabaseManagerForTest. + static void SetDatabaseManager( + scoped_refptr<SafeBrowsingDatabaseManager> database_manager); + static scoped_refptr<SafeBrowsingDatabaseManager> GetDatabaseManager(); + + // Handles the |safebrowsing_blacklisted_ids| response from querying the + // safebrowsing blacklist, given that we know |pref_blacklisted_ids| are + // already blacklisted. Responds to |callback| with the union. + void OnSafeBrowsingResponse( + const std::set<std::string>& pref_blacklisted_ids, + const GetBlacklistedIDsCallback& callback, + const std::set<std::string>& safebrowsing_blacklisted_ids); + + // content::NotificationObserver + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + ObserverList<Observer> observers_; ExtensionPrefs* const prefs_; std::set<std::string> prefs_blacklist_; + content::NotificationRegistrar registrar_; + DISALLOW_COPY_AND_ASSIGN(Blacklist); }; diff --git a/chrome/browser/extensions/blacklist_unittest.cc b/chrome/browser/extensions/blacklist_unittest.cc index 9572a5c..31d20ba 100644 --- a/chrome/browser/extensions/blacklist_unittest.cc +++ b/chrome/browser/extensions/blacklist_unittest.cc @@ -7,8 +7,10 @@ #include "base/run_loop.h" #include "chrome/browser/extensions/blacklist.h" #include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/fake_safe_browsing_database_manager.h" #include "chrome/browser/extensions/test_blacklist.h" #include "chrome/browser/extensions/test_extension_prefs.h" +#include "content/public/test/test_browser_thread.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { @@ -18,6 +20,11 @@ class BlacklistTest : public testing::Test { public: BlacklistTest() : prefs_(message_loop_.message_loop_proxy()), + ui_thread_(content::BrowserThread::UI, &message_loop_), + io_thread_(content::BrowserThread::IO, &message_loop_), + safe_browsing_database_manager_( + new FakeSafeBrowsingDatabaseManager()), + scoped_blacklist_database_manager_(safe_browsing_database_manager_), blacklist_(prefs_.prefs()) { } @@ -30,6 +37,13 @@ class BlacklistTest : public testing::Test { TestExtensionPrefs prefs_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread io_thread_; + + scoped_refptr<FakeSafeBrowsingDatabaseManager> + safe_browsing_database_manager_; + Blacklist::ScopedDatabaseManagerForTest scoped_blacklist_database_manager_; + Blacklist blacklist_; }; @@ -141,5 +155,51 @@ TEST_F(BlacklistTest, OnlyIncludesRequestedIDs) { EXPECT_EQ(blacklist_expected, blacklist_actual); } +TEST_F(BlacklistTest, PrefsVsSafeBrowsing) { + scoped_refptr<const Extension> extension_a = prefs_.AddExtension("a"); + scoped_refptr<const Extension> extension_b = prefs_.AddExtension("b"); + scoped_refptr<const Extension> extension_c = prefs_.AddExtension("c"); + + // Prefs have a and b blacklisted, safebrowsing has b and c. + prefs_.prefs()->SetExtensionBlacklisted(extension_a->id(), true); + prefs_.prefs()->SetExtensionBlacklisted(extension_b->id(), true); + { + std::set<std::string> bc; + bc.insert(extension_b->id()); + bc.insert(extension_c->id()); + safe_browsing_database_manager_->set_unsafe_ids(bc); + } + + // The manager is still disabled at this point, so c won't be blacklisted. + EXPECT_TRUE(IsBlacklisted(extension_a)); + EXPECT_TRUE(IsBlacklisted(extension_b)); + EXPECT_FALSE(IsBlacklisted(extension_c)); + + // Now it should be. + safe_browsing_database_manager_->set_enabled(true); + EXPECT_TRUE(IsBlacklisted(extension_a)); + EXPECT_TRUE(IsBlacklisted(extension_b)); + EXPECT_TRUE(IsBlacklisted(extension_c)); + + // Corner case: nothing in safebrowsing (but still enabled). + safe_browsing_database_manager_->set_unsafe_ids(std::set<std::string>()); + EXPECT_TRUE(IsBlacklisted(extension_a)); + EXPECT_TRUE(IsBlacklisted(extension_b)); + EXPECT_FALSE(IsBlacklisted(extension_c)); + + // Corner case: nothing in prefs. + prefs_.prefs()->SetExtensionBlacklisted(extension_a->id(), false); + prefs_.prefs()->SetExtensionBlacklisted(extension_b->id(), false); + { + std::set<std::string> bc; + bc.insert(extension_b->id()); + bc.insert(extension_c->id()); + safe_browsing_database_manager_->set_unsafe_ids(bc); + } + EXPECT_FALSE(IsBlacklisted(extension_a)); + EXPECT_TRUE(IsBlacklisted(extension_b)); + EXPECT_TRUE(IsBlacklisted(extension_c)); +} + } // namespace extensions } // namespace diff --git a/chrome/browser/extensions/extension_blacklist_browsertest.cc b/chrome/browser/extensions/extension_blacklist_browsertest.cc index cc6bc24..a7307f1 100644 --- a/chrome/browser/extensions/extension_blacklist_browsertest.cc +++ b/chrome/browser/extensions/extension_blacklist_browsertest.cc @@ -169,7 +169,12 @@ class ExtensionBlacklistBrowserTest : public ExtensionBrowserTest { ExtensionBlacklistBrowserTest() : info_a_("install/install.crx", "ogdbpbegnmindpdjfafpmpicikegejdj"), info_b_("autoupdate/v1.crx", "ogjcoiohnmldgjemafoockdghcjciccf"), - info_c_("hosted_app.crx", "kbmnembihfiondgfjekmnmcbddelicoi") {} + info_c_("hosted_app.crx", "kbmnembihfiondgfjekmnmcbddelicoi"), + // Just disable the safe browsing altogether. + // TODO(kalman): a different approach will be needed when the blacklist + // comes entirely from safe browsing. + scoped_blacklist_database_manager_( + scoped_refptr<SafeBrowsingDatabaseManager>(NULL)) {} virtual ~ExtensionBlacklistBrowserTest() {} @@ -230,6 +235,8 @@ class ExtensionBlacklistBrowserTest : public ExtensionBrowserTest { } return testing::AssertionSuccess(); } + + Blacklist::ScopedDatabaseManagerForTest scoped_blacklist_database_manager_; }; // Stage 1: blacklisting when there weren't any extensions installed when the diff --git a/chrome/browser/extensions/fake_safe_browsing_database_manager.cc b/chrome/browser/extensions/fake_safe_browsing_database_manager.cc new file mode 100644 index 0000000..573c8d6 --- /dev/null +++ b/chrome/browser/extensions/fake_safe_browsing_database_manager.cc @@ -0,0 +1,70 @@ +// Copyright 2013 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. + +#include "chrome/browser/extensions/fake_safe_browsing_database_manager.h" + +#include <algorithm> +#include <iterator> +#include <vector> + +#include "base/bind_helpers.h" +#include "base/memory/ref_counted.h" +#include "base/message_loop_proxy.h" +#include "chrome/browser/safe_browsing/safe_browsing_service.h" +#include "chrome/browser/safe_browsing/safe_browsing_util.h" + +namespace extensions { + +FakeSafeBrowsingDatabaseManager::FakeSafeBrowsingDatabaseManager() + : SafeBrowsingDatabaseManager( + make_scoped_refptr(SafeBrowsingService::CreateSafeBrowsingService())), + enabled_(false) { +} + +FakeSafeBrowsingDatabaseManager::~FakeSafeBrowsingDatabaseManager() { +} + +bool FakeSafeBrowsingDatabaseManager::CheckExtensionIDs( + const std::set<std::string>& extension_ids, + Client* client) { + if (!enabled_) + return true; + + // Need to construct the full SafeBrowsingCheck rather than calling + // OnCheckExtensionsResult directly because it's protected. Grr! + std::vector<std::string> extension_ids_vector(extension_ids.begin(), + extension_ids.end()); + std::vector<SBFullHash> extension_id_hashes; + std::transform(extension_ids_vector.begin(), extension_ids_vector.end(), + std::back_inserter(extension_id_hashes), + safe_browsing_util::StringToSBFullHash); + + scoped_ptr<SafeBrowsingCheck> safe_browsing_check( + new SafeBrowsingCheck(std::vector<GURL>(), + extension_id_hashes, + client, + safe_browsing_util::EXTENSIONBLACKLIST)); + + for (size_t i = 0; i < extension_ids_vector.size(); ++i) { + const std::string& extension_id = extension_ids_vector[i]; + if (unsafe_ids_.count(extension_id)) + safe_browsing_check->full_hash_results[i] = SB_THREAT_TYPE_EXTENSION; + } + + base::MessageLoopProxy::current()->PostTask( + FROM_HERE, + base::Bind(&FakeSafeBrowsingDatabaseManager::OnSafeBrowsingResult, + this, + base::Passed(&safe_browsing_check), + client)); + return false; +} + +void FakeSafeBrowsingDatabaseManager::OnSafeBrowsingResult( + scoped_ptr<SafeBrowsingCheck> result, + Client* client) { + client->OnSafeBrowsingResult(*result); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/fake_safe_browsing_database_manager.h b/chrome/browser/extensions/fake_safe_browsing_database_manager.h new file mode 100644 index 0000000..2a91b73 --- /dev/null +++ b/chrome/browser/extensions/fake_safe_browsing_database_manager.h @@ -0,0 +1,52 @@ +// Copyright 2013 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_FAKE_SAFE_BROWSING_DATABASE_MANAGER_H_ +#define CHROME_BROWSER_EXTENSIONS_FAKE_SAFE_BROWSING_DATABASE_MANAGER_H_ + +#include <set> +#include <string> + +#include "chrome/browser/safe_browsing/database_manager.h" + +namespace extensions { + +// A fake safe browsing database manager for use with extensions tests. +// +// By default it is disabled (returning true and ignoring |unsafe_ids_|); +// call set_enabled to enable it. +class FakeSafeBrowsingDatabaseManager : public SafeBrowsingDatabaseManager { + public: + FakeSafeBrowsingDatabaseManager(); + + // Returns true if synchronously safe, false if not in which case the unsafe + // IDs taken from |unsafe_ids_| are passed to to |client| on the current + // message loop. + virtual bool CheckExtensionIDs(const std::set<std::string>& extension_ids, + Client* client) OVERRIDE; + + void set_enabled(bool enabled) { enabled_ = enabled; } + + void set_unsafe_ids(const std::set<std::string>& unsafe_ids) { + unsafe_ids_ = unsafe_ids; + } + + private: + virtual ~FakeSafeBrowsingDatabaseManager(); + + // Runs client->SafeBrowsingResult(result). + void OnSafeBrowsingResult(scoped_ptr<SafeBrowsingCheck> result, + Client* client); + + // Whether to respond to CheckExtensionIDs immediately with true (indicating + // that there is definitely no extension ID match). + bool enabled_; + + // The extension IDs considered unsafe. + std::set<std::string> unsafe_ids_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_FAKE_SAFE_BROWSING_DATABASE_MANAGER_H_ diff --git a/chrome/browser/extensions/test_blacklist.cc b/chrome/browser/extensions/test_blacklist.cc index 138804d..249d929 100644 --- a/chrome/browser/extensions/test_blacklist.cc +++ b/chrome/browser/extensions/test_blacklist.cc @@ -31,9 +31,7 @@ bool TestBlacklist::IsBlacklisted(const std::string& extension_id) { std::set<std::string> blacklist_set; blacklist_->GetBlacklistedIDs(id_set, base::Bind(&Assign, &blacklist_set)); - base::RunLoop run_loop; - MessageLoop::current()->PostTask(FROM_HERE, run_loop.QuitClosure()); - run_loop.Run(); + base::RunLoop().RunUntilIdle(); return blacklist_set.count(extension_id) > 0; } diff --git a/chrome/browser/safe_browsing/database_manager.cc b/chrome/browser/safe_browsing/database_manager.cc index 1cbb761..fe37cbc 100644 --- a/chrome/browser/safe_browsing/database_manager.cc +++ b/chrome/browser/safe_browsing/database_manager.cc @@ -75,23 +75,22 @@ SafeBrowsingDatabaseManager::SafeBrowsingCheck::SafeBrowsingCheck( need_get_hash(false), check_type(check_type), timeout_factory_(NULL) { - DCHECK(urls.empty() || full_hashes.empty()) << - "There must be either urls or full_hashes to check"; - DCHECK(!urls.empty() || !full_hashes.empty()) << - "There cannot be both urls and full_hashes to check"; + DCHECK_EQ(urls.empty(), !full_hashes.empty()) + << "Exactly one of urls and full_hashes must be set"; } SafeBrowsingDatabaseManager::SafeBrowsingCheck::~SafeBrowsingCheck() {} void SafeBrowsingDatabaseManager::Client::OnSafeBrowsingResult( const SafeBrowsingCheck& check) { + DCHECK_EQ(check.urls.size(), check.url_results.size()); + DCHECK_EQ(check.full_hashes.size(), check.full_hash_results.size()); if (!check.urls.empty()) { DCHECK(check.full_hashes.empty()); switch (check.check_type) { case safe_browsing_util::MALWARE: case safe_browsing_util::PHISH: DCHECK_EQ(1u, check.urls.size()); - DCHECK_EQ(1u, check.url_results.size()); OnCheckBrowseUrlResult(check.urls[0], check.url_results[0]); break; case safe_browsing_util::BINURL: @@ -108,11 +107,21 @@ void SafeBrowsingDatabaseManager::Client::OnSafeBrowsingResult( switch (check.check_type) { case safe_browsing_util::BINHASH: DCHECK_EQ(1u, check.full_hashes.size()); - DCHECK_EQ(1u, check.full_hash_results.size()); OnCheckDownloadHashResult( safe_browsing_util::SBFullHashToString(check.full_hashes[0]), check.full_hash_results[0]); break; + case safe_browsing_util::EXTENSIONBLACKLIST: { + std::set<std::string> unsafe_extension_ids; + for (size_t i = 0; i < check.full_hashes.size(); ++i) { + std::string extension_id = + safe_browsing_util::SBFullHashToString(check.full_hashes[i]); + if (check.full_hash_results[i] == SB_THREAT_TYPE_EXTENSION) + unsafe_extension_ids.insert(extension_id); + } + OnCheckExtensionsResult(unsafe_extension_ids); + break; + } default: NOTREACHED(); } @@ -129,6 +138,7 @@ SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager( enable_download_protection_(false), enable_csd_whitelist_(false), enable_download_whitelist_(false), + enable_extension_blacklist_(false), update_in_progress_(false), database_update_in_progress_(false), closing_database_(false), @@ -149,6 +159,10 @@ SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager( // list right now. This means that we need to be able to disable this list // for the SafeBrowsing test to pass. enable_download_whitelist_ = enable_csd_whitelist_; + + // TODO(kalman): there really shouldn't be a flag for this. + enable_extension_blacklist_ = + !cmdline->HasSwitch(switches::kSbDisableExtensionBlacklist); } SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() { @@ -206,6 +220,33 @@ bool SafeBrowsingDatabaseManager::CheckDownloadHash( return false; } +bool SafeBrowsingDatabaseManager::CheckExtensionIDs( + const std::set<std::string>& extension_ids, + Client* client) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!enabled_ || !enable_extension_blacklist_) + return true; + + std::vector<SBFullHash> extension_id_hashes; + std::transform(extension_ids.begin(), extension_ids.end(), + std::back_inserter(extension_id_hashes), + safe_browsing_util::StringToSBFullHash); + + SafeBrowsingCheck* check = new SafeBrowsingCheck( + std::vector<GURL>(), + extension_id_hashes, + client, + safe_browsing_util::EXTENSIONBLACKLIST); + + StartSafeBrowsingCheck( + check, + base::Bind(&SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread, + this, + check)); + return false; +} + bool SafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) { @@ -533,7 +574,8 @@ SafeBrowsingDatabase* SafeBrowsingDatabaseManager::GetDatabase() { SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create(enable_download_protection_, enable_csd_whitelist_, - enable_download_whitelist_); + enable_download_whitelist_, + enable_extension_blacklist_); database->Init(SafeBrowsingService::GetBaseFilename()); { @@ -711,6 +753,10 @@ SBThreatType SafeBrowsingDatabaseManager::GetThreatTypeFromListname( return SB_THREAT_TYPE_BINARY_MALWARE_HASH; } + if (safe_browsing_util::IsExtensionList(list_name)) { + return SB_THREAT_TYPE_EXTENSION; + } + DVLOG(1) << "Unknown safe browsing list " << list_name; return SB_THREAT_TYPE_SAFE; } @@ -880,6 +926,34 @@ void SafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread( base::Bind(&SafeBrowsingDatabaseManager::OnCheckDone, this, check)); } +void SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread( + SafeBrowsingCheck* check) { + DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop()); + + std::vector<SBPrefix> prefixes; + for (std::vector<SBFullHash>::iterator it = check->full_hashes.begin(); + it != check->full_hashes.end(); ++it) { + prefixes.push_back((*it).prefix); + } + database_->ContainsExtensionPrefixes(prefixes, &check->prefix_hits); + + if (check->prefix_hits.empty()) { + // No matches for any extensions. + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&SafeBrowsingDatabaseManager::SafeBrowsingCheckDone, this, + check)); + } else { + // Some prefixes matched, we need to ask Google whether they're legit. + check->need_get_hash = true; + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&SafeBrowsingDatabaseManager::OnCheckDone, this, check)); + } +} + void SafeBrowsingDatabaseManager::TimeoutCallback(SafeBrowsingCheck* check) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); DCHECK(check); diff --git a/chrome/browser/safe_browsing/database_manager.h b/chrome/browser/safe_browsing/database_manager.h index 06669bb..922715a 100644 --- a/chrome/browser/safe_browsing/database_manager.h +++ b/chrome/browser/safe_browsing/database_manager.h @@ -107,6 +107,10 @@ class SafeBrowsingDatabaseManager // Called when the result of checking a download binary hash is known. virtual void OnCheckDownloadHashResult(const std::string& hash, SBThreatType threat_type) {} + + // Called when the result of checking a set of extensions is known. + virtual void OnCheckExtensionsResult( + const std::set<std::string>& threats) {} }; // Creates the safe browsing service. Need to initialize before using. @@ -136,6 +140,12 @@ class SafeBrowsingDatabaseManager // Result will be passed to callback in |client|. virtual bool CheckDownloadHash(const std::string& full_hash, Client* client); + // Check which prefixes in |extension_ids| are in the safebrowsing blacklist. + // Returns true if not, false if further checks need to be made in which case + // the result will be passed to |client|. + virtual bool CheckExtensionIDs(const std::set<std::string>& extension_ids, + Client* client); + // Check if the |url| matches any of the full-length hashes from the // client-side phishing detection whitelist. Returns true if there was a // match and false otherwise. To make sure we are conservative we will return @@ -304,6 +314,9 @@ class SafeBrowsingDatabaseManager // Calls the Client's callback on IO thread after CheckDownloadHash finishes. void CheckDownloadHashDone(SafeBrowsingCheck* check); + // Checks all extension ID hashes on safe_browsing_thread_. + void CheckExtensionIDsOnSBThread(SafeBrowsingCheck* check); + // Helper function that calls safe browsing client and cleans up |checks_|. void SafeBrowsingCheckDone(SafeBrowsingCheck* check); @@ -352,6 +365,9 @@ class SafeBrowsingDatabaseManager // Indicate if the download whitelist should be enabled or not. bool enable_download_whitelist_; + // Indicate if the extension blacklist should be enabled. + bool enable_extension_blacklist_; + // The SafeBrowsing thread that runs database operations. // // Note: Functions that run on this thread should run synchronously and return diff --git a/chrome/browser/safe_browsing/protocol_parser.cc b/chrome/browser/safe_browsing/protocol_parser.cc index 59a98db..7aac1d0 100644 --- a/chrome/browser/safe_browsing/protocol_parser.cc +++ b/chrome/browser/safe_browsing/protocol_parser.cc @@ -264,7 +264,8 @@ bool SafeBrowsingProtocolParser::ParseAddChunk(const std::string& list_name, SBEntry::ADD_PREFIX : SBEntry::ADD_FULL_HASH; if (list_name == safe_browsing_util::kBinHashList || - list_name == safe_browsing_util::kDownloadWhiteList) { + list_name == safe_browsing_util::kDownloadWhiteList || + list_name == safe_browsing_util::kExtensionBlacklist) { // These lists only contain prefixes, no HOSTKEY and COUNT. DCHECK_EQ(0, remaining % hash_len); prefix_count = remaining / hash_len; @@ -309,7 +310,8 @@ bool SafeBrowsingProtocolParser::ParseSubChunk(const std::string& list_name, SBEntry::SUB_PREFIX : SBEntry::SUB_FULL_HASH; if (list_name == safe_browsing_util::kBinHashList || - list_name == safe_browsing_util::kDownloadWhiteList) { + list_name == safe_browsing_util::kDownloadWhiteList || + list_name == safe_browsing_util::kExtensionBlacklist) { SBChunkHost chunk_host; // Set host to 0 and it won't be used for kBinHashList. chunk_host.host = 0; diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc index f2d95ca..e74528a 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc @@ -40,6 +40,9 @@ const FilePath::CharType kCsdWhitelistDBFile[] = // Filename suffix for the download whitelist store. const FilePath::CharType kDownloadWhitelistDBFile[] = FILE_PATH_LITERAL(" Download Whitelist"); +// Filename suffix for the extension blacklist store. +const FilePath::CharType kExtensionBlacklistDBFile[] = + FILE_PATH_LITERAL(" Extension Blacklist"); // Filename suffix for browse store. // TODO(shess): "Safe Browsing Bloom Prefix Set" is full of win. // Unfortunately, to change the name implies lots of transition code @@ -310,12 +313,14 @@ class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory { virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( bool enable_download_protection, bool enable_client_side_whitelist, - bool enable_download_whitelist) { + bool enable_download_whitelist, + bool enable_extension_blacklist) { return new SafeBrowsingDatabaseNew( new SafeBrowsingStoreFile, enable_download_protection ? new SafeBrowsingStoreFile : NULL, enable_client_side_whitelist ? new SafeBrowsingStoreFile : NULL, - enable_download_whitelist ? new SafeBrowsingStoreFile : NULL); + enable_download_whitelist ? new SafeBrowsingStoreFile : NULL, + enable_extension_blacklist ? new SafeBrowsingStoreFile : NULL); } SafeBrowsingDatabaseFactoryImpl() { } @@ -335,12 +340,14 @@ SafeBrowsingDatabaseFactory* SafeBrowsingDatabase::factory_ = NULL; SafeBrowsingDatabase* SafeBrowsingDatabase::Create( bool enable_download_protection, bool enable_client_side_whitelist, - bool enable_download_whitelist) { + bool enable_download_whitelist, + bool enable_extension_blacklist) { if (!factory_) factory_ = new SafeBrowsingDatabaseFactoryImpl(); return factory_->CreateSafeBrowsingDatabase(enable_download_protection, enable_client_side_whitelist, - enable_download_whitelist); + enable_download_whitelist, + enable_extension_blacklist); } SafeBrowsingDatabase::~SafeBrowsingDatabase() { @@ -382,6 +389,12 @@ FilePath SafeBrowsingDatabase::DownloadWhitelistDBFilename( return FilePath(db_filename.value() + kDownloadWhitelistDBFile); } +// static +FilePath SafeBrowsingDatabase::ExtensionBlacklistDBFilename( + const FilePath& db_filename) { + return FilePath(db_filename.value() + kExtensionBlacklistDBFile); +} + SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) { if (list_id == safe_browsing_util::PHISH || list_id == safe_browsing_util::MALWARE) { @@ -393,6 +406,8 @@ SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) { return csd_whitelist_store_.get(); } else if (list_id == safe_browsing_util::DOWNLOADWHITELIST) { return download_whitelist_store_.get(); + } else if (list_id == safe_browsing_util::EXTENSIONBLACKLIST) { + return extension_blacklist_store_.get(); } return NULL; } @@ -416,18 +431,21 @@ SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew() DCHECK(!download_store_.get()); DCHECK(!csd_whitelist_store_.get()); DCHECK(!download_whitelist_store_.get()); + DCHECK(!extension_blacklist_store_.get()); } SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew( SafeBrowsingStore* browse_store, SafeBrowsingStore* download_store, SafeBrowsingStore* csd_whitelist_store, - SafeBrowsingStore* download_whitelist_store) + SafeBrowsingStore* download_whitelist_store, + SafeBrowsingStore* extension_blacklist_store) : creation_loop_(MessageLoop::current()), browse_store_(browse_store), download_store_(download_store), csd_whitelist_store_(csd_whitelist_store), download_whitelist_store_(download_whitelist_store), + extension_blacklist_store_(extension_blacklist_store), ALLOW_THIS_IN_INITIALIZER_LIST(reset_factory_(this)), corruption_detected_(false) { DCHECK(browse_store_.get()); @@ -444,6 +462,7 @@ void SafeBrowsingDatabaseNew::Init(const FilePath& filename_base) { DCHECK(download_filename_.empty()); DCHECK(csd_whitelist_filename_.empty()); DCHECK(download_whitelist_filename_.empty()); + DCHECK(extension_blacklist_filename_.empty()); browse_filename_ = BrowseDBFilename(filename_base); prefix_set_filename_ = PrefixSetForFilename(browse_filename_); @@ -508,6 +527,16 @@ void SafeBrowsingDatabaseNew::Init(const FilePath& filename_base) { } else { WhitelistEverything(&download_whitelist_); // Just to be safe. } + + if (extension_blacklist_store_.get()) { + extension_blacklist_filename_ = ExtensionBlacklistDBFilename(filename_base); + extension_blacklist_store_->Init( + extension_blacklist_filename_, + base::Bind(&SafeBrowsingDatabaseNew::HandleCorruptDatabase, + base::Unretained(this))); + DVLOG(1) << "Init extension blacklist store: " + << extension_blacklist_filename_.value(); + } } bool SafeBrowsingDatabaseNew::ResetDatabase() { @@ -634,6 +663,19 @@ bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedUrl(const GURL& url) { return ContainsWhitelistedHashes(download_whitelist_, full_hashes); } +bool SafeBrowsingDatabaseNew::ContainsExtensionPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) { + DCHECK_EQ(creation_loop_, MessageLoop::current()); + if (!extension_blacklist_store_) + return false; + + return MatchAddPrefixes(extension_blacklist_store_.get(), + safe_browsing_util::EXTENSIONBLACKLIST % 2, + prefixes, + prefix_hits); +} + bool SafeBrowsingDatabaseNew::ContainsDownloadWhitelistedString( const std::string& str) { SBFullHash hash; @@ -925,6 +967,13 @@ bool SafeBrowsingDatabaseNew::UpdateStarted( return false; } + if (extension_blacklist_store_ && + !extension_blacklist_store_->BeginUpdate()) { + RecordFailure(FAILURE_EXTENSION_BLACKLIST_UPDATE_BEGIN); + HandleCorruptDatabase(); + return false; + } + std::vector<std::string> browse_listnames; browse_listnames.push_back(safe_browsing_util::kMalwareList); browse_listnames.push_back(safe_browsing_util::kPhishingList); @@ -973,6 +1022,13 @@ bool SafeBrowsingDatabaseNew::UpdateStarted( download_whitelist_listnames, lists); } + if (extension_blacklist_store_) { + UpdateChunkRanges( + extension_blacklist_store_.get(), + std::vector<std::string>(1, safe_browsing_util::kExtensionBlacklist), + lists); + } + corruption_detected_ = false; change_detected_ = false; return true; @@ -1001,6 +1057,11 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { !download_whitelist_store_->CheckValidity()) { DLOG(ERROR) << "Safe-browsing download whitelist database corrupt."; } + + if (extension_blacklist_store_ && + !extension_blacklist_store_->CheckValidity()) { + DLOG(ERROR) << "Safe-browsing extension blacklist database corrupt."; + } } if (corruption_detected_) @@ -1020,10 +1081,20 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { csd_whitelist_store_->CancelUpdate(); if (download_whitelist_store_.get()) download_whitelist_store_->CancelUpdate(); + if (extension_blacklist_store_) + extension_blacklist_store_->CancelUpdate(); return; } - UpdateDownloadStore(); + if (download_store_) { + int64 size_bytes = UpdateHashPrefixStore( + download_filename_, + download_store_.get(), + FAILURE_DOWNLOAD_DATABASE_UPDATE_FINISH); + UMA_HISTOGRAM_COUNTS("SB2.DownloadDatabaseKilobytes", + static_cast<int>(size_bytes / 1024)); + } + UpdateBrowseStore(); UpdateWhitelistStore(csd_whitelist_filename_, csd_whitelist_store_.get(), @@ -1031,6 +1102,15 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { UpdateWhitelistStore(download_whitelist_filename_, download_whitelist_store_.get(), &download_whitelist_); + + if (extension_blacklist_store_) { + int64 size_bytes = UpdateHashPrefixStore( + extension_blacklist_filename_, + extension_blacklist_store_.get(), + FAILURE_EXTENSION_BLACKLIST_UPDATE_FINISH); + UMA_HISTOGRAM_COUNTS("SB2.ExtensionBlacklistKilobytes", + static_cast<int>(size_bytes / 1024)); + } } void SafeBrowsingDatabaseNew::UpdateWhitelistStore( @@ -1065,14 +1145,14 @@ void SafeBrowsingDatabaseNew::UpdateWhitelistStore( LoadWhitelist(full_hashes, whitelist); } -void SafeBrowsingDatabaseNew::UpdateDownloadStore() { - if (!download_store_.get()) - return; - - // For download, we don't cache and save full hashes. +int64 SafeBrowsingDatabaseNew::UpdateHashPrefixStore( + const FilePath& store_filename, + SafeBrowsingStore* store, + FailureType failure_type) { + // We don't cache and save full hashes. std::vector<SBAddFullHash> empty_add_hashes; - // For download, backend lookup happens only if a prefix is in add list. + // Backend lookup happens only if a prefix is in add list. std::set<SBPrefix> empty_miss_cache; // These results are not used after this call. Simply ignore the @@ -1080,19 +1160,18 @@ void SafeBrowsingDatabaseNew::UpdateDownloadStore() { SBAddPrefixes add_prefixes_result; std::vector<SBAddFullHash> add_full_hashes_result; - if (!download_store_->FinishUpdate(empty_add_hashes, - empty_miss_cache, - &add_prefixes_result, - &add_full_hashes_result)) - RecordFailure(FAILURE_DOWNLOAD_DATABASE_UPDATE_FINISH); - - int64 file_size = GetFileSizeOrZero(download_filename_); - UMA_HISTOGRAM_COUNTS("SB2.DownloadDatabaseKilobytes", - static_cast<int>(file_size / 1024)); + if (!store->FinishUpdate(empty_add_hashes, + empty_miss_cache, + &add_prefixes_result, + &add_full_hashes_result)) { + RecordFailure(failure_type); + } #if defined(OS_MACOSX) - base::mac::SetFileBackupExclusion(download_filename_); + base::mac::SetFileBackupExclusion(store_filename); #endif + + return GetFileSizeOrZero(store_filename); } void SafeBrowsingDatabaseNew::UpdateBrowseStore() { @@ -1278,7 +1357,11 @@ bool SafeBrowsingDatabaseNew::Delete() { if (!r6) RecordFailure(FAILURE_DATABASE_PREFIX_SET_DELETE); - return r1 && r2 && r3 && r4 && r5 && r6; + const bool r7 = file_util::Delete(extension_blacklist_filename_, false); + if (!r7) + RecordFailure(FAILURE_EXTENSION_BLACKLIST_DELETE); + + return r1 && r2 && r3 && r4 && r5 && r6 && r7; } void SafeBrowsingDatabaseNew::WritePrefixSet() { diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index 5abc97f..f24f37f 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h @@ -36,7 +36,8 @@ class SafeBrowsingDatabaseFactory { virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( bool enable_download_protection, bool enable_client_side_whitelist, - bool enable_download_whitelist) = 0; + bool enable_download_whitelist, + bool enable_extension_blacklist) = 0; private: DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactory); }; @@ -66,7 +67,8 @@ class SafeBrowsingDatabase { // database feature. static SafeBrowsingDatabase* Create(bool enable_download_protection, bool enable_client_side_whitelist, - bool enable_download_whitelist); + bool enable_download_whitelist, + bool enable_extension_blacklist); // Makes the passed |factory| the factory used to instantiate // a SafeBrowsingDatabase. This is used for tests. @@ -119,6 +121,14 @@ class SafeBrowsingDatabase { virtual bool ContainsDownloadWhitelistedUrl(const GURL& url) = 0; virtual bool ContainsDownloadWhitelistedString(const std::string& str) = 0; + // Populates |prefix_hits| with any prefixes in |prefixes| that have matches + // in the database. + // + // This function can ONLY be accessed from the creation thread. + virtual bool ContainsExtensionPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) = 0; + // A database transaction should look like: // // std::vector<SBListChunkRanges> lists; @@ -175,6 +185,10 @@ class SafeBrowsingDatabase { static FilePath DownloadWhitelistDBFilename( const FilePath& download_whitelist_base_filename); + // Filename for extension blacklist database. + static FilePath ExtensionBlacklistDBFilename( + const FilePath& extension_blacklist_base_filename); + // Enumerate failures for histogramming purposes. DO NOT CHANGE THE // ORDERING OF THESE VALUES. enum FailureType { @@ -196,6 +210,9 @@ class SafeBrowsingDatabase { FAILURE_DATABASE_PREFIX_SET_READ, FAILURE_DATABASE_PREFIX_SET_WRITE, FAILURE_DATABASE_PREFIX_SET_DELETE, + FAILURE_EXTENSION_BLACKLIST_UPDATE_BEGIN, + FAILURE_EXTENSION_BLACKLIST_UPDATE_FINISH, + FAILURE_EXTENSION_BLACKLIST_DELETE, // Memory space for histograms is determined by the max. ALWAYS // ADD NEW VALUES BEFORE THIS ONE. @@ -221,7 +238,8 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { SafeBrowsingDatabaseNew(SafeBrowsingStore* browse_store, SafeBrowsingStore* download_store, SafeBrowsingStore* csd_whitelist_store, - SafeBrowsingStore* download_whitelist_store); + SafeBrowsingStore* download_whitelist_store, + SafeBrowsingStore* extension_blacklist_store); // Create a database with a browse store. This is a legacy interface that // useds Sqlite. @@ -244,6 +262,9 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { virtual bool ContainsDownloadWhitelistedUrl(const GURL& url) OVERRIDE; virtual bool ContainsDownloadWhitelistedString( const std::string& str) OVERRIDE; + virtual bool ContainsExtensionPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) OVERRIDE; virtual bool UpdateStarted(std::vector<SBListChunkRanges>* lists) OVERRIDE; virtual void InsertChunks(const std::string& list_name, const SBChunkList& chunks) OVERRIDE; @@ -309,7 +330,10 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { void InsertSubChunks(safe_browsing_util::ListType list_id, const SBChunkList& chunks); - void UpdateDownloadStore(); + // Returns the size in bytes of the store after the update. + int64 UpdateHashPrefixStore(const FilePath& store_filename, + SafeBrowsingStore* store, + FailureType failure_type); void UpdateBrowseStore(); void UpdateWhitelistStore(const FilePath& store_filename, SafeBrowsingStore* store, @@ -344,8 +368,13 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { FilePath download_whitelist_filename_; scoped_ptr<SafeBrowsingStore> download_whitelist_store_; + // For extension IDs. + FilePath extension_blacklist_filename_; + scoped_ptr<SafeBrowsingStore> extension_blacklist_store_; + SBWhitelist csd_whitelist_; SBWhitelist download_whitelist_; + SBWhitelist extension_blacklist_; // Cached browse store related full-hash items, ordered by prefix for // efficient scanning. diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc index d53e6a1..d973ffb 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc @@ -368,10 +368,13 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* download_whitelist_store = new SafeBrowsingStoreFile(); + SafeBrowsingStoreFile* extension_blacklist_store = + new SafeBrowsingStoreFile(); database_.reset(new SafeBrowsingDatabaseNew(browse_store, download_store, csd_whitelist_store, - download_whitelist_store)); + download_whitelist_store, + extension_blacklist_store)); database_->Init(database_filename_); SBChunkList chunks; @@ -421,10 +424,20 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { chunks.push_back(chunk); database_->InsertChunks(safe_browsing_util::kDownloadWhiteList, chunks); + + chunk.hosts.clear(); + InsertAddChunkHostFullHashes(&chunk, 8, + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + + chunks.clear(); + chunks.push_back(chunk); + database_->InsertChunks(safe_browsing_util::kExtensionBlacklist, chunks); + database_->UpdateFinished(true); GetListsInfo(&lists); - ASSERT_EQ(5U, lists.size()); + ASSERT_EQ(6U, lists.size()); EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].adds, "1"); EXPECT_TRUE(lists[0].subs.empty()); @@ -441,6 +454,9 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { EXPECT_TRUE(lists[4].name == safe_browsing_util::kDownloadWhiteList); EXPECT_EQ(lists[4].adds, "6"); EXPECT_TRUE(lists[4].subs.empty()); + EXPECT_TRUE(lists[5].name == safe_browsing_util::kExtensionBlacklist); + EXPECT_EQ(lists[5].adds, "8"); + EXPECT_TRUE(lists[5].subs.empty()); database_.reset(); } @@ -1064,7 +1080,7 @@ TEST_F(SafeBrowsingDatabaseTest, DISABLED_FileCorruptionHandling) { database_.reset(); MessageLoop loop(MessageLoop::TYPE_DEFAULT); SafeBrowsingStoreFile* store = new SafeBrowsingStoreFile(); - database_.reset(new SafeBrowsingDatabaseNew(store, NULL, NULL, NULL)); + database_.reset(new SafeBrowsingDatabaseNew(store, NULL, NULL, NULL, NULL)); database_->Init(database_filename_); // This will cause an empty database to be created. @@ -1137,6 +1153,7 @@ TEST_F(SafeBrowsingDatabaseTest, ContainsDownloadUrl) { database_.reset(new SafeBrowsingDatabaseNew(browse_store, download_store, csd_whitelist_store, + NULL, NULL)); database_->Init(database_filename_); @@ -1239,7 +1256,7 @@ TEST_F(SafeBrowsingDatabaseTest, Whitelists) { // If the whitelist is disabled everything should match the whitelist. database_.reset(new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile(), - NULL, NULL, NULL)); + NULL, NULL, NULL, NULL)); database_->Init(database_filename_); EXPECT_TRUE(database_->ContainsDownloadWhitelistedUrl( GURL(std::string("http://www.phishing.com/")))); @@ -1250,9 +1267,12 @@ TEST_F(SafeBrowsingDatabaseTest, Whitelists) { SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* download_whitelist_store = new SafeBrowsingStoreFile(); + SafeBrowsingStoreFile* extension_blacklist_store = + new SafeBrowsingStoreFile(); database_.reset(new SafeBrowsingDatabaseNew(browse_store, NULL, csd_whitelist_store, - download_whitelist_store)); + download_whitelist_store, + extension_blacklist_store)); database_->Init(database_filename_); const char kGood1Host[] = "www.good1.com/"; diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc index 8fbd933..2a373e9 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc @@ -114,6 +114,11 @@ class TestSafeBrowsingDatabase : public SafeBrowsingDatabase { virtual bool ContainsDownloadWhitelistedUrl(const GURL& url) { return true; } + virtual bool ContainsExtensionPrefixes( + const std::vector<SBPrefix>& prefixes, + std::vector<SBPrefix>* prefix_hits) OVERRIDE { + return true; + } virtual bool UpdateStarted(std::vector<SBListChunkRanges>* lists) { ADD_FAILURE() << "Not implemented."; return false; @@ -197,7 +202,8 @@ class TestSafeBrowsingDatabaseFactory : public SafeBrowsingDatabaseFactory { virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( bool enable_download_protection, bool enable_client_side_whitelist, - bool enable_download_whitelist) { + bool enable_download_whitelist, + bool enable_extension_blacklist) { db_ = new TestSafeBrowsingDatabase(); return db_; } diff --git a/chrome/browser/safe_browsing/safe_browsing_test.cc b/chrome/browser/safe_browsing/safe_browsing_test.cc index cad8262..d0d6d46 100644 --- a/chrome/browser/safe_browsing/safe_browsing_test.cc +++ b/chrome/browser/safe_browsing/safe_browsing_test.cc @@ -247,6 +247,10 @@ class SafeBrowsingServerTest : public InProcessBrowserTest { command_line->AppendSwitch( switches::kDisableClientSidePhishingDetection); + // TODO(kalman): Generate new testing data that includes the extension + // blacklist. + command_line->AppendSwitch(switches::kSbDisableExtensionBlacklist); + // Point to the testing server for all SafeBrowsing requests. std::string url_prefix = test_server_->GetURL("safebrowsing").spec(); command_line->AppendSwitchASCII(switches::kSbURLPrefix, url_prefix); diff --git a/chrome/browser/safe_browsing/safe_browsing_util.cc b/chrome/browser/safe_browsing/safe_browsing_util.cc index 3951484..ef620a7 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.cc +++ b/chrome/browser/safe_browsing/safe_browsing_util.cc @@ -163,6 +163,7 @@ const char kBinUrlList[] = "goog-badbinurl-shavar"; const char kBinHashList[] = "goog-badbin-digestvar-disabled"; const char kCsdWhiteList[] = "goog-csdwhite-sha256"; const char kDownloadWhiteList[] = "goog-downloadwhite-digest256"; +const char kExtensionBlacklist[] = "goog-badcrxids-digestvar"; ListType GetListId(const std::string& name) { ListType id; @@ -178,6 +179,8 @@ ListType GetListId(const std::string& name) { id = CSDWHITELIST; } else if (name == safe_browsing_util::kDownloadWhiteList) { id = DOWNLOADWHITELIST; + } else if (name == safe_browsing_util::kExtensionBlacklist) { + id = EXTENSIONBLACKLIST; } else { id = INVALID; } @@ -204,6 +207,9 @@ bool GetListName(ListType list_id, std::string* list) { case DOWNLOADWHITELIST: *list = safe_browsing_util::kDownloadWhiteList; break; + case EXTENSIONBLACKLIST: + *list = safe_browsing_util::kExtensionBlacklist; + break; default: return false; } @@ -469,6 +475,10 @@ bool IsBadbinhashList(const std::string& list_name) { return list_name.compare(kBinHashList) == 0; } +bool IsExtensionList(const std::string& list_name) { + return list_name.compare(kExtensionBlacklist) == 0; +} + GURL GeneratePhishingReportUrl(const std::string& report_page, const std::string& url_to_report, bool is_client_side_detection) { diff --git a/chrome/browser/safe_browsing/safe_browsing_util.h b/chrome/browser/safe_browsing/safe_browsing_util.h index 0ba14fa..4e0fd51 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.h +++ b/chrome/browser/safe_browsing/safe_browsing_util.h @@ -9,6 +9,7 @@ #include <cstring> #include <deque> +#include <set> #include <string> #include <vector> @@ -147,6 +148,9 @@ enum SBThreatType { // Url detected by the client-side phishing model. Note that unlike the // above values, this does not correspond to a downloaded list. SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL, + + // The Chrome extension or app (given by its ID) is malware. + SB_THREAT_TYPE_EXTENSION, }; // SBEntry --------------------------------------------------------------------- @@ -287,6 +291,8 @@ extern const char kBinHashList[]; extern const char kCsdWhiteList[]; // SafeBrowsing download whitelist list name. extern const char kDownloadWhiteList[]; +// SafeBrowsing extension list name. +extern const char kExtensionBlacklist[]; enum ListType { INVALID = -1, @@ -299,6 +305,8 @@ enum ListType { // available for a potential second list that we would store in the // csd-whitelist store file. DOWNLOADWHITELIST = 6, + // See above comment. Leave 7 available. + EXTENSIONBLACKLIST = 8, }; // Maps a list name to ListType. @@ -337,6 +345,7 @@ bool IsPhishingList(const std::string& list_name); bool IsMalwareList(const std::string& list_name); bool IsBadbinurlList(const std::string& list_name); bool IsBadbinhashList(const std::string& list_name); +bool IsExtensionList(const std::string& list_name); GURL GeneratePhishingReportUrl(const std::string& report_page, const std::string& url_to_report, diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 9bcaad8..4f579c5 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -106,6 +106,8 @@ 'browser/download/download_test_file_activity_observer.h', 'browser/download/test_download_shelf.cc', 'browser/download/test_download_shelf.h', + 'browser/extensions/fake_safe_browsing_database_manager.cc', + 'browser/extensions/fake_safe_browsing_database_manager.h', 'browser/extensions/mock_extension_special_storage_policy.cc', 'browser/extensions/mock_extension_special_storage_policy.h', 'browser/extensions/test_blacklist.cc', @@ -301,6 +303,13 @@ '../chromeos/chromeos.gyp:chromeos_test_support', ], }], + ['safe_browsing!=1', { + 'sources/': [ + ['exclude', '^browser/extensions/blacklist_unittest.cc'], + ['exclude', '^browser/extensions/fake_safe_browsing_database_manager.cc'], + ['exclude', '^browser/extensions/fake_safe_browsing_database_manager.h'], + ], + }], ['toolkit_uses_gtk == 1', { 'dependencies': [ '../build/linux/system.gyp:gtk', @@ -1834,14 +1843,8 @@ 'FULL_SAFE_BROWSING', ], }], - # TODO(sgurun): enable tests. - ['safe_browsing==2', { - 'sources/': [ - ['exclude', '^browser/safe_browsing/'], - ['exclude', '^renderer/safe_browsing/'], - ], - }], - ['safe_browsing==0', { + # TODO(sgurun): enable tests for safe_browsing==2. + ['safe_browsing!=1', { 'sources/': [ ['exclude', '^browser/safe_browsing/'], ['exclude', '^renderer/safe_browsing/'], @@ -2260,6 +2263,11 @@ ['exclude', '^browser/managed_mode/'], ], }], + ['safe_browsing!=1', { + 'sources/': [ + ['exclude', '^browser/extensions/blacklist_unittest.cc'], + ], + }], ], 'target_conditions': [ ['OS == "ios"', { diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 024d66f..c2bf004 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -1167,6 +1167,10 @@ const char kSbDisableAutoUpdate[] = "safebrowsing-disable-auto-update"; const char kSbDisableDownloadProtection[] = "safebrowsing-disable-download-protection"; +// Disables safebrowsing feature that checks for blacklisted extensions. +const char kSbDisableExtensionBlacklist[] = + "safebrowsing-disable-extension-blacklist"; + // Enables or disables extension scripts badges in the location bar. const char kScriptBadges[] = "script-badges"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 2f138e7..d8a1f3a 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -308,6 +308,7 @@ extern const char kSavePageAsMHTML[]; extern const char kSbURLPrefix[]; extern const char kSbDisableAutoUpdate[]; extern const char kSbDisableDownloadProtection[]; +extern const char kSbDisableExtensionBlacklist[]; extern const char kScriptBadges[]; extern const char kScriptBubble[]; extern const char kSearchInOmniboxHint[]; |