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 // This test uses the safebrowsing test server published at 6 // http://code.google.com/p/google-safe-browsing/ to test the safebrowsing 7 // protocol implemetation. Details of the safebrowsing testing flow is 8 // documented at 9 // http://code.google.com/p/google-safe-browsing/wiki/ProtocolTesting 10 // 11 // This test launches safebrowsing test server and issues several update 12 // requests against that server. Each update would get different data and after 13 // each update, the test will get a list of URLs from the test server to verify 14 // its repository. The test will succeed only if all updates are performed and 15 // URLs match what the server expected. 16 17 #include <vector> 18 19 #include "base/bind.h" 20 #include "base/command_line.h" 21 #include "base/environment.h" 22 #include "base/path_service.h" 23 #include "base/strings/string_number_conversions.h" 24 #include "base/strings/string_split.h" 25 #include "base/strings/stringprintf.h" 26 #include "base/strings/utf_string_conversions.h" 27 #include "base/synchronization/lock.h" 28 #include "base/test/test_timeouts.h" 29 #include "base/threading/platform_thread.h" 30 #include "base/threading/thread.h" 31 #include "base/time/time.h" 32 #include "chrome/browser/browser_process.h" 33 #include "chrome/browser/chrome_notification_types.h" 34 #include "chrome/browser/profiles/profile.h" 35 #include "chrome/browser/safe_browsing/database_manager.h" 36 #include "chrome/browser/safe_browsing/local_safebrowsing_test_server.h" 37 #include "chrome/browser/safe_browsing/protocol_manager.h" 38 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 39 #include "chrome/browser/ui/browser.h" 40 #include "chrome/common/chrome_switches.h" 41 #include "chrome/common/url_constants.h" 42 #include "chrome/test/base/in_process_browser_test.h" 43 #include "chrome/test/base/ui_test_utils.h" 44 #include "content/public/browser/browser_context.h" 45 #include "content/public/test/test_browser_thread.h" 46 #include "net/base/load_flags.h" 47 #include "net/base/net_log.h" 48 #include "net/dns/host_resolver.h" 49 #include "net/test/python_utils.h" 50 #include "net/url_request/url_fetcher.h" 51 #include "net/url_request/url_fetcher_delegate.h" 52 #include "net/url_request/url_request_status.h" 53 #include "testing/gtest/include/gtest/gtest.h" 54 55 using content::BrowserThread; 56 57 namespace { 58 59 const base::FilePath::CharType kDataFile[] = 60 FILE_PATH_LITERAL("testing_input_nomac.dat"); 61 const char kUrlVerifyPath[] = "safebrowsing/verify_urls"; 62 const char kDBVerifyPath[] = "safebrowsing/verify_database"; 63 const char kTestCompletePath[] = "test_complete"; 64 65 struct PhishingUrl { 66 std::string url; 67 std::string list_name; 68 bool is_phishing; 69 }; 70 71 // Parses server response for verify_urls. The expected format is: 72 // 73 // first.random.url.com/ internal-test-shavar yes 74 // second.random.url.com/ internal-test-shavar yes 75 // ... 76 bool ParsePhishingUrls(const std::string& data, 77 std::vector<PhishingUrl>* phishing_urls) { 78 if (data.empty()) 79 return false; 80 81 std::vector<std::string> urls; 82 base::SplitString(data, '\n', &urls); 83 for (size_t i = 0; i < urls.size(); ++i) { 84 if (urls[i].empty()) 85 continue; 86 PhishingUrl phishing_url; 87 std::vector<std::string> record_parts; 88 base::SplitString(urls[i], '\t', &record_parts); 89 if (record_parts.size() != 3) { 90 LOG(ERROR) << "Unexpected URL format in phishing URL list: " 91 << urls[i]; 92 return false; 93 } 94 phishing_url.url = std::string(url::kHttpScheme) + "://" + record_parts[0]; 95 phishing_url.list_name = record_parts[1]; 96 if (record_parts[2] == "yes") { 97 phishing_url.is_phishing = true; 98 } else if (record_parts[2] == "no") { 99 phishing_url.is_phishing = false; 100 } else { 101 LOG(ERROR) << "Unrecognized expectation in " << urls[i] 102 << ": " << record_parts[2]; 103 return false; 104 } 105 phishing_urls->push_back(phishing_url); 106 } 107 return true; 108 } 109 110 class FakeSafeBrowsingService : public SafeBrowsingService { 111 public: 112 explicit FakeSafeBrowsingService(const std::string& url_prefix) 113 : url_prefix_(url_prefix) {} 114 115 virtual SafeBrowsingProtocolConfig GetProtocolConfig() const OVERRIDE { 116 SafeBrowsingProtocolConfig config; 117 config.url_prefix = url_prefix_; 118 // Makes sure the auto update is not triggered. The tests will force the 119 // update when needed. 120 config.disable_auto_update = true; 121 #if defined(OS_ANDROID) 122 config.disable_connection_check = true; 123 #endif 124 config.client_name = "browser_tests"; 125 return config; 126 } 127 128 private: 129 virtual ~FakeSafeBrowsingService() {} 130 131 std::string url_prefix_; 132 133 DISALLOW_COPY_AND_ASSIGN(FakeSafeBrowsingService); 134 }; 135 136 // Factory that creates FakeSafeBrowsingService instances. 137 class TestSafeBrowsingServiceFactory : public SafeBrowsingServiceFactory { 138 public: 139 explicit TestSafeBrowsingServiceFactory(const std::string& url_prefix) 140 : url_prefix_(url_prefix) {} 141 142 virtual SafeBrowsingService* CreateSafeBrowsingService() OVERRIDE { 143 return new FakeSafeBrowsingService(url_prefix_); 144 } 145 146 private: 147 std::string url_prefix_; 148 }; 149 150 } // namespace 151 152 // This starts the browser and keeps status of states related to SafeBrowsing. 153 class SafeBrowsingServerTest : public InProcessBrowserTest { 154 public: 155 SafeBrowsingServerTest() 156 : safe_browsing_service_(NULL), 157 is_database_ready_(true), 158 is_update_scheduled_(false), 159 is_checked_url_in_db_(false), 160 is_checked_url_safe_(false) { 161 } 162 163 virtual ~SafeBrowsingServerTest() { 164 } 165 166 void UpdateSafeBrowsingStatus() { 167 ASSERT_TRUE(safe_browsing_service_); 168 base::AutoLock lock(update_status_mutex_); 169 last_update_ = safe_browsing_service_->protocol_manager_->last_update(); 170 is_update_scheduled_ = 171 safe_browsing_service_->protocol_manager_->update_timer_.IsRunning(); 172 } 173 174 void ForceUpdate() { 175 content::WindowedNotificationObserver observer( 176 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, 177 content::Source<SafeBrowsingDatabaseManager>(database_manager())); 178 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 179 base::Bind(&SafeBrowsingServerTest::ForceUpdateOnIOThread, 180 this)); 181 observer.Wait(); 182 } 183 184 void ForceUpdateOnIOThread() { 185 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 186 ASSERT_TRUE(safe_browsing_service_); 187 safe_browsing_service_->protocol_manager_->ForceScheduleNextUpdate( 188 base::TimeDelta::FromSeconds(0)); 189 } 190 191 void CheckIsDatabaseReady() { 192 base::AutoLock lock(update_status_mutex_); 193 is_database_ready_ = !database_manager()->database_update_in_progress_; 194 } 195 196 void CheckUrl(SafeBrowsingDatabaseManager::Client* helper, const GURL& url) { 197 ASSERT_TRUE(safe_browsing_service_); 198 base::AutoLock lock(update_status_mutex_); 199 if (database_manager()->CheckBrowseUrl(url, helper)) { 200 is_checked_url_in_db_ = false; 201 is_checked_url_safe_ = true; 202 } else { 203 // In this case, Safebrowsing service will fetch the full hash 204 // from the server and examine that. Once it is done, 205 // set_is_checked_url_safe() will be called via callback. 206 is_checked_url_in_db_ = true; 207 } 208 } 209 210 SafeBrowsingDatabaseManager* database_manager() { 211 return safe_browsing_service_->database_manager().get(); 212 } 213 214 bool is_checked_url_in_db() { 215 base::AutoLock l(update_status_mutex_); 216 return is_checked_url_in_db_; 217 } 218 219 void set_is_checked_url_safe(bool safe) { 220 base::AutoLock l(update_status_mutex_); 221 is_checked_url_safe_ = safe; 222 } 223 224 bool is_checked_url_safe() { 225 base::AutoLock l(update_status_mutex_); 226 return is_checked_url_safe_; 227 } 228 229 bool is_database_ready() { 230 base::AutoLock l(update_status_mutex_); 231 return is_database_ready_; 232 } 233 234 base::Time last_update() { 235 base::AutoLock l(update_status_mutex_); 236 return last_update_; 237 } 238 239 bool is_update_scheduled() { 240 base::AutoLock l(update_status_mutex_); 241 return is_update_scheduled_; 242 } 243 244 base::MessageLoop* SafeBrowsingMessageLoop() { 245 return database_manager()->safe_browsing_thread_->message_loop(); 246 } 247 248 const net::SpawnedTestServer& test_server() const { 249 return *test_server_; 250 } 251 252 protected: 253 bool InitSafeBrowsingService() { 254 safe_browsing_service_ = g_browser_process->safe_browsing_service(); 255 return safe_browsing_service_ != NULL; 256 } 257 258 virtual void SetUp() OVERRIDE { 259 base::FilePath datafile_path; 260 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &datafile_path)); 261 262 datafile_path = datafile_path.Append(FILE_PATH_LITERAL("third_party")) 263 .Append(FILE_PATH_LITERAL("safe_browsing")) 264 .Append(FILE_PATH_LITERAL("testing")) 265 .Append(kDataFile); 266 test_server_.reset(new LocalSafeBrowsingTestServer(datafile_path)); 267 ASSERT_TRUE(test_server_->Start()); 268 LOG(INFO) << "server is " << test_server_->host_port_pair().ToString(); 269 270 // Point to the testing server for all SafeBrowsing requests. 271 std::string url_prefix = test_server_->GetURL("safebrowsing").spec(); 272 sb_factory_.reset(new TestSafeBrowsingServiceFactory(url_prefix)); 273 SafeBrowsingService::RegisterFactory(sb_factory_.get()); 274 275 InProcessBrowserTest::SetUp(); 276 } 277 278 virtual void TearDown() OVERRIDE { 279 InProcessBrowserTest::TearDown(); 280 281 SafeBrowsingService::RegisterFactory(NULL); 282 } 283 284 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 285 // This test uses loopback. No need to use IPv6 especially it makes 286 // local requests slow on Windows trybot when ipv6 local address [::1] 287 // is not setup. 288 command_line->AppendSwitch(switches::kDisableIPv6); 289 290 // TODO(lzheng): The test server does not understand download related 291 // requests. We need to fix the server. 292 command_line->AppendSwitch(switches::kSbDisableDownloadProtection); 293 294 // TODO(gcasto): Generate new testing data that includes the 295 // client-side phishing whitelist. 296 command_line->AppendSwitch( 297 switches::kDisableClientSidePhishingDetection); 298 299 // TODO(kalman): Generate new testing data that includes the extension 300 // blacklist. 301 command_line->AppendSwitch(switches::kSbDisableExtensionBlacklist); 302 303 // TODO(tburkard): Generate new testing data that includes the side-effect 304 // free whitelist. 305 command_line->AppendSwitch(switches::kSbDisableSideEffectFreeWhitelist); 306 } 307 308 void SetTestStep(int step) { 309 std::string test_step = base::StringPrintf("test_step=%d", step); 310 safe_browsing_service_->protocol_manager_->set_additional_query(test_step); 311 } 312 313 private: 314 scoped_ptr<TestSafeBrowsingServiceFactory> sb_factory_; 315 316 SafeBrowsingService* safe_browsing_service_; 317 318 scoped_ptr<net::SpawnedTestServer> test_server_; 319 320 // Protects all variables below since they are read on UI thread 321 // but updated on IO thread or safebrowsing thread. 322 base::Lock update_status_mutex_; 323 324 // States associated with safebrowsing service updates. 325 bool is_database_ready_; 326 base::Time last_update_; 327 bool is_update_scheduled_; 328 // Indicates if there is a match between a URL's prefix and safebrowsing 329 // database (thus potentially it is a phishing URL). 330 bool is_checked_url_in_db_; 331 // True if last verified URL is not a phishing URL and thus it is safe. 332 bool is_checked_url_safe_; 333 334 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTest); 335 }; 336 337 // A ref counted helper class that handles callbacks between IO thread and UI 338 // thread. 339 class SafeBrowsingServerTestHelper 340 : public base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>, 341 public SafeBrowsingDatabaseManager::Client, 342 public net::URLFetcherDelegate { 343 public: 344 SafeBrowsingServerTestHelper(SafeBrowsingServerTest* safe_browsing_test, 345 net::URLRequestContextGetter* request_context) 346 : safe_browsing_test_(safe_browsing_test), 347 response_status_(net::URLRequestStatus::FAILED), 348 request_context_(request_context) { 349 } 350 351 // Callbacks for SafeBrowsingDatabaseManager::Client. 352 virtual void OnCheckBrowseUrlResult(const GURL& url, 353 SBThreatType threat_type, 354 const std::string& metadata) OVERRIDE { 355 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 356 EXPECT_TRUE(safe_browsing_test_->is_checked_url_in_db()); 357 safe_browsing_test_->set_is_checked_url_safe( 358 threat_type == SB_THREAT_TYPE_SAFE); 359 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 360 base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this)); 361 } 362 363 virtual void OnBlockingPageComplete(bool proceed) { 364 NOTREACHED() << "Not implemented."; 365 } 366 367 // Functions and callbacks related to CheckUrl. These are used to verify 368 // phishing URLs. 369 void CheckUrl(const GURL& url) { 370 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, 371 base::Bind(&SafeBrowsingServerTestHelper::CheckUrlOnIOThread, 372 this, url)); 373 content::RunMessageLoop(); 374 } 375 void CheckUrlOnIOThread(const GURL& url) { 376 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 377 safe_browsing_test_->CheckUrl(this, url); 378 if (!safe_browsing_test_->is_checked_url_in_db()) { 379 // Ends the checking since this URL's prefix is not in database. 380 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 381 base::Bind(&SafeBrowsingServerTestHelper::OnCheckUrlDone, this)); 382 } 383 // Otherwise, OnCheckUrlDone is called in OnUrlCheckResult since 384 // safebrowsing service further fetches hashes from safebrowsing server. 385 } 386 387 void OnCheckUrlDone() { 388 StopUILoop(); 389 } 390 391 // Updates status from IO Thread. 392 void CheckStatusOnIOThread() { 393 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); 394 safe_browsing_test_->UpdateSafeBrowsingStatus(); 395 safe_browsing_test_->SafeBrowsingMessageLoop()->PostTask(FROM_HERE, 396 base::Bind(&SafeBrowsingServerTestHelper::CheckIsDatabaseReady, this)); 397 } 398 399 // Checks status in SafeBrowsing Thread. 400 void CheckIsDatabaseReady() { 401 EXPECT_EQ(base::MessageLoop::current(), 402 safe_browsing_test_->SafeBrowsingMessageLoop()); 403 safe_browsing_test_->CheckIsDatabaseReady(); 404 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, 405 base::Bind(&SafeBrowsingServerTestHelper::OnWaitForStatusUpdateDone, 406 this)); 407 } 408 409 void OnWaitForStatusUpdateDone() { 410 StopUILoop(); 411 } 412 413 // Update safebrowsing status. 414 void UpdateStatus() { 415 BrowserThread::PostTask( 416 BrowserThread::IO, 417 FROM_HERE, 418 base::Bind(&SafeBrowsingServerTestHelper::CheckStatusOnIOThread, this)); 419 // Will continue after OnWaitForStatusUpdateDone(). 420 content::RunMessageLoop(); 421 } 422 423 // Calls test server to fetch database for verification. 424 net::URLRequestStatus::Status FetchDBToVerify( 425 const net::SpawnedTestServer& test_server, 426 int test_step) { 427 // TODO(lzheng): Remove chunk_type=add once it is not needed by the server. 428 std::string path = base::StringPrintf( 429 "%s?client=chromium&appver=1.0&pver=3.0&test_step=%d&chunk_type=add", 430 kDBVerifyPath, test_step); 431 return FetchUrl(test_server.GetURL(path)); 432 } 433 434 // Calls test server to fetch URLs for verification. 435 net::URLRequestStatus::Status FetchUrlsToVerify( 436 const net::SpawnedTestServer& test_server, 437 int test_step) { 438 std::string path = base::StringPrintf( 439 "%s?client=chromium&appver=1.0&pver=3.0&test_step=%d", 440 kUrlVerifyPath, test_step); 441 return FetchUrl(test_server.GetURL(path)); 442 } 443 444 // Calls test server to check if test data is done. E.g.: if there is a 445 // bad URL that server expects test to fetch full hash but the test didn't, 446 // this verification will fail. 447 net::URLRequestStatus::Status VerifyTestComplete( 448 const net::SpawnedTestServer& test_server, 449 int test_step) { 450 std::string path = base::StringPrintf( 451 "%s?test_step=%d", kTestCompletePath, test_step); 452 return FetchUrl(test_server.GetURL(path)); 453 } 454 455 // Callback for URLFetcher. 456 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 457 source->GetResponseAsString(&response_data_); 458 response_status_ = source->GetStatus().status(); 459 StopUILoop(); 460 } 461 462 const std::string& response_data() { 463 return response_data_; 464 } 465 466 private: 467 friend class base::RefCountedThreadSafe<SafeBrowsingServerTestHelper>; 468 virtual ~SafeBrowsingServerTestHelper() {} 469 470 // Stops UI loop after desired status is updated. 471 void StopUILoop() { 472 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI)); 473 base::MessageLoopForUI::current()->Quit(); 474 } 475 476 // Fetch a URL. If message_loop_started is true, starts the message loop 477 // so the caller could wait till OnURLFetchComplete is called. 478 net::URLRequestStatus::Status FetchUrl(const GURL& url) { 479 url_fetcher_.reset(net::URLFetcher::Create( 480 url, net::URLFetcher::GET, this)); 481 url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); 482 url_fetcher_->SetRequestContext(request_context_); 483 url_fetcher_->Start(); 484 content::RunMessageLoop(); 485 return response_status_; 486 } 487 488 base::OneShotTimer<SafeBrowsingServerTestHelper> check_update_timer_; 489 SafeBrowsingServerTest* safe_browsing_test_; 490 scoped_ptr<net::URLFetcher> url_fetcher_; 491 std::string response_data_; 492 net::URLRequestStatus::Status response_status_; 493 net::URLRequestContextGetter* request_context_; 494 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServerTestHelper); 495 }; 496 497 // TODO(shess): Disabled pending new data for third_party/safe_browsing/testing/ 498 IN_PROC_BROWSER_TEST_F(SafeBrowsingServerTest, 499 DISABLED_SafeBrowsingServerTest) { 500 LOG(INFO) << "Start test"; 501 ASSERT_TRUE(InitSafeBrowsingService()); 502 503 net::URLRequestContextGetter* request_context = 504 browser()->profile()->GetRequestContext(); 505 scoped_refptr<SafeBrowsingServerTestHelper> safe_browsing_helper( 506 new SafeBrowsingServerTestHelper(this, request_context)); 507 int last_step = 0; 508 509 // Waits and makes sure safebrowsing update is not happening. 510 // The wait will stop once OnWaitForStatusUpdateDone in 511 // safe_browsing_helper is called and status from safe_browsing_service_ 512 // is checked. 513 safe_browsing_helper->UpdateStatus(); 514 EXPECT_TRUE(is_database_ready()); 515 EXPECT_FALSE(is_update_scheduled()); 516 EXPECT_TRUE(last_update().is_null()); 517 // Starts updates. After each update, the test will fetch a list of URLs with 518 // expected results to verify with safebrowsing service. If there is no error, 519 // the test moves on to the next step to get more update chunks. 520 // This repeats till there is no update data. 521 for (int step = 1;; step++) { 522 // Every step should be a fresh start. 523 SCOPED_TRACE(base::StringPrintf("step=%d", step)); 524 EXPECT_TRUE(is_database_ready()); 525 EXPECT_FALSE(is_update_scheduled()); 526 527 // Starts safebrowsing update on IO thread. Waits till scheduled 528 // update finishes. 529 base::Time now = base::Time::Now(); 530 SetTestStep(step); 531 ForceUpdate(); 532 533 safe_browsing_helper->UpdateStatus(); 534 EXPECT_TRUE(is_database_ready()); 535 EXPECT_FALSE(is_update_scheduled()); 536 EXPECT_FALSE(last_update().is_null()); 537 if (last_update() < now) { 538 // This means no data available anymore. 539 break; 540 } 541 542 // Fetches URLs to verify and waits till server responses with data. 543 EXPECT_EQ(net::URLRequestStatus::SUCCESS, 544 safe_browsing_helper->FetchUrlsToVerify(test_server(), step)); 545 546 std::vector<PhishingUrl> phishing_urls; 547 EXPECT_TRUE(ParsePhishingUrls(safe_browsing_helper->response_data(), 548 &phishing_urls)); 549 EXPECT_GT(phishing_urls.size(), 0U); 550 for (size_t j = 0; j < phishing_urls.size(); ++j) { 551 // Verifes with server if a URL is a phishing URL and waits till server 552 // responses. 553 safe_browsing_helper->CheckUrl(GURL(phishing_urls[j].url)); 554 if (phishing_urls[j].is_phishing) { 555 EXPECT_TRUE(is_checked_url_in_db()) 556 << phishing_urls[j].url 557 << " is_phishing: " << phishing_urls[j].is_phishing 558 << " test step: " << step; 559 EXPECT_FALSE(is_checked_url_safe()) 560 << phishing_urls[j].url 561 << " is_phishing: " << phishing_urls[j].is_phishing 562 << " test step: " << step; 563 } else { 564 EXPECT_TRUE(is_checked_url_safe()) 565 << phishing_urls[j].url 566 << " is_phishing: " << phishing_urls[j].is_phishing 567 << " test step: " << step; 568 } 569 } 570 // TODO(lzheng): We should verify the fetched database with local 571 // database to make sure they match. 572 EXPECT_EQ(net::URLRequestStatus::SUCCESS, 573 safe_browsing_helper->FetchDBToVerify(test_server(), step)); 574 EXPECT_GT(safe_browsing_helper->response_data().size(), 0U); 575 last_step = step; 576 } 577 578 // Verifies with server if test is done and waits till server responses. 579 EXPECT_EQ(net::URLRequestStatus::SUCCESS, 580 safe_browsing_helper->VerifyTestComplete(test_server(), last_step)); 581 EXPECT_EQ("yes", safe_browsing_helper->response_data()); 582 } 583