Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 
      7 #include "base/pickle.h"
      8 #include "base/time.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/browser/safe_browsing/malware_details.h"
     11 #include "chrome/browser/safe_browsing/report.pb.h"
     12 #include "chrome/common/render_messages.h"
     13 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
     14 #include "chrome/test/test_url_request_context_getter.h"
     15 #include "chrome/test/testing_profile.h"
     16 #include "content/browser/browser_thread.h"
     17 #include "content/browser/renderer_host/test_render_view_host.h"
     18 #include "content/browser/tab_contents/navigation_entry.h"
     19 #include "content/browser/tab_contents/test_tab_contents.h"
     20 #include "net/base/io_buffer.h"
     21 #include "net/base/test_completion_callback.h"
     22 #include "net/disk_cache/disk_cache.h"
     23 #include "net/http/http_cache.h"
     24 #include "net/http/http_response_headers.h"
     25 #include "net/http/http_response_info.h"
     26 #include "net/http/http_util.h"
     27 #include "net/url_request/url_request_context.h"
     28 #include "net/url_request/url_request_context_getter.h"
     29 
     30 static const char* kOriginalLandingURL = "http://www.originallandingpage.com/";
     31 static const char* kHttpsURL = "https://www.url.com/";
     32 static const char* kDOMChildURL = "http://www.domparent.com/";
     33 static const char* kDOMParentURL = "http://www.domchild.com/";
     34 static const char* kFirstRedirectURL = "http://redirectone.com/";
     35 static const char* kSecondRedirectURL = "http://redirecttwo.com/";
     36 
     37 static const char* kMalwareURL = "http://www.malware.com/";
     38 static const char* kMalwareHeaders =
     39     "HTTP/1.1 200 OK\n"
     40     "Content-Type: image/jpeg\n";
     41 static const char* kMalwareData = "exploit();";
     42 
     43 static const char* kLandingURL = "http://www.landingpage.com/";
     44 static const char* kLandingHeaders =
     45     "HTTP/1.1 200 OK\n"
     46     "Content-Type: text/html\n"
     47     "Content-Length: 1024\n"
     48     "Set-Cookie: tastycookie\n";  // This header is stripped.
     49 static const char* kLandingData = "<iframe src='http://www.malware.com'>";
     50 
     51 using safe_browsing::ClientMalwareReportRequest;
     52 
     53 namespace {
     54 
     55 void WriteHeaders(disk_cache::Entry* entry, const std::string headers) {
     56   net::HttpResponseInfo responseinfo;
     57   std::string raw_headers = net::HttpUtil::AssembleRawHeaders(
     58       headers.c_str(), headers.size());
     59   responseinfo.headers = new net::HttpResponseHeaders(raw_headers);
     60 
     61   Pickle pickle;
     62   responseinfo.Persist(&pickle, false, false);
     63 
     64   scoped_refptr<net::WrappedIOBuffer> buf(new net::WrappedIOBuffer(
     65       reinterpret_cast<const char*>(pickle.data())));
     66   int len = static_cast<int>(pickle.size());
     67 
     68   TestCompletionCallback cb;
     69   int rv = entry->WriteData(0, 0, buf, len, &cb, true);
     70   ASSERT_EQ(len, cb.GetResult(rv));
     71 }
     72 
     73 void WriteData(disk_cache::Entry* entry, const std::string data) {
     74   if (data.empty())
     75     return;
     76 
     77   int len = data.length();
     78   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(len));
     79   memcpy(buf->data(), data.data(), data.length());
     80 
     81   TestCompletionCallback cb;
     82   int rv = entry->WriteData(1, 0, buf, len, &cb, true);
     83   ASSERT_EQ(len, cb.GetResult(rv));
     84 }
     85 
     86 void WriteToEntry(disk_cache::Backend* cache, const std::string key,
     87                   const std::string headers, const std::string data) {
     88   TestCompletionCallback cb;
     89   disk_cache::Entry* entry;
     90   int rv = cache->CreateEntry(key, &entry, &cb);
     91   rv = cb.GetResult(rv);
     92   if (rv != net::OK) {
     93     rv = cache->OpenEntry(key, &entry, &cb);
     94     ASSERT_EQ(net::OK, cb.GetResult(rv));
     95   }
     96 
     97   WriteHeaders(entry, headers);
     98   WriteData(entry, data);
     99 
    100   entry->Close();
    101 }
    102 
    103 void FillCache(net::URLRequestContext* context) {
    104   TestCompletionCallback cb;
    105   disk_cache::Backend* cache;
    106   int rv =
    107       context->http_transaction_factory()->GetCache()->GetBackend(&cache, &cb);
    108   ASSERT_EQ(net::OK, cb.GetResult(rv));
    109 
    110   std::string empty;
    111   WriteToEntry(cache, kMalwareURL, kMalwareHeaders, kMalwareData);
    112   WriteToEntry(cache, kLandingURL, kLandingHeaders, kLandingData);
    113 }
    114 
    115 void QuitUIMessageLoop() {
    116   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    117   BrowserThread::PostTask(BrowserThread::UI,
    118                           FROM_HERE,
    119                           new MessageLoop::QuitTask());
    120 }
    121 
    122 // Lets us provide a MockURLRequestContext with an HTTP Cache we pre-populate.
    123 // Also exposes the constructor.
    124 class MalwareDetailsWrap : public MalwareDetails {
    125  public:
    126   MalwareDetailsWrap(SafeBrowsingService* sb_service,
    127                      TabContents* tab_contents,
    128                      const SafeBrowsingService::UnsafeResource& unsafe_resource,
    129                      net::URLRequestContextGetter* request_context_getter)
    130       : MalwareDetails(sb_service, tab_contents, unsafe_resource) {
    131     request_context_getter_ = request_context_getter;
    132   }
    133 
    134   virtual ~MalwareDetailsWrap() {}
    135 };
    136 
    137 class MockSafeBrowsingService : public SafeBrowsingService {
    138  public:
    139   MockSafeBrowsingService() {}
    140   virtual ~MockSafeBrowsingService() {}
    141 
    142   // When the MalwareDetails is done, this is called.
    143   virtual void SendSerializedMalwareDetails(const std::string& serialized) {
    144     DVLOG(1) << "SendSerializedMalwareDetails";
    145     // Notify WaitForSerializedReport.
    146     BrowserThread::PostTask(BrowserThread::IO,
    147                             FROM_HERE,
    148                             NewRunnableFunction(&QuitUIMessageLoop));
    149     serialized_ = serialized;
    150   }
    151 
    152   const std::string& GetSerialized() {
    153     return serialized_;
    154   }
    155 
    156  private:
    157   std::string serialized_;
    158   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService);
    159 };
    160 
    161 }  // namespace.
    162 
    163 class MalwareDetailsTest : public RenderViewHostTestHarness {
    164  public:
    165   MalwareDetailsTest()
    166       : ui_thread_(BrowserThread::UI, &message_loop_),
    167         io_thread_(BrowserThread::IO),
    168         sb_service_(new MockSafeBrowsingService()) {
    169   }
    170 
    171   virtual void SetUp() {
    172     RenderViewHostTestHarness::SetUp();
    173     // request_context_getter_ = new TestURLRequestContextGetter();
    174 
    175     // The URLFetcher checks that the messageloop type is IO.
    176     ASSERT_TRUE(io_thread_.StartWithOptions(
    177         base::Thread::Options(MessageLoop::TYPE_IO, 0)));
    178   }
    179 
    180   virtual void TearDown() {
    181     io_thread_.Stop();
    182     RenderViewHostTestHarness::TearDown();
    183   }
    184 
    185   static bool ResourceLessThan(
    186       const ClientMalwareReportRequest::Resource* lhs,
    187       const ClientMalwareReportRequest::Resource* rhs) {
    188     return lhs->id() < rhs->id();
    189   }
    190 
    191   std::string WaitForSerializedReport(MalwareDetails* report) {
    192     BrowserThread::PostTask(
    193         BrowserThread::IO,
    194         FROM_HERE,
    195         NewRunnableMethod(
    196             report, &MalwareDetails::FinishCollection));
    197     // Wait for the callback (SendSerializedMalwareDetails).
    198     DVLOG(1) << "Waiting for SendSerializedMalwareDetails";
    199     MessageLoop::current()->Run();
    200     return sb_service_->GetSerialized();
    201   }
    202 
    203  protected:
    204   void InitResource(SafeBrowsingService::UnsafeResource* resource,
    205                     ResourceType::Type resource_type,
    206                     const GURL& url) {
    207     resource->client = NULL;
    208     resource->url = url;
    209     resource->resource_type = resource_type;
    210     resource->threat_type = SafeBrowsingService::URL_MALWARE;
    211     resource->render_process_host_id = contents()->GetRenderProcessHost()->id();
    212     resource->render_view_id = contents()->render_view_host()->routing_id();
    213   }
    214 
    215   void VerifyResults(const ClientMalwareReportRequest& report_pb,
    216                      const ClientMalwareReportRequest& expected_pb) {
    217     EXPECT_EQ(expected_pb.malware_url(), report_pb.malware_url());
    218     EXPECT_EQ(expected_pb.page_url(), report_pb.page_url());
    219     EXPECT_EQ(expected_pb.referrer_url(), report_pb.referrer_url());
    220 
    221     ASSERT_EQ(expected_pb.resources_size(), report_pb.resources_size());
    222     // Sort the resources, to make the test deterministic
    223     std::vector<const ClientMalwareReportRequest::Resource*> resources;
    224     for (int i = 0; i < report_pb.resources_size(); ++i) {
    225       const ClientMalwareReportRequest::Resource& resource =
    226           report_pb.resources(i);
    227       resources.push_back(&resource);
    228     }
    229     std::sort(resources.begin(), resources.end(),
    230               &MalwareDetailsTest::ResourceLessThan);
    231 
    232     std::vector<const ClientMalwareReportRequest::Resource*> expected;
    233     for (int i = 0; i < report_pb.resources_size(); ++i) {
    234       const ClientMalwareReportRequest::Resource& resource =
    235           expected_pb.resources(i);
    236       expected.push_back(&resource);
    237     }
    238     std::sort(expected.begin(), expected.end(),
    239               &MalwareDetailsTest::ResourceLessThan);
    240 
    241     for (uint32 i = 0; i < expected.size(); ++i) {
    242       VerifyResource(resources[i], expected[i]);
    243     }
    244 
    245     EXPECT_EQ(expected_pb.complete(), report_pb.complete());
    246   }
    247 
    248   void VerifyResource(const ClientMalwareReportRequest::Resource* resource,
    249                       const ClientMalwareReportRequest::Resource* expected) {
    250     EXPECT_EQ(expected->id(), resource->id());
    251     EXPECT_EQ(expected->url(), resource->url());
    252     EXPECT_EQ(expected->parent_id(), resource->parent_id());
    253     ASSERT_EQ(expected->child_ids_size(), resource->child_ids_size());
    254     for (int i = 0; i < expected->child_ids_size(); i++) {
    255       EXPECT_EQ(expected->child_ids(i), resource->child_ids(i));
    256     }
    257 
    258     // Verify HTTP Responses
    259     if (expected->has_response()) {
    260       ASSERT_TRUE(resource->has_response());
    261       EXPECT_EQ(expected->response().firstline().code(),
    262                 resource->response().firstline().code());
    263 
    264       ASSERT_EQ(expected->response().headers_size(),
    265                 resource->response().headers_size());
    266       for (int i = 0; i < expected->response().headers_size(); ++i) {
    267         EXPECT_EQ(expected->response().headers(i).name(),
    268                   resource->response().headers(i).name());
    269         EXPECT_EQ(expected->response().headers(i).value(),
    270                   resource->response().headers(i).value());
    271       }
    272 
    273       EXPECT_EQ(expected->response().body(), resource->response().body());
    274       EXPECT_EQ(expected->response().bodylength(),
    275                 resource->response().bodylength());
    276       EXPECT_EQ(expected->response().bodydigest(),
    277                 resource->response().bodydigest());
    278     }
    279   }
    280 
    281   BrowserThread ui_thread_;
    282   BrowserThread io_thread_;
    283   scoped_refptr<MockSafeBrowsingService> sb_service_;
    284 };
    285 
    286 // Tests creating a simple malware report.
    287 TEST_F(MalwareDetailsTest, MalwareSubResource) {
    288   // Start a load.
    289   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    290 
    291   SafeBrowsingService::UnsafeResource resource;
    292   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    293 
    294   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    295       sb_service_, contents(), resource, NULL);
    296 
    297   std::string serialized = WaitForSerializedReport(report);
    298 
    299   ClientMalwareReportRequest actual;
    300   actual.ParseFromString(serialized);
    301 
    302   ClientMalwareReportRequest expected;
    303   expected.set_malware_url(kMalwareURL);
    304   expected.set_page_url(kLandingURL);
    305   expected.set_referrer_url("");
    306 
    307   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    308   pb_resource->set_id(0);
    309   pb_resource->set_url(kLandingURL);
    310   pb_resource = expected.add_resources();
    311   pb_resource->set_id(1);
    312   pb_resource->set_url(kMalwareURL);
    313 
    314   VerifyResults(actual, expected);
    315 }
    316 
    317 // Tests creating a simple malware report where the subresource has a
    318 // different original_url.
    319 TEST_F(MalwareDetailsTest, MalwareSubResourceWithOriginalUrl) {
    320   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    321 
    322   SafeBrowsingService::UnsafeResource resource;
    323   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    324   resource.original_url = GURL(kOriginalLandingURL);
    325 
    326   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    327       sb_service_.get(), contents(), resource, NULL);
    328 
    329   std::string serialized = WaitForSerializedReport(report);
    330 
    331   ClientMalwareReportRequest actual;
    332   actual.ParseFromString(serialized);
    333 
    334   ClientMalwareReportRequest expected;
    335   expected.set_malware_url(kMalwareURL);
    336   expected.set_page_url(kLandingURL);
    337   expected.set_referrer_url("");
    338 
    339   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    340   pb_resource->set_id(0);
    341   pb_resource->set_url(kLandingURL);
    342 
    343   pb_resource = expected.add_resources();
    344   pb_resource->set_id(1);
    345   pb_resource->set_url(kOriginalLandingURL);
    346 
    347   pb_resource = expected.add_resources();
    348   pb_resource->set_id(2);
    349   pb_resource->set_url(kMalwareURL);
    350   // The Resource for kMmalwareUrl should have the Resource for
    351   // kOriginalLandingURL (with id 1) as parent.
    352   pb_resource->set_parent_id(1);
    353 
    354   VerifyResults(actual, expected);
    355 }
    356 
    357 // Tests creating a malware report with data from the renderer.
    358 TEST_F(MalwareDetailsTest, MalwareDOMDetails) {
    359   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    360 
    361   SafeBrowsingService::UnsafeResource resource;
    362   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    363 
    364   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    365       sb_service_.get(), contents(), resource, NULL);
    366 
    367   // Send a message from the DOM, with 2 nodes, a parent and a child.
    368   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
    369   SafeBrowsingHostMsg_MalwareDOMDetails_Node child_node;
    370   child_node.url = GURL(kDOMChildURL);
    371   child_node.tag_name = "iframe";
    372   child_node.parent = GURL(kDOMParentURL);
    373   params.push_back(child_node);
    374   SafeBrowsingHostMsg_MalwareDOMDetails_Node parent_node;
    375   parent_node.url = GURL(kDOMParentURL);
    376   parent_node.children.push_back(GURL(kDOMChildURL));
    377   params.push_back(parent_node);
    378   report->OnReceivedMalwareDOMDetails(params);
    379 
    380   MessageLoop::current()->RunAllPending();
    381 
    382   std::string serialized = WaitForSerializedReport(report);
    383   ClientMalwareReportRequest actual;
    384   actual.ParseFromString(serialized);
    385 
    386   ClientMalwareReportRequest expected;
    387   expected.set_malware_url(kMalwareURL);
    388   expected.set_page_url(kLandingURL);
    389   expected.set_referrer_url("");
    390 
    391   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    392   pb_resource->set_id(0);
    393   pb_resource->set_url(kLandingURL);
    394 
    395   pb_resource = expected.add_resources();
    396   pb_resource->set_id(1);
    397   pb_resource->set_url(kMalwareURL);
    398 
    399   pb_resource = expected.add_resources();
    400   pb_resource->set_id(2);
    401   pb_resource->set_url(kDOMChildURL);
    402   pb_resource->set_parent_id(3);
    403 
    404   pb_resource = expected.add_resources();
    405   pb_resource->set_id(3);
    406   pb_resource->set_url(kDOMParentURL);
    407   pb_resource->add_child_ids(2);
    408   expected.set_complete(false);  // Since the cache was missing.
    409 
    410   VerifyResults(actual, expected);
    411 }
    412 
    413 // Verify that https:// urls are dropped.
    414 TEST_F(MalwareDetailsTest, NotPublicUrl) {
    415   controller().LoadURL(GURL(kHttpsURL), GURL(), PageTransition::TYPED);
    416   SafeBrowsingService::UnsafeResource resource;
    417   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    418   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    419       sb_service_.get(), contents(), resource, NULL);
    420 
    421   std::string serialized = WaitForSerializedReport(report);
    422   ClientMalwareReportRequest actual;
    423   actual.ParseFromString(serialized);
    424 
    425   ClientMalwareReportRequest expected;
    426   expected.set_malware_url(kMalwareURL);  // No page_url
    427   expected.set_referrer_url("");
    428 
    429   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    430   pb_resource->set_url(kMalwareURL);  // Only one resource
    431 
    432   VerifyResults(actual, expected);
    433 }
    434 
    435 // Tests creating a malware report where there are redirect urls to an unsafe
    436 // resource url
    437 TEST_F(MalwareDetailsTest, MalwareWithRedirectUrl) {
    438   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    439 
    440   SafeBrowsingService::UnsafeResource resource;
    441   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    442   resource.original_url = GURL(kOriginalLandingURL);
    443 
    444   // add some redirect urls
    445   resource.redirect_urls.push_back(GURL(kFirstRedirectURL));
    446   resource.redirect_urls.push_back(GURL(kSecondRedirectURL));
    447   resource.redirect_urls.push_back(GURL(kMalwareURL));
    448 
    449   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    450       sb_service_.get(), contents(), resource, NULL);
    451 
    452   std::string serialized = WaitForSerializedReport(report);
    453   ClientMalwareReportRequest actual;
    454   actual.ParseFromString(serialized);
    455 
    456   ClientMalwareReportRequest expected;
    457   expected.set_malware_url(kMalwareURL);
    458   expected.set_page_url(kLandingURL);
    459   expected.set_referrer_url("");
    460 
    461   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    462   pb_resource->set_id(0);
    463   pb_resource->set_url(kLandingURL);
    464 
    465   pb_resource = expected.add_resources();
    466   pb_resource->set_id(1);
    467   pb_resource->set_url(kOriginalLandingURL);
    468 
    469   pb_resource = expected.add_resources();
    470   pb_resource->set_id(2);
    471   pb_resource->set_url(kMalwareURL);
    472   pb_resource->set_parent_id(4);
    473 
    474   pb_resource = expected.add_resources();
    475   pb_resource->set_id(3);
    476   pb_resource->set_url(kFirstRedirectURL);
    477   pb_resource->set_parent_id(1);
    478 
    479   pb_resource = expected.add_resources();
    480   pb_resource->set_id(4);
    481   pb_resource->set_url(kSecondRedirectURL);
    482   pb_resource->set_parent_id(3);
    483 
    484   VerifyResults(actual, expected);
    485 }
    486 
    487 // Tests the interaction with the HTTP cache.
    488 TEST_F(MalwareDetailsTest, HTTPCache) {
    489   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    490 
    491   SafeBrowsingService::UnsafeResource resource;
    492   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    493 
    494   profile()->CreateRequestContext();
    495   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    496       sb_service_.get(), contents(), resource
    497       , profile()->GetRequestContext());
    498 
    499   FillCache(profile()->GetRequestContext()->GetURLRequestContext());
    500 
    501   // The cache collection starts after the IPC from the DOM is fired.
    502   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
    503   report->OnReceivedMalwareDOMDetails(params);
    504 
    505   // Let the cache callbacks complete
    506   MessageLoop::current()->RunAllPending();
    507 
    508   DVLOG(1) << "Getting serialized report";
    509   std::string serialized = WaitForSerializedReport(report);
    510   ClientMalwareReportRequest actual;
    511   actual.ParseFromString(serialized);
    512 
    513   ClientMalwareReportRequest expected;
    514   expected.set_malware_url(kMalwareURL);
    515   expected.set_page_url(kLandingURL);
    516   expected.set_referrer_url("");
    517 
    518   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    519   pb_resource->set_id(0);
    520   pb_resource->set_url(kLandingURL);
    521   safe_browsing::ClientMalwareReportRequest::HTTPResponse* pb_response =
    522       pb_resource->mutable_response();
    523   pb_response->mutable_firstline()->set_code(200);
    524   safe_browsing::ClientMalwareReportRequest::HTTPHeader* pb_header =
    525       pb_response->add_headers();
    526   pb_header->set_name("Content-Type");
    527   pb_header->set_value("text/html");
    528   pb_header = pb_response->add_headers();
    529   pb_header->set_name("Content-Length");
    530   pb_header->set_value("1024");
    531   pb_header = pb_response->add_headers();
    532   pb_header->set_name("Set-Cookie");
    533   pb_header->set_value("");  // The cookie is dropped.
    534   pb_response->set_body(kLandingData);
    535   pb_response->set_bodylength(37);
    536   pb_response->set_bodydigest("9ca97475598a79bc1e8fc9bd6c72cd35");
    537 
    538   pb_resource = expected.add_resources();
    539   pb_resource->set_id(1);
    540   pb_resource->set_url(kMalwareURL);
    541   pb_response = pb_resource->mutable_response();
    542   pb_response->mutable_firstline()->set_code(200);
    543   pb_header = pb_response->add_headers();
    544   pb_header->set_name("Content-Type");
    545   pb_header->set_value("image/jpeg");
    546   pb_response->set_body(kMalwareData);
    547   pb_response->set_bodylength(10);
    548   pb_response->set_bodydigest("581373551c43d4cf33bfb3b26838ff95");
    549   expected.set_complete(true);
    550 
    551   VerifyResults(actual, expected);
    552 }
    553 
    554 // Tests the interaction with the HTTP cache (where the cache is empty).
    555 TEST_F(MalwareDetailsTest, HTTPCacheNoEntries) {
    556   controller().LoadURL(GURL(kLandingURL), GURL(), PageTransition::TYPED);
    557 
    558   SafeBrowsingService::UnsafeResource resource;
    559   InitResource(&resource, ResourceType::SUB_RESOURCE, GURL(kMalwareURL));
    560 
    561   profile()->CreateRequestContext();
    562   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
    563       sb_service_.get(), contents(), resource,
    564       profile()->GetRequestContext());
    565 
    566   // No call to FillCache
    567 
    568   // The cache collection starts after the IPC from the DOM is fired.
    569   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
    570   report->OnReceivedMalwareDOMDetails(params);
    571 
    572   // Let the cache callbacks complete
    573   MessageLoop::current()->RunAllPending();
    574 
    575   DVLOG(1) << "Getting serialized report";
    576   std::string serialized = WaitForSerializedReport(report);
    577   ClientMalwareReportRequest actual;
    578   actual.ParseFromString(serialized);
    579 
    580   ClientMalwareReportRequest expected;
    581   expected.set_malware_url(kMalwareURL);
    582   expected.set_page_url(kLandingURL);
    583   expected.set_referrer_url("");
    584 
    585   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
    586   pb_resource->set_id(0);
    587   pb_resource->set_url(kLandingURL);
    588   pb_resource = expected.add_resources();
    589   pb_resource->set_id(1);
    590   pb_resource->set_url(kMalwareURL);
    591   expected.set_complete(true);
    592 
    593   VerifyResults(actual, expected);
    594 }
    595