// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <map> #include <string> #include "base/callback.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/file_util_proxy.h" #include "base/logging.h" #include "base/message_loop.h" #include "base/platform_file.h" #include "base/scoped_ptr.h" #include "base/scoped_temp_dir.h" #include "base/task.h" #include "testing/gtest/include/gtest/gtest.h" #include "chrome/browser/browser_thread.h" #include "chrome/browser/safe_browsing/client_side_detection_service.h" #include "chrome/browser/safe_browsing/csd.pb.h" #include "chrome/common/net/test_url_fetcher_factory.h" #include "chrome/common/net/url_fetcher.h" #include "googleurl/src/gurl.h" #include "net/url_request/url_request_status.h" #include "third_party/skia/include/core/SkBitmap.h" namespace safe_browsing { class ClientSideDetectionServiceTest : public testing::Test { protected: virtual void SetUp() { file_thread_.reset(new BrowserThread(BrowserThread::FILE, &msg_loop_)); factory_.reset(new FakeURLFetcherFactory()); URLFetcher::set_factory(factory_.get()); browser_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_)); } virtual void TearDown() { msg_loop_.RunAllPending(); csd_service_.reset(); URLFetcher::set_factory(NULL); file_thread_.reset(); browser_thread_.reset(); } base::PlatformFile GetModelFile() { model_file_ = base::kInvalidPlatformFileValue; csd_service_->GetModelFile(NewCallback( this, &ClientSideDetectionServiceTest::GetModelFileDone)); // This method will block this thread until GetModelFileDone is called. msg_loop_.Run(); return model_file_; } std::string ReadModelFile(base::PlatformFile model_file) { char buf[1024]; int n = base::ReadPlatformFile(model_file, 0, buf, 1024); EXPECT_LE(0, n); return (n < 0) ? "" : std::string(buf, n); } bool SendClientReportPhishingRequest(const GURL& phishing_url, double score, SkBitmap thumbnail) { csd_service_->SendClientReportPhishingRequest( phishing_url, score, thumbnail, NewCallback(this, &ClientSideDetectionServiceTest::SendRequestDone)); phishing_url_ = phishing_url; msg_loop_.Run(); // Waits until callback is called. return is_phishing_; } void SetModelFetchResponse(std::string response_data, bool success) { factory_->SetFakeResponse(ClientSideDetectionService::kClientModelUrl, response_data, success); } void SetClientReportPhishingResponse(std::string response_data, bool success) { factory_->SetFakeResponse( ClientSideDetectionService::kClientReportPhishingUrl, response_data, success); } protected: scoped_ptr<ClientSideDetectionService> csd_service_; scoped_ptr<FakeURLFetcherFactory> factory_; MessageLoop msg_loop_; private: void GetModelFileDone(base::PlatformFile model_file) { model_file_ = model_file; msg_loop_.Quit(); } void SendRequestDone(GURL phishing_url, bool is_phishing) { ASSERT_EQ(phishing_url, phishing_url_); is_phishing_ = is_phishing; msg_loop_.Quit(); } scoped_ptr<BrowserThread> browser_thread_; base::PlatformFile model_file_; scoped_ptr<BrowserThread> file_thread_; GURL phishing_url_; bool is_phishing_; }; TEST_F(ClientSideDetectionServiceTest, TestFetchingModel) { ScopedTempDir tmp_dir; ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); FilePath model_path = tmp_dir.path().AppendASCII("model"); // The first time we create the csd service the model file does not exist so // we expect there to be a fetch. SetModelFetchResponse("BOGUS MODEL", true); csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); base::PlatformFile model_file = GetModelFile(); EXPECT_NE(model_file, base::kInvalidPlatformFileValue); EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL"); // If you call GetModelFile() multiple times you always get the same platform // file back. We don't re-open the file. EXPECT_EQ(GetModelFile(), model_file); // The second time the model already exists on disk. In this case there // should not be any fetch. To ensure that we clear the factory. factory_->ClearFakeReponses(); csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); model_file = GetModelFile(); EXPECT_NE(model_file, base::kInvalidPlatformFileValue); EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL"); // If the model does not exist and the fetch fails we should get an error. model_path = tmp_dir.path().AppendASCII("another_model"); SetModelFetchResponse("", false /* success */); csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); EXPECT_EQ(GetModelFile(), base::kInvalidPlatformFileValue); } TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) { SetModelFetchResponse("bogus model", true /* success */); ScopedTempDir tmp_dir; ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); csd_service_.reset(ClientSideDetectionService::Create( tmp_dir.path().AppendASCII("model"), NULL)); EXPECT_TRUE(csd_service_.get() != NULL); // We delete the client-side detection service class even though the callbacks // haven't run yet. csd_service_.reset(); // Waiting for the callbacks to run should not crash even if the service // object is gone. msg_loop_.RunAllPending(); } TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) { SetModelFetchResponse("bogus model", true /* success */); ScopedTempDir tmp_dir; ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); csd_service_.reset(ClientSideDetectionService::Create( tmp_dir.path().AppendASCII("model"), NULL)); // Invalid thumbnail. SkBitmap thumbnail; GURL url("http://a.com/"); double score = 0.4; // Some random client score. EXPECT_FALSE(SendClientReportPhishingRequest(url, score, thumbnail)); // Valid thumbnail but the server returns an error. thumbnail.setConfig(SkBitmap::kARGB_8888_Config, 100, 100); ASSERT_TRUE(thumbnail.allocPixels()); thumbnail.eraseRGB(255, 0, 0); SetClientReportPhishingResponse("", false /* fail */); EXPECT_FALSE(SendClientReportPhishingRequest(url, score, thumbnail)); // Invalid response body from the server. SetClientReportPhishingResponse("invalid proto response", true /* success */); EXPECT_FALSE(SendClientReportPhishingRequest(url, score, thumbnail)); // Normal behavior. ClientPhishingResponse response; response.set_phishy(true); SetClientReportPhishingResponse(response.SerializeAsString(), true /* success */); EXPECT_TRUE(SendClientReportPhishingRequest(url, score, thumbnail)); response.set_phishy(false); SetClientReportPhishingResponse(response.SerializeAsString(), true /* success */); EXPECT_FALSE(SendClientReportPhishingRequest(url, score, thumbnail)); } } // namespace safe_browsing