blob: 76380f29d117115681387092f365e677fcaa1768 [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 <stdint.h>
#include <algorithm>
#include "base/bind.h"
#include "base/macros.h"
#include "base/pickle.h"
#include "base/run_loop.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/safe_browsing_service.h"
#include "chrome/browser/safe_browsing/threat_details.h"
#include "chrome/browser/safe_browsing/threat_details_history.h"
#include "chrome/browser/safe_browsing/ui_manager.h"
#include "chrome/common/safe_browsing/csd.pb.h"
#include "chrome/common/safe_browsing/safebrowsing_messages.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 "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/http/http_util.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
using content::BrowserThread;
using content::WebContents;
namespace safe_browsing {
namespace {
// Mixture of HTTP and HTTPS. No special treatment for HTTPS.
static const char* kOriginalLandingURL =
"http://www.originallandingpage.com/with/path";
static const char* kDOMChildURL = "https://www.domparent.com/with/path";
static const char* kDOMParentURL = "https://www.domchild.com/with/path";
static const char* kFirstRedirectURL = "http://redirectone.com/with/path";
static const char* kSecondRedirectURL = "https://redirecttwo.com/with/path";
static const char* kReferrerURL = "http://www.referrer.com/with/path";
static const char* kThreatURL = "http://www.threat.com/with/path";
static const char* kThreatHeaders =
"HTTP/1.1 200 OK\n"
"Content-Type: image/jpeg\n";
static const char* kThreatData = "exploit();";
static const char* kLandingURL = "http://www.landingpage.com/with/path";
static const char* kLandingHeaders =
"HTTP/1.1 200 OK\n"
"Content-Type: text/html\n"
"Content-Length: 1024\n"
"Set-Cookie: tastycookie\n"; // This header is stripped.
static const char* kLandingData =
"<iframe src='http://www.threat.com/with/path'>";
using content::BrowserThread;
using content::WebContents;
void WriteHeaders(disk_cache::Entry* entry, const std::string& headers) {
net::HttpResponseInfo responseinfo;
std::string raw_headers =
net::HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size());
responseinfo.socket_address = net::HostPortPair("1.2.3.4", 80);
responseinfo.headers = new net::HttpResponseHeaders(raw_headers);
base::Pickle pickle;
responseinfo.Persist(&pickle, false, false);
scoped_refptr<net::WrappedIOBuffer> buf(
new net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle.data())));
int len = static_cast<int>(pickle.size());
net::TestCompletionCallback cb;
int rv = entry->WriteData(0, 0, buf.get(), len, cb.callback(), true);
ASSERT_EQ(len, cb.GetResult(rv));
}
void WriteData(disk_cache::Entry* entry, const std::string& data) {
if (data.empty())
return;
int len = data.length();
scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(len));
memcpy(buf->data(), data.data(), data.length());
net::TestCompletionCallback cb;
int rv = entry->WriteData(1, 0, buf.get(), len, cb.callback(), true);
ASSERT_EQ(len, cb.GetResult(rv));
}
void WriteToEntry(disk_cache::Backend* cache,
const std::string& key,
const std::string& headers,
const std::string& data) {
net::TestCompletionCallback cb;
disk_cache::Entry* entry;
int rv = cache->CreateEntry(key, &entry, cb.callback());
rv = cb.GetResult(rv);
if (rv != net::OK) {
rv = cache->OpenEntry(key, &entry, cb.callback());
ASSERT_EQ(net::OK, cb.GetResult(rv));
}
WriteHeaders(entry, headers);
WriteData(entry, data);
entry->Close();
}
void FillCache(net::URLRequestContextGetter* context_getter) {
net::TestCompletionCallback cb;
disk_cache::Backend* cache;
int rv = context_getter->GetURLRequestContext()
->http_transaction_factory()
->GetCache()
->GetBackend(&cache, cb.callback());
ASSERT_EQ(net::OK, cb.GetResult(rv));
WriteToEntry(cache, kThreatURL, kThreatHeaders, kThreatData);
WriteToEntry(cache, kLandingURL, kLandingHeaders, kLandingData);
}
// Lets us provide a MockURLRequestContext with an HTTP Cache we pre-populate.
// Also exposes the constructor.
class ThreatDetailsWrap : public ThreatDetails {
public:
ThreatDetailsWrap(
SafeBrowsingUIManager* ui_manager,
WebContents* web_contents,
const SafeBrowsingUIManager::UnsafeResource& unsafe_resource,
net::URLRequestContextGetter* request_context_getter)
: ThreatDetails(ui_manager, web_contents, unsafe_resource) {
request_context_getter_ = request_context_getter;
}
private:
~ThreatDetailsWrap() override {}
};
class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
public:
base::RunLoop* run_loop_;
// The safe browsing UI manager does not need a service for this test.
MockSafeBrowsingUIManager() : SafeBrowsingUIManager(NULL), run_loop_(NULL) {}
// When the ThreatDetails is done, this is called.
void SendSerializedThreatDetails(const std::string& serialized) override {
DVLOG(1) << "SendSerializedThreatDetails";
run_loop_->Quit();
run_loop_ = NULL;
serialized_ = serialized;
}
// Used to synchronize SendSerializedThreatDetails() with
// WaitForSerializedReport(). RunLoop::RunUntilIdle() is not sufficient
// because the MessageLoop task queue completely drains at some point
// between the send and the wait.
void SetRunLoopToQuit(base::RunLoop* run_loop) {
DCHECK(run_loop_ == NULL);
run_loop_ = run_loop;
}
const std::string& GetSerialized() { return serialized_; }
private:
~MockSafeBrowsingUIManager() override {}
std::string serialized_;
DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
};
} // namespace
class ThreatDetailsTest : public ChromeRenderViewHostTestHarness {
public:
typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
ThreatDetailsTest() : ui_manager_(new MockSafeBrowsingUIManager()) {}
void SetUp() override {
ChromeRenderViewHostTestHarness::SetUp();
ASSERT_TRUE(profile()->CreateHistoryService(true /* delete_file */,
false /* no_db */));
}
void TearDown() override {
profile()->DestroyHistoryService();
ChromeRenderViewHostTestHarness::TearDown();
}
static bool ResourceLessThan(
const ClientSafeBrowsingReportRequest::Resource* lhs,
const ClientSafeBrowsingReportRequest::Resource* rhs) {
return lhs->id() < rhs->id();
}
std::string WaitForSerializedReport(ThreatDetails* report,
bool did_proceed,
int num_visit) {
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
base::Bind(&ThreatDetails::FinishCollection, report,
did_proceed, num_visit));
// Wait for the callback (SendSerializedThreatDetails).
DVLOG(1) << "Waiting for SendSerializedThreatDetails";
base::RunLoop run_loop;
ui_manager_->SetRunLoopToQuit(&run_loop);
run_loop.Run();
return ui_manager_->GetSerialized();
}
history::HistoryService* history_service() {
return HistoryServiceFactory::GetForProfile(
profile(), ServiceAccessType::EXPLICIT_ACCESS);
}
protected:
void InitResource(UnsafeResource* resource,
SBThreatType threat_type,
bool is_subresource,
const GURL& url) {
resource->url = url;
resource->is_subresource = is_subresource;
resource->threat_type = threat_type;
resource->render_process_host_id =
web_contents()->GetRenderProcessHost()->GetID();
resource->render_frame_id = web_contents()->GetMainFrame()->GetRoutingID();
}
void VerifyResults(const ClientSafeBrowsingReportRequest& report_pb,
const ClientSafeBrowsingReportRequest& expected_pb) {
EXPECT_EQ(expected_pb.type(), report_pb.type());
EXPECT_EQ(expected_pb.url(), report_pb.url());
EXPECT_EQ(expected_pb.page_url(), report_pb.page_url());
EXPECT_EQ(expected_pb.referrer_url(), report_pb.referrer_url());
EXPECT_EQ(expected_pb.did_proceed(), report_pb.did_proceed());
EXPECT_EQ(expected_pb.has_repeat_visit(), report_pb.has_repeat_visit());
if (expected_pb.has_repeat_visit() && report_pb.has_repeat_visit()) {
EXPECT_EQ(expected_pb.repeat_visit(), report_pb.repeat_visit());
}
ASSERT_EQ(expected_pb.resources_size(), report_pb.resources_size());
// Sort the resources, to make the test deterministic
std::vector<const ClientSafeBrowsingReportRequest::Resource*> resources;
for (int i = 0; i < report_pb.resources_size(); ++i) {
const ClientSafeBrowsingReportRequest::Resource& resource =
report_pb.resources(i);
resources.push_back(&resource);
}
std::sort(resources.begin(), resources.end(),
&ThreatDetailsTest::ResourceLessThan);
std::vector<const ClientSafeBrowsingReportRequest::Resource*> expected;
for (int i = 0; i < report_pb.resources_size(); ++i) {
const ClientSafeBrowsingReportRequest::Resource& resource =
expected_pb.resources(i);
expected.push_back(&resource);
}
std::sort(expected.begin(), expected.end(),
&ThreatDetailsTest::ResourceLessThan);
for (uint32_t i = 0; i < expected.size(); ++i) {
VerifyResource(resources[i], expected[i]);
}
EXPECT_EQ(expected_pb.complete(), report_pb.complete());
}
void VerifyResource(
const ClientSafeBrowsingReportRequest::Resource* resource,
const ClientSafeBrowsingReportRequest::Resource* expected) {
EXPECT_EQ(expected->id(), resource->id());
EXPECT_EQ(expected->url(), resource->url());
EXPECT_EQ(expected->parent_id(), resource->parent_id());
ASSERT_EQ(expected->child_ids_size(), resource->child_ids_size());
for (int i = 0; i < expected->child_ids_size(); i++) {
EXPECT_EQ(expected->child_ids(i), resource->child_ids(i));
}
// Verify HTTP Responses
if (expected->has_response()) {
ASSERT_TRUE(resource->has_response());
EXPECT_EQ(expected->response().firstline().code(),
resource->response().firstline().code());
ASSERT_EQ(expected->response().headers_size(),
resource->response().headers_size());
for (int i = 0; i < expected->response().headers_size(); ++i) {
EXPECT_EQ(expected->response().headers(i).name(),
resource->response().headers(i).name());
EXPECT_EQ(expected->response().headers(i).value(),
resource->response().headers(i).value());
}
EXPECT_EQ(expected->response().body(), resource->response().body());
EXPECT_EQ(expected->response().bodylength(),
resource->response().bodylength());
EXPECT_EQ(expected->response().bodydigest(),
resource->response().bodydigest());
}
// Verify IP:port pair
EXPECT_EQ(expected->response().remote_ip(),
resource->response().remote_ip());
}
// Adds a page to history.
// The redirects is the redirect url chain leading to the url.
void AddPageToHistory(const GURL& url, history::RedirectList* redirects) {
// The last item of the redirect chain has to be the final url when adding
// to history backend.
redirects->push_back(url);
history_service()->AddPage(url, base::Time::Now(),
reinterpret_cast<history::ContextID>(1), 0,
GURL(), *redirects, ui::PAGE_TRANSITION_TYPED,
history::SOURCE_BROWSED, false);
}
scoped_refptr<MockSafeBrowsingUIManager> ui_manager_;
};
// Tests creating a simple threat report of a malware URL.
TEST_F(ThreatDetailsTest, ThreatSubResource) {
// Commit a load.
content::WebContentsTester::For(web_contents())
->TestDidNavigateWithReferrer(
web_contents()->GetMainFrame(), 1 /* page_id */, 0 /* nav_entry_id */,
true /* did_create_new_entry */, GURL(kLandingURL),
content::Referrer(GURL(kReferrerURL),
blink::WebReferrerPolicyDefault),
ui::PAGE_TRANSITION_TYPED);
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE, true /* is_subresource */,
GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
// Note that the referrer policy is not actually enacted here, since that's
// done in Blink.
expected.set_referrer_url(kReferrerURL);
expected.set_did_proceed(true);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kThreatURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_url(kReferrerURL);
VerifyResults(actual, expected);
}
// Tests creating a simple threat report of a phishing page where the
// subresource has a different original_url.
TEST_F(ThreatDetailsTest, ThreatSubResourceWithOriginalUrl) {
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_PHISHING,
true /* is_subresource */, GURL(kThreatURL));
resource.original_url = GURL(kOriginalLandingURL);
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
std::string serialized = WaitForSerializedReport(
report.get(), false /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_PHISHING);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(false);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kOriginalLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_url(kThreatURL);
// The Resource for kThreatURL should have the Resource for
// kOriginalLandingURL (with id 1) as parent.
pb_resource->set_parent_id(1);
VerifyResults(actual, expected);
}
// Tests creating a threat report of a UwS page with data from the renderer.
TEST_F(ThreatDetailsTest, ThreatDOMDetails) {
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_UNWANTED,
true /* is_subresource */, GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
// Send a message from the DOM, with 2 nodes, a parent and a child.
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node> params;
SafeBrowsingHostMsg_ThreatDOMDetails_Node child_node;
child_node.url = GURL(kDOMChildURL);
child_node.tag_name = "iframe";
child_node.parent = GURL(kDOMParentURL);
params.push_back(child_node);
SafeBrowsingHostMsg_ThreatDOMDetails_Node parent_node;
parent_node.url = GURL(kDOMParentURL);
parent_node.children.push_back(GURL(kDOMChildURL));
params.push_back(parent_node);
report->OnReceivedThreatDOMDetails(params);
std::string serialized = WaitForSerializedReport(
report.get(), false /* did_proceed*/, 0 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_UNWANTED);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(false);
expected.set_repeat_visit(false);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kThreatURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_url(kDOMChildURL);
pb_resource->set_parent_id(3);
pb_resource = expected.add_resources();
pb_resource->set_id(3);
pb_resource->set_url(kDOMParentURL);
pb_resource->add_child_ids(2);
expected.set_complete(false); // Since the cache was missing.
VerifyResults(actual, expected);
}
// Tests creating a threat report of a malware page where there are redirect
// urls to an unsafe resource url.
TEST_F(ThreatDetailsTest, ThreatWithRedirectUrl) {
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE, true /* is_subresource */,
GURL(kThreatURL));
resource.original_url = GURL(kOriginalLandingURL);
// add some redirect urls
resource.redirect_urls.push_back(GURL(kFirstRedirectURL));
resource.redirect_urls.push_back(GURL(kSecondRedirectURL));
resource.redirect_urls.push_back(GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, 0 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(true);
expected.set_repeat_visit(false);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kOriginalLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_url(kThreatURL);
pb_resource->set_parent_id(4);
pb_resource = expected.add_resources();
pb_resource->set_id(3);
pb_resource->set_url(kFirstRedirectURL);
pb_resource->set_parent_id(1);
pb_resource = expected.add_resources();
pb_resource->set_id(4);
pb_resource->set_url(kSecondRedirectURL);
pb_resource->set_parent_id(3);
VerifyResults(actual, expected);
}
// Test collecting threat details for a blocked main frame load.
TEST_F(ThreatDetailsTest, ThreatOnMainPageLoadBlocked) {
const char* kUnrelatedReferrerURL =
"http://www.unrelatedreferrer.com/some/path";
const char* kUnrelatedURL = "http://www.unrelated.com/some/path";
// Load and commit an unrelated URL. The ThreatDetails should not use this
// navigation entry.
content::WebContentsTester::For(web_contents())
->TestDidNavigateWithReferrer(
web_contents()->GetMainFrame(), 1 /* page_id */, 0 /* nav_entry_id */,
true /* did_create_new_entry */, GURL(kUnrelatedURL),
content::Referrer(GURL(kUnrelatedReferrerURL),
blink::WebReferrerPolicyDefault),
ui::PAGE_TRANSITION_TYPED);
// Start a pending load with a referrer.
controller().LoadURL(GURL(kLandingURL),
content::Referrer(GURL(kReferrerURL),
blink::WebReferrerPolicyDefault),
ui::PAGE_TRANSITION_TYPED, std::string());
// Create UnsafeResource for the pending main page load.
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE,
false /* is_subresource */, GURL(kLandingURL));
// Start ThreatDetails collection.
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
// Simulate clicking don't proceed.
controller().DiscardNonCommittedEntries();
// Finish ThreatDetails collection.
std::string serialized = WaitForSerializedReport(
report.get(), false /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kLandingURL);
expected.set_page_url(kLandingURL);
// Note that the referrer policy is not actually enacted here, since that's
// done in Blink.
expected.set_referrer_url(kReferrerURL);
expected.set_did_proceed(false);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kReferrerURL);
VerifyResults(actual, expected);
}
// Tests that a pending load does not interfere with collecting threat details
// for the committed page.
TEST_F(ThreatDetailsTest, ThreatWithPendingLoad) {
const char* kPendingReferrerURL = "http://www.pendingreferrer.com/some/path";
const char* kPendingURL = "http://www.pending.com/some/path";
// Load and commit the landing URL with a referrer.
content::WebContentsTester::For(web_contents())
->TestDidNavigateWithReferrer(
web_contents()->GetMainFrame(), 1 /* page_id */, 0 /* nav_entry_id */,
true /* did_create_new_entry */, GURL(kLandingURL),
content::Referrer(GURL(kReferrerURL),
blink::WebReferrerPolicyDefault),
ui::PAGE_TRANSITION_TYPED);
// Create UnsafeResource for fake sub-resource of landing page.
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE, true /* is_subresource */,
GURL(kThreatURL));
// Start a pending load before creating ThreatDetails.
controller().LoadURL(GURL(kPendingURL),
content::Referrer(GURL(kPendingReferrerURL),
blink::WebReferrerPolicyDefault),
ui::PAGE_TRANSITION_TYPED, std::string());
// Do ThreatDetails collection.
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
// Note that the referrer policy is not actually enacted here, since that's
// done in Blink.
expected.set_referrer_url(kReferrerURL);
expected.set_did_proceed(true);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kThreatURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_url(kReferrerURL);
VerifyResults(actual, expected);
}
TEST_F(ThreatDetailsTest, ThreatOnFreshTab) {
// A fresh WebContents should not have any NavigationEntries yet. (See
// https://crbug.com/524208.)
EXPECT_EQ(nullptr, controller().GetLastCommittedEntry());
EXPECT_EQ(nullptr, controller().GetPendingEntry());
// Simulate a subresource malware hit (this could happen if the WebContents
// was created with window.open, and had content injected into it).
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE, true /* is_subresource */,
GURL(kThreatURL));
// Do ThreatDetails collection.
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kThreatURL);
expected.set_did_proceed(true);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kThreatURL);
VerifyResults(actual, expected);
}
// Tests the interaction with the HTTP cache.
TEST_F(ThreatDetailsTest, HTTPCache) {
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL,
true /* is_subresource */, GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource,
profile()->GetRequestContext());
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&FillCache,
base::RetainedRef(profile()->GetRequestContext())));
// The cache collection starts after the IPC from the DOM is fired.
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node> params;
report->OnReceivedThreatDOMDetails(params);
// Let the cache callbacks complete.
base::RunLoop().RunUntilIdle();
DVLOG(1) << "Getting serialized report";
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, -1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::CLIENT_SIDE_PHISHING_URL);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
ClientSafeBrowsingReportRequest::HTTPResponse* pb_response =
pb_resource->mutable_response();
pb_response->mutable_firstline()->set_code(200);
ClientSafeBrowsingReportRequest::HTTPHeader* pb_header =
pb_response->add_headers();
pb_header->set_name("Content-Type");
pb_header->set_value("text/html");
pb_header = pb_response->add_headers();
pb_header->set_name("Content-Length");
pb_header->set_value("1024");
pb_header = pb_response->add_headers();
pb_header->set_name("Set-Cookie");
pb_header->set_value(""); // The cookie is dropped.
pb_response->set_body(kLandingData);
std::string landing_data(kLandingData);
pb_response->set_bodylength(landing_data.size());
pb_response->set_bodydigest(base::MD5String(landing_data));
pb_response->set_remote_ip("1.2.3.4:80");
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kThreatURL);
pb_response = pb_resource->mutable_response();
pb_response->mutable_firstline()->set_code(200);
pb_header = pb_response->add_headers();
pb_header->set_name("Content-Type");
pb_header->set_value("image/jpeg");
pb_response->set_body(kThreatData);
std::string threat_data(kThreatData);
pb_response->set_bodylength(threat_data.size());
pb_response->set_bodydigest(base::MD5String(threat_data));
pb_response->set_remote_ip("1.2.3.4:80");
expected.set_complete(true);
VerifyResults(actual, expected);
}
// Tests the interaction with the HTTP cache (where the cache is empty).
TEST_F(ThreatDetailsTest, HTTPCacheNoEntries) {
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL,
true /* is_subresource */, GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource,
profile()->GetRequestContext());
// No call to FillCache
// The cache collection starts after the IPC from the DOM is fired.
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node> params;
report->OnReceivedThreatDOMDetails(params);
// Let the cache callbacks complete.
base::RunLoop().RunUntilIdle();
DVLOG(1) << "Getting serialized report";
std::string serialized = WaitForSerializedReport(
report.get(), false /* did_proceed*/, -1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::CLIENT_SIDE_MALWARE_URL);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(false);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_url(kThreatURL);
expected.set_complete(true);
VerifyResults(actual, expected);
}
// Test getting redirects from history service.
TEST_F(ThreatDetailsTest, HistoryServiceUrls) {
// Add content to history service.
// There are two redirect urls before reacing malware url:
// kFirstRedirectURL -> kSecondRedirectURL -> kThreatURL
GURL baseurl(kThreatURL);
history::RedirectList redirects;
redirects.push_back(GURL(kFirstRedirectURL));
redirects.push_back(GURL(kSecondRedirectURL));
AddPageToHistory(baseurl, &redirects);
// Wait for history service operation finished.
profile()->BlockUntilHistoryProcessesPendingRequests();
content::WebContentsTester::For(web_contents())
->NavigateAndCommit(GURL(kLandingURL));
UnsafeResource resource;
InitResource(&resource, SB_THREAT_TYPE_URL_MALWARE, true /* is_subresource */,
GURL(kThreatURL));
scoped_refptr<ThreatDetailsWrap> report =
new ThreatDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
// The redirects collection starts after the IPC from the DOM is fired.
std::vector<SafeBrowsingHostMsg_ThreatDOMDetails_Node> params;
report->OnReceivedThreatDOMDetails(params);
// Let the redirects callbacks complete.
base::RunLoop().RunUntilIdle();
std::string serialized = WaitForSerializedReport(
report.get(), true /* did_proceed*/, 1 /* num_visit */);
ClientSafeBrowsingReportRequest actual;
actual.ParseFromString(serialized);
ClientSafeBrowsingReportRequest expected;
expected.set_type(ClientSafeBrowsingReportRequest::URL_MALWARE);
expected.set_url(kThreatURL);
expected.set_page_url(kLandingURL);
expected.set_referrer_url("");
expected.set_did_proceed(true);
expected.set_repeat_visit(true);
ClientSafeBrowsingReportRequest::Resource* pb_resource =
expected.add_resources();
pb_resource->set_id(0);
pb_resource->set_url(kLandingURL);
pb_resource = expected.add_resources();
pb_resource->set_id(1);
pb_resource->set_parent_id(2);
pb_resource->set_url(kThreatURL);
pb_resource = expected.add_resources();
pb_resource->set_id(2);
pb_resource->set_parent_id(3);
pb_resource->set_url(kSecondRedirectURL);
pb_resource = expected.add_resources();
pb_resource->set_id(3);
pb_resource->set_url(kFirstRedirectURL);
VerifyResults(actual, expected);
}
} // namespace safe_browsing