blob: c66b45f41558746856e789845b6d6d5c1d702cfc [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/browser_feature_extractor.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/safe_browsing/browser_features.h"
#include "chrome/browser/safe_browsing/client_side_detection_host.h"
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "chrome/browser/safe_browsing/ui_manager.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/history/core/browser/history_backend.h"
#include "components/history/core/browser/history_service.h"
#include "components/safe_browsing/db/database_manager.h"
#include "components/safe_browsing/db/test_database_manager.h"
#include "components/safe_browsing/proto/csd.pb.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/referrer.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/test_browser_thread.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/page_transition_types.h"
#include "url/gurl.h"
using content::BrowserThread;
using content::WebContentsTester;
using testing::DoAll;
using testing::Return;
using testing::StrictMock;
namespace safe_browsing {
namespace {
class MockSafeBrowsingDatabaseManager : public TestSafeBrowsingDatabaseManager {
public:
MockSafeBrowsingDatabaseManager() {}
MOCK_METHOD1(MatchMalwareIP, bool(const std::string& ip_address));
protected:
~MockSafeBrowsingDatabaseManager() override {}
private:
DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager);
};
class MockClientSideDetectionHost : public ClientSideDetectionHost {
public:
MockClientSideDetectionHost(
content::WebContents* tab,
SafeBrowsingDatabaseManager* database_manager)
: ClientSideDetectionHost(tab) {
set_safe_browsing_managers(NULL, database_manager);
}
~MockClientSideDetectionHost() override {}
MOCK_METHOD1(IsBadIpAddress, bool(const std::string&));
};
} // namespace
class BrowserFeatureExtractorTest : public ChromeRenderViewHostTestHarness {
protected:
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
ASSERT_TRUE(profile()->CreateHistoryService(
true /* delete_file */, false /* no_db */));
db_manager_ = new StrictMock<MockSafeBrowsingDatabaseManager>();
host_.reset(new StrictMock<MockClientSideDetectionHost>(
web_contents(), db_manager_.get()));
extractor_.reset(
new BrowserFeatureExtractor(web_contents(), host_.get()));
num_pending_ = 0;
browse_info_.reset(new BrowseInfo);
}
void TearDown() override {
extractor_.reset();
host_.reset();
db_manager_ = NULL;
ChromeRenderViewHostTestHarness::TearDown();
ASSERT_EQ(0, num_pending_);
}
history::HistoryService* history_service() {
return HistoryServiceFactory::GetForProfile(
profile(), ServiceAccessType::EXPLICIT_ACCESS);
}
void SetRedirectChain(const std::vector<GURL>& redirect_chain,
bool new_host) {
browse_info_->url_redirects = redirect_chain;
if (new_host) {
browse_info_->host_redirects = redirect_chain;
}
}
// Wrapper around NavigateAndCommit that also sets the redirect chain to
// a sane value.
void SimpleNavigateAndCommit(const GURL& url) {
std::vector<GURL> redirect_chain;
redirect_chain.push_back(url);
SetRedirectChain(redirect_chain, true);
NavigateAndCommit(url, GURL(), ui::PAGE_TRANSITION_LINK);
}
// This is similar to NavigateAndCommit that is in WebContentsTester, but
// allows us to specify the referrer and page_transition_type.
void NavigateAndCommit(const GURL& url,
const GURL& referrer,
ui::PageTransition type) {
auto navigation = content::NavigationSimulator::CreateBrowserInitiated(
url, web_contents());
navigation->SetReferrer(
content::Referrer(referrer, network::mojom::ReferrerPolicy::kDefault));
navigation->SetTransition(type);
navigation->Commit();
}
bool ExtractFeatures(ClientPhishingRequest* request) {
StartExtractFeatures(request);
base::RunLoop().Run();
EXPECT_EQ(1U, success_.count(request));
return success_.count(request) ? success_[request] : false;
}
void StartExtractFeatures(ClientPhishingRequest* request) {
success_.erase(request);
++num_pending_;
extractor_->ExtractFeatures(
browse_info_.get(),
request,
base::Bind(&BrowserFeatureExtractorTest::ExtractFeaturesDone,
base::Unretained(this)));
}
void GetFeatureMap(const ClientPhishingRequest& request,
std::map<std::string, double>* features) {
for (int i = 0; i < request.non_model_feature_map_size(); ++i) {
const ClientPhishingRequest::Feature& feature =
request.non_model_feature_map(i);
EXPECT_EQ(0U, features->count(feature.name()));
(*features)[feature.name()] = feature.value();
}
}
void ExtractMalwareFeatures(ClientMalwareRequest* request) {
// Feature extraction takes ownership of the request object
// and passes it along to the done callback in the end.
StartExtractMalwareFeatures(request);
ASSERT_TRUE(base::MessageLoopCurrentForUI::IsSet());
base::RunLoop().Run();
EXPECT_EQ(1U, success_.count(request));
EXPECT_TRUE(success_[request]);
}
void StartExtractMalwareFeatures(ClientMalwareRequest* request) {
success_.erase(request);
++num_pending_;
// We temporarily give up ownership of request to ExtractMalwareFeatures
// but we'll regain ownership of it in ExtractMalwareFeaturesDone.
extractor_->ExtractMalwareFeatures(
browse_info_.get(),
request,
base::Bind(&BrowserFeatureExtractorTest::ExtractMalwareFeaturesDone,
base::Unretained(this)));
}
void GetMalwareUrls(
const ClientMalwareRequest& request,
std::map<std::string, std::set<std::string> >* urls) {
for (int i = 0; i < request.bad_ip_url_info_size(); ++i) {
const ClientMalwareRequest::UrlInfo& urlinfo =
request.bad_ip_url_info(i);
(*urls)[urlinfo.ip()].insert(urlinfo.url());
}
}
int num_pending_; // Number of pending feature extractions.
std::unique_ptr<BrowserFeatureExtractor> extractor_;
std::map<void*, bool> success_;
std::unique_ptr<BrowseInfo> browse_info_;
std::unique_ptr<StrictMock<MockClientSideDetectionHost>> host_;
scoped_refptr<StrictMock<MockSafeBrowsingDatabaseManager> > db_manager_;
private:
void ExtractFeaturesDone(bool success,
std::unique_ptr<ClientPhishingRequest> request) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
ASSERT_EQ(0U, success_.count(request.get()));
// The pointer doesn't really belong to us. It belongs to
// the test case which passed it to ExtractFeatures above.
success_[request.release()] = success;
if (--num_pending_ == 0) {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
}
void ExtractMalwareFeaturesDone(
bool success,
std::unique_ptr<ClientMalwareRequest> request) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
ASSERT_EQ(0U, success_.count(request.get()));
// The pointer doesn't really belong to us. It belongs to
// the test case which passed it to ExtractMalwareFeatures above.
success_[request.release()] = success;
if (--num_pending_ == 0) {
base::RunLoop::QuitCurrentWhenIdleDeprecated();
}
}
};
TEST_F(BrowserFeatureExtractorTest, UrlNotInHistory) {
ClientPhishingRequest request;
SimpleNavigateAndCommit(GURL("http://www.google.com"));
request.set_url("http://www.google.com/");
request.set_client_score(0.5);
EXPECT_FALSE(ExtractFeatures(&request));
}
TEST_F(BrowserFeatureExtractorTest, RequestNotInitialized) {
ClientPhishingRequest request;
request.set_url("http://www.google.com/");
// Request is missing the score value.
SimpleNavigateAndCommit(GURL("http://www.google.com"));
EXPECT_FALSE(ExtractFeatures(&request));
}
TEST_F(BrowserFeatureExtractorTest, UrlInHistory) {
history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
base::Time::Now(),
history::SOURCE_BROWSED);
history_service()->AddPage(GURL("https://www.foo.com/gaa.html"),
base::Time::Now(),
history::SOURCE_BROWSED); // same host HTTPS.
history_service()->AddPage(GURL("http://www.foo.com/gaa.html"),
base::Time::Now(),
history::SOURCE_BROWSED); // same host HTTP.
history_service()->AddPage(GURL("http://bar.foo.com/gaa.html"),
base::Time::Now(),
history::SOURCE_BROWSED); // different host.
history_service()->AddPage(GURL("http://www.foo.com/bar.html?a=b"),
base::Time::Now() - base::TimeDelta::FromHours(23),
NULL, 0, GURL(), history::RedirectList(),
ui::PAGE_TRANSITION_LINK,
history::SOURCE_BROWSED, false);
history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
base::Time::Now() - base::TimeDelta::FromHours(25),
NULL, 0, GURL(), history::RedirectList(),
ui::PAGE_TRANSITION_TYPED,
history::SOURCE_BROWSED, false);
history_service()->AddPage(GURL("https://www.foo.com/goo.html"),
base::Time::Now() - base::TimeDelta::FromDays(5),
NULL, 0, GURL(), history::RedirectList(),
ui::PAGE_TRANSITION_TYPED,
history::SOURCE_BROWSED, false);
SimpleNavigateAndCommit(GURL("http://www.foo.com/bar.html"));
ClientPhishingRequest request;
request.set_url("http://www.foo.com/bar.html");
request.set_client_score(0.5);
EXPECT_TRUE(ExtractFeatures(&request));
std::map<std::string, double> features;
GetFeatureMap(request, &features);
EXPECT_EQ(12U, features.size());
EXPECT_DOUBLE_EQ(2.0, features[kUrlHistoryVisitCount]);
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryVisitCountMoreThan24hAgo]);
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryTypedCount]);
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryLinkCount]);
EXPECT_DOUBLE_EQ(4.0, features[kHttpHostVisitCount]);
EXPECT_DOUBLE_EQ(2.0, features[kHttpsHostVisitCount]);
EXPECT_DOUBLE_EQ(1.0, features[kFirstHttpHostVisitMoreThan24hAgo]);
EXPECT_DOUBLE_EQ(1.0, features[kFirstHttpsHostVisitMoreThan24hAgo]);
request.Clear();
request.set_url("https://www.foo.com/gaa.html");
request.set_client_score(0.5);
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
EXPECT_EQ(8U, features.size());
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryVisitCount]);
EXPECT_DOUBLE_EQ(0.0, features[kUrlHistoryVisitCountMoreThan24hAgo]);
EXPECT_DOUBLE_EQ(0.0, features[kUrlHistoryTypedCount]);
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryLinkCount]);
EXPECT_DOUBLE_EQ(4.0, features[kHttpHostVisitCount]);
EXPECT_DOUBLE_EQ(2.0, features[kHttpsHostVisitCount]);
EXPECT_DOUBLE_EQ(1.0, features[kFirstHttpHostVisitMoreThan24hAgo]);
EXPECT_DOUBLE_EQ(1.0, features[kFirstHttpsHostVisitMoreThan24hAgo]);
request.Clear();
request.set_url("http://bar.foo.com/gaa.html");
request.set_client_score(0.5);
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
// We have less features because we didn't Navigate to this page, so we don't
// have Referrer, IsFirstNavigation, HasSSLReferrer, etc.
EXPECT_EQ(7U, features.size());
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryVisitCount]);
EXPECT_DOUBLE_EQ(0.0, features[kUrlHistoryVisitCountMoreThan24hAgo]);
EXPECT_DOUBLE_EQ(0.0, features[kUrlHistoryTypedCount]);
EXPECT_DOUBLE_EQ(1.0, features[kUrlHistoryLinkCount]);
EXPECT_DOUBLE_EQ(1.0, features[kHttpHostVisitCount]);
EXPECT_DOUBLE_EQ(0.0, features[kHttpsHostVisitCount]);
EXPECT_DOUBLE_EQ(0.0, features[kFirstHttpHostVisitMoreThan24hAgo]);
EXPECT_FALSE(features.count(kFirstHttpsHostVisitMoreThan24hAgo));
}
TEST_F(BrowserFeatureExtractorTest, MultipleRequestsAtOnce) {
history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
base::Time::Now(),
history::SOURCE_BROWSED);
SimpleNavigateAndCommit(GURL("http:/www.foo.com/bar.html"));
ClientPhishingRequest request;
request.set_url("http://www.foo.com/bar.html");
request.set_client_score(0.5);
StartExtractFeatures(&request);
SimpleNavigateAndCommit(GURL("http://www.foo.com/goo.html"));
ClientPhishingRequest request2;
request2.set_url("http://www.foo.com/goo.html");
request2.set_client_score(1.0);
StartExtractFeatures(&request2);
base::RunLoop().Run();
EXPECT_TRUE(success_[&request]);
// Success is false because the second URL is not in the history and we are
// not able to distinguish between a missing URL in the history and an error.
EXPECT_FALSE(success_[&request2]);
}
TEST_F(BrowserFeatureExtractorTest, BrowseFeatures) {
history_service()->AddPage(GURL("http://www.foo.com/"),
base::Time::Now(),
history::SOURCE_BROWSED);
history_service()->AddPage(GURL("http://www.foo.com/page.html"),
base::Time::Now(),
history::SOURCE_BROWSED);
history_service()->AddPage(GURL("http://www.bar.com/"),
base::Time::Now(),
history::SOURCE_BROWSED);
history_service()->AddPage(GURL("http://www.bar.com/other_page.html"),
base::Time::Now(),
history::SOURCE_BROWSED);
history_service()->AddPage(GURL("http://www.baz.com/"),
base::Time::Now(),
history::SOURCE_BROWSED);
ClientPhishingRequest request;
request.set_url("http://www.foo.com/");
request.set_client_score(0.5);
std::vector<GURL> redirect_chain;
redirect_chain.push_back(GURL("http://somerandomwebsite.com/"));
redirect_chain.push_back(GURL("http://www.foo.com/"));
SetRedirectChain(redirect_chain, true);
browse_info_->http_status_code = 200;
NavigateAndCommit(GURL("http://www.foo.com/"),
GURL("http://google.com/"),
ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_AUTO_BOOKMARK |
ui::PAGE_TRANSITION_FORWARD_BACK));
EXPECT_TRUE(ExtractFeatures(&request));
std::map<std::string, double> features;
GetFeatureMap(request, &features);
EXPECT_EQ(
1.0,
features[base::StringPrintf("%s=%s", kReferrer, "http://google.com/")]);
EXPECT_EQ(1.0, features[base::StringPrintf("%s[0]=%s", kRedirect,
"http://somerandomwebsite.com/")]);
// We shouldn't have a feature for the last redirect in the chain, since it
// should always be the URL that we navigated to.
EXPECT_EQ(
0.0,
features[base::StringPrintf("%s[1]=%s", kRedirect, "http://foo.com/")]);
EXPECT_EQ(0.0, features[kHasSSLReferrer]);
EXPECT_EQ(2.0, features[kPageTransitionType]);
EXPECT_EQ(1.0, features[kIsFirstNavigation]);
EXPECT_EQ(200.0, features[kHttpStatusCode]);
request.Clear();
request.set_url("http://www.foo.com/page.html");
request.set_client_score(0.5);
redirect_chain.clear();
redirect_chain.push_back(GURL("http://www.foo.com/redirect"));
redirect_chain.push_back(GURL("http://www.foo.com/second_redirect"));
redirect_chain.push_back(GURL("http://www.foo.com/page.html"));
SetRedirectChain(redirect_chain, false);
browse_info_->http_status_code = 404;
NavigateAndCommit(GURL("http://www.foo.com/page.html"),
GURL("http://www.foo.com"),
ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED |
ui::PAGE_TRANSITION_CHAIN_START |
ui::PAGE_TRANSITION_CLIENT_REDIRECT));
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
EXPECT_EQ(
1,
features[base::StringPrintf("%s=%s", kReferrer, "http://www.foo.com/")]);
EXPECT_EQ(1.0, features[base::StringPrintf("%s[0]=%s", kRedirect,
"http://www.foo.com/redirect")]);
EXPECT_EQ(1.0,
features[base::StringPrintf("%s[1]=%s", kRedirect,
"http://www.foo.com/second_redirect")]);
EXPECT_EQ(0.0, features[kHasSSLReferrer]);
EXPECT_EQ(1.0, features[kPageTransitionType]);
EXPECT_EQ(0.0, features[kIsFirstNavigation]);
EXPECT_EQ(1.0, features[base::StringPrintf("%s%s=%s", kHostPrefix, kReferrer,
"http://google.com/")]);
EXPECT_EQ(1.0,
features[base::StringPrintf("%s%s[0]=%s", kHostPrefix, kRedirect,
"http://somerandomwebsite.com/")]);
EXPECT_EQ(
2.0,
features[base::StringPrintf("%s%s", kHostPrefix, kPageTransitionType)]);
EXPECT_EQ(
1.0,
features[base::StringPrintf("%s%s", kHostPrefix, kIsFirstNavigation)]);
EXPECT_EQ(404.0, features[kHttpStatusCode]);
request.Clear();
request.set_url("http://www.bar.com/");
request.set_client_score(0.5);
redirect_chain.clear();
redirect_chain.push_back(GURL("http://www.foo.com/page.html"));
redirect_chain.push_back(GURL("http://www.bar.com/"));
SetRedirectChain(redirect_chain, true);
NavigateAndCommit(GURL("http://www.bar.com/"),
GURL("http://www.foo.com/page.html"),
ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_LINK |
ui::PAGE_TRANSITION_CHAIN_END |
ui::PAGE_TRANSITION_CLIENT_REDIRECT));
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
EXPECT_EQ(1.0, features[base::StringPrintf("%s=%s", kReferrer,
"http://www.foo.com/page.html")]);
EXPECT_EQ(1.0, features[base::StringPrintf("%s[0]=%s", kRedirect,
"http://www.foo.com/page.html")]);
EXPECT_EQ(0.0, features[kHasSSLReferrer]);
EXPECT_EQ(0.0, features[kPageTransitionType]);
EXPECT_EQ(0.0, features[kIsFirstNavigation]);
// Should not have host features.
EXPECT_EQ(0U, features.count(base::StringPrintf("%s%s", kHostPrefix,
kPageTransitionType)));
EXPECT_EQ(0U, features.count(base::StringPrintf("%s%s", kHostPrefix,
kIsFirstNavigation)));
request.Clear();
request.set_url("http://www.bar.com/other_page.html");
request.set_client_score(0.5);
redirect_chain.clear();
redirect_chain.push_back(GURL("http://www.bar.com/other_page.html"));
SetRedirectChain(redirect_chain, false);
NavigateAndCommit(GURL("http://www.bar.com/other_page.html"),
GURL("http://www.bar.com/"),
ui::PAGE_TRANSITION_LINK);
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
EXPECT_EQ(
1.0,
features[base::StringPrintf("%s=%s", kReferrer, "http://www.bar.com/")]);
EXPECT_EQ(0.0, features[kHasSSLReferrer]);
EXPECT_EQ(0.0, features[kPageTransitionType]);
EXPECT_EQ(0.0, features[kIsFirstNavigation]);
EXPECT_EQ(1.0, features[base::StringPrintf("%s%s=%s", kHostPrefix, kReferrer,
"http://www.foo.com/page.html")]);
EXPECT_EQ(1.0,
features[base::StringPrintf("%s%s[0]=%s", kHostPrefix, kRedirect,
"http://www.foo.com/page.html")]);
EXPECT_EQ(
0.0,
features[base::StringPrintf("%s%s", kHostPrefix, kPageTransitionType)]);
EXPECT_EQ(
0.0,
features[base::StringPrintf("%s%s", kHostPrefix, kIsFirstNavigation)]);
request.Clear();
request.set_url("http://www.baz.com/");
request.set_client_score(0.5);
redirect_chain.clear();
redirect_chain.push_back(GURL("https://bankofamerica.com"));
redirect_chain.push_back(GURL("http://www.baz.com/"));
SetRedirectChain(redirect_chain, true);
NavigateAndCommit(GURL("http://www.baz.com"),
GURL("https://bankofamerica.com"),
ui::PAGE_TRANSITION_GENERATED);
EXPECT_TRUE(ExtractFeatures(&request));
features.clear();
GetFeatureMap(request, &features);
EXPECT_EQ(1.0, features[base::StringPrintf("%s[0]=%s", kRedirect,
kSecureRedirectValue)]);
EXPECT_EQ(1.0, features[kHasSSLReferrer]);
EXPECT_EQ(5.0, features[kPageTransitionType]);
// Should not have redirect or host features.
EXPECT_EQ(0U, features.count(base::StringPrintf("%s%s", kHostPrefix,
kPageTransitionType)));
EXPECT_EQ(0U, features.count(base::StringPrintf("%s%s", kHostPrefix,
kIsFirstNavigation)));
EXPECT_EQ(5.0, features[kPageTransitionType]);
}
TEST_F(BrowserFeatureExtractorTest, SafeBrowsingFeatures) {
SimpleNavigateAndCommit(GURL("http://www.foo.com/malware.html"));
ClientPhishingRequest request;
request.set_url("http://www.foo.com/malware.html");
request.set_client_score(0.5);
browse_info_->unsafe_resource.reset(
new security_interstitials::UnsafeResource);
browse_info_->unsafe_resource->url = GURL("http://www.malware.com/");
browse_info_->unsafe_resource->original_url = GURL("http://www.good.com/");
browse_info_->unsafe_resource->is_subresource = true;
browse_info_->unsafe_resource->threat_type = SB_THREAT_TYPE_URL_MALWARE;
ExtractFeatures(&request);
std::map<std::string, double> features;
GetFeatureMap(request, &features);
EXPECT_TRUE(features.count(base::StringPrintf(
"%s%s", kSafeBrowsingMaliciousUrl, "http://www.malware.com/")));
EXPECT_TRUE(features.count(base::StringPrintf(
"%s%s", kSafeBrowsingOriginalUrl, "http://www.good.com/")));
EXPECT_DOUBLE_EQ(1.0, features[kSafeBrowsingIsSubresource]);
EXPECT_DOUBLE_EQ(SB_THREAT_TYPE_URL_MALWARE,
features[kSafeBrowsingThreatType]);
}
TEST_F(BrowserFeatureExtractorTest, MalwareFeatures) {
ClientMalwareRequest request;
request.set_url("http://www.foo.com/");
std::vector<IPUrlInfo> bad_urls;
bad_urls.push_back(
IPUrlInfo("http://bad.com", "GET", "", content::RESOURCE_TYPE_SCRIPT));
bad_urls.push_back(
IPUrlInfo("http://evil.com", "GET", "", content::RESOURCE_TYPE_SCRIPT));
browse_info_->ips.insert(std::make_pair("193.5.163.8", bad_urls));
browse_info_->ips.insert(std::make_pair("92.92.92.92", bad_urls));
std::vector<IPUrlInfo> good_urls;
good_urls.push_back(
IPUrlInfo("http://ok.com", "GET", "", content::RESOURCE_TYPE_SCRIPT));
browse_info_->ips.insert(std::make_pair("23.94.78.1", good_urls));
EXPECT_CALL(*db_manager_, MatchMalwareIP("193.5.163.8"))
.WillOnce(Return(true));
EXPECT_CALL(*db_manager_, MatchMalwareIP("92.92.92.92"))
.WillOnce(Return(true));
EXPECT_CALL(*db_manager_, MatchMalwareIP("23.94.78.1"))
.WillOnce(Return(false));
ExtractMalwareFeatures(&request);
EXPECT_EQ(4, request.bad_ip_url_info_size());
std::map<std::string, std::set<std::string> > result_urls;
GetMalwareUrls(request, &result_urls);
EXPECT_EQ(2U, result_urls.size());
EXPECT_TRUE(result_urls.count("193.5.163.8"));
std::set<std::string> urls = result_urls["193.5.163.8"];
EXPECT_EQ(2U, urls.size());
EXPECT_TRUE(urls.find("http://bad.com") != urls.end());
EXPECT_TRUE(urls.find("http://evil.com") != urls.end());
EXPECT_TRUE(result_urls.count("92.92.92.92"));
urls = result_urls["92.92.92.92"];
EXPECT_EQ(2U, urls.size());
EXPECT_TRUE(urls.find("http://bad.com") != urls.end());
EXPECT_TRUE(urls.find("http://evil.com") != urls.end());
}
TEST_F(BrowserFeatureExtractorTest, MalwareFeatures_ExceedLimit) {
ClientMalwareRequest request;
request.set_url("http://www.foo.com/");
std::vector<IPUrlInfo> bad_urls;
bad_urls.push_back(
IPUrlInfo("http://bad.com", "GET", "", content::RESOURCE_TYPE_SCRIPT));
std::vector<std::string> ips;
for (int i = 0; i < 7; ++i) { // Add 7 ips
std::string ip = base::StringPrintf("%d.%d.%d.%d", i, i, i, i);
ips.push_back(ip);
browse_info_->ips.insert(std::make_pair(ip, bad_urls));
// First ip is good but all the others are bad.
EXPECT_CALL(*db_manager_, MatchMalwareIP(ip)).WillOnce(Return(i > 0));
}
ExtractMalwareFeatures(&request);
// The number of IP matched url we store is capped at 5 IPs per request.
EXPECT_EQ(5, request.bad_ip_url_info_size());
}
} // namespace safe_browsing