1 // Copyright (c) 2011 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 <map> 6 #include <queue> 7 #include <string> 8 9 #include "base/callback.h" 10 #include "base/file_path.h" 11 #include "base/file_util.h" 12 #include "base/file_util_proxy.h" 13 #include "base/logging.h" 14 #include "base/memory/scoped_ptr.h" 15 #include "base/memory/scoped_temp_dir.h" 16 #include "base/message_loop.h" 17 #include "base/platform_file.h" 18 #include "base/task.h" 19 #include "base/time.h" 20 #include "chrome/browser/safe_browsing/client_side_detection_service.h" 21 #include "chrome/common/net/test_url_fetcher_factory.h" 22 #include "chrome/common/net/url_fetcher.h" 23 #include "chrome/common/safe_browsing/csd.pb.h" 24 #include "content/browser/browser_thread.h" 25 #include "googleurl/src/gurl.h" 26 #include "net/url_request/url_request_status.h" 27 #include "testing/gtest/include/gtest/gtest.h" 28 29 namespace safe_browsing { 30 31 class ClientSideDetectionServiceTest : public testing::Test { 32 protected: 33 virtual void SetUp() { 34 file_thread_.reset(new BrowserThread(BrowserThread::FILE, &msg_loop_)); 35 36 factory_.reset(new FakeURLFetcherFactory()); 37 URLFetcher::set_factory(factory_.get()); 38 39 browser_thread_.reset(new BrowserThread(BrowserThread::UI, &msg_loop_)); 40 } 41 42 virtual void TearDown() { 43 msg_loop_.RunAllPending(); 44 csd_service_.reset(); 45 URLFetcher::set_factory(NULL); 46 file_thread_.reset(); 47 browser_thread_.reset(); 48 } 49 50 base::PlatformFile GetModelFile() { 51 model_file_ = base::kInvalidPlatformFileValue; 52 csd_service_->GetModelFile(NewCallback( 53 this, &ClientSideDetectionServiceTest::GetModelFileDone)); 54 // This method will block this thread until GetModelFileDone is called. 55 msg_loop_.Run(); 56 return model_file_; 57 } 58 59 std::string ReadModelFile(base::PlatformFile model_file) { 60 char buf[1024]; 61 int n = base::ReadPlatformFile(model_file, 0, buf, 1024); 62 EXPECT_LE(0, n); 63 return (n < 0) ? "" : std::string(buf, n); 64 } 65 66 bool SendClientReportPhishingRequest(const GURL& phishing_url, 67 float score) { 68 ClientPhishingRequest* request = new ClientPhishingRequest(); 69 request->set_url(phishing_url.spec()); 70 request->set_client_score(score); 71 request->set_is_phishing(true); // client thinks the URL is phishing. 72 csd_service_->SendClientReportPhishingRequest( 73 request, 74 NewCallback(this, &ClientSideDetectionServiceTest::SendRequestDone)); 75 phishing_url_ = phishing_url; 76 msg_loop_.Run(); // Waits until callback is called. 77 return is_phishing_; 78 } 79 80 void SetModelFetchResponse(std::string response_data, bool success) { 81 factory_->SetFakeResponse(ClientSideDetectionService::kClientModelUrl, 82 response_data, success); 83 } 84 85 void SetClientReportPhishingResponse(std::string response_data, 86 bool success) { 87 factory_->SetFakeResponse( 88 ClientSideDetectionService::kClientReportPhishingUrl, 89 response_data, success); 90 } 91 92 int GetNumReports() { 93 return csd_service_->GetNumReports(); 94 } 95 96 std::queue<base::Time>& GetPhishingReportTimes() { 97 return csd_service_->phishing_report_times_; 98 } 99 100 void SetCache(const GURL& gurl, bool is_phishing, base::Time time) { 101 csd_service_->cache_[gurl] = 102 make_linked_ptr(new ClientSideDetectionService::CacheState(is_phishing, 103 time)); 104 } 105 106 void TestCache() { 107 ClientSideDetectionService::PhishingCache& cache = csd_service_->cache_; 108 base::Time now = base::Time::Now(); 109 base::Time time = now - ClientSideDetectionService::kNegativeCacheInterval + 110 base::TimeDelta::FromMinutes(5); 111 cache[GURL("http://first.url.com/")] = 112 make_linked_ptr(new ClientSideDetectionService::CacheState(false, 113 time)); 114 115 time = now - ClientSideDetectionService::kNegativeCacheInterval - 116 base::TimeDelta::FromHours(1); 117 cache[GURL("http://second.url.com/")] = 118 make_linked_ptr(new ClientSideDetectionService::CacheState(false, 119 time)); 120 121 time = now - ClientSideDetectionService::kPositiveCacheInterval - 122 base::TimeDelta::FromMinutes(5); 123 cache[GURL("http://third.url.com/")] = 124 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time)); 125 126 time = now - ClientSideDetectionService::kPositiveCacheInterval + 127 base::TimeDelta::FromMinutes(5); 128 cache[GURL("http://fourth.url.com/")] = 129 make_linked_ptr(new ClientSideDetectionService::CacheState(true, time)); 130 131 csd_service_->UpdateCache(); 132 133 // 3 elements should be in the cache, the first, third, and fourth. 134 EXPECT_EQ(3U, cache.size()); 135 EXPECT_TRUE(cache.find(GURL("http://first.url.com/")) != cache.end()); 136 EXPECT_TRUE(cache.find(GURL("http://third.url.com/")) != cache.end()); 137 EXPECT_TRUE(cache.find(GURL("http://fourth.url.com/")) != cache.end()); 138 139 // While 3 elements remain, only the first and the fourth are actually 140 // valid. 141 bool is_phishing; 142 EXPECT_TRUE(csd_service_->GetValidCachedResult( 143 GURL("http://first.url.com"), &is_phishing)); 144 EXPECT_FALSE(is_phishing); 145 EXPECT_FALSE(csd_service_->GetValidCachedResult( 146 GURL("http://third.url.com"), &is_phishing)); 147 EXPECT_TRUE(csd_service_->GetValidCachedResult( 148 GURL("http://fourth.url.com"), &is_phishing)); 149 EXPECT_TRUE(is_phishing); 150 } 151 152 protected: 153 scoped_ptr<ClientSideDetectionService> csd_service_; 154 scoped_ptr<FakeURLFetcherFactory> factory_; 155 MessageLoop msg_loop_; 156 157 private: 158 void GetModelFileDone(base::PlatformFile model_file) { 159 model_file_ = model_file; 160 msg_loop_.Quit(); 161 } 162 163 void SendRequestDone(GURL phishing_url, bool is_phishing) { 164 ASSERT_EQ(phishing_url, phishing_url_); 165 is_phishing_ = is_phishing; 166 msg_loop_.Quit(); 167 } 168 169 scoped_ptr<BrowserThread> browser_thread_; 170 base::PlatformFile model_file_; 171 scoped_ptr<BrowserThread> file_thread_; 172 173 GURL phishing_url_; 174 bool is_phishing_; 175 }; 176 177 TEST_F(ClientSideDetectionServiceTest, TestFetchingModel) { 178 ScopedTempDir tmp_dir; 179 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 180 FilePath model_path = tmp_dir.path().AppendASCII("model"); 181 182 // The first time we create the csd service the model file does not exist so 183 // we expect there to be a fetch. 184 SetModelFetchResponse("BOGUS MODEL", true); 185 csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); 186 base::PlatformFile model_file = GetModelFile(); 187 EXPECT_NE(model_file, base::kInvalidPlatformFileValue); 188 EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL"); 189 190 // If you call GetModelFile() multiple times you always get the same platform 191 // file back. We don't re-open the file. 192 EXPECT_EQ(GetModelFile(), model_file); 193 194 // The second time the model already exists on disk. In this case there 195 // should not be any fetch. To ensure that we clear the factory. 196 factory_->ClearFakeReponses(); 197 csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); 198 model_file = GetModelFile(); 199 EXPECT_NE(model_file, base::kInvalidPlatformFileValue); 200 EXPECT_EQ(ReadModelFile(model_file), "BOGUS MODEL"); 201 202 // If the model does not exist and the fetch fails we should get an error. 203 model_path = tmp_dir.path().AppendASCII("another_model"); 204 SetModelFetchResponse("", false /* success */); 205 csd_service_.reset(ClientSideDetectionService::Create(model_path, NULL)); 206 EXPECT_EQ(GetModelFile(), base::kInvalidPlatformFileValue); 207 } 208 209 TEST_F(ClientSideDetectionServiceTest, ServiceObjectDeletedBeforeCallbackDone) { 210 SetModelFetchResponse("bogus model", true /* success */); 211 ScopedTempDir tmp_dir; 212 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 213 csd_service_.reset(ClientSideDetectionService::Create( 214 tmp_dir.path().AppendASCII("model"), NULL)); 215 EXPECT_TRUE(csd_service_.get() != NULL); 216 // We delete the client-side detection service class even though the callbacks 217 // haven't run yet. 218 csd_service_.reset(); 219 // Waiting for the callbacks to run should not crash even if the service 220 // object is gone. 221 msg_loop_.RunAllPending(); 222 } 223 224 TEST_F(ClientSideDetectionServiceTest, SendClientReportPhishingRequest) { 225 SetModelFetchResponse("bogus model", true /* success */); 226 ScopedTempDir tmp_dir; 227 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 228 csd_service_.reset(ClientSideDetectionService::Create( 229 tmp_dir.path().AppendASCII("model"), NULL)); 230 231 GURL url("http://a.com/"); 232 float score = 0.4f; // Some random client score. 233 234 base::Time before = base::Time::Now(); 235 236 // Invalid response body from the server. 237 SetClientReportPhishingResponse("invalid proto response", true /* success */); 238 EXPECT_FALSE(SendClientReportPhishingRequest(url, score)); 239 240 // Normal behavior. 241 ClientPhishingResponse response; 242 response.set_phishy(true); 243 SetClientReportPhishingResponse(response.SerializeAsString(), 244 true /* success */); 245 EXPECT_TRUE(SendClientReportPhishingRequest(url, score)); 246 247 // This request will fail 248 GURL second_url("http://b.com/"); 249 response.set_phishy(false); 250 SetClientReportPhishingResponse(response.SerializeAsString(), 251 false /* success*/); 252 EXPECT_FALSE(SendClientReportPhishingRequest(second_url, score)); 253 254 base::Time after = base::Time::Now(); 255 256 // Check that we have recorded all 3 requests within the correct time range. 257 std::queue<base::Time>& report_times = GetPhishingReportTimes(); 258 EXPECT_EQ(3U, report_times.size()); 259 while (!report_times.empty()) { 260 base::Time time = report_times.back(); 261 report_times.pop(); 262 EXPECT_LE(before, time); 263 EXPECT_GE(after, time); 264 } 265 266 // Only the first url should be in the cache. 267 bool is_phishing; 268 EXPECT_TRUE(csd_service_->IsInCache(url)); 269 EXPECT_TRUE(csd_service_->GetValidCachedResult(url, &is_phishing)); 270 EXPECT_TRUE(is_phishing); 271 EXPECT_FALSE(csd_service_->IsInCache(second_url)); 272 } 273 274 TEST_F(ClientSideDetectionServiceTest, GetNumReportTest) { 275 SetModelFetchResponse("bogus model", true /* success */); 276 ScopedTempDir tmp_dir; 277 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 278 csd_service_.reset(ClientSideDetectionService::Create( 279 tmp_dir.path().AppendASCII("model"), NULL)); 280 281 std::queue<base::Time>& report_times = GetPhishingReportTimes(); 282 base::Time now = base::Time::Now(); 283 base::TimeDelta twenty_five_hours = base::TimeDelta::FromHours(25); 284 report_times.push(now - twenty_five_hours); 285 report_times.push(now - twenty_five_hours); 286 report_times.push(now); 287 report_times.push(now); 288 289 EXPECT_EQ(2, GetNumReports()); 290 } 291 292 TEST_F(ClientSideDetectionServiceTest, CacheTest) { 293 SetModelFetchResponse("bogus model", true /* success */); 294 ScopedTempDir tmp_dir; 295 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 296 csd_service_.reset(ClientSideDetectionService::Create( 297 tmp_dir.path().AppendASCII("model"), NULL)); 298 299 TestCache(); 300 } 301 302 TEST_F(ClientSideDetectionServiceTest, IsPrivateIPAddress) { 303 SetModelFetchResponse("bogus model", true /* success */); 304 ScopedTempDir tmp_dir; 305 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); 306 csd_service_.reset(ClientSideDetectionService::Create( 307 tmp_dir.path().AppendASCII("model"), NULL)); 308 309 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("10.1.2.3")); 310 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("127.0.0.1")); 311 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("172.24.3.4")); 312 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("192.168.1.1")); 313 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fc00::")); 314 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0::")); 315 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("fec0:1:2::3")); 316 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("::1")); 317 318 EXPECT_FALSE(csd_service_->IsPrivateIPAddress("1.2.3.4")); 319 EXPECT_FALSE(csd_service_->IsPrivateIPAddress("200.1.1.1")); 320 EXPECT_FALSE(csd_service_->IsPrivateIPAddress("2001:0db8:ac10:fe01::")); 321 322 // If the address can't be parsed, the default is true. 323 EXPECT_TRUE(csd_service_->IsPrivateIPAddress("blah")); 324 } 325 326 } // namespace safe_browsing 327