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.
5 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/history/history_backend.h"
16 #include "chrome/browser/history/history_service.h"
17 #include "chrome/browser/history/history_service_factory.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/safe_browsing/browser_features.h"
20 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
21 #include "chrome/browser/safe_browsing/database_manager.h"
22 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
23 #include "chrome/browser/safe_browsing/ui_manager.h"
24 #include "chrome/common/safe_browsing/csd.pb.h"
25 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
26 #include "chrome/test/base/testing_profile.h"
27 #include "content/public/browser/navigation_controller.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/page_transition_types.h"
30 #include "content/public/common/referrer.h"
31 #include "content/public/test/test_browser_thread.h"
32 #include "content/public/test/web_contents_tester.h"
33 #include "testing/gmock/include/gmock/gmock.h"
34 #include "testing/gtest/include/gtest/gtest.h"
37 using content::BrowserThread
;
38 using content::ResourceType
;
39 using content::WebContentsTester
;
42 using testing::Return
;
43 using testing::StrictMock
;
45 namespace safe_browsing
{
49 class MockSafeBrowsingDatabaseManager
: public SafeBrowsingDatabaseManager
{
51 explicit MockSafeBrowsingDatabaseManager(
52 const scoped_refptr
<SafeBrowsingService
>& service
)
53 : SafeBrowsingDatabaseManager(service
) { }
55 MOCK_METHOD1(MatchMalwareIP
, bool(const std::string
& ip_address
));
58 virtual ~MockSafeBrowsingDatabaseManager() {}
61 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingDatabaseManager
);
64 class MockClientSideDetectionHost
: public ClientSideDetectionHost
{
66 MockClientSideDetectionHost(
67 content::WebContents
* tab
,
68 SafeBrowsingDatabaseManager
* database_manager
)
69 : ClientSideDetectionHost(tab
) {
70 set_safe_browsing_managers(NULL
, database_manager
);
73 virtual ~MockClientSideDetectionHost() {}
75 MOCK_METHOD1(IsBadIpAddress
, bool(const std::string
&));
79 class BrowserFeatureExtractorTest
: public ChromeRenderViewHostTestHarness
{
81 virtual void SetUp() {
82 ChromeRenderViewHostTestHarness::SetUp();
83 ASSERT_TRUE(profile()->CreateHistoryService(
84 true /* delete_file */, false /* no_db */));
86 db_manager_
= new StrictMock
<MockSafeBrowsingDatabaseManager
>(
87 SafeBrowsingService::CreateSafeBrowsingService());
88 host_
.reset(new StrictMock
<MockClientSideDetectionHost
>(
89 web_contents(), db_manager_
.get()));
91 new BrowserFeatureExtractor(web_contents(), host_
.get()));
93 browse_info_
.reset(new BrowseInfo
);
96 virtual void TearDown() {
100 profile()->DestroyHistoryService();
101 ChromeRenderViewHostTestHarness::TearDown();
102 ASSERT_EQ(0, num_pending_
);
105 HistoryService
* history_service() {
106 return HistoryServiceFactory::GetForProfile(profile(),
107 Profile::EXPLICIT_ACCESS
);
110 void SetRedirectChain(const std::vector
<GURL
>& redirect_chain
,
112 browse_info_
->url_redirects
= redirect_chain
;
114 browse_info_
->host_redirects
= redirect_chain
;
118 // Wrapper around NavigateAndCommit that also sets the redirect chain to
120 void SimpleNavigateAndCommit(const GURL
& url
) {
121 std::vector
<GURL
> redirect_chain
;
122 redirect_chain
.push_back(url
);
123 SetRedirectChain(redirect_chain
, true);
124 NavigateAndCommit(url
, GURL(), content::PAGE_TRANSITION_LINK
);
127 // This is similar to NavigateAndCommit that is in WebContentsTester, but
128 // allows us to specify the referrer and page_transition_type.
129 void NavigateAndCommit(const GURL
& url
,
130 const GURL
& referrer
,
131 content::PageTransition type
) {
132 web_contents()->GetController().LoadURL(
133 url
, content::Referrer(referrer
, blink::WebReferrerPolicyDefault
),
134 type
, std::string());
136 static int page_id
= 0;
137 content::RenderFrameHost
* rfh
=
138 WebContentsTester::For(web_contents())->GetPendingMainFrame();
140 rfh
= web_contents()->GetMainFrame();
142 WebContentsTester::For(web_contents())->ProceedWithCrossSiteNavigation();
143 WebContentsTester::For(web_contents())->TestDidNavigateWithReferrer(
145 content::Referrer(referrer
, blink::WebReferrerPolicyDefault
), type
);
148 bool ExtractFeatures(ClientPhishingRequest
* request
) {
149 StartExtractFeatures(request
);
150 base::MessageLoop::current()->Run();
151 EXPECT_EQ(1U, success_
.count(request
));
152 return success_
.count(request
) ? success_
[request
] : false;
155 void StartExtractFeatures(ClientPhishingRequest
* request
) {
156 success_
.erase(request
);
158 extractor_
->ExtractFeatures(
161 base::Bind(&BrowserFeatureExtractorTest::ExtractFeaturesDone
,
162 base::Unretained(this)));
165 void GetFeatureMap(const ClientPhishingRequest
& request
,
166 std::map
<std::string
, double>* features
) {
167 for (int i
= 0; i
< request
.non_model_feature_map_size(); ++i
) {
168 const ClientPhishingRequest::Feature
& feature
=
169 request
.non_model_feature_map(i
);
170 EXPECT_EQ(0U, features
->count(feature
.name()));
171 (*features
)[feature
.name()] = feature
.value();
175 void ExtractMalwareFeatures(ClientMalwareRequest
* request
) {
176 // Feature extraction takes ownership of the request object
177 // and passes it along to the done callback in the end.
178 StartExtractMalwareFeatures(request
);
179 base::MessageLoopForUI::current()->Run();
180 EXPECT_EQ(1U, success_
.count(request
));
181 EXPECT_TRUE(success_
[request
]);
184 void StartExtractMalwareFeatures(ClientMalwareRequest
* request
) {
185 success_
.erase(request
);
187 // We temporarily give up ownership of request to ExtractMalwareFeatures
188 // but we'll regain ownership of it in ExtractMalwareFeaturesDone.
189 extractor_
->ExtractMalwareFeatures(
192 base::Bind(&BrowserFeatureExtractorTest::ExtractMalwareFeaturesDone
,
193 base::Unretained(this)));
197 const ClientMalwareRequest
& request
,
198 std::map
<std::string
, std::set
<std::string
> >* urls
) {
199 for (int i
= 0; i
< request
.bad_ip_url_info_size(); ++i
) {
200 const ClientMalwareRequest::UrlInfo
& urlinfo
=
201 request
.bad_ip_url_info(i
);
202 (*urls
)[urlinfo
.ip()].insert(urlinfo
.url());
206 int num_pending_
; // Number of pending feature extractions.
207 scoped_ptr
<BrowserFeatureExtractor
> extractor_
;
208 std::map
<void*, bool> success_
;
209 scoped_ptr
<BrowseInfo
> browse_info_
;
210 scoped_ptr
<StrictMock
<MockClientSideDetectionHost
> > host_
;
211 scoped_refptr
<StrictMock
<MockSafeBrowsingDatabaseManager
> > db_manager_
;
214 void ExtractFeaturesDone(bool success
,
215 scoped_ptr
<ClientPhishingRequest
> request
) {
216 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI
));
217 ASSERT_EQ(0U, success_
.count(request
.get()));
218 // The pointer doesn't really belong to us. It belongs to
219 // the test case which passed it to ExtractFeatures above.
220 success_
[request
.release()] = success
;
221 if (--num_pending_
== 0) {
222 base::MessageLoop::current()->Quit();
226 void ExtractMalwareFeaturesDone(
228 scoped_ptr
<ClientMalwareRequest
> request
) {
229 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI
));
230 ASSERT_EQ(0U, success_
.count(request
.get()));
231 // The pointer doesn't really belong to us. It belongs to
232 // the test case which passed it to ExtractMalwareFeatures above.
233 success_
[request
.release()] = success
;
234 if (--num_pending_
== 0) {
235 base::MessageLoopForUI::current()->Quit();
240 TEST_F(BrowserFeatureExtractorTest
, UrlNotInHistory
) {
241 ClientPhishingRequest request
;
242 SimpleNavigateAndCommit(GURL("http://www.google.com"));
243 request
.set_url("http://www.google.com/");
244 request
.set_client_score(0.5);
245 EXPECT_FALSE(ExtractFeatures(&request
));
248 TEST_F(BrowserFeatureExtractorTest
, RequestNotInitialized
) {
249 ClientPhishingRequest request
;
250 request
.set_url("http://www.google.com/");
251 // Request is missing the score value.
252 SimpleNavigateAndCommit(GURL("http://www.google.com"));
253 EXPECT_FALSE(ExtractFeatures(&request
));
256 TEST_F(BrowserFeatureExtractorTest
, UrlInHistory
) {
257 history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
259 history::SOURCE_BROWSED
);
260 history_service()->AddPage(GURL("https://www.foo.com/gaa.html"),
262 history::SOURCE_BROWSED
); // same host HTTPS.
263 history_service()->AddPage(GURL("http://www.foo.com/gaa.html"),
265 history::SOURCE_BROWSED
); // same host HTTP.
266 history_service()->AddPage(GURL("http://bar.foo.com/gaa.html"),
268 history::SOURCE_BROWSED
); // different host.
269 history_service()->AddPage(GURL("http://www.foo.com/bar.html?a=b"),
270 base::Time::Now() - base::TimeDelta::FromHours(23),
271 NULL
, 0, GURL(), history::RedirectList(),
272 content::PAGE_TRANSITION_LINK
,
273 history::SOURCE_BROWSED
, false);
274 history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
275 base::Time::Now() - base::TimeDelta::FromHours(25),
276 NULL
, 0, GURL(), history::RedirectList(),
277 content::PAGE_TRANSITION_TYPED
,
278 history::SOURCE_BROWSED
, false);
279 history_service()->AddPage(GURL("https://www.foo.com/goo.html"),
280 base::Time::Now() - base::TimeDelta::FromDays(5),
281 NULL
, 0, GURL(), history::RedirectList(),
282 content::PAGE_TRANSITION_TYPED
,
283 history::SOURCE_BROWSED
, false);
285 SimpleNavigateAndCommit(GURL("http://www.foo.com/bar.html"));
287 ClientPhishingRequest request
;
288 request
.set_url("http://www.foo.com/bar.html");
289 request
.set_client_score(0.5);
290 EXPECT_TRUE(ExtractFeatures(&request
));
291 std::map
<std::string
, double> features
;
292 GetFeatureMap(request
, &features
);
294 EXPECT_EQ(12U, features
.size());
295 EXPECT_DOUBLE_EQ(2.0, features
[features::kUrlHistoryVisitCount
]);
296 EXPECT_DOUBLE_EQ(1.0,
297 features
[features::kUrlHistoryVisitCountMoreThan24hAgo
]);
298 EXPECT_DOUBLE_EQ(1.0, features
[features::kUrlHistoryTypedCount
]);
299 EXPECT_DOUBLE_EQ(1.0, features
[features::kUrlHistoryLinkCount
]);
300 EXPECT_DOUBLE_EQ(4.0, features
[features::kHttpHostVisitCount
]);
301 EXPECT_DOUBLE_EQ(2.0, features
[features::kHttpsHostVisitCount
]);
302 EXPECT_DOUBLE_EQ(1.0, features
[features::kFirstHttpHostVisitMoreThan24hAgo
]);
303 EXPECT_DOUBLE_EQ(1.0, features
[features::kFirstHttpsHostVisitMoreThan24hAgo
]);
306 request
.set_url("http://bar.foo.com/gaa.html");
307 request
.set_client_score(0.5);
308 EXPECT_TRUE(ExtractFeatures(&request
));
310 GetFeatureMap(request
, &features
);
311 // We have less features because we didn't Navigate to this page, so we don't
312 // have Referrer, IsFirstNavigation, HasSSLReferrer, etc.
313 EXPECT_EQ(7U, features
.size());
314 EXPECT_DOUBLE_EQ(1.0, features
[features::kUrlHistoryVisitCount
]);
315 EXPECT_DOUBLE_EQ(0.0,
316 features
[features::kUrlHistoryVisitCountMoreThan24hAgo
]);
317 EXPECT_DOUBLE_EQ(0.0, features
[features::kUrlHistoryTypedCount
]);
318 EXPECT_DOUBLE_EQ(1.0, features
[features::kUrlHistoryLinkCount
]);
319 EXPECT_DOUBLE_EQ(1.0, features
[features::kHttpHostVisitCount
]);
320 EXPECT_DOUBLE_EQ(0.0, features
[features::kHttpsHostVisitCount
]);
321 EXPECT_DOUBLE_EQ(0.0, features
[features::kFirstHttpHostVisitMoreThan24hAgo
]);
322 EXPECT_FALSE(features
.count(features::kFirstHttpsHostVisitMoreThan24hAgo
));
325 TEST_F(BrowserFeatureExtractorTest
, MultipleRequestsAtOnce
) {
326 history_service()->AddPage(GURL("http://www.foo.com/bar.html"),
328 history::SOURCE_BROWSED
);
329 SimpleNavigateAndCommit(GURL("http:/www.foo.com/bar.html"));
330 ClientPhishingRequest request
;
331 request
.set_url("http://www.foo.com/bar.html");
332 request
.set_client_score(0.5);
333 StartExtractFeatures(&request
);
335 SimpleNavigateAndCommit(GURL("http://www.foo.com/goo.html"));
336 ClientPhishingRequest request2
;
337 request2
.set_url("http://www.foo.com/goo.html");
338 request2
.set_client_score(1.0);
339 StartExtractFeatures(&request2
);
341 base::MessageLoop::current()->Run();
342 EXPECT_TRUE(success_
[&request
]);
343 // Success is false because the second URL is not in the history and we are
344 // not able to distinguish between a missing URL in the history and an error.
345 EXPECT_FALSE(success_
[&request2
]);
348 TEST_F(BrowserFeatureExtractorTest
, BrowseFeatures
) {
349 history_service()->AddPage(GURL("http://www.foo.com/"),
351 history::SOURCE_BROWSED
);
352 history_service()->AddPage(GURL("http://www.foo.com/page.html"),
354 history::SOURCE_BROWSED
);
355 history_service()->AddPage(GURL("http://www.bar.com/"),
357 history::SOURCE_BROWSED
);
358 history_service()->AddPage(GURL("http://www.bar.com/other_page.html"),
360 history::SOURCE_BROWSED
);
361 history_service()->AddPage(GURL("http://www.baz.com/"),
363 history::SOURCE_BROWSED
);
365 ClientPhishingRequest request
;
366 request
.set_url("http://www.foo.com/");
367 request
.set_client_score(0.5);
368 std::vector
<GURL
> redirect_chain
;
369 redirect_chain
.push_back(GURL("http://somerandomwebsite.com/"));
370 redirect_chain
.push_back(GURL("http://www.foo.com/"));
371 SetRedirectChain(redirect_chain
, true);
372 browse_info_
->http_status_code
= 200;
373 NavigateAndCommit(GURL("http://www.foo.com/"),
374 GURL("http://google.com/"),
375 content::PageTransitionFromInt(
376 content::PAGE_TRANSITION_AUTO_BOOKMARK
|
377 content::PAGE_TRANSITION_FORWARD_BACK
));
379 EXPECT_TRUE(ExtractFeatures(&request
));
380 std::map
<std::string
, double> features
;
381 GetFeatureMap(request
, &features
);
384 features
[base::StringPrintf("%s=%s",
386 "http://google.com/")]);
388 features
[base::StringPrintf("%s[0]=%s",
390 "http://somerandomwebsite.com/")]);
391 // We shouldn't have a feature for the last redirect in the chain, since it
392 // should always be the URL that we navigated to.
394 features
[base::StringPrintf("%s[1]=%s",
396 "http://foo.com/")]);
397 EXPECT_EQ(0.0, features
[features::kHasSSLReferrer
]);
398 EXPECT_EQ(2.0, features
[features::kPageTransitionType
]);
399 EXPECT_EQ(1.0, features
[features::kIsFirstNavigation
]);
400 EXPECT_EQ(200.0, features
[features::kHttpStatusCode
]);
403 request
.set_url("http://www.foo.com/page.html");
404 request
.set_client_score(0.5);
405 redirect_chain
.clear();
406 redirect_chain
.push_back(GURL("http://www.foo.com/redirect"));
407 redirect_chain
.push_back(GURL("http://www.foo.com/second_redirect"));
408 redirect_chain
.push_back(GURL("http://www.foo.com/page.html"));
409 SetRedirectChain(redirect_chain
, false);
410 browse_info_
->http_status_code
= 404;
411 NavigateAndCommit(GURL("http://www.foo.com/page.html"),
412 GURL("http://www.foo.com"),
413 content::PageTransitionFromInt(
414 content::PAGE_TRANSITION_TYPED
|
415 content::PAGE_TRANSITION_CHAIN_START
|
416 content::PAGE_TRANSITION_CLIENT_REDIRECT
));
418 EXPECT_TRUE(ExtractFeatures(&request
));
420 GetFeatureMap(request
, &features
);
423 features
[base::StringPrintf("%s=%s",
425 "http://www.foo.com/")]);
427 features
[base::StringPrintf("%s[0]=%s",
429 "http://www.foo.com/redirect")]);
431 features
[base::StringPrintf("%s[1]=%s",
433 "http://www.foo.com/second_redirect")]);
434 EXPECT_EQ(0.0, features
[features::kHasSSLReferrer
]);
435 EXPECT_EQ(1.0, features
[features::kPageTransitionType
]);
436 EXPECT_EQ(0.0, features
[features::kIsFirstNavigation
]);
438 features
[base::StringPrintf("%s%s=%s",
439 features::kHostPrefix
,
441 "http://google.com/")]);
443 features
[base::StringPrintf("%s%s[0]=%s",
444 features::kHostPrefix
,
446 "http://somerandomwebsite.com/")]);
448 features
[base::StringPrintf("%s%s",
449 features::kHostPrefix
,
450 features::kPageTransitionType
)]);
452 features
[base::StringPrintf("%s%s",
453 features::kHostPrefix
,
454 features::kIsFirstNavigation
)]);
455 EXPECT_EQ(404.0, features
[features::kHttpStatusCode
]);
458 request
.set_url("http://www.bar.com/");
459 request
.set_client_score(0.5);
460 redirect_chain
.clear();
461 redirect_chain
.push_back(GURL("http://www.foo.com/page.html"));
462 redirect_chain
.push_back(GURL("http://www.bar.com/"));
463 SetRedirectChain(redirect_chain
, true);
464 NavigateAndCommit(GURL("http://www.bar.com/"),
465 GURL("http://www.foo.com/page.html"),
466 content::PageTransitionFromInt(
467 content::PAGE_TRANSITION_LINK
|
468 content::PAGE_TRANSITION_CHAIN_END
|
469 content::PAGE_TRANSITION_CLIENT_REDIRECT
));
471 EXPECT_TRUE(ExtractFeatures(&request
));
473 GetFeatureMap(request
, &features
);
476 features
[base::StringPrintf("%s=%s",
478 "http://www.foo.com/page.html")]);
480 features
[base::StringPrintf("%s[0]=%s",
482 "http://www.foo.com/page.html")]);
483 EXPECT_EQ(0.0, features
[features::kHasSSLReferrer
]);
484 EXPECT_EQ(0.0, features
[features::kPageTransitionType
]);
485 EXPECT_EQ(0.0, features
[features::kIsFirstNavigation
]);
487 // Should not have host features.
489 features
.count(base::StringPrintf("%s%s",
490 features::kHostPrefix
,
491 features::kPageTransitionType
)));
493 features
.count(base::StringPrintf("%s%s",
494 features::kHostPrefix
,
495 features::kIsFirstNavigation
)));
498 request
.set_url("http://www.bar.com/other_page.html");
499 request
.set_client_score(0.5);
500 redirect_chain
.clear();
501 redirect_chain
.push_back(GURL("http://www.bar.com/other_page.html"));
502 SetRedirectChain(redirect_chain
, false);
503 NavigateAndCommit(GURL("http://www.bar.com/other_page.html"),
504 GURL("http://www.bar.com/"),
505 content::PAGE_TRANSITION_LINK
);
507 EXPECT_TRUE(ExtractFeatures(&request
));
509 GetFeatureMap(request
, &features
);
512 features
[base::StringPrintf("%s=%s",
514 "http://www.bar.com/")]);
515 EXPECT_EQ(0.0, features
[features::kHasSSLReferrer
]);
516 EXPECT_EQ(0.0, features
[features::kPageTransitionType
]);
517 EXPECT_EQ(0.0, features
[features::kIsFirstNavigation
]);
519 features
[base::StringPrintf("%s%s=%s",
520 features::kHostPrefix
,
522 "http://www.foo.com/page.html")]);
524 features
[base::StringPrintf("%s%s[0]=%s",
525 features::kHostPrefix
,
527 "http://www.foo.com/page.html")]);
529 features
[base::StringPrintf("%s%s",
530 features::kHostPrefix
,
531 features::kPageTransitionType
)]);
533 features
[base::StringPrintf("%s%s",
534 features::kHostPrefix
,
535 features::kIsFirstNavigation
)]);
537 request
.set_url("http://www.baz.com/");
538 request
.set_client_score(0.5);
539 redirect_chain
.clear();
540 redirect_chain
.push_back(GURL("https://bankofamerica.com"));
541 redirect_chain
.push_back(GURL("http://www.baz.com/"));
542 SetRedirectChain(redirect_chain
, true);
543 NavigateAndCommit(GURL("http://www.baz.com"),
544 GURL("https://bankofamerica.com"),
545 content::PAGE_TRANSITION_GENERATED
);
547 EXPECT_TRUE(ExtractFeatures(&request
));
549 GetFeatureMap(request
, &features
);
552 features
[base::StringPrintf("%s[0]=%s",
554 features::kSecureRedirectValue
)]);
555 EXPECT_EQ(1.0, features
[features::kHasSSLReferrer
]);
556 EXPECT_EQ(5.0, features
[features::kPageTransitionType
]);
557 // Should not have redirect or host features.
559 features
.count(base::StringPrintf("%s%s",
560 features::kHostPrefix
,
561 features::kPageTransitionType
)));
563 features
.count(base::StringPrintf("%s%s",
564 features::kHostPrefix
,
565 features::kIsFirstNavigation
)));
566 EXPECT_EQ(5.0, features
[features::kPageTransitionType
]);
569 TEST_F(BrowserFeatureExtractorTest
, SafeBrowsingFeatures
) {
570 SimpleNavigateAndCommit(GURL("http://www.foo.com/malware.html"));
571 ClientPhishingRequest request
;
572 request
.set_url("http://www.foo.com/malware.html");
573 request
.set_client_score(0.5);
575 browse_info_
->unsafe_resource
.reset(
576 new SafeBrowsingUIManager::UnsafeResource
);
577 browse_info_
->unsafe_resource
->url
= GURL("http://www.malware.com/");
578 browse_info_
->unsafe_resource
->original_url
= GURL("http://www.good.com/");
579 browse_info_
->unsafe_resource
->is_subresource
= true;
580 browse_info_
->unsafe_resource
->threat_type
= SB_THREAT_TYPE_URL_MALWARE
;
582 ExtractFeatures(&request
);
583 std::map
<std::string
, double> features
;
584 GetFeatureMap(request
, &features
);
585 EXPECT_TRUE(features
.count(base::StringPrintf(
587 features::kSafeBrowsingMaliciousUrl
,
588 "http://www.malware.com/")));
589 EXPECT_TRUE(features
.count(base::StringPrintf(
591 features::kSafeBrowsingOriginalUrl
,
592 "http://www.good.com/")));
593 EXPECT_DOUBLE_EQ(1.0, features
[features::kSafeBrowsingIsSubresource
]);
594 EXPECT_DOUBLE_EQ(2.0, features
[features::kSafeBrowsingThreatType
]);
597 TEST_F(BrowserFeatureExtractorTest
, MalwareFeatures
) {
598 ClientMalwareRequest request
;
599 request
.set_url("http://www.foo.com/");
601 std::vector
<IPUrlInfo
> bad_urls
;
603 IPUrlInfo("http://bad.com", "GET", "", content::RESOURCE_TYPE_SCRIPT
));
605 IPUrlInfo("http://evil.com", "GET", "", content::RESOURCE_TYPE_SCRIPT
));
606 browse_info_
->ips
.insert(std::make_pair("193.5.163.8", bad_urls
));
607 browse_info_
->ips
.insert(std::make_pair("92.92.92.92", bad_urls
));
608 std::vector
<IPUrlInfo
> good_urls
;
610 IPUrlInfo("http://ok.com", "GET", "", content::RESOURCE_TYPE_SCRIPT
));
611 browse_info_
->ips
.insert(std::make_pair("23.94.78.1", good_urls
));
612 EXPECT_CALL(*db_manager_
, MatchMalwareIP("193.5.163.8"))
613 .WillOnce(Return(true));
614 EXPECT_CALL(*db_manager_
, MatchMalwareIP("92.92.92.92"))
615 .WillOnce(Return(true));
616 EXPECT_CALL(*db_manager_
, MatchMalwareIP("23.94.78.1"))
617 .WillOnce(Return(false));
619 ExtractMalwareFeatures(&request
);
620 EXPECT_EQ(4, request
.bad_ip_url_info_size());
621 std::map
<std::string
, std::set
<std::string
> > result_urls
;
622 GetMalwareUrls(request
, &result_urls
);
624 EXPECT_EQ(2U, result_urls
.size());
625 EXPECT_TRUE(result_urls
.count("193.5.163.8"));
626 std::set
<std::string
> urls
= result_urls
["193.5.163.8"];
627 EXPECT_EQ(2U, urls
.size());
628 EXPECT_TRUE(urls
.find("http://bad.com") != urls
.end());
629 EXPECT_TRUE(urls
.find("http://evil.com") != urls
.end());
630 EXPECT_TRUE(result_urls
.count("92.92.92.92"));
631 urls
= result_urls
["92.92.92.92"];
632 EXPECT_EQ(2U, urls
.size());
633 EXPECT_TRUE(urls
.find("http://bad.com") != urls
.end());
634 EXPECT_TRUE(urls
.find("http://evil.com") != urls
.end());
637 TEST_F(BrowserFeatureExtractorTest
, MalwareFeatures_ExceedLimit
) {
638 ClientMalwareRequest request
;
639 request
.set_url("http://www.foo.com/");
641 std::vector
<IPUrlInfo
> bad_urls
;
643 IPUrlInfo("http://bad.com", "GET", "", content::RESOURCE_TYPE_SCRIPT
));
644 std::vector
<std::string
> ips
;
645 for (int i
= 0; i
< 7; ++i
) { // Add 7 ips
646 std::string ip
= base::StringPrintf("%d.%d.%d.%d", i
, i
, i
, i
);
648 browse_info_
->ips
.insert(std::make_pair(ip
, bad_urls
));
650 // First ip is good but all the others are bad.
651 EXPECT_CALL(*db_manager_
, MatchMalwareIP(ip
)).WillOnce(Return(i
> 0));
654 ExtractMalwareFeatures(&request
);
655 // The number of IP matched url we store is capped at 5 IPs per request.
656 EXPECT_EQ(5, request
.bad_ip_url_info_size());
659 } // namespace safe_browsing