OLD | NEW |
(Empty) | |
| 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 "base/command_line.h" |
| 6 #include "base/file_path.h" |
| 7 #include "base/ref_counted.h" |
| 8 #include "base/scoped_ptr.h" |
| 9 #include "base/scoped_temp_dir.h" |
| 10 #include "base/task.h" |
| 11 #include "chrome/browser/browser_thread.h" |
| 12 #include "chrome/browser/renderer_host/test/test_render_view_host.h" |
| 13 #include "chrome/browser/safe_browsing/client_side_detection_host.h" |
| 14 #include "chrome/browser/safe_browsing/client_side_detection_service.h" |
| 15 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
| 16 #include "chrome/browser/tab_contents/test_tab_contents.h" |
| 17 #include "chrome/common/chrome_switches.h" |
| 18 #include "chrome/test/ui_test_utils.h" |
| 19 #include "googleurl/src/gurl.h" |
| 20 #include "testing/gmock/include/gmock/gmock.h" |
| 21 #include "testing/gmock/include/gmock/gmock-spec-builders.h" |
| 22 |
| 23 using ::testing::_; |
| 24 using ::testing::DoAll; |
| 25 using ::testing::Mock; |
| 26 using ::testing::SaveArg; |
| 27 |
| 28 namespace safe_browsing { |
| 29 |
| 30 class MockClientSideDetectionService : public ClientSideDetectionService { |
| 31 public: |
| 32 MockClientSideDetectionService(const FilePath& model_path) |
| 33 : ClientSideDetectionService(model_path, NULL) {} |
| 34 virtual ~MockClientSideDetectionService() {}; |
| 35 |
| 36 MOCK_METHOD3(SendClientReportPhishingRequest, |
| 37 void(const GURL&, double, ClientReportPhishingRequestCallback*)); |
| 38 |
| 39 private: |
| 40 DISALLOW_COPY_AND_ASSIGN(MockClientSideDetectionService); |
| 41 }; |
| 42 |
| 43 class MockSafeBrowsingService : public SafeBrowsingService { |
| 44 public: |
| 45 MockSafeBrowsingService() {} |
| 46 virtual ~MockSafeBrowsingService() {} |
| 47 |
| 48 MOCK_METHOD8(DisplayBlockingPage, |
| 49 void(const GURL&, const GURL&, const std::vector<GURL>&, |
| 50 ResourceType::Type, UrlCheckResult, Client*, int, int)); |
| 51 |
| 52 // Helper function which calls OnBlockingPageComplete for this client |
| 53 // object. |
| 54 void InvokeOnBlockingPageComplete(SafeBrowsingService::Client* client) { |
| 55 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 56 DCHECK(client); |
| 57 // Note: this will delete the client object in the case of the CsdClient |
| 58 // implementation. |
| 59 client->OnBlockingPageComplete(false); |
| 60 } |
| 61 |
| 62 private: |
| 63 DISALLOW_COPY_AND_ASSIGN(MockSafeBrowsingService); |
| 64 }; |
| 65 |
| 66 // Helper function which quits the UI message loop from the IO message loop. |
| 67 void QuitUIMessageLoop() { |
| 68 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| 69 BrowserThread::PostTask(BrowserThread::UI, |
| 70 FROM_HERE, |
| 71 new MessageLoop::QuitTask()); |
| 72 } |
| 73 |
| 74 class ClientSideDetectionHostTest : public RenderViewHostTestHarness { |
| 75 public: |
| 76 virtual void SetUp() { |
| 77 RenderViewHostTestHarness::SetUp(); |
| 78 ui_thread_.reset(new BrowserThread(BrowserThread::UI, &message_loop_)); |
| 79 // Note: we're starting a real IO thread to make sure our DCHECKs that |
| 80 // verify which thread is running are actually tested. |
| 81 io_thread_.reset(new BrowserThread(BrowserThread::IO)); |
| 82 ASSERT_TRUE(io_thread_->Start()); |
| 83 |
| 84 // Inject service classes. |
| 85 ScopedTempDir tmp_dir; |
| 86 ASSERT_TRUE(tmp_dir.CreateUniqueTempDir()); |
| 87 FilePath model_path = tmp_dir.path().AppendASCII("model"); |
| 88 |
| 89 csd_service_.reset(new MockClientSideDetectionService(model_path)); |
| 90 sb_service_ = new MockSafeBrowsingService(); |
| 91 csd_host_ = contents()->safebrowsing_detection_host(); |
| 92 csd_host_->set_client_side_detection_service(csd_service_.get()); |
| 93 csd_host_->set_safe_browsing_service(sb_service_.get()); |
| 94 } |
| 95 |
| 96 virtual void TearDown() { |
| 97 io_thread_.reset(); |
| 98 ui_thread_.reset(); |
| 99 RenderViewHostTestHarness::TearDown(); |
| 100 } |
| 101 |
| 102 void OnDetectedPhishingSite(const GURL& phishing_url, double phishing_score) { |
| 103 csd_host_->OnDetectedPhishingSite(phishing_url, phishing_score); |
| 104 } |
| 105 |
| 106 void FlushIOMessageLoop() { |
| 107 // If there was a message posted on the IO thread to display the |
| 108 // interstitial page we know that it would have been posted before |
| 109 // we put the quit message there. |
| 110 BrowserThread::PostTask(BrowserThread::IO, |
| 111 FROM_HERE, |
| 112 NewRunnableFunction(&QuitUIMessageLoop)); |
| 113 MessageLoop::current()->Run(); |
| 114 } |
| 115 |
| 116 protected: |
| 117 ClientSideDetectionHost* csd_host_; |
| 118 scoped_ptr<MockClientSideDetectionService> csd_service_; |
| 119 scoped_refptr<MockSafeBrowsingService> sb_service_; |
| 120 |
| 121 private: |
| 122 scoped_ptr<BrowserThread> ui_thread_; |
| 123 scoped_ptr<BrowserThread> io_thread_; |
| 124 }; |
| 125 |
| 126 TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteNotPhishing) { |
| 127 // Case 1: client thinks the page is phishing. The server does not agree. |
| 128 // No interstitial is shown. |
| 129 ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
| 130 GURL phishing_url("http://phishingurl.com/"); |
| 131 |
| 132 EXPECT_CALL(*csd_service_, |
| 133 SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
| 134 .WillOnce(SaveArg<2>(&cb)); |
| 135 OnDetectedPhishingSite(phishing_url, 1.0); |
| 136 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
| 137 ASSERT_TRUE(cb != NULL); |
| 138 |
| 139 // Make sure DisplayBlockingPage is not going to be called. |
| 140 EXPECT_CALL(*sb_service_, |
| 141 DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0); |
| 142 cb->Run(phishing_url, false); |
| 143 delete cb; |
| 144 // If there was a message posted on the IO thread to display the |
| 145 // interstitial page we know that it would have been posted before |
| 146 // we put the quit message there. |
| 147 BrowserThread::PostTask(BrowserThread::IO, |
| 148 FROM_HERE, |
| 149 NewRunnableFunction(&QuitUIMessageLoop)); |
| 150 MessageLoop::current()->Run(); |
| 151 EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
| 152 } |
| 153 |
| 154 TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteDisabled) { |
| 155 // Case 2: client thinks the page is phishing and so does the server but |
| 156 // showing the interstitial is disabled => no interstitial is shown. |
| 157 ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
| 158 GURL phishing_url("http://phishingurl.com/"); |
| 159 |
| 160 EXPECT_CALL(*csd_service_, |
| 161 SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
| 162 .WillOnce(SaveArg<2>(&cb)); |
| 163 OnDetectedPhishingSite(phishing_url, 1.0); |
| 164 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
| 165 ASSERT_TRUE(cb != NULL); |
| 166 |
| 167 // Make sure DisplayBlockingPage is not going to be called. |
| 168 EXPECT_CALL(*sb_service_, |
| 169 DisplayBlockingPage(_, _, _, _, _, _, _, _)).Times(0); |
| 170 cb->Run(phishing_url, false); |
| 171 delete cb; |
| 172 |
| 173 FlushIOMessageLoop(); |
| 174 EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
| 175 } |
| 176 |
| 177 TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteShowInterstitial) { |
| 178 // Case 3: client thinks the page is phishing and so does the server. |
| 179 // We show an interstitial. |
| 180 ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
| 181 GURL phishing_url("http://phishingurl.com/"); |
| 182 |
| 183 CommandLine::ForCurrentProcess()->AppendSwitch( |
| 184 switches::kEnableClientSidePhishingInterstitial); |
| 185 |
| 186 EXPECT_CALL(*csd_service_, |
| 187 SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
| 188 .WillOnce(SaveArg<2>(&cb)); |
| 189 OnDetectedPhishingSite(phishing_url, 1.0); |
| 190 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
| 191 ASSERT_TRUE(cb != NULL); |
| 192 |
| 193 SafeBrowsingService::Client* client; |
| 194 EXPECT_CALL(*sb_service_, |
| 195 DisplayBlockingPage( |
| 196 phishing_url, |
| 197 phishing_url, |
| 198 _, |
| 199 ResourceType::MAIN_FRAME, |
| 200 SafeBrowsingService::URL_PHISHING, |
| 201 _ /* a CsdClient object */, |
| 202 contents()->GetRenderProcessHost()->id(), |
| 203 contents()->render_view_host()->routing_id())) |
| 204 .WillOnce(SaveArg<5>(&client)); |
| 205 |
| 206 cb->Run(phishing_url, true); |
| 207 delete cb; |
| 208 |
| 209 FlushIOMessageLoop(); |
| 210 EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
| 211 |
| 212 // Make sure the client object will be deleted. |
| 213 BrowserThread::PostTask( |
| 214 BrowserThread::IO, |
| 215 FROM_HERE, |
| 216 NewRunnableMethod( |
| 217 sb_service_.get(), |
| 218 &MockSafeBrowsingService::InvokeOnBlockingPageComplete, |
| 219 client)); |
| 220 // Since the CsdClient object will be deleted on the UI thread I need |
| 221 // to run the UI message loop. Post a task to stop the UI message loop |
| 222 // after the client object destructor is called. |
| 223 FlushIOMessageLoop(); |
| 224 } |
| 225 |
| 226 TEST_F(ClientSideDetectionHostTest, OnDetectedPhishingSiteMultiplePings) { |
| 227 // Case 4 & 5: client thinks a page is phishing then navigates to |
| 228 // another page which is also considered phishing by the client |
| 229 // before the server responds with a verdict. After a while the |
| 230 // server responds for both requests with a phishing verdict. Only |
| 231 // a single interstitial is shown for the second URL. |
| 232 ClientSideDetectionService::ClientReportPhishingRequestCallback* cb; |
| 233 GURL phishing_url("http://phishingurl.com/"); |
| 234 |
| 235 CommandLine::ForCurrentProcess()->AppendSwitch( |
| 236 switches::kEnableClientSidePhishingInterstitial); |
| 237 |
| 238 EXPECT_CALL(*csd_service_, |
| 239 SendClientReportPhishingRequest(phishing_url, 1.0, _)) |
| 240 .WillOnce(SaveArg<2>(&cb)); |
| 241 OnDetectedPhishingSite(phishing_url, 1.0); |
| 242 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
| 243 ASSERT_TRUE(cb != NULL); |
| 244 GURL other_phishing_url("http://other_phishing_url.com/bla"); |
| 245 // We navigate away. The callback cb should be revoked. |
| 246 NavigateAndCommit(other_phishing_url); |
| 247 |
| 248 ClientSideDetectionService::ClientReportPhishingRequestCallback* cb_other; |
| 249 EXPECT_CALL(*csd_service_, |
| 250 SendClientReportPhishingRequest(other_phishing_url, 0.8, _)) |
| 251 .WillOnce(SaveArg<2>(&cb_other)); |
| 252 OnDetectedPhishingSite(other_phishing_url, 0.8); |
| 253 EXPECT_TRUE(Mock::VerifyAndClear(csd_service_.get())); |
| 254 ASSERT_TRUE(cb_other); |
| 255 |
| 256 // We expect that the interstitial is shown for the second phishing URL and |
| 257 // not for the first phishing URL. |
| 258 EXPECT_CALL(*sb_service_, |
| 259 DisplayBlockingPage(phishing_url, phishing_url,_, _, _, _, _, _)) |
| 260 .Times(0); |
| 261 SafeBrowsingService::Client* client; |
| 262 EXPECT_CALL(*sb_service_, |
| 263 DisplayBlockingPage( |
| 264 other_phishing_url, |
| 265 other_phishing_url, |
| 266 _, |
| 267 ResourceType::MAIN_FRAME, |
| 268 SafeBrowsingService::URL_PHISHING, |
| 269 _ /* a CsdClient object */, |
| 270 contents()->GetRenderProcessHost()->id(), |
| 271 contents()->render_view_host()->routing_id())) |
| 272 .WillOnce(SaveArg<5>(&client)); |
| 273 |
| 274 cb->Run(phishing_url, true); // Should have no effect. |
| 275 delete cb; |
| 276 cb_other->Run(other_phishing_url, true); // Should show interstitial. |
| 277 delete cb_other; |
| 278 |
| 279 FlushIOMessageLoop(); |
| 280 EXPECT_TRUE(Mock::VerifyAndClear(sb_service_.get())); |
| 281 |
| 282 // Make sure the client object will be deleted. |
| 283 BrowserThread::PostTask( |
| 284 BrowserThread::IO, |
| 285 FROM_HERE, |
| 286 NewRunnableMethod( |
| 287 sb_service_.get(), |
| 288 &MockSafeBrowsingService::InvokeOnBlockingPageComplete, |
| 289 client)); |
| 290 // Since the CsdClient object will be deleted on the UI thread I need |
| 291 // to run the UI message loop. Post a task to stop the UI message loop |
| 292 // after the client object destructor is called. |
| 293 FlushIOMessageLoop(); |
| 294 } |
| 295 |
| 296 } // namespace safe_browsing |
OLD | NEW |