• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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/bind.h"
8 #include "base/pickle.h"
9 #include "base/run_loop.h"
10 #include "base/time/time.h"
11 #include "chrome/browser/history/history_backend.h"
12 #include "chrome/browser/history/history_service.h"
13 #include "chrome/browser/history/history_service_factory.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/safe_browsing/malware_details.h"
16 #include "chrome/browser/safe_browsing/malware_details_history.h"
17 #include "chrome/browser/safe_browsing/report.pb.h"
18 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
19 #include "chrome/browser/safe_browsing/ui_manager.h"
20 #include "chrome/common/render_messages.h"
21 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
22 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
23 #include "chrome/test/base/testing_profile.h"
24 #include "content/public/browser/render_process_host.h"
25 #include "content/public/browser/web_contents.h"
26 #include "net/base/io_buffer.h"
27 #include "net/base/net_errors.h"
28 #include "net/base/test_completion_callback.h"
29 #include "net/disk_cache/disk_cache.h"
30 #include "net/http/http_cache.h"
31 #include "net/http/http_response_headers.h"
32 #include "net/http/http_response_info.h"
33 #include "net/http/http_util.h"
34 #include "net/url_request/url_request_context.h"
35 #include "net/url_request/url_request_context_getter.h"
36 
37 static const char* kOriginalLandingURL = "http://www.originallandingpage.com/";
38 static const char* kHttpsURL = "https://www.url.com/";
39 static const char* kDOMChildURL = "http://www.domparent.com/";
40 static const char* kDOMParentURL = "http://www.domchild.com/";
41 static const char* kFirstRedirectURL = "http://redirectone.com/";
42 static const char* kSecondRedirectURL = "http://redirecttwo.com/";
43 
44 static const char* kMalwareURL = "http://www.malware.com/";
45 static const char* kMalwareHeaders =
46     "HTTP/1.1 200 OK\n"
47     "Content-Type: image/jpeg\n";
48 static const char* kMalwareData = "exploit();";
49 
50 static const char* kLandingURL = "http://www.landingpage.com/";
51 static const char* kLandingHeaders =
52     "HTTP/1.1 200 OK\n"
53     "Content-Type: text/html\n"
54     "Content-Length: 1024\n"
55     "Set-Cookie: tastycookie\n";  // This header is stripped.
56 static const char* kLandingData = "<iframe src='http://www.malware.com'>";
57 
58 using content::BrowserThread;
59 using content::WebContents;
60 using safe_browsing::ClientMalwareReportRequest;
61 
62 namespace {
63 
WriteHeaders(disk_cache::Entry * entry,const std::string & headers)64 void WriteHeaders(disk_cache::Entry* entry, const std::string& headers) {
65   net::HttpResponseInfo responseinfo;
66   std::string raw_headers = net::HttpUtil::AssembleRawHeaders(
67       headers.c_str(), headers.size());
68   responseinfo.socket_address = net::HostPortPair("1.2.3.4", 80);
69   responseinfo.headers = new net::HttpResponseHeaders(raw_headers);
70 
71   Pickle pickle;
72   responseinfo.Persist(&pickle, false, false);
73 
74   scoped_refptr<net::WrappedIOBuffer> buf(new net::WrappedIOBuffer(
75       reinterpret_cast<const char*>(pickle.data())));
76   int len = static_cast<int>(pickle.size());
77 
78   net::TestCompletionCallback cb;
79   int rv = entry->WriteData(0, 0, buf.get(), len, cb.callback(), true);
80   ASSERT_EQ(len, cb.GetResult(rv));
81 }
82 
WriteData(disk_cache::Entry * entry,const std::string & data)83 void WriteData(disk_cache::Entry* entry, const std::string& data) {
84   if (data.empty())
85     return;
86 
87   int len = data.length();
88   scoped_refptr<net::IOBuffer> buf(new net::IOBuffer(len));
89   memcpy(buf->data(), data.data(), data.length());
90 
91   net::TestCompletionCallback cb;
92   int rv = entry->WriteData(1, 0, buf.get(), len, cb.callback(), true);
93   ASSERT_EQ(len, cb.GetResult(rv));
94 }
95 
WriteToEntry(disk_cache::Backend * cache,const std::string & key,const std::string & headers,const std::string & data)96 void WriteToEntry(disk_cache::Backend* cache, const std::string& key,
97                   const std::string& headers, const std::string& data) {
98   net::TestCompletionCallback cb;
99   disk_cache::Entry* entry;
100   int rv = cache->CreateEntry(key, &entry, cb.callback());
101   rv = cb.GetResult(rv);
102   if (rv != net::OK) {
103     rv = cache->OpenEntry(key, &entry, cb.callback());
104     ASSERT_EQ(net::OK, cb.GetResult(rv));
105   }
106 
107   WriteHeaders(entry, headers);
108   WriteData(entry, data);
109   entry->Close();
110 }
111 
FillCache(net::URLRequestContextGetter * context_getter)112 void FillCache(net::URLRequestContextGetter* context_getter) {
113   net::TestCompletionCallback cb;
114   disk_cache::Backend* cache;
115   int rv =
116       context_getter->GetURLRequestContext()->http_transaction_factory()->
117       GetCache()->GetBackend(&cache, cb.callback());
118   ASSERT_EQ(net::OK, cb.GetResult(rv));
119 
120   WriteToEntry(cache, kMalwareURL, kMalwareHeaders, kMalwareData);
121   WriteToEntry(cache, kLandingURL, kLandingHeaders, kLandingData);
122 }
123 
124 // Lets us provide a MockURLRequestContext with an HTTP Cache we pre-populate.
125 // Also exposes the constructor.
126 class MalwareDetailsWrap : public MalwareDetails {
127  public:
MalwareDetailsWrap(SafeBrowsingUIManager * ui_manager,WebContents * web_contents,const SafeBrowsingUIManager::UnsafeResource & unsafe_resource,net::URLRequestContextGetter * request_context_getter)128   MalwareDetailsWrap(
129       SafeBrowsingUIManager* ui_manager,
130       WebContents* web_contents,
131       const SafeBrowsingUIManager::UnsafeResource& unsafe_resource,
132       net::URLRequestContextGetter* request_context_getter)
133       : MalwareDetails(ui_manager, web_contents, unsafe_resource) {
134 
135     request_context_getter_ = request_context_getter;
136   }
137 
138  private:
~MalwareDetailsWrap()139   virtual ~MalwareDetailsWrap() {}
140 };
141 
142 class MockSafeBrowsingUIManager : public SafeBrowsingUIManager {
143  public:
144   base::RunLoop* run_loop_;
145   // The safe browsing UI manager does not need a service for this test.
MockSafeBrowsingUIManager()146   MockSafeBrowsingUIManager()
147       : SafeBrowsingUIManager(NULL), run_loop_(NULL) {}
148 
149   // When the MalwareDetails is done, this is called.
SendSerializedMalwareDetails(const std::string & serialized)150   virtual void SendSerializedMalwareDetails(
151       const std::string& serialized) OVERRIDE {
152     DVLOG(1) << "SendSerializedMalwareDetails";
153     run_loop_->Quit();
154     run_loop_ = NULL;
155     serialized_ = serialized;
156   }
157 
158   // Used to synchronize SendSerializedMalwareDetails() with
159   // WaitForSerializedReport(). RunLoop::RunUntilIdle() is not sufficient
160   // because the MessageLoop task queue completely drains at some point
161   // between the send and the wait.
SetRunLoopToQuit(base::RunLoop * run_loop)162   void SetRunLoopToQuit(base::RunLoop* run_loop) {
163     DCHECK(run_loop_ == NULL);
164     run_loop_ = run_loop;
165   }
166 
GetSerialized()167   const std::string& GetSerialized() {
168     return serialized_;
169   }
170 
171  private:
~MockSafeBrowsingUIManager()172   virtual ~MockSafeBrowsingUIManager() {}
173 
174   std::string serialized_;
175   DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingUIManager);
176 };
177 
178 }  // namespace.
179 
180 class MalwareDetailsTest : public ChromeRenderViewHostTestHarness {
181  public:
182   typedef SafeBrowsingUIManager::UnsafeResource UnsafeResource;
183 
MalwareDetailsTest()184   MalwareDetailsTest()
185       : ui_manager_(new MockSafeBrowsingUIManager()) {
186   }
187 
SetUp()188   virtual void SetUp() OVERRIDE {
189     ChromeRenderViewHostTestHarness::SetUp();
190     ASSERT_TRUE(profile()->CreateHistoryService(
191         true /* delete_file */, false /* no_db */));
192   }
193 
TearDown()194   virtual void TearDown() OVERRIDE {
195     profile()->DestroyHistoryService();
196     ChromeRenderViewHostTestHarness::TearDown();
197   }
198 
ResourceLessThan(const ClientMalwareReportRequest::Resource * lhs,const ClientMalwareReportRequest::Resource * rhs)199   static bool ResourceLessThan(
200       const ClientMalwareReportRequest::Resource* lhs,
201       const ClientMalwareReportRequest::Resource* rhs) {
202     return lhs->id() < rhs->id();
203   }
204 
WaitForSerializedReport(MalwareDetails * report)205   std::string WaitForSerializedReport(MalwareDetails* report) {
206     BrowserThread::PostTask(
207         BrowserThread::IO,
208         FROM_HERE,
209         base::Bind(&MalwareDetails::FinishCollection, report));
210     // Wait for the callback (SendSerializedMalwareDetails).
211     DVLOG(1) << "Waiting for SendSerializedMalwareDetails";
212     base::RunLoop run_loop;
213     ui_manager_->SetRunLoopToQuit(&run_loop);
214     run_loop.Run();
215     return ui_manager_->GetSerialized();
216   }
217 
history_service()218   HistoryService* history_service() {
219     return HistoryServiceFactory::GetForProfile(profile(),
220                                                 Profile::EXPLICIT_ACCESS);
221   }
222 
223  protected:
InitResource(UnsafeResource * resource,bool is_subresource,const GURL & url)224   void InitResource(UnsafeResource* resource,
225                     bool is_subresource,
226                     const GURL& url) {
227     resource->url = url;
228     resource->is_subresource = is_subresource;
229     resource->threat_type = SB_THREAT_TYPE_URL_MALWARE;
230     resource->render_process_host_id =
231         web_contents()->GetRenderProcessHost()->GetID();
232     resource->render_view_id =
233         web_contents()->GetRenderViewHost()->GetRoutingID();
234   }
235 
VerifyResults(const ClientMalwareReportRequest & report_pb,const ClientMalwareReportRequest & expected_pb)236   void VerifyResults(const ClientMalwareReportRequest& report_pb,
237                      const ClientMalwareReportRequest& expected_pb) {
238     EXPECT_EQ(expected_pb.malware_url(), report_pb.malware_url());
239     EXPECT_EQ(expected_pb.page_url(), report_pb.page_url());
240     EXPECT_EQ(expected_pb.referrer_url(), report_pb.referrer_url());
241 
242     ASSERT_EQ(expected_pb.resources_size(), report_pb.resources_size());
243     // Sort the resources, to make the test deterministic
244     std::vector<const ClientMalwareReportRequest::Resource*> resources;
245     for (int i = 0; i < report_pb.resources_size(); ++i) {
246       const ClientMalwareReportRequest::Resource& resource =
247           report_pb.resources(i);
248       resources.push_back(&resource);
249     }
250     std::sort(resources.begin(), resources.end(),
251               &MalwareDetailsTest::ResourceLessThan);
252 
253     std::vector<const ClientMalwareReportRequest::Resource*> expected;
254     for (int i = 0; i < report_pb.resources_size(); ++i) {
255       const ClientMalwareReportRequest::Resource& resource =
256           expected_pb.resources(i);
257       expected.push_back(&resource);
258     }
259     std::sort(expected.begin(), expected.end(),
260               &MalwareDetailsTest::ResourceLessThan);
261 
262     for (uint32 i = 0; i < expected.size(); ++i) {
263       VerifyResource(resources[i], expected[i]);
264     }
265 
266     EXPECT_EQ(expected_pb.complete(), report_pb.complete());
267   }
268 
VerifyResource(const ClientMalwareReportRequest::Resource * resource,const ClientMalwareReportRequest::Resource * expected)269   void VerifyResource(const ClientMalwareReportRequest::Resource* resource,
270                       const ClientMalwareReportRequest::Resource* expected) {
271     EXPECT_EQ(expected->id(), resource->id());
272     EXPECT_EQ(expected->url(), resource->url());
273     EXPECT_EQ(expected->parent_id(), resource->parent_id());
274     ASSERT_EQ(expected->child_ids_size(), resource->child_ids_size());
275     for (int i = 0; i < expected->child_ids_size(); i++) {
276       EXPECT_EQ(expected->child_ids(i), resource->child_ids(i));
277     }
278 
279     // Verify HTTP Responses
280     if (expected->has_response()) {
281       ASSERT_TRUE(resource->has_response());
282       EXPECT_EQ(expected->response().firstline().code(),
283                 resource->response().firstline().code());
284 
285       ASSERT_EQ(expected->response().headers_size(),
286                 resource->response().headers_size());
287       for (int i = 0; i < expected->response().headers_size(); ++i) {
288         EXPECT_EQ(expected->response().headers(i).name(),
289                   resource->response().headers(i).name());
290         EXPECT_EQ(expected->response().headers(i).value(),
291                   resource->response().headers(i).value());
292       }
293 
294       EXPECT_EQ(expected->response().body(), resource->response().body());
295       EXPECT_EQ(expected->response().bodylength(),
296                 resource->response().bodylength());
297       EXPECT_EQ(expected->response().bodydigest(),
298                 resource->response().bodydigest());
299     }
300 
301     // Verify IP:port pair
302     EXPECT_EQ(expected->response().remote_ip(),
303               resource->response().remote_ip());
304   }
305 
306   // Adds a page to history.
307   // The redirects is the redirect url chain leading to the url.
AddPageToHistory(const GURL & url,history::RedirectList * redirects)308   void AddPageToHistory(const GURL& url,
309                         history::RedirectList* redirects) {
310     // The last item of the redirect chain has to be the final url when adding
311     // to history backend.
312     redirects->push_back(url);
313     history_service()->AddPage(
314         url, base::Time::Now(), static_cast<void*>(this), 0, GURL(),
315         *redirects, content::PAGE_TRANSITION_TYPED, history::SOURCE_BROWSED,
316         false);
317   }
318 
319   scoped_refptr<MockSafeBrowsingUIManager> ui_manager_;
320 };
321 
322 // Tests creating a simple malware report.
TEST_F(MalwareDetailsTest,MalwareSubResource)323 TEST_F(MalwareDetailsTest, MalwareSubResource) {
324   // Start a load.
325   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
326                        content::PAGE_TRANSITION_TYPED, std::string());
327 
328   UnsafeResource resource;
329   InitResource(&resource, true, GURL(kMalwareURL));
330 
331   scoped_refptr<MalwareDetailsWrap> report =
332       new MalwareDetailsWrap(ui_manager_.get(), web_contents(), resource, NULL);
333 
334   std::string serialized = WaitForSerializedReport(report.get());
335 
336   ClientMalwareReportRequest actual;
337   actual.ParseFromString(serialized);
338 
339   ClientMalwareReportRequest expected;
340   expected.set_malware_url(kMalwareURL);
341   expected.set_page_url(kLandingURL);
342   expected.set_referrer_url("");
343 
344   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
345   pb_resource->set_id(0);
346   pb_resource->set_url(kLandingURL);
347   pb_resource = expected.add_resources();
348   pb_resource->set_id(1);
349   pb_resource->set_url(kMalwareURL);
350 
351   VerifyResults(actual, expected);
352 }
353 
354 // Tests creating a simple malware report where the subresource has a
355 // different original_url.
TEST_F(MalwareDetailsTest,MalwareSubResourceWithOriginalUrl)356 TEST_F(MalwareDetailsTest, MalwareSubResourceWithOriginalUrl) {
357   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
358                        content::PAGE_TRANSITION_TYPED, std::string());
359 
360   UnsafeResource resource;
361   InitResource(&resource, true, GURL(kMalwareURL));
362   resource.original_url = GURL(kOriginalLandingURL);
363 
364   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
365       ui_manager_.get(), web_contents(), resource, NULL);
366 
367   std::string serialized = WaitForSerializedReport(report.get());
368 
369   ClientMalwareReportRequest actual;
370   actual.ParseFromString(serialized);
371 
372   ClientMalwareReportRequest expected;
373   expected.set_malware_url(kMalwareURL);
374   expected.set_page_url(kLandingURL);
375   expected.set_referrer_url("");
376 
377   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
378   pb_resource->set_id(0);
379   pb_resource->set_url(kLandingURL);
380 
381   pb_resource = expected.add_resources();
382   pb_resource->set_id(1);
383   pb_resource->set_url(kOriginalLandingURL);
384 
385   pb_resource = expected.add_resources();
386   pb_resource->set_id(2);
387   pb_resource->set_url(kMalwareURL);
388   // The Resource for kMmalwareUrl should have the Resource for
389   // kOriginalLandingURL (with id 1) as parent.
390   pb_resource->set_parent_id(1);
391 
392   VerifyResults(actual, expected);
393 }
394 
395 // Tests creating a malware report with data from the renderer.
TEST_F(MalwareDetailsTest,MalwareDOMDetails)396 TEST_F(MalwareDetailsTest, MalwareDOMDetails) {
397   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
398                        content::PAGE_TRANSITION_TYPED, std::string());
399 
400   UnsafeResource resource;
401   InitResource(&resource, true, GURL(kMalwareURL));
402 
403   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
404       ui_manager_.get(), web_contents(), resource, NULL);
405 
406   // Send a message from the DOM, with 2 nodes, a parent and a child.
407   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
408   SafeBrowsingHostMsg_MalwareDOMDetails_Node child_node;
409   child_node.url = GURL(kDOMChildURL);
410   child_node.tag_name = "iframe";
411   child_node.parent = GURL(kDOMParentURL);
412   params.push_back(child_node);
413   SafeBrowsingHostMsg_MalwareDOMDetails_Node parent_node;
414   parent_node.url = GURL(kDOMParentURL);
415   parent_node.children.push_back(GURL(kDOMChildURL));
416   params.push_back(parent_node);
417   report->OnReceivedMalwareDOMDetails(params);
418 
419   std::string serialized = WaitForSerializedReport(report.get());
420   ClientMalwareReportRequest actual;
421   actual.ParseFromString(serialized);
422 
423   ClientMalwareReportRequest expected;
424   expected.set_malware_url(kMalwareURL);
425   expected.set_page_url(kLandingURL);
426   expected.set_referrer_url("");
427 
428   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
429   pb_resource->set_id(0);
430   pb_resource->set_url(kLandingURL);
431 
432   pb_resource = expected.add_resources();
433   pb_resource->set_id(1);
434   pb_resource->set_url(kMalwareURL);
435 
436   pb_resource = expected.add_resources();
437   pb_resource->set_id(2);
438   pb_resource->set_url(kDOMChildURL);
439   pb_resource->set_parent_id(3);
440 
441   pb_resource = expected.add_resources();
442   pb_resource->set_id(3);
443   pb_resource->set_url(kDOMParentURL);
444   pb_resource->add_child_ids(2);
445   expected.set_complete(false);  // Since the cache was missing.
446 
447   VerifyResults(actual, expected);
448 }
449 
450 // Verify that https:// urls are dropped.
TEST_F(MalwareDetailsTest,NotPublicUrl)451 TEST_F(MalwareDetailsTest, NotPublicUrl) {
452   controller().LoadURL(GURL(kHttpsURL), content::Referrer(),
453                        content::PAGE_TRANSITION_TYPED, std::string());
454   UnsafeResource resource;
455   InitResource(&resource, true, GURL(kMalwareURL));
456   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
457       ui_manager_.get(), web_contents(), resource, NULL);
458 
459   std::string serialized = WaitForSerializedReport(report.get());
460   ClientMalwareReportRequest actual;
461   actual.ParseFromString(serialized);
462 
463   ClientMalwareReportRequest expected;
464   expected.set_malware_url(kMalwareURL);  // No page_url
465   expected.set_referrer_url("");
466 
467   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
468   pb_resource->set_url(kMalwareURL);  // Only one resource
469 
470   VerifyResults(actual, expected);
471 }
472 
473 // Tests creating a malware report where there are redirect urls to an unsafe
474 // resource url
TEST_F(MalwareDetailsTest,MalwareWithRedirectUrl)475 TEST_F(MalwareDetailsTest, MalwareWithRedirectUrl) {
476   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
477                        content::PAGE_TRANSITION_TYPED, std::string());
478 
479   UnsafeResource resource;
480   InitResource(&resource, true, GURL(kMalwareURL));
481   resource.original_url = GURL(kOriginalLandingURL);
482 
483   // add some redirect urls
484   resource.redirect_urls.push_back(GURL(kFirstRedirectURL));
485   resource.redirect_urls.push_back(GURL(kSecondRedirectURL));
486   resource.redirect_urls.push_back(GURL(kMalwareURL));
487 
488   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
489       ui_manager_.get(), web_contents(), resource, NULL);
490 
491   std::string serialized = WaitForSerializedReport(report.get());
492   ClientMalwareReportRequest actual;
493   actual.ParseFromString(serialized);
494 
495   ClientMalwareReportRequest expected;
496   expected.set_malware_url(kMalwareURL);
497   expected.set_page_url(kLandingURL);
498   expected.set_referrer_url("");
499 
500   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
501   pb_resource->set_id(0);
502   pb_resource->set_url(kLandingURL);
503 
504   pb_resource = expected.add_resources();
505   pb_resource->set_id(1);
506   pb_resource->set_url(kOriginalLandingURL);
507 
508   pb_resource = expected.add_resources();
509   pb_resource->set_id(2);
510   pb_resource->set_url(kMalwareURL);
511   pb_resource->set_parent_id(4);
512 
513   pb_resource = expected.add_resources();
514   pb_resource->set_id(3);
515   pb_resource->set_url(kFirstRedirectURL);
516   pb_resource->set_parent_id(1);
517 
518   pb_resource = expected.add_resources();
519   pb_resource->set_id(4);
520   pb_resource->set_url(kSecondRedirectURL);
521   pb_resource->set_parent_id(3);
522 
523   VerifyResults(actual, expected);
524 }
525 
526 // Tests the interaction with the HTTP cache.
TEST_F(MalwareDetailsTest,HTTPCache)527 TEST_F(MalwareDetailsTest, HTTPCache) {
528   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
529                        content::PAGE_TRANSITION_TYPED, std::string());
530 
531   UnsafeResource resource;
532   InitResource(&resource, true, GURL(kMalwareURL));
533 
534   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
535       ui_manager_.get(), web_contents(), resource,
536       profile()->GetRequestContext());
537 
538   BrowserThread::PostTask(
539       BrowserThread::IO, FROM_HERE,
540       base::Bind(&FillCache,
541                  make_scoped_refptr(profile()->GetRequestContext())));
542 
543   // The cache collection starts after the IPC from the DOM is fired.
544   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
545   report->OnReceivedMalwareDOMDetails(params);
546 
547   // Let the cache callbacks complete.
548   base::RunLoop().RunUntilIdle();
549 
550   DVLOG(1) << "Getting serialized report";
551   std::string serialized = WaitForSerializedReport(report.get());
552   ClientMalwareReportRequest actual;
553   actual.ParseFromString(serialized);
554 
555   ClientMalwareReportRequest expected;
556   expected.set_malware_url(kMalwareURL);
557   expected.set_page_url(kLandingURL);
558   expected.set_referrer_url("");
559 
560   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
561   pb_resource->set_id(0);
562   pb_resource->set_url(kLandingURL);
563   safe_browsing::ClientMalwareReportRequest::HTTPResponse* pb_response =
564       pb_resource->mutable_response();
565   pb_response->mutable_firstline()->set_code(200);
566   safe_browsing::ClientMalwareReportRequest::HTTPHeader* pb_header =
567       pb_response->add_headers();
568   pb_header->set_name("Content-Type");
569   pb_header->set_value("text/html");
570   pb_header = pb_response->add_headers();
571   pb_header->set_name("Content-Length");
572   pb_header->set_value("1024");
573   pb_header = pb_response->add_headers();
574   pb_header->set_name("Set-Cookie");
575   pb_header->set_value("");  // The cookie is dropped.
576   pb_response->set_body(kLandingData);
577   pb_response->set_bodylength(37);
578   pb_response->set_bodydigest("9ca97475598a79bc1e8fc9bd6c72cd35");
579   pb_response->set_remote_ip("1.2.3.4:80");
580 
581   pb_resource = expected.add_resources();
582   pb_resource->set_id(1);
583   pb_resource->set_url(kMalwareURL);
584   pb_response = pb_resource->mutable_response();
585   pb_response->mutable_firstline()->set_code(200);
586   pb_header = pb_response->add_headers();
587   pb_header->set_name("Content-Type");
588   pb_header->set_value("image/jpeg");
589   pb_response->set_body(kMalwareData);
590   pb_response->set_bodylength(10);
591   pb_response->set_bodydigest("581373551c43d4cf33bfb3b26838ff95");
592   pb_response->set_remote_ip("1.2.3.4:80");
593   expected.set_complete(true);
594 
595   VerifyResults(actual, expected);
596 }
597 
598 // Tests the interaction with the HTTP cache (where the cache is empty).
TEST_F(MalwareDetailsTest,HTTPCacheNoEntries)599 TEST_F(MalwareDetailsTest, HTTPCacheNoEntries) {
600   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
601                        content::PAGE_TRANSITION_TYPED, std::string());
602 
603   UnsafeResource resource;
604   InitResource(&resource, true, GURL(kMalwareURL));
605 
606   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
607       ui_manager_.get(), web_contents(), resource,
608       profile()->GetRequestContext());
609 
610   // No call to FillCache
611 
612   // The cache collection starts after the IPC from the DOM is fired.
613   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
614   report->OnReceivedMalwareDOMDetails(params);
615 
616   // Let the cache callbacks complete.
617   base::RunLoop().RunUntilIdle();
618 
619   DVLOG(1) << "Getting serialized report";
620   std::string serialized = WaitForSerializedReport(report.get());
621   ClientMalwareReportRequest actual;
622   actual.ParseFromString(serialized);
623 
624   ClientMalwareReportRequest expected;
625   expected.set_malware_url(kMalwareURL);
626   expected.set_page_url(kLandingURL);
627   expected.set_referrer_url("");
628 
629   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
630   pb_resource->set_id(0);
631   pb_resource->set_url(kLandingURL);
632   pb_resource = expected.add_resources();
633   pb_resource->set_id(1);
634   pb_resource->set_url(kMalwareURL);
635   expected.set_complete(true);
636 
637   VerifyResults(actual, expected);
638 }
639 
640 // Test getting redirects from history service.
TEST_F(MalwareDetailsTest,HistoryServiceUrls)641 TEST_F(MalwareDetailsTest, HistoryServiceUrls) {
642   // Add content to history service.
643   // There are two redirect urls before reacing malware url:
644   // kFirstRedirectURL -> kSecondRedirectURL -> kMalwareURL
645   GURL baseurl(kMalwareURL);
646   history::RedirectList redirects;
647   redirects.push_back(GURL(kFirstRedirectURL));
648   redirects.push_back(GURL(kSecondRedirectURL));
649   AddPageToHistory(baseurl, &redirects);
650   // Wait for history service operation finished.
651   profile()->BlockUntilHistoryProcessesPendingRequests();
652 
653   controller().LoadURL(GURL(kLandingURL), content::Referrer(),
654                        content::PAGE_TRANSITION_TYPED, std::string());
655 
656   UnsafeResource resource;
657   InitResource(&resource, true, GURL(kMalwareURL));
658   scoped_refptr<MalwareDetailsWrap> report = new MalwareDetailsWrap(
659       ui_manager_.get(), web_contents(), resource, NULL);
660 
661   // The redirects collection starts after the IPC from the DOM is fired.
662   std::vector<SafeBrowsingHostMsg_MalwareDOMDetails_Node> params;
663   report->OnReceivedMalwareDOMDetails(params);
664 
665   // Let the redirects callbacks complete.
666   base::RunLoop().RunUntilIdle();
667 
668   std::string serialized = WaitForSerializedReport(report.get());
669   ClientMalwareReportRequest actual;
670   actual.ParseFromString(serialized);
671 
672   ClientMalwareReportRequest expected;
673   expected.set_malware_url(kMalwareURL);
674   expected.set_page_url(kLandingURL);
675   expected.set_referrer_url("");
676 
677   ClientMalwareReportRequest::Resource* pb_resource = expected.add_resources();
678   pb_resource->set_id(0);
679   pb_resource->set_url(kLandingURL);
680   pb_resource = expected.add_resources();
681   pb_resource->set_id(1);
682   pb_resource->set_parent_id(2);
683   pb_resource->set_url(kMalwareURL);
684   pb_resource = expected.add_resources();
685   pb_resource->set_id(2);
686   pb_resource->set_parent_id(3);
687   pb_resource->set_url(kSecondRedirectURL);
688   pb_resource = expected.add_resources();
689   pb_resource->set_id(3);
690   pb_resource->set_url(kFirstRedirectURL);
691 
692   VerifyResults(actual, expected);
693 }
694