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.
10 #include "base/callback.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/metrics/field_trial.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/time/time.h"
17 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
18 #include "chrome/common/safe_browsing/client_model.pb.h"
19 #include "chrome/common/safe_browsing/csd.pb.h"
20 #include "components/variations/variations_associated_data.h"
21 #include "content/public/test/test_browser_thread.h"
22 #include "crypto/sha2.h"
23 #include "net/http/http_status_code.h"
24 #include "net/url_request/test_url_fetcher_factory.h"
25 #include "net/url_request/url_request_status.h"
26 #include "testing/gmock/include/gmock/gmock.h"
27 #include "testing/gtest/include/gtest/gtest.h"
30 using ::testing::Invoke
;
31 using ::testing::Mock
;
32 using ::testing::StrictMock
;
34 using content::BrowserThread
;
36 namespace safe_browsing
{
39 class MockModelLoader
: public ModelLoader
{
41 explicit MockModelLoader(const std::string model_name
)
42 : ModelLoader(base::Closure(), model_name
) {}
43 ~MockModelLoader() override
{}
45 MOCK_METHOD1(ScheduleFetch
, void(int64
));
46 MOCK_METHOD0(CancelFetcher
, void());
49 DISALLOW_COPY_AND_ASSIGN(MockModelLoader
);
52 class MockClientSideDetectionService
: public ClientSideDetectionService
{
54 MockClientSideDetectionService() : ClientSideDetectionService(NULL
) {}
56 ~MockClientSideDetectionService() override
{}
59 DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService
);
64 class ClientSideDetectionServiceTest
: public testing::Test
{
66 void SetUp() override
{
67 file_thread_
.reset(new content::TestBrowserThread(BrowserThread::FILE,
69 factory_
.reset(new net::FakeURLFetcherFactory(NULL
));
70 browser_thread_
.reset(new content::TestBrowserThread(BrowserThread::UI
,
74 void TearDown() override
{
75 msg_loop_
.RunUntilIdle();
78 browser_thread_
.reset();
81 bool SendClientReportPhishingRequest(const GURL
& phishing_url
,
83 ClientPhishingRequest
* request
= new ClientPhishingRequest();
84 request
->set_url(phishing_url
.spec());
85 request
->set_client_score(score
);
86 request
->set_is_phishing(true); // client thinks the URL is phishing.
87 csd_service_
->SendClientReportPhishingRequest(
90 base::Bind(&ClientSideDetectionServiceTest::SendRequestDone
,
91 base::Unretained(this)));
92 phishing_url_
= phishing_url
;
93 msg_loop_
.Run(); // Waits until callback is called.
97 bool SendClientReportMalwareRequest(const GURL
& url
) {
98 scoped_ptr
<ClientMalwareRequest
> request(new ClientMalwareRequest());
99 request
->set_url(url
.spec());
100 csd_service_
->SendClientReportMalwareRequest(
102 base::Bind(&ClientSideDetectionServiceTest::SendMalwareRequestDone
,
103 base::Unretained(this)));
105 msg_loop_
.Run(); // Waits until callback is called.
109 void SetModelFetchResponses() {
110 // Set reponses for both models.
111 factory_
->SetFakeResponse(GURL(ModelLoader::kClientModelUrlPrefix
+
112 ModelLoader::FillInModelName(false, 0)),
113 "bogusmodel", net::HTTP_OK
,
114 net::URLRequestStatus::SUCCESS
);
115 factory_
->SetFakeResponse(GURL(ModelLoader::kClientModelUrlPrefix
+
116 ModelLoader::FillInModelName(true, 0)),
117 "bogusmodel", net::HTTP_OK
,
118 net::URLRequestStatus::SUCCESS
);
121 void SetClientReportPhishingResponse(std::string response_data
,
122 net::HttpStatusCode response_code
,
123 net::URLRequestStatus::Status status
) {
124 factory_
->SetFakeResponse(
125 ClientSideDetectionService::GetClientReportUrl(
126 ClientSideDetectionService::kClientReportPhishingUrl
),
127 response_data
, response_code
, status
);
130 void SetClientReportMalwareResponse(std::string response_data
,
131 net::HttpStatusCode response_code
,
132 net::URLRequestStatus::Status status
) {
133 factory_
->SetFakeResponse(
134 ClientSideDetectionService::GetClientReportUrl(
135 ClientSideDetectionService::kClientReportMalwareUrl
),
136 response_data
, response_code
, status
);
139 int GetNumReports(std::queue
<base::Time
>* report_times
) {
140 return csd_service_
->GetNumReports(report_times
);
143 std::queue
<base::Time
>& GetPhishingReportTimes() {
144 return csd_service_
->phishing_report_times_
;
147 std::queue
<base::Time
>& GetMalwareReportTimes() {
148 return csd_service_
->malware_report_times_
;
151 void SetCache(const GURL
& gurl
, bool is_phishing
, base::Time time
) {
152 csd_service_
->cache_
[gurl
] =
153 make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing
,
158 ClientSideDetectionService::PhishingCache
& cache
= csd_service_
->cache_
;
159 base::Time now
= base::Time::Now();
161 now
- base::TimeDelta::FromDays(
162 ClientSideDetectionService::kNegativeCacheIntervalDays
) +
163 base::TimeDelta::FromMinutes(5);
164 cache
[GURL("http://first.url.com/")] =
165 make_linked_ptr(new ClientSideDetectionService::CacheState(false,
169 now
- base::TimeDelta::FromDays(
170 ClientSideDetectionService::kNegativeCacheIntervalDays
) -
171 base::TimeDelta::FromHours(1);
172 cache
[GURL("http://second.url.com/")] =
173 make_linked_ptr(new ClientSideDetectionService::CacheState(false,
177 now
- base::TimeDelta::FromMinutes(
178 ClientSideDetectionService::kPositiveCacheIntervalMinutes
) -
179 base::TimeDelta::FromMinutes(5);
180 cache
[GURL("http://third.url.com/")] =
181 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time
));
184 now
- base::TimeDelta::FromMinutes(
185 ClientSideDetectionService::kPositiveCacheIntervalMinutes
) +
186 base::TimeDelta::FromMinutes(5);
187 cache
[GURL("http://fourth.url.com/")] =
188 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time
));
190 csd_service_
->UpdateCache();
192 // 3 elements should be in the cache, the first, third, and fourth.
193 EXPECT_EQ(3U, cache
.size());
194 EXPECT_TRUE(cache
.find(GURL("http://first.url.com/")) != cache
.end());
195 EXPECT_TRUE(cache
.find(GURL("http://third.url.com/")) != cache
.end());
196 EXPECT_TRUE(cache
.find(GURL("http://fourth.url.com/")) != cache
.end());
198 // While 3 elements remain, only the first and the fourth are actually
201 EXPECT_TRUE(csd_service_
->GetValidCachedResult(
202 GURL("http://first.url.com"), &is_phishing
));
203 EXPECT_FALSE(is_phishing
);
204 EXPECT_FALSE(csd_service_
->GetValidCachedResult(
205 GURL("http://third.url.com"), &is_phishing
));
206 EXPECT_TRUE(csd_service_
->GetValidCachedResult(
207 GURL("http://fourth.url.com"), &is_phishing
));
208 EXPECT_TRUE(is_phishing
);
211 void AddFeature(const std::string
& name
, double value
,
212 ClientPhishingRequest
* request
) {
213 ClientPhishingRequest_Feature
* feature
= request
->add_feature_map();
214 feature
->set_name(name
);
215 feature
->set_value(value
);
218 void AddNonModelFeature(const std::string
& name
, double value
,
219 ClientPhishingRequest
* request
) {
220 ClientPhishingRequest_Feature
* feature
=
221 request
->add_non_model_feature_map();
222 feature
->set_name(name
);
223 feature
->set_value(value
);
226 void CheckConfirmedMalwareUrl(GURL url
) {
227 ASSERT_EQ(confirmed_malware_url_
, url
);
231 scoped_ptr
<ClientSideDetectionService
> csd_service_
;
232 scoped_ptr
<net::FakeURLFetcherFactory
> factory_
;
233 base::MessageLoop msg_loop_
;
236 void SendRequestDone(GURL phishing_url
, bool is_phishing
) {
237 ASSERT_EQ(phishing_url
, phishing_url_
);
238 is_phishing_
= is_phishing
;
242 void SendMalwareRequestDone(GURL original_url
, GURL malware_url
,
244 ASSERT_EQ(phishing_url_
, original_url
);
245 confirmed_malware_url_
= malware_url
;
246 is_malware_
= is_malware
;
250 scoped_ptr
<content::TestBrowserThread
> browser_thread_
;
251 scoped_ptr
<content::TestBrowserThread
> file_thread_
;
252 scoped_ptr
<base::FieldTrialList
> field_trials_
;
255 GURL confirmed_malware_url_
;
261 TEST_F(ClientSideDetectionServiceTest
, ServiceObjectDeletedBeforeCallbackDone
) {
262 SetModelFetchResponses();
263 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
264 csd_service_
->SetEnabledAndRefreshState(true);
265 EXPECT_TRUE(csd_service_
.get() != NULL
);
266 // We delete the client-side detection service class even though the callbacks
268 csd_service_
.reset();
269 // Waiting for the callbacks to run should not crash even if the service
271 msg_loop_
.RunUntilIdle();
274 TEST_F(ClientSideDetectionServiceTest
, SendClientReportPhishingRequest
) {
275 SetModelFetchResponses();
276 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
277 csd_service_
->SetEnabledAndRefreshState(true);
279 GURL
url("http://a.com/");
280 float score
= 0.4f
; // Some random client score.
282 base::Time before
= base::Time::Now();
284 // Invalid response body from the server.
285 SetClientReportPhishingResponse("invalid proto response", net::HTTP_OK
,
286 net::URLRequestStatus::SUCCESS
);
287 EXPECT_FALSE(SendClientReportPhishingRequest(url
, score
));
290 ClientPhishingResponse response
;
291 response
.set_phishy(true);
292 SetClientReportPhishingResponse(response
.SerializeAsString(), net::HTTP_OK
,
293 net::URLRequestStatus::SUCCESS
);
294 EXPECT_TRUE(SendClientReportPhishingRequest(url
, score
));
296 // This request will fail
297 GURL
second_url("http://b.com/");
298 response
.set_phishy(false);
299 SetClientReportPhishingResponse(response
.SerializeAsString(),
300 net::HTTP_INTERNAL_SERVER_ERROR
,
301 net::URLRequestStatus::FAILED
);
302 EXPECT_FALSE(SendClientReportPhishingRequest(second_url
, score
));
304 base::Time after
= base::Time::Now();
306 // Check that we have recorded all 3 requests within the correct time range.
307 std::queue
<base::Time
>& report_times
= GetPhishingReportTimes();
308 EXPECT_EQ(3U, report_times
.size());
309 while (!report_times
.empty()) {
310 base::Time time
= report_times
.back();
312 EXPECT_LE(before
, time
);
313 EXPECT_GE(after
, time
);
316 // Only the first url should be in the cache.
318 EXPECT_TRUE(csd_service_
->IsInCache(url
));
319 EXPECT_TRUE(csd_service_
->GetValidCachedResult(url
, &is_phishing
));
320 EXPECT_TRUE(is_phishing
);
321 EXPECT_FALSE(csd_service_
->IsInCache(second_url
));
324 TEST_F(ClientSideDetectionServiceTest
, SendClientReportMalwareRequest
) {
325 SetModelFetchResponses();
326 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
327 csd_service_
->SetEnabledAndRefreshState(true);
328 GURL
url("http://a.com/");
330 base::Time before
= base::Time::Now();
331 // Invalid response body from the server.
332 SetClientReportMalwareResponse("invalid proto response", net::HTTP_OK
,
333 net::URLRequestStatus::SUCCESS
);
334 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
337 ClientMalwareResponse response
;
338 response
.set_blacklist(true);
339 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
340 net::URLRequestStatus::SUCCESS
);
341 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
344 response
.set_blacklist(true);
345 response
.set_bad_url("http://response-bad.com/");
346 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
347 net::URLRequestStatus::SUCCESS
);
348 EXPECT_TRUE(SendClientReportMalwareRequest(url
));
349 CheckConfirmedMalwareUrl(GURL("http://response-bad.com/"));
351 // This request will fail
352 response
.set_blacklist(false);
353 SetClientReportMalwareResponse(response
.SerializeAsString(),
354 net::HTTP_INTERNAL_SERVER_ERROR
,
355 net::URLRequestStatus::FAILED
);
356 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
358 // Server blacklist decision is false, and response is successful
359 response
.set_blacklist(false);
360 SetClientReportMalwareResponse(response
.SerializeAsString(), net::HTTP_OK
,
361 net::URLRequestStatus::SUCCESS
);
362 EXPECT_FALSE(SendClientReportMalwareRequest(url
));
364 // Check that we have recorded all 5 requests within the correct time range.
365 base::Time after
= base::Time::Now();
366 std::queue
<base::Time
>& report_times
= GetMalwareReportTimes();
367 EXPECT_EQ(5U, report_times
.size());
369 // Check that the malware report limit was reached.
370 EXPECT_TRUE(csd_service_
->OverMalwareReportLimit());
372 report_times
= GetMalwareReportTimes();
373 EXPECT_EQ(5U, report_times
.size());
374 while (!report_times
.empty()) {
375 base::Time time
= report_times
.back();
377 EXPECT_LE(before
, time
);
378 EXPECT_GE(after
, time
);
382 TEST_F(ClientSideDetectionServiceTest
, GetNumReportTest
) {
383 SetModelFetchResponses();
384 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
386 std::queue
<base::Time
>& report_times
= GetPhishingReportTimes();
387 base::Time now
= base::Time::Now();
388 base::TimeDelta twenty_five_hours
= base::TimeDelta::FromHours(25);
389 report_times
.push(now
- twenty_five_hours
);
390 report_times
.push(now
- twenty_five_hours
);
391 report_times
.push(now
);
392 report_times
.push(now
);
394 EXPECT_EQ(2, GetNumReports(&report_times
));
397 TEST_F(ClientSideDetectionServiceTest
, CacheTest
) {
398 SetModelFetchResponses();
399 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
404 TEST_F(ClientSideDetectionServiceTest
, IsPrivateIPAddress
) {
405 SetModelFetchResponses();
406 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
408 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("10.1.2.3"));
409 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("127.0.0.1"));
410 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("172.24.3.4"));
411 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("192.168.1.1"));
412 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fc00::"));
413 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fec0::"));
414 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("fec0:1:2::3"));
415 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("::1"));
417 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("1.2.3.4"));
418 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("200.1.1.1"));
419 EXPECT_FALSE(csd_service_
->IsPrivateIPAddress("2001:0db8:ac10:fe01::"));
421 // If the address can't be parsed, the default is true.
422 EXPECT_TRUE(csd_service_
->IsPrivateIPAddress("blah"));
425 TEST_F(ClientSideDetectionServiceTest
, SetEnabledAndRefreshState
) {
426 // Check that the model isn't downloaded until the service is enabled.
427 csd_service_
.reset(ClientSideDetectionService::Create(NULL
));
428 EXPECT_FALSE(csd_service_
->enabled());
429 EXPECT_TRUE(csd_service_
->model_loader_standard_
->fetcher_
.get() == NULL
);
431 // Use a MockClientSideDetectionService for the rest of the test, to avoid
432 // the scheduling delay.
433 MockClientSideDetectionService
* service
=
434 new StrictMock
<MockClientSideDetectionService
>();
435 // Inject mock loaders.
436 MockModelLoader
* loader_1
= new StrictMock
<MockModelLoader
>("model1");
437 MockModelLoader
* loader_2
= new StrictMock
<MockModelLoader
>("model2");
438 service
->model_loader_standard_
.reset(loader_1
);
439 service
->model_loader_extended_
.reset(loader_2
);
440 csd_service_
.reset(service
);
442 EXPECT_FALSE(csd_service_
->enabled());
443 // No calls expected yet.
444 Mock::VerifyAndClearExpectations(service
);
445 Mock::VerifyAndClearExpectations(loader_1
);
446 Mock::VerifyAndClearExpectations(loader_2
);
448 // Check that initial ScheduleFetch() calls are made.
449 EXPECT_CALL(*loader_1
,
451 ClientSideDetectionService::kInitialClientModelFetchDelayMs
));
452 EXPECT_CALL(*loader_2
,
454 ClientSideDetectionService::kInitialClientModelFetchDelayMs
));
455 csd_service_
->SetEnabledAndRefreshState(true);
456 msg_loop_
.RunUntilIdle();
457 Mock::VerifyAndClearExpectations(service
);
458 Mock::VerifyAndClearExpectations(loader_1
);
459 Mock::VerifyAndClearExpectations(loader_2
);
461 // Check that enabling again doesn't request the model.
462 csd_service_
->SetEnabledAndRefreshState(true);
463 // No calls expected.
464 msg_loop_
.RunUntilIdle();
465 Mock::VerifyAndClearExpectations(service
);
466 Mock::VerifyAndClearExpectations(loader_1
);
467 Mock::VerifyAndClearExpectations(loader_2
);
469 // Check that disabling the service cancels pending requests.
470 EXPECT_CALL(*loader_1
, CancelFetcher());
471 EXPECT_CALL(*loader_2
, CancelFetcher());
472 csd_service_
->SetEnabledAndRefreshState(false);
473 msg_loop_
.RunUntilIdle();
474 Mock::VerifyAndClearExpectations(service
);
475 Mock::VerifyAndClearExpectations(loader_1
);
476 Mock::VerifyAndClearExpectations(loader_2
);
478 // Check that disabling again doesn't request the model.
479 csd_service_
->SetEnabledAndRefreshState(false);
480 // No calls expected.
481 msg_loop_
.RunUntilIdle();
482 Mock::VerifyAndClearExpectations(service
);
483 Mock::VerifyAndClearExpectations(loader_1
);
484 Mock::VerifyAndClearExpectations(loader_2
);
486 } // namespace safe_browsing