// Copyright (c) 2011 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/safe_browsing/download_protection_service.h" #include #include #include "base/bind.h" #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "chrome/common/safe_browsing/csd.pb.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "content/browser/browser_thread.h" #include "content/common/net/url_fetcher.h" #include "content/test/test_url_fetcher_factory.h" #include "googleurl/src/gurl.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::Return; using ::testing::_; namespace safe_browsing { namespace { class MockSafeBrowsingService : public SafeBrowsingService { public: MockSafeBrowsingService() {} virtual ~MockSafeBrowsingService() {} MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL&)); private: DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService); }; } // namespace class DownloadProtectionServiceTest : public testing::Test { protected: virtual void SetUp() { ui_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_)); io_thread_.reset(new BrowserThread(BrowserThread::IO, &msg_loop_)); sb_service_ = new MockSafeBrowsingService(); download_service_ = new DownloadProtectionService(sb_service_.get(), NULL); download_service_->SetEnabled(true); msg_loop_.RunAllPending(); } virtual void TearDown() { msg_loop_.RunAllPending(); download_service_ = NULL; sb_service_ = NULL; io_thread_.reset(); ui_thread_.reset(); } bool RequestContainsResource(const ClientDownloadRequest& request, ClientDownloadRequest::ResourceType type, const std::string& url, const std::string& referrer) { for (int i = 0; i < request.resources_size(); ++i) { if (request.resources(i).url() == url && request.resources(i).type() == type && (referrer.empty() || request.resources(i).referrer() == referrer)) { return true; } } return false; } public: void CheckDoneCallback( DownloadProtectionService::DownloadCheckResult result) { result_ = result; msg_loop_.Quit(); } protected: scoped_refptr sb_service_; scoped_refptr download_service_; MessageLoop msg_loop_; DownloadProtectionService::DownloadCheckResult result_; scoped_ptr io_thread_; scoped_ptr ui_thread_; }; TEST_F(DownloadProtectionServiceTest, CheckClientDownloadInvalidUrl) { DownloadProtectionService::DownloadInfo info; EXPECT_TRUE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); // Only http is supported for now. info.download_url_chain.push_back(GURL("https://www.google.com/")); EXPECT_TRUE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); info.download_url_chain[0] = GURL("ftp://www.google.com/"); EXPECT_TRUE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadWhitelistedUrl) { DownloadProtectionService::DownloadInfo info; info.download_url_chain.push_back(GURL("http://www.evil.com/bla.exe")); info.download_url_chain.push_back(GURL("http://www.google.com/a.exe")); info.referrer_url = GURL("http://www.google.com/"); EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(GURL("http://www.google.com/a.exe"))) .WillRepeatedly(Return(true)); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.Run(); EXPECT_EQ(DownloadProtectionService::SAFE, result_); // Check that the referrer is matched against the whitelist. info.download_url_chain.pop_back(); info.referrer_url = GURL("http://www.google.com/a.exe"); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.Run(); EXPECT_EQ(DownloadProtectionService::SAFE, result_); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) { FakeURLFetcherFactory factory; // HTTP request will fail. factory.SetFakeResponse( DownloadProtectionService::kDownloadRequestUrl, "", false); EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); DownloadProtectionService::DownloadInfo info; info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); info.referrer_url = GURL("http://www.google.com/"); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.Run(); EXPECT_EQ(DownloadProtectionService::SAFE, result_); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) { FakeURLFetcherFactory factory; // Empty response means SAFE. factory.SetFakeResponse( DownloadProtectionService::kDownloadRequestUrl, "", true); EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); DownloadProtectionService::DownloadInfo info; info.download_url_chain.push_back(GURL("http://www.evil.com/a.exe")); info.referrer_url = GURL("http://www.google.com/"); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.Run(); EXPECT_EQ(DownloadProtectionService::SAFE, result_); // Invalid response should be safe too. factory.SetFakeResponse( DownloadProtectionService::kDownloadRequestUrl, "bla", true); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.Run(); EXPECT_EQ(DownloadProtectionService::SAFE, result_); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { TestURLFetcherFactory factory; DownloadProtectionService::DownloadInfo info; info.download_url_chain.push_back(GURL("http://www.google.com/")); info.download_url_chain.push_back(GURL("http://www.google.com/bla.exe")); info.referrer_url = GURL("http://www.google.com/"); info.sha256_hash = "hash"; info.total_bytes = 100; info.user_initiated = false; EXPECT_CALL(*sb_service_, MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_FALSE(download_service_->CheckClientDownload( info, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this)))); msg_loop_.RunAllPending(); // Wait until StartCheckClientDownload is called. ASSERT_TRUE(factory.GetFetcherByID(0)); ClientDownloadRequest request; EXPECT_TRUE(request.ParseFromString( factory.GetFetcherByID(0)->upload_data())); EXPECT_EQ("http://www.google.com/bla.exe", request.url()); EXPECT_EQ(info.sha256_hash, request.digests().sha256()); EXPECT_EQ(info.total_bytes, request.length()); EXPECT_EQ(info.user_initiated, request.user_initiated()); EXPECT_EQ(2, request.resources_size()); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::DOWNLOAD_REDIRECT, "http://www.google.com/", "")); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::DOWNLOAD_URL, "http://www.google.com/bla.exe", info.referrer_url.spec())); } } // namespace safe_browsing