// Copyright (c) 2012 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 #include "base/base_paths.h" #include "base/bind.h" #include "base/callback.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/threading/sequenced_worker_pool.h" #include "chrome/browser/history/history_service_factory.h" #include "chrome/browser/safe_browsing/database_manager.h" #include "chrome/browser/safe_browsing/download_feedback_service.h" #include "chrome/browser/safe_browsing/local_database_manager.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/safe_browsing/test_database_manager.h" #include "chrome/common/safe_browsing/binary_feature_extractor.h" #include "chrome/common/safe_browsing/csd.pb.h" #include "chrome/test/base/testing_profile.h" #include "components/history/core/browser/history_service.h" #include "content/public/browser/download_danger_type.h" #include "content/public/browser/page_navigator.h" #include "content/public/test/mock_download_item.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_utils.h" #include "net/base/url_util.h" #include "net/cert/x509_certificate.h" #include "net/http/http_status_code.h" #include "net/url_request/test_url_fetcher_factory.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_request_status.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/zlib/google/zip.h" #include "url/gurl.h" using ::testing::Assign; using ::testing::ContainerEq; using ::testing::DoAll; using ::testing::ElementsAre; using ::testing::Invoke; using ::testing::Mock; using ::testing::NotNull; using ::testing::Return; using ::testing::ReturnRef; using ::testing::SaveArg; using ::testing::StrictMock; using ::testing::_; using base::MessageLoop; using content::BrowserThread; namespace safe_browsing { namespace { // A SafeBrowsingDatabaseManager implementation that returns a fixed result for // a given URL. class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager { public: MockSafeBrowsingDatabaseManager() {} MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL&)); MOCK_METHOD1(MatchDownloadWhitelistString, bool(const std::string&)); MOCK_METHOD2(CheckDownloadUrl, bool( const std::vector& url_chain, SafeBrowsingDatabaseManager::Client* client)); private: virtual ~MockSafeBrowsingDatabaseManager() {} DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager); }; class FakeSafeBrowsingService : public SafeBrowsingService { public: FakeSafeBrowsingService() { } // Returned pointer has the same lifespan as the database_manager_ refcounted // object. MockSafeBrowsingDatabaseManager* mock_database_manager() { return mock_database_manager_; } protected: ~FakeSafeBrowsingService() override {} SafeBrowsingDatabaseManager* CreateDatabaseManager() override { mock_database_manager_ = new MockSafeBrowsingDatabaseManager(); return mock_database_manager_; } SafeBrowsingProtocolManagerDelegate* GetProtocolManagerDelegate() override { // Our SafeBrowsingDatabaseManager doesn't implement this delegate. return NULL; } void RegisterAllDelayedAnalysis() override {} private: MockSafeBrowsingDatabaseManager* mock_database_manager_; DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService); }; class MockBinaryFeatureExtractor : public BinaryFeatureExtractor { public: MockBinaryFeatureExtractor() {} MOCK_METHOD2(CheckSignature, void(const base::FilePath&, ClientDownloadRequest_SignatureInfo*)); MOCK_METHOD4(ExtractImageFeatures, bool( const base::FilePath&, ExtractHeadersOption, ClientDownloadRequest_ImageHeaders*, google::protobuf::RepeatedPtrField*)); protected: virtual ~MockBinaryFeatureExtractor() {} private: DISALLOW_COPY_AND_ASSIGN(MockBinaryFeatureExtractor); }; class TestURLFetcherWatcher : public net::TestURLFetcherDelegateForTests { public: explicit TestURLFetcherWatcher(net::TestURLFetcherFactory* factory) : factory_(factory), fetcher_id_(-1) { factory_->SetDelegateForTests(this); } ~TestURLFetcherWatcher() { factory_->SetDelegateForTests(NULL); } // TestURLFetcherDelegateForTests impl: void OnRequestStart(int fetcher_id) override { fetcher_id_ = fetcher_id; run_loop_.Quit(); } void OnChunkUpload(int fetcher_id) override {} void OnRequestEnd(int fetcher_id) override {} int WaitForRequest() { run_loop_.Run(); return fetcher_id_; } private: net::TestURLFetcherFactory* factory_; int fetcher_id_; base::RunLoop run_loop_; }; } // namespace ACTION_P(SetCertificateContents, contents) { arg1->add_certificate_chain()->add_element()->set_certificate(contents); } ACTION_P(SetDosHeaderContents, contents) { arg2->mutable_pe_headers()->set_dos_header(contents); return true; } ACTION_P(TrustSignature, certificate_file) { arg1->set_trusted(true); // Add a certificate chain. Note that we add the certificate twice so that // it appears as its own issuer. std::string cert_data; ASSERT_TRUE(base::ReadFileToString(certificate_file, &cert_data)); ClientDownloadRequest_CertificateChain* chain = arg1->add_certificate_chain(); chain->add_element()->set_certificate(cert_data); chain->add_element()->set_certificate(cert_data); } // We can't call OnSafeBrowsingResult directly because SafeBrowsingCheck does // not have any copy constructor which means it can't be stored in a callback // easily. Note: check will be deleted automatically when the callback is // deleted. void OnSafeBrowsingResult( LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck* check) { check->OnSafeBrowsingResult(); } ACTION_P(CheckDownloadUrlDone, threat_type) { // TODO(nparker): Remove use of SafeBrowsingCheck and instead call // client->OnCheckDownloadUrlResult(..) directly. LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck* check = new LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck( arg0, std::vector(), arg1, safe_browsing_util::BINURL, std::vector(1, SB_THREAT_TYPE_BINARY_MALWARE_URL)); for (size_t i = 0; i < check->url_results.size(); ++i) check->url_results[i] = threat_type; BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&OnSafeBrowsingResult, base::Owned(check))); } class DownloadProtectionServiceTest : public testing::Test { protected: DownloadProtectionServiceTest() : test_browser_thread_bundle_( content::TestBrowserThreadBundle::IO_MAINLOOP) { } void SetUp() override { // Start real threads for the IO and File threads so that the DCHECKs // to test that we're on the correct thread work. sb_service_ = new StrictMock(); sb_service_->Initialize(); binary_feature_extractor_ = new StrictMock(); ON_CALL(*binary_feature_extractor_, ExtractImageFeatures(_, _, _, _)) .WillByDefault(Return(true)); download_service_ = sb_service_->download_protection_service(); download_service_->binary_feature_extractor_ = binary_feature_extractor_; download_service_->SetEnabled(true); client_download_request_subscription_ = download_service_->RegisterClientDownloadRequestCallback( base::Bind(&DownloadProtectionServiceTest::OnClientDownloadRequest, base::Unretained(this))); base::RunLoop().RunUntilIdle(); has_result_ = false; base::FilePath source_path; ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &source_path)); testdata_path_ = source_path .AppendASCII("chrome") .AppendASCII("test") .AppendASCII("data") .AppendASCII("safe_browsing") .AppendASCII("download_protection"); } void TearDown() override { client_download_request_subscription_.reset(); sb_service_->ShutDown(); // Flush all of the thread message loops to ensure that there are no // tasks currently running. FlushThreadMessageLoops(); sb_service_ = NULL; } 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; } // At this point we only set the server IP for the download itself. bool RequestContainsServerIp(const ClientDownloadRequest& request, const std::string& remote_address) { for (int i = 0; i < request.resources_size(); ++i) { // We want the last DOWNLOAD_URL in the chain. if (request.resources(i).type() == ClientDownloadRequest::DOWNLOAD_URL && (i + 1 == request.resources_size() || request.resources(i + 1).type() != ClientDownloadRequest::DOWNLOAD_URL)) { return remote_address == request.resources(i).remote_ip(); } } return false; } static const ClientDownloadRequest_ArchivedBinary* GetRequestArchivedBinary( const ClientDownloadRequest& request, const std::string& file_basename) { for (const auto& archived_binary : request.archived_binary()) { if (archived_binary.file_basename() == file_basename) return &archived_binary; } return nullptr; } // Flushes any pending tasks in the message loops of all threads. void FlushThreadMessageLoops() { BrowserThread::GetBlockingPool()->FlushForTesting(); FlushMessageLoop(BrowserThread::IO); base::RunLoop().RunUntilIdle(); } // Proxy for private method. static void GetCertificateWhitelistStrings( const net::X509Certificate& certificate, const net::X509Certificate& issuer, std::vector* whitelist_strings) { DownloadProtectionService::GetCertificateWhitelistStrings( certificate, issuer, whitelist_strings); } // Reads a single PEM-encoded certificate from the testdata directory. // Returns NULL on failure. scoped_refptr ReadTestCertificate( const std::string& filename) { std::string cert_data; if (!base::ReadFileToString(testdata_path_.AppendASCII(filename), &cert_data)) { return NULL; } net::CertificateList certs = net::X509Certificate::CreateCertificateListFromBytes( cert_data.data(), cert_data.size(), net::X509Certificate::FORMAT_PEM_CERT_SEQUENCE); return certs.empty() ? NULL : certs[0]; } const ClientDownloadRequest* GetClientDownloadRequest() const { return last_client_download_request_.get(); } bool HasClientDownloadRequest() const { return last_client_download_request_.get() != NULL; } void ClearClientDownloadRequest() { last_client_download_request_.reset(); } private: // Helper functions for FlushThreadMessageLoops. void RunAllPendingAndQuitUI() { base::MessageLoop::current()->RunUntilIdle(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DownloadProtectionServiceTest::QuitMessageLoop, base::Unretained(this))); } void QuitMessageLoop() { base::MessageLoop::current()->Quit(); } void PostRunMessageLoopTask(BrowserThread::ID thread) { BrowserThread::PostTask( thread, FROM_HERE, base::Bind(&DownloadProtectionServiceTest::RunAllPendingAndQuitUI, base::Unretained(this))); } void FlushMessageLoop(BrowserThread::ID thread) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&DownloadProtectionServiceTest::PostRunMessageLoopTask, base::Unretained(this), thread)); MessageLoop::current()->Run(); } void OnClientDownloadRequest(content::DownloadItem* download, const ClientDownloadRequest* request) { if (request) last_client_download_request_.reset(new ClientDownloadRequest(*request)); else last_client_download_request_.reset(); } public: void CheckDoneCallback( DownloadProtectionService::DownloadCheckResult result) { result_ = result; has_result_ = true; MessageLoop::current()->Quit(); } void SyncCheckDoneCallback( DownloadProtectionService::DownloadCheckResult result) { result_ = result; has_result_ = true; } void SendURLFetchComplete(net::TestURLFetcher* fetcher) { fetcher->delegate()->OnURLFetchComplete(fetcher); } testing::AssertionResult IsResult( DownloadProtectionService::DownloadCheckResult expected) { if (!has_result_) return testing::AssertionFailure() << "No result"; has_result_ = false; return result_ == expected ? testing::AssertionSuccess() : testing::AssertionFailure() << "Expected " << expected << ", got " << result_; } protected: scoped_refptr sb_service_; scoped_refptr binary_feature_extractor_; DownloadProtectionService* download_service_; DownloadProtectionService::DownloadCheckResult result_; bool has_result_; content::TestBrowserThreadBundle test_browser_thread_bundle_; content::InProcessUtilityThreadHelper in_process_utility_thread_helper_; base::FilePath testdata_path_; DownloadProtectionService::ClientDownloadRequestSubscription client_download_request_subscription_; scoped_ptr last_client_download_request_; }; TEST_F(DownloadProtectionServiceTest, CheckClientDownloadInvalidUrl) { base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; GURL referrer("http://www.google.com/"); content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); Mock::VerifyAndClearExpectations(&item); url_chain.push_back(GURL("file://www.google.com/")); EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadNotABinary) { base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_txt(FILE_PATH_LITERAL("a.txt")); std::vector url_chain; GURL referrer("http://www.google.com/"); content::MockDownloadItem item; url_chain.push_back(GURL("http://www.example.com/foo")); EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_txt)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadWhitelistedUrl) { // Response to any requests will be DANGEROUS. ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::DANGEROUS); net::FakeURLFetcherFactory factory(NULL); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); std::string hash = "hash"; base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; GURL referrer; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(4); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(4); // We should not get whilelist checks for other URLs than specified below. EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)).Times(0); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(GURL("http://www.evil.com/bla.exe"))) .WillRepeatedly(Return(false)); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(GURL("http://www.google.com/a.exe"))) .WillRepeatedly(Return(true)); // With no referrer and just the bad url, should be marked DANGEROUS. url_chain.push_back(GURL("http://www.evil.com/bla.exe")); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif // Check that the referrer is not matched against the whitelist. referrer = GURL("http://www.google.com/"); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif // Redirect from a site shouldn't be checked either. url_chain.insert(url_chain.begin(), GURL("http://www.google.com/redirect")); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif // Only if the final url is whitelisted should it be SAFE. url_chain.push_back(GURL("http://www.google.com/a.exe")); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); // TODO(grt): Make the service produce the request even when the URL is // whitelisted. EXPECT_FALSE(HasClientDownloadRequest()); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadFetchFailed) { net::FakeURLFetcherFactory factory(NULL); // HTTP request will fail. factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), std::string(), net::HTTP_INTERNAL_SERVER_ERROR, net::URLRequestStatus::FAILED); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)); EXPECT_CALL( *binary_feature_extractor_.get(), ExtractImageFeatures(a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadSuccess) { ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::SAFE); net::FakeURLFetcherFactory factory(NULL); // Empty response means SAFE. factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(6); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(6); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else // On !(OS_WIN || OS_MACOSX), no file types are currently supported. Hence all // requests to CheckClientDownload() result in a verdict of UNKNOWN. EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif // Invalid response should result in UNKNOWN. response.Clear(); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializePartialAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_FALSE(HasClientDownloadRequest()); #endif std::string feedback_ping; std::string feedback_response; EXPECT_FALSE(DownloadFeedbackService::GetPingsForDownloadForTesting( item, &feedback_ping, &feedback_response)); // If the response is dangerous the result should also be marked as dangerous. response.set_verdict(ClientDownloadResponse::DANGEROUS); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_FALSE(DownloadFeedbackService::GetPingsForDownloadForTesting( item, &feedback_ping, &feedback_response)); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif // If the response is uncommon the result should also be marked as uncommon. response.set_verdict(ClientDownloadResponse::UNCOMMON); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::UNCOMMON)); EXPECT_TRUE(DownloadFeedbackService::GetPingsForDownloadForTesting( item, &feedback_ping, &feedback_response)); ClientDownloadRequest decoded_request; EXPECT_TRUE(decoded_request.ParseFromString(feedback_ping)); EXPECT_EQ(url_chain.back().spec(), decoded_request.url()); EXPECT_EQ(response.SerializeAsString(), feedback_response); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); #endif // If the response is dangerous_host the result should also be marked as // dangerous_host. response.set_verdict(ClientDownloadResponse::DANGEROUS_HOST); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS_HOST)); EXPECT_TRUE(DownloadFeedbackService::GetPingsForDownloadForTesting( item, &feedback_ping, &feedback_response)); EXPECT_EQ(response.SerializeAsString(), feedback_response); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); #endif // If the response is POTENTIALLY_UNWANTED the result should also be marked as // POTENTIALLY_UNWANTED. response.set_verdict(ClientDownloadResponse::POTENTIALLY_UNWANTED); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::POTENTIALLY_UNWANTED)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadHTTPS) { ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::DANGEROUS); net::FakeURLFetcherFactory factory(NULL); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(1); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadBlob) { ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::DANGEROUS); net::FakeURLFetcherFactory factory(NULL); factory.SetFakeResponse(DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; url_chain.push_back( GURL("blob:http://www.evil.com/50b85f60-71e4-11e4-82f8-0800200c9a66")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)).WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(1); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadData) { ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::DANGEROUS); net::FakeURLFetcherFactory factory(NULL); factory.SetFakeResponse(DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_exe(FILE_PATH_LITERAL("a.exe")); std::vector url_chain; url_chain.push_back( GURL("data:text/html:base64,")); url_chain.push_back( GURL("data:text/html:base64,blahblahblah")); url_chain.push_back( GURL("data:application/octet-stream:base64,blahblah")); GURL referrer("data:text/html:base64,foobar"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_exe)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)).WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(1); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); ASSERT_TRUE(HasClientDownloadRequest()); const ClientDownloadRequest& request = *GetClientDownloadRequest(); const char kExpectedUrl[] = "data:application/octet-stream:base64," "ACBF6DFC6F907662F566CA0241DFE8690C48661F440BA1BBD0B86C582845CCC8"; const char kExpectedRedirect1[] = "data:text/html:base64,"; const char kExpectedRedirect2[] = "data:text/html:base64," "620680767E15717A57DB11D94D1BEBD32B3344EBC5994DF4FB07B0D473F4EF6B"; const char kExpectedReferrer[] = "data:text/html:base64," "06E2C655B9F7130B508FFF86FD19B57E6BF1A1CFEFD6EFE1C3EB09FE24EF456A"; EXPECT_EQ(hash, request.digests().sha256()); EXPECT_EQ(kExpectedUrl, request.url()); EXPECT_EQ(3, request.resources_size()); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::DOWNLOAD_REDIRECT, kExpectedRedirect1, "")); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::DOWNLOAD_REDIRECT, kExpectedRedirect2, "")); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::DOWNLOAD_URL, kExpectedUrl, kExpectedReferrer)); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadZip) { ClientDownloadResponse response; response.set_verdict(ClientDownloadResponse::SAFE); net::FakeURLFetcherFactory factory(NULL); // Empty response means SAFE. factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); base::FilePath a_tmp(download_dir.path().Append(FILE_PATH_LITERAL("a.tmp"))); base::FilePath a_zip(FILE_PATH_LITERAL("a.zip")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.zip")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_zip)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); // Write out a zip archive to the temporary file. In this case, it // only contains a text file. base::ScopedTempDir zip_source_dir; ASSERT_TRUE(zip_source_dir.CreateUniqueTempDir()); std::string file_contents = "dummy file"; ASSERT_EQ(static_cast(file_contents.size()), base::WriteFile( zip_source_dir.path().Append(FILE_PATH_LITERAL("file.txt")), file_contents.data(), file_contents.size())); ASSERT_TRUE(zip::Zip(zip_source_dir.path(), a_tmp, false)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); Mock::VerifyAndClearExpectations(sb_service_.get()); Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); // Now check with an executable in the zip file as well. ASSERT_EQ(static_cast(file_contents.size()), base::WriteFile( zip_source_dir.path().Append(FILE_PATH_LITERAL("file.exe")), file_contents.data(), file_contents.size())); ASSERT_TRUE(zip::Zip(zip_source_dir.path(), a_tmp, false)); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); EXPECT_TRUE(HasClientDownloadRequest()); const ClientDownloadRequest& request = *GetClientDownloadRequest(); EXPECT_TRUE(request.has_download_type()); EXPECT_EQ(ClientDownloadRequest_DownloadType_ZIPPED_EXECUTABLE, request.download_type()); EXPECT_EQ(1, request.archived_binary_size()); const ClientDownloadRequest_ArchivedBinary* archived_binary = GetRequestArchivedBinary(request, "file.exe"); ASSERT_NE(nullptr, archived_binary); EXPECT_EQ(ClientDownloadRequest_DownloadType_WIN_EXECUTABLE, archived_binary->download_type()); EXPECT_EQ(static_cast(file_contents.size()), archived_binary->length()); ClearClientDownloadRequest(); #else // For !(OS_WIN || OS_MACOSX), no file types are currently supported. Hence // the resulting verdict is UNKNOWN. EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); // If the response is dangerous the result should also be marked as // dangerous. response.set_verdict(ClientDownloadResponse::DANGEROUS); factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); // Repeat the test with an archive inside the zip file in addition to the // executable. ASSERT_EQ(static_cast(file_contents.size()), base::WriteFile( zip_source_dir.path().Append(FILE_PATH_LITERAL("file.rar")), file_contents.data(), file_contents.size())); ASSERT_TRUE(zip::Zip(zip_source_dir.path(), a_tmp, false)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) ASSERT_TRUE(HasClientDownloadRequest()); EXPECT_EQ(1, GetClientDownloadRequest()->archived_binary_size()); EXPECT_TRUE(GetClientDownloadRequest()->has_download_type()); EXPECT_EQ(ClientDownloadRequest_DownloadType_ZIPPED_EXECUTABLE, GetClientDownloadRequest()->download_type()); ClearClientDownloadRequest(); #else // For !(OS_WIN || OS_MACOSX), no file types are currently supported. Hence // the resulting verdict is UNKNOWN. EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); // Repeat the test with just the archive inside the zip file. ASSERT_TRUE( base::DeleteFile(zip_source_dir.path().AppendASCII("file.exe"), false)); ASSERT_TRUE(zip::Zip(zip_source_dir.path(), a_tmp, false)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); #if defined(OS_WIN) || defined(OS_MACOSX) ASSERT_TRUE(HasClientDownloadRequest()); EXPECT_EQ(0, GetClientDownloadRequest()->archived_binary_size()); EXPECT_TRUE(GetClientDownloadRequest()->has_download_type()); EXPECT_EQ(ClientDownloadRequest_DownloadType_ZIPPED_ARCHIVE, GetClientDownloadRequest()->download_type()); ClearClientDownloadRequest(); #else // For !(OS_WIN || OS_MACOSX), no file types are currently supported. Hence // the resulting verdict is UNKNOWN. EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); #endif Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadCorruptZip) { base::ScopedTempDir download_dir; ASSERT_TRUE(download_dir.CreateUniqueTempDir()); base::FilePath a_tmp(download_dir.path().Append(FILE_PATH_LITERAL("a.tmp"))); base::FilePath a_zip(FILE_PATH_LITERAL("a.zip")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.zip")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_zip)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); std::string file_contents = "corrupt zip file"; ASSERT_EQ(static_cast(file_contents.size()), base::WriteFile( a_tmp, file_contents.data(), file_contents.size())); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); Mock::VerifyAndClearExpectations(sb_service_.get()); Mock::VerifyAndClearExpectations(binary_feature_extractor_.get()); } TEST_F(DownloadProtectionServiceTest, CheckClientCrxDownloadSuccess) { ClientDownloadResponse response; // Even if the server verdict is dangerous we should return SAFE because // DownloadProtectionService::IsSupportedDownload() will return false // for crx downloads. response.set_verdict(ClientDownloadResponse::DANGEROUS); net::FakeURLFetcherFactory factory(NULL); // Empty response means SAFE. factory.SetFakeResponse( DownloadProtectionService::GetDownloadRequestUrl(), response.SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp")); base::FilePath a_crx(FILE_PATH_LITERAL("a.crx")); std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/a.crx")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(a_tmp)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(a_crx)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(a_tmp, _)) .Times(1); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( a_tmp, BinaryFeatureExtractor::kDefaultOptions, _, _)) .Times(1); EXPECT_FALSE(download_service_->IsSupportedDownload(item, a_crx)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); } TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequest) { net::TestURLFetcherFactory factory; base::FilePath tmp_path(FILE_PATH_LITERAL("bla.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("bla.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.google.com/")); url_chain.push_back(GURL("http://www.google.com/bla.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; std::string remote_address = "10.11.12.13"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return(remote_address)); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)) .WillOnce(SetCertificateContents("dummy cert data")); EXPECT_CALL( *binary_feature_extractor_.get(), ExtractImageFeatures(tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)) .WillOnce(SetDosHeaderContents("dummy dos header")); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); #if !defined(OS_WIN) && !defined(OS_MACOSX) // SendRequest is not called. Wait for FinishRequest to call our callback. MessageLoop::current()->Run(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); EXPECT_EQ(NULL, fetcher); EXPECT_FALSE(HasClientDownloadRequest()); #else // Run the message loop(s) until SendRequest is called. FlushThreadMessageLoops(); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); ASSERT_TRUE(fetcher); ClientDownloadRequest request; EXPECT_TRUE(request.ParseFromString(fetcher->upload_data())); EXPECT_EQ("http://www.google.com/bla.exe", request.url()); EXPECT_EQ(hash, request.digests().sha256()); EXPECT_EQ(item.GetReceivedBytes(), request.length()); EXPECT_EQ(item.HasUserGesture(), request.user_initiated()); EXPECT_TRUE(RequestContainsServerIp(request, remote_address)); 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", referrer.spec())); EXPECT_TRUE(request.has_signature()); ASSERT_EQ(1, request.signature().certificate_chain_size()); const ClientDownloadRequest_CertificateChain& chain = request.signature().certificate_chain(0); ASSERT_EQ(1, chain.element_size()); EXPECT_EQ("dummy cert data", chain.element(0).certificate()); EXPECT_TRUE(request.has_image_headers()); const ClientDownloadRequest_ImageHeaders& headers = request.image_headers(); EXPECT_TRUE(headers.has_pe_headers()); EXPECT_TRUE(headers.pe_headers().has_dos_header()); EXPECT_EQ("dummy dos header", headers.pe_headers().dos_header()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DownloadProtectionServiceTest::SendURLFetchComplete, base::Unretained(this), fetcher)); MessageLoop::current()->Run(); #endif } // Similar to above, but with an unsigned binary. TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequestNoSignature) { net::TestURLFetcherFactory factory; base::FilePath tmp_path(FILE_PATH_LITERAL("bla.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("bla.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.google.com/")); url_chain.push_back(GURL("ftp://www.google.com/bla.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; std::string remote_address = "10.11.12.13"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return(remote_address)); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures(tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); #if !defined(OS_WIN) && !defined(OS_MACOSX) // SendRequest is not called. Wait for FinishRequest to call our callback. MessageLoop::current()->Run(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); EXPECT_EQ(NULL, fetcher); EXPECT_FALSE(HasClientDownloadRequest()); #else // Run the message loop(s) until SendRequest is called. FlushThreadMessageLoops(); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); ASSERT_TRUE(fetcher); ClientDownloadRequest request; EXPECT_TRUE(request.ParseFromString(fetcher->upload_data())); EXPECT_EQ("ftp://www.google.com/bla.exe", request.url()); EXPECT_EQ(hash, request.digests().sha256()); EXPECT_EQ(item.GetReceivedBytes(), request.length()); EXPECT_EQ(item.HasUserGesture(), 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, "ftp://www.google.com/bla.exe", referrer.spec())); EXPECT_TRUE(request.has_signature()); EXPECT_EQ(0, request.signature().certificate_chain_size()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DownloadProtectionServiceTest::SendURLFetchComplete, base::Unretained(this), fetcher)); MessageLoop::current()->Run(); #endif } // Similar to above, but with tab history. TEST_F(DownloadProtectionServiceTest, CheckClientDownloadValidateRequestTabHistory) { net::TestURLFetcherFactory factory; base::ScopedTempDir profile_dir; ASSERT_TRUE(profile_dir.CreateUniqueTempDir()); TestingProfile profile(profile_dir.path()); ASSERT_TRUE( profile.CreateHistoryService(true /* delete_file */, false /* no_db */)); base::FilePath tmp_path(FILE_PATH_LITERAL("bla.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("bla.exe")); std::vector url_chain; url_chain.push_back(GURL("http://www.google.com/")); url_chain.push_back(GURL("http://www.google.com/bla.exe")); GURL referrer("http://www.google.com/"); GURL tab_url("http://tab.com/final"); GURL tab_referrer("http://tab.com/referrer"); std::string hash = "hash"; std::string remote_address = "10.11.12.13"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(tab_url)); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(tab_referrer)); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return(remote_address)); EXPECT_CALL(item, GetBrowserContext()).WillRepeatedly(Return(&profile)); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)) .WillRepeatedly(SetCertificateContents("dummy cert data")); EXPECT_CALL( *binary_feature_extractor_.get(), ExtractImageFeatures(tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)) .WillRepeatedly(SetDosHeaderContents("dummy dos header")); // First test with no history match for the tab URL. { TestURLFetcherWatcher fetcher_watcher(&factory); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); #if !defined(OS_WIN) && !defined(OS_MACOSX) // SendRequest is not called. Wait for FinishRequest to call our callback. MessageLoop::current()->Run(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); EXPECT_EQ(NULL, fetcher); EXPECT_FALSE(HasClientDownloadRequest()); #else EXPECT_EQ(0, fetcher_watcher.WaitForRequest()); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); ASSERT_TRUE(fetcher); ClientDownloadRequest request; EXPECT_TRUE(request.ParseFromString(fetcher->upload_data())); EXPECT_EQ("http://www.google.com/bla.exe", request.url()); EXPECT_EQ(hash, request.digests().sha256()); EXPECT_EQ(item.GetReceivedBytes(), request.length()); EXPECT_EQ(item.HasUserGesture(), request.user_initiated()); EXPECT_TRUE(RequestContainsServerIp(request, remote_address)); EXPECT_EQ(3, 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", referrer.spec())); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::TAB_URL, tab_url.spec(), tab_referrer.spec())); EXPECT_TRUE(request.has_signature()); ASSERT_EQ(1, request.signature().certificate_chain_size()); const ClientDownloadRequest_CertificateChain& chain = request.signature().certificate_chain(0); ASSERT_EQ(1, chain.element_size()); EXPECT_EQ("dummy cert data", chain.element(0).certificate()); EXPECT_TRUE(request.has_image_headers()); const ClientDownloadRequest_ImageHeaders& headers = request.image_headers(); EXPECT_TRUE(headers.has_pe_headers()); EXPECT_TRUE(headers.pe_headers().has_dos_header()); EXPECT_EQ("dummy dos header", headers.pe_headers().dos_header()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DownloadProtectionServiceTest::SendURLFetchComplete, base::Unretained(this), fetcher)); MessageLoop::current()->Run(); #endif } // Now try with a history match. { history::RedirectList redirects; redirects.push_back(GURL("http://tab.com/ref1")); redirects.push_back(GURL("http://tab.com/ref2")); redirects.push_back(tab_url); HistoryServiceFactory::GetForProfile(&profile, ServiceAccessType::EXPLICIT_ACCESS) ->AddPage(tab_url, base::Time::Now(), reinterpret_cast(1), 0, GURL(), redirects, ui::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED, false); TestURLFetcherWatcher fetcher_watcher(&factory); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); #if !defined(OS_WIN) && !defined(OS_MACOSX) // SendRequest is not called. Wait for FinishRequest to call our callback. MessageLoop::current()->Run(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); EXPECT_EQ(NULL, fetcher); EXPECT_FALSE(HasClientDownloadRequest()); #else EXPECT_EQ(0, fetcher_watcher.WaitForRequest()); EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); net::TestURLFetcher* fetcher = factory.GetFetcherByID(0); ASSERT_TRUE(fetcher); ClientDownloadRequest request; EXPECT_TRUE(request.ParseFromString(fetcher->upload_data())); EXPECT_EQ("http://www.google.com/bla.exe", request.url()); EXPECT_EQ(hash, request.digests().sha256()); EXPECT_EQ(item.GetReceivedBytes(), request.length()); EXPECT_EQ(item.HasUserGesture(), request.user_initiated()); EXPECT_TRUE(RequestContainsServerIp(request, remote_address)); EXPECT_EQ(5, 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", referrer.spec())); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::TAB_REDIRECT, "http://tab.com/ref1", "")); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::TAB_REDIRECT, "http://tab.com/ref2", "")); EXPECT_TRUE(RequestContainsResource(request, ClientDownloadRequest::TAB_URL, tab_url.spec(), tab_referrer.spec())); EXPECT_TRUE(request.has_signature()); ASSERT_EQ(1, request.signature().certificate_chain_size()); const ClientDownloadRequest_CertificateChain& chain = request.signature().certificate_chain(0); ASSERT_EQ(1, chain.element_size()); EXPECT_EQ("dummy cert data", chain.element(0).certificate()); // Simulate the request finishing. base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DownloadProtectionServiceTest::SendURLFetchComplete, base::Unretained(this), fetcher)); MessageLoop::current()->Run(); #endif } } TEST_F(DownloadProtectionServiceTest, TestCheckDownloadUrl) { std::vector url_chain; url_chain.push_back(GURL("http://www.google.com/")); url_chain.push_back(GURL("http://www.google.com/bla.exe")); GURL referrer("http://www.google.com/"); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); // CheckDownloadURL returns immediately which means the client object callback // will never be called. Nevertheless the callback provided to // CheckClientDownload must still be called. EXPECT_CALL(*sb_service_->mock_database_manager(), CheckDownloadUrl(ContainerEq(url_chain), NotNull())) .WillOnce(Return(true)); download_service_->CheckDownloadUrl( item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); Mock::VerifyAndClearExpectations(sb_service_.get()); EXPECT_CALL(*sb_service_->mock_database_manager(), CheckDownloadUrl(ContainerEq(url_chain), NotNull())) .WillOnce(DoAll(CheckDownloadUrlDone(SB_THREAT_TYPE_SAFE), Return(false))); download_service_->CheckDownloadUrl( item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); Mock::VerifyAndClearExpectations(sb_service_.get()); EXPECT_CALL(*sb_service_->mock_database_manager(), CheckDownloadUrl(ContainerEq(url_chain), NotNull())) .WillOnce(DoAll( CheckDownloadUrlDone(SB_THREAT_TYPE_URL_MALWARE), Return(false))); download_service_->CheckDownloadUrl( item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE)); Mock::VerifyAndClearExpectations(sb_service_.get()); EXPECT_CALL(*sb_service_->mock_database_manager(), CheckDownloadUrl(ContainerEq(url_chain), NotNull())) .WillOnce(DoAll( CheckDownloadUrlDone(SB_THREAT_TYPE_BINARY_MALWARE_URL), Return(false))); download_service_->CheckDownloadUrl( item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS)); } TEST_F(DownloadProtectionServiceTest, TestDownloadRequestTimeout) { net::TestURLFetcherFactory factory; std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/bla.exe")); GURL referrer("http://www.google.com/"); base::FilePath tmp_path(FILE_PATH_LITERAL("a.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("a.exe")); std::string hash = "hash"; content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(item, GetTargetFilePath()).WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures(tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)); download_service_->download_request_timeout_ms_ = 10; download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); // The request should time out because the HTTP request hasn't returned // anything yet. MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); #if defined(OS_WIN) || defined(OS_MACOSX) EXPECT_TRUE(HasClientDownloadRequest()); ClearClientDownloadRequest(); #else EXPECT_FALSE(HasClientDownloadRequest()); #endif } TEST_F(DownloadProtectionServiceTest, TestDownloadItemDestroyed) { net::TestURLFetcherFactory factory; std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/bla.exe")); GURL referrer("http://www.google.com/"); GURL tab_url("http://www.google.com/tab"); base::FilePath tmp_path(FILE_PATH_LITERAL("a.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("a.exe")); std::string hash = "hash"; { content::MockDownloadItem item; EXPECT_CALL(item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(item, GetTargetFilePath()) .WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(item, GetTabUrl()).WillRepeatedly(ReturnRef(tab_url)); EXPECT_CALL(item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Return(false)); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures( tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)); download_service_->CheckClientDownload( &item, base::Bind(&DownloadProtectionServiceTest::SyncCheckDoneCallback, base::Unretained(this))); // MockDownloadItem going out of scope triggers the OnDownloadDestroyed // notification. } EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); } TEST_F(DownloadProtectionServiceTest, TestDownloadItemDestroyedDuringWhitelistCheck) { net::TestURLFetcherFactory factory; std::vector url_chain; url_chain.push_back(GURL("http://www.evil.com/bla.exe")); GURL referrer("http://www.google.com/"); GURL tab_url("http://www.google.com/tab"); base::FilePath tmp_path(FILE_PATH_LITERAL("a.tmp")); base::FilePath final_path(FILE_PATH_LITERAL("a.exe")); std::string hash = "hash"; scoped_ptr item(new content::MockDownloadItem); EXPECT_CALL(*item, GetFullPath()).WillRepeatedly(ReturnRef(tmp_path)); EXPECT_CALL(*item, GetTargetFilePath()) .WillRepeatedly(ReturnRef(final_path)); EXPECT_CALL(*item, GetUrlChain()).WillRepeatedly(ReturnRef(url_chain)); EXPECT_CALL(*item, GetReferrerUrl()).WillRepeatedly(ReturnRef(referrer)); EXPECT_CALL(*item, GetTabUrl()).WillRepeatedly(ReturnRef(tab_url)); EXPECT_CALL(*item, GetTabReferrerUrl()) .WillRepeatedly(ReturnRef(GURL::EmptyGURL())); EXPECT_CALL(*item, GetHash()).WillRepeatedly(ReturnRef(hash)); EXPECT_CALL(*item, GetReceivedBytes()).WillRepeatedly(Return(100)); EXPECT_CALL(*item, HasUserGesture()).WillRepeatedly(Return(true)); EXPECT_CALL(*item, GetRemoteAddress()).WillRepeatedly(Return("")); EXPECT_CALL(*sb_service_->mock_database_manager(), MatchDownloadWhitelistUrl(_)) .WillRepeatedly(Invoke([&item](const GURL&) { item.reset(); return false; })); EXPECT_CALL(*binary_feature_extractor_.get(), CheckSignature(tmp_path, _)); EXPECT_CALL(*binary_feature_extractor_.get(), ExtractImageFeatures(tmp_path, BinaryFeatureExtractor::kDefaultOptions, _, _)); download_service_->CheckClientDownload( item.get(), base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback, base::Unretained(this))); MessageLoop::current()->Run(); EXPECT_TRUE(IsResult(DownloadProtectionService::UNKNOWN)); EXPECT_FALSE(HasClientDownloadRequest()); } TEST_F(DownloadProtectionServiceTest, GetCertificateWhitelistStrings) { // We'll pass this cert in as the "issuer", even though it isn't really // used to sign the certs below. GetCertificateWhitelistStirngs doesn't care // about this. scoped_refptr issuer_cert( ReadTestCertificate("issuer.pem")); ASSERT_TRUE(issuer_cert.get()); std::string cert_base = "cert/" + base::HexEncode( issuer_cert->fingerprint().data, sizeof(issuer_cert->fingerprint().data)); scoped_refptr cert(ReadTestCertificate("test_cn.pem")); ASSERT_TRUE(cert.get()); std::vector whitelist_strings; GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); // This also tests escaping of characters in the certificate attributes. EXPECT_THAT(whitelist_strings, ElementsAre( cert_base + "/CN=subject%2F%251")); cert = ReadTestCertificate("test_cn_o.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/CN=subject", cert_base + "/CN=subject/O=org", cert_base + "/O=org")); cert = ReadTestCertificate("test_cn_o_ou.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/CN=subject", cert_base + "/CN=subject/O=org", cert_base + "/CN=subject/O=org/OU=unit", cert_base + "/CN=subject/OU=unit", cert_base + "/O=org", cert_base + "/O=org/OU=unit", cert_base + "/OU=unit")); cert = ReadTestCertificate("test_cn_ou.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/CN=subject", cert_base + "/CN=subject/OU=unit", cert_base + "/OU=unit")); cert = ReadTestCertificate("test_o.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/O=org")); cert = ReadTestCertificate("test_o_ou.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/O=org", cert_base + "/O=org/OU=unit", cert_base + "/OU=unit")); cert = ReadTestCertificate("test_ou.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre(cert_base + "/OU=unit")); cert = ReadTestCertificate("test_c.pem"); ASSERT_TRUE(cert.get()); whitelist_strings.clear(); GetCertificateWhitelistStrings( *cert.get(), *issuer_cert.get(), &whitelist_strings); EXPECT_THAT(whitelist_strings, ElementsAre()); } namespace { class MockPageNavigator : public content::PageNavigator { public: MOCK_METHOD1(OpenURL, content::WebContents*(const content::OpenURLParams&)); }; // A custom matcher that matches a OpenURLParams value with a url with a query // parameter patching |value|. MATCHER_P(OpenURLParamsWithContextValue, value, "") { std::string query_value; return net::GetValueForKeyInQuery(arg.url, "ctx", &query_value) && query_value == value; } } // namespace // ShowDetailsForDownload() should open a URL showing more information about why // a download was flagged by SafeBrowsing. The URL should have a &ctx= parameter // whose value is the DownloadDangerType. TEST_F(DownloadProtectionServiceTest, ShowDetailsForDownloadHasContext) { StrictMock mock_page_navigator; StrictMock mock_download_item; EXPECT_CALL(mock_download_item, GetDangerType()) .WillOnce(Return(content::DOWNLOAD_DANGER_TYPE_DANGEROUS_HOST)); EXPECT_CALL(mock_page_navigator, OpenURL(OpenURLParamsWithContextValue("7"))); download_service_->ShowDetailsForDownload(mock_download_item, &mock_page_navigator); } } // namespace safe_browsing