blob: cdc3270aa81fca468fadfc411f300d1dcc8886ff [file] [log] [blame]
// 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 <map>
#include <string>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/file_util.h"
#include "base/files/file_path.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.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/safe_browsing/binary_feature_extractor.h"
#include "chrome/browser/safe_browsing/database_manager.h"
#include "chrome/browser/safe_browsing/download_feedback_service.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/common/safe_browsing/csd.pb.h"
#include "chrome/test/base/testing_profile.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/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::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 SafeBrowsingDatabaseManager {
public:
explicit MockSafeBrowsingDatabaseManager(SafeBrowsingService* service)
: SafeBrowsingDatabaseManager(service) { }
MOCK_METHOD1(MatchDownloadWhitelistUrl, bool(const GURL&));
MOCK_METHOD1(MatchDownloadWhitelistString, bool(const std::string&));
MOCK_METHOD2(CheckDownloadUrl, bool(
const std::vector<GURL>& 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:
virtual ~FakeSafeBrowsingService() { }
virtual SafeBrowsingDatabaseManager* CreateDatabaseManager() OVERRIDE {
mock_database_manager_ = new MockSafeBrowsingDatabaseManager(this);
return mock_database_manager_;
}
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_METHOD2(ExtractImageHeaders, void(const base::FilePath&,
ClientDownloadRequest_ImageHeaders*));
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:
virtual void OnRequestStart(int fetcher_id) OVERRIDE {
fetcher_id_ = fetcher_id;
run_loop_.Quit();
}
virtual void OnChunkUpload(int fetcher_id) OVERRIDE {}
virtual 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) {
arg1->mutable_pe_headers()->set_dos_header(contents);
}
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(
SafeBrowsingDatabaseManager::SafeBrowsingCheck* check) {
check->client->OnSafeBrowsingResult(*check);
}
ACTION_P(CheckDownloadUrlDone, threat_type) {
SafeBrowsingDatabaseManager::SafeBrowsingCheck* check =
new SafeBrowsingDatabaseManager::SafeBrowsingCheck(
arg0,
std::vector<SBFullHash>(),
arg1,
safe_browsing_util::BINURL,
std::vector<SBThreatType>(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) {
}
virtual void SetUp() {
// 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<FakeSafeBrowsingService>();
sb_service_->Initialize();
binary_feature_extractor_ = new StrictMock<MockBinaryFeatureExtractor>();
download_service_ = sb_service_->download_protection_service();
download_service_->binary_feature_extractor_ = binary_feature_extractor_;
download_service_->SetEnabled(true);
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");
}
virtual void TearDown() {
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;
}
// 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<std::string>* whitelist_strings) {
DownloadProtectionService::GetCertificateWhitelistStrings(
certificate, issuer, whitelist_strings);
}
// Reads a single PEM-encoded certificate from the testdata directory.
// Returns NULL on failure.
scoped_refptr<net::X509Certificate> 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];
}
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();
}
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<FakeSafeBrowsingService> sb_service_;
scoped_refptr<MockBinaryFeatureExtractor> 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_;
};
TEST_F(DownloadProtectionServiceTest, CheckClientDownloadInvalidUrl) {
base::FilePath a_tmp(FILE_PATH_LITERAL("a.tmp"));
base::FilePath a_exe(FILE_PATH_LITERAL("a.exe"));
std::vector<GURL> 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::SAFE));
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::SAFE));
}
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<GURL> 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(), ExtractImageHeaders(a_tmp, _))
.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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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));
}
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<GURL> 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(), ExtractImageHeaders(a_tmp, _));
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback,
base::Unretained(this)));
MessageLoop::current()->Run();
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
}
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<GURL> 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(), ExtractImageHeaders(a_tmp, _))
.Times(6);
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback,
base::Unretained(this)));
MessageLoop::current()->Run();
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
// Invalid response should be safe too.
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::SAFE));
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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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)
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);
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS_HOST));
EXPECT_TRUE(DownloadFeedbackService::GetPingsForDownloadForTesting(
item, &feedback_ping, &feedback_response));
EXPECT_EQ(response.SerializeAsString(), feedback_response);
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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)
EXPECT_TRUE(IsResult(DownloadProtectionService::POTENTIALLY_UNWANTED));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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<GURL> 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(), ExtractImageHeaders(a_tmp, _))
.Times(1);
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback,
base::Unretained(this)));
MessageLoop::current()->Run();
#if defined(OS_WIN)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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<GURL> 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<int>(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::SAFE));
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<int>(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();
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
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)
EXPECT_TRUE(IsResult(DownloadProtectionService::DANGEROUS));
#else
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
#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<GURL> 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<int>(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::SAFE));
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<GURL> 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(), ExtractImageHeaders(a_tmp, _))
.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::SAFE));
}
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<GURL> 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(),
ExtractImageHeaders(tmp_path, _))
.WillOnce(SetDosHeaderContents("dummy dos header"));
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback,
base::Unretained(this)));
#if !defined(OS_WIN)
// SendRequest is not called. Wait for FinishRequest to call our callback.
MessageLoop::current()->Run();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
EXPECT_EQ(NULL, fetcher);
#else
// Run the message loop(s) until SendRequest is called.
FlushThreadMessageLoops();
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<GURL> 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(),
ExtractImageHeaders(tmp_path, _));
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::CheckDoneCallback,
base::Unretained(this)));
#if !defined(OS_WIN)
// SendRequest is not called. Wait for FinishRequest to call our callback.
MessageLoop::current()->Run();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
EXPECT_EQ(NULL, fetcher);
#else
// Run the message loop(s) until SendRequest is called.
FlushThreadMessageLoops();
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<GURL> 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(),
ExtractImageHeaders(tmp_path, _))
.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)
// SendRequest is not called. Wait for FinishRequest to call our callback.
MessageLoop::current()->Run();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
EXPECT_EQ(NULL, fetcher);
#else
EXPECT_EQ(0, fetcher_watcher.WaitForRequest());
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, Profile::EXPLICIT_ACCESS)
->AddPage(tab_url,
base::Time::Now(),
static_cast<void*>(this),
0,
GURL(),
redirects,
content::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)
// SendRequest is not called. Wait for FinishRequest to call our callback.
MessageLoop::current()->Run();
net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
EXPECT_EQ(NULL, fetcher);
#else
EXPECT_EQ(0, fetcher_watcher.WaitForRequest());
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<GURL> 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<GURL> 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(),
ExtractImageHeaders(tmp_path, _));
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::SAFE));
}
TEST_F(DownloadProtectionServiceTest, TestDownloadItemDestroyed) {
net::TestURLFetcherFactory factory;
std::vector<GURL> 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(),
ExtractImageHeaders(tmp_path, _));
download_service_->CheckClientDownload(
&item,
base::Bind(&DownloadProtectionServiceTest::SyncCheckDoneCallback,
base::Unretained(this)));
// MockDownloadItem going out of scope triggers the OnDownloadDestroyed
// notification.
}
EXPECT_TRUE(IsResult(DownloadProtectionService::SAFE));
}
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<net::X509Certificate> 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<net::X509Certificate> cert(ReadTestCertificate("test_cn.pem"));
ASSERT_TRUE(cert.get());
std::vector<std::string> 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 safe_browsing