blob: d0dfd2477deade33d410d378a47f41485b402616 [file] [log] [blame]
feltbc2eda2d2015-06-23 02:06:031// Copyright 2015 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
dcheng7bacc0e2016-04-11 20:10:545#include "chrome/browser/safe_browsing/ui_manager.h"
6
clamy4edbf0e2015-12-02 13:35:417#include "base/run_loop.h"
dalecurtis6c58ed02016-10-28 23:02:378#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
feltbc2eda2d2015-06-23 02:06:039#include "chrome/browser/safe_browsing/safe_browsing_service.h"
scottmg22e4f25a2016-08-15 21:09:0310#include "chrome/browser/safe_browsing/ui_manager.h"
feltfb118572015-08-18 05:22:0111#include "chrome/test/base/chrome_render_view_host_test_harness.h"
12#include "chrome/test/base/testing_profile.h"
dalecurtis6c58ed02016-10-28 23:02:3713#include "components/safe_browsing_db/safe_browsing_prefs.h"
vakhfa183fa2016-03-29 17:33:3714#include "components/safe_browsing_db/util.h"
estark1ca09ca2016-11-01 04:04:1215#include "content/public/browser/navigation_entry.h"
feltfb118572015-08-18 05:22:0116#include "content/public/browser/render_process_host.h"
17#include "content/public/browser/render_view_host.h"
18#include "content/public/browser/web_contents.h"
dalecurtis6c58ed02016-10-28 23:02:3719#include "content/public/browser/web_contents_delegate.h"
feltbc2eda2d2015-06-23 02:06:0320#include "content/public/test/test_browser_thread_bundle.h"
feltfb118572015-08-18 05:22:0121#include "content/public/test/web_contents_tester.h"
feltbc2eda2d2015-06-23 02:06:0322#include "testing/gtest/include/gtest/gtest.h"
23#include "url/gurl.h"
24
clamy4edbf0e2015-12-02 13:35:4125using content::BrowserThread;
26
feltfb118572015-08-18 05:22:0127static const char* kGoodURL = "https://www.good.com";
28static const char* kBadURL = "https://www.malware.com";
29static const char* kBadURLWithPath = "https://www.malware.com/index.html";
mattmbfc4060d2015-12-18 23:11:3830static const char* kAnotherBadURL = "https://www.badware.com";
31static const char* kLandingURL = "https://www.landing.com";
feltfb118572015-08-18 05:22:0132
vakh9a474d832015-11-13 01:43:0933namespace safe_browsing {
34
clamy4edbf0e2015-12-02 13:35:4135class SafeBrowsingCallbackWaiter {
36 public:
37 SafeBrowsingCallbackWaiter() {}
38
39 bool callback_called() const { return callback_called_; }
40 bool proceed() const { return proceed_; }
41
42 void OnBlockingPageDone(bool proceed) {
43 DCHECK_CURRENTLY_ON(BrowserThread::UI);
44 callback_called_ = true;
45 proceed_ = proceed;
46 loop_.Quit();
47 }
48
49 void OnBlockingPageDoneOnIO(bool proceed) {
50 DCHECK_CURRENTLY_ON(BrowserThread::IO);
51 BrowserThread::PostTask(
52 BrowserThread::UI, FROM_HERE,
53 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
54 base::Unretained(this), proceed));
55 }
56
57 void WaitForCallback() {
58 DCHECK_CURRENTLY_ON(BrowserThread::UI);
59 loop_.Run();
60 }
61
62 private:
63 bool callback_called_ = false;
64 bool proceed_ = false;
65 base::RunLoop loop_;
66};
67
feltfb118572015-08-18 05:22:0168class SafeBrowsingUIManagerTest : public ChromeRenderViewHostTestHarness {
feltbc2eda2d2015-06-23 02:06:0369 public:
70 SafeBrowsingUIManagerTest() : ui_manager_(new SafeBrowsingUIManager(NULL)) {}
feltfb118572015-08-18 05:22:0171
feltbc2eda2d2015-06-23 02:06:0372 ~SafeBrowsingUIManagerTest() override{};
73
clamy4edbf0e2015-12-02 13:35:4174 void SetUp() override {
75 SetThreadBundleOptions(content::TestBrowserThreadBundle::REAL_IO_THREAD);
76 ChromeRenderViewHostTestHarness::SetUp();
estark1ca09ca2016-11-01 04:04:1277 SafeBrowsingUIManager::CreateWhitelistForTesting(web_contents());
clamy4edbf0e2015-12-02 13:35:4178 }
feltfb118572015-08-18 05:22:0179
80 void TearDown() override { ChromeRenderViewHostTestHarness::TearDown(); }
81
feltbc2eda2d2015-06-23 02:06:0382 bool IsWhitelisted(SafeBrowsingUIManager::UnsafeResource resource) {
83 return ui_manager_->IsWhitelisted(resource);
84 }
85
86 void AddToWhitelist(SafeBrowsingUIManager::UnsafeResource resource) {
estark1ca09ca2016-11-01 04:04:1287 ui_manager_->AddToWhitelistUrlSet(
88 SafeBrowsingUIManager::GetMainFrameWhitelistUrlForResourceForTesting(
89 resource),
estark7ffa8c62016-11-11 23:21:5590 web_contents(), false, resource.threat_type);
feltfb118572015-08-18 05:22:0191 }
92
mattmbfc4060d2015-12-18 23:11:3893 SafeBrowsingUIManager::UnsafeResource MakeUnsafeResource(
94 const char* url,
95 bool is_subresource) {
feltfb118572015-08-18 05:22:0196 SafeBrowsingUIManager::UnsafeResource resource;
97 resource.url = GURL(url);
mattmbfc4060d2015-12-18 23:11:3898 resource.is_subresource = is_subresource;
scottmg22e4f25a2016-08-15 21:09:0399 resource.web_contents_getter =
100 SafeBrowsingUIManager::UnsafeResource::GetWebContentsGetter(
101 web_contents()->GetRenderProcessHost()->GetID(),
102 web_contents()->GetMainFrame()->GetRoutingID());
feltfb118572015-08-18 05:22:01103 resource.threat_type = SB_THREAT_TYPE_URL_MALWARE;
104 return resource;
105 }
106
mattmbfc4060d2015-12-18 23:11:38107 SafeBrowsingUIManager::UnsafeResource MakeUnsafeResourceAndStartNavigation(
feltfb118572015-08-18 05:22:01108 const char* url) {
mattmbfc4060d2015-12-18 23:11:38109 SafeBrowsingUIManager::UnsafeResource resource =
110 MakeUnsafeResource(url, false /* is_subresource */);
feltfb118572015-08-18 05:22:01111
mattmbfc4060d2015-12-18 23:11:38112 // The WC doesn't have a URL without a navigation. A main-frame malware
113 // unsafe resource must be a pending navigation.
114 content::WebContentsTester::For(web_contents())->StartNavigation(GURL(url));
feltfb118572015-08-18 05:22:01115 return resource;
feltbc2eda2d2015-06-23 02:06:03116 }
117
clamy4edbf0e2015-12-02 13:35:41118 void SimulateBlockingPageDone(
119 const std::vector<SafeBrowsingUIManager::UnsafeResource>& resources,
120 bool proceed) {
estark1ca09ca2016-11-01 04:04:12121 GURL main_frame_url;
122 content::NavigationEntry* entry =
123 web_contents()->GetController().GetVisibleEntry();
124 if (entry)
125 main_frame_url = entry->GetURL();
126
127 ui_manager_->OnBlockingPageDone(resources, proceed, web_contents(),
128 main_frame_url);
clamy4edbf0e2015-12-02 13:35:41129 }
130
dalecurtis6c58ed02016-10-28 23:02:37131 protected:
132 SafeBrowsingUIManager* ui_manager() { return ui_manager_.get(); }
133
feltbc2eda2d2015-06-23 02:06:03134 private:
135 scoped_refptr<SafeBrowsingUIManager> ui_manager_;
feltbc2eda2d2015-06-23 02:06:03136};
137
138TEST_F(SafeBrowsingUIManagerTest, Whitelist) {
feltfb118572015-08-18 05:22:01139 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38140 MakeUnsafeResourceAndStartNavigation(kBadURL);
feltbc2eda2d2015-06-23 02:06:03141 AddToWhitelist(resource);
142 EXPECT_TRUE(IsWhitelisted(resource));
143}
144
feltfb118572015-08-18 05:22:01145TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresSitesNotAdded) {
146 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38147 MakeUnsafeResourceAndStartNavigation(kGoodURL);
feltbc2eda2d2015-06-23 02:06:03148 EXPECT_FALSE(IsWhitelisted(resource));
feltfb118572015-08-18 05:22:01149}
150
estark7ffa8c62016-11-11 23:21:55151TEST_F(SafeBrowsingUIManagerTest, WhitelistRemembersThreatType) {
152 SafeBrowsingUIManager::UnsafeResource resource =
153 MakeUnsafeResourceAndStartNavigation(kBadURL);
154 AddToWhitelist(resource);
155 EXPECT_TRUE(IsWhitelisted(resource));
156 SBThreatType threat_type;
157 content::NavigationEntry* entry =
158 web_contents()->GetController().GetVisibleEntry();
159 ASSERT_TRUE(entry);
160 EXPECT_TRUE(ui_manager()->IsUrlWhitelistedOrPendingForWebContents(
161 resource.url, resource.is_subresource, entry,
162 resource.web_contents_getter.Run(), true, &threat_type));
163 EXPECT_EQ(resource.threat_type, threat_type);
164}
165
feltfb118572015-08-18 05:22:01166TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresPath) {
167 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38168 MakeUnsafeResourceAndStartNavigation(kBadURL);
feltbc2eda2d2015-06-23 02:06:03169 AddToWhitelist(resource);
170 EXPECT_TRUE(IsWhitelisted(resource));
feltfb118572015-08-18 05:22:01171
mattmbfc4060d2015-12-18 23:11:38172 content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
173
feltfb118572015-08-18 05:22:01174 SafeBrowsingUIManager::UnsafeResource resource_path =
mattmbfc4060d2015-12-18 23:11:38175 MakeUnsafeResourceAndStartNavigation(kBadURLWithPath);
feltfb118572015-08-18 05:22:01176 EXPECT_TRUE(IsWhitelisted(resource_path));
feltbc2eda2d2015-06-23 02:06:03177}
178
feltfb118572015-08-18 05:22:01179TEST_F(SafeBrowsingUIManagerTest, WhitelistIgnoresThreatType) {
180 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38181 MakeUnsafeResourceAndStartNavigation(kBadURL);
feltfb118572015-08-18 05:22:01182 AddToWhitelist(resource);
183 EXPECT_TRUE(IsWhitelisted(resource));
feltbc2eda2d2015-06-23 02:06:03184
feltfb118572015-08-18 05:22:01185 SafeBrowsingUIManager::UnsafeResource resource_phishing =
mattmbfc4060d2015-12-18 23:11:38186 MakeUnsafeResource(kBadURL, false /* is_subresource */);
feltfb118572015-08-18 05:22:01187 resource_phishing.threat_type = SB_THREAT_TYPE_URL_PHISHING;
188 EXPECT_TRUE(IsWhitelisted(resource_phishing));
feltbc2eda2d2015-06-23 02:06:03189}
190
mattmbfc4060d2015-12-18 23:11:38191TEST_F(SafeBrowsingUIManagerTest, WhitelistWithUnrelatedPendingLoad) {
192 // Commit load of landing page.
193 NavigateAndCommit(GURL(kLandingURL));
194 {
195 // Simulate subresource malware hit on the landing page.
196 SafeBrowsingUIManager::UnsafeResource resource =
197 MakeUnsafeResource(kBadURL, true /* is_subresource */);
198
199 // Start pending load to unrelated site.
200 content::WebContentsTester::For(web_contents())
201 ->StartNavigation(GURL(kGoodURL));
202
203 // Whitelist the resource on the landing page.
204 AddToWhitelist(resource);
205 EXPECT_TRUE(IsWhitelisted(resource));
206 }
207
208 // Commit the pending load of unrelated site.
209 content::WebContentsTester::For(web_contents())->CommitPendingNavigation();
210 {
211 // The unrelated site is not on the whitelist, even if the same subresource
212 // was on it.
213 SafeBrowsingUIManager::UnsafeResource resource =
214 MakeUnsafeResource(kBadURL, true /* is_subresource */);
215 EXPECT_FALSE(IsWhitelisted(resource));
216 }
217
218 // Navigate back to the original landing url.
219 NavigateAndCommit(GURL(kLandingURL));
220 {
221 SafeBrowsingUIManager::UnsafeResource resource =
222 MakeUnsafeResource(kBadURL, true /* is_subresource */);
223 // Original resource url is whitelisted.
224 EXPECT_TRUE(IsWhitelisted(resource));
225 }
226 {
227 // A different malware subresource on the same page is also whitelisted.
228 // (The whitelist is by the page url, not the resource url.)
229 SafeBrowsingUIManager::UnsafeResource resource2 =
230 MakeUnsafeResource(kAnotherBadURL, true /* is_subresource */);
231 EXPECT_TRUE(IsWhitelisted(resource2));
232 }
233}
234
clamy4edbf0e2015-12-02 13:35:41235TEST_F(SafeBrowsingUIManagerTest, UICallbackProceed) {
236 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38237 MakeUnsafeResourceAndStartNavigation(kBadURL);
clamy4edbf0e2015-12-02 13:35:41238 SafeBrowsingCallbackWaiter waiter;
239 resource.callback =
240 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
241 base::Unretained(&waiter));
242 resource.callback_thread =
thestig529ad8a2016-07-08 20:30:12243 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI);
clamy4edbf0e2015-12-02 13:35:41244 std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
245 resources.push_back(resource);
246 SimulateBlockingPageDone(resources, true);
247 EXPECT_TRUE(IsWhitelisted(resource));
248 waiter.WaitForCallback();
249 EXPECT_TRUE(waiter.callback_called());
250 EXPECT_TRUE(waiter.proceed());
251}
252
253TEST_F(SafeBrowsingUIManagerTest, UICallbackDontProceed) {
254 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38255 MakeUnsafeResourceAndStartNavigation(kBadURL);
clamy4edbf0e2015-12-02 13:35:41256 SafeBrowsingCallbackWaiter waiter;
257 resource.callback =
258 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDone,
259 base::Unretained(&waiter));
260 resource.callback_thread =
thestig529ad8a2016-07-08 20:30:12261 BrowserThread::GetTaskRunnerForThread(BrowserThread::UI);
clamy4edbf0e2015-12-02 13:35:41262 std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
263 resources.push_back(resource);
264 SimulateBlockingPageDone(resources, false);
265 EXPECT_FALSE(IsWhitelisted(resource));
266 waiter.WaitForCallback();
267 EXPECT_TRUE(waiter.callback_called());
268 EXPECT_FALSE(waiter.proceed());
269}
270
271TEST_F(SafeBrowsingUIManagerTest, IOCallbackProceed) {
272 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38273 MakeUnsafeResourceAndStartNavigation(kBadURL);
clamy4edbf0e2015-12-02 13:35:41274 SafeBrowsingCallbackWaiter waiter;
275 resource.callback =
276 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDoneOnIO,
277 base::Unretained(&waiter));
278 resource.callback_thread =
thestig529ad8a2016-07-08 20:30:12279 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
clamy4edbf0e2015-12-02 13:35:41280 std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
281 resources.push_back(resource);
282 SimulateBlockingPageDone(resources, true);
283 EXPECT_TRUE(IsWhitelisted(resource));
284 waiter.WaitForCallback();
285 EXPECT_TRUE(waiter.callback_called());
286 EXPECT_TRUE(waiter.proceed());
287}
288
289TEST_F(SafeBrowsingUIManagerTest, IOCallbackDontProceed) {
290 SafeBrowsingUIManager::UnsafeResource resource =
mattmbfc4060d2015-12-18 23:11:38291 MakeUnsafeResourceAndStartNavigation(kBadURL);
clamy4edbf0e2015-12-02 13:35:41292 SafeBrowsingCallbackWaiter waiter;
293 resource.callback =
294 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDoneOnIO,
295 base::Unretained(&waiter));
296 resource.callback_thread =
thestig529ad8a2016-07-08 20:30:12297 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
clamy4edbf0e2015-12-02 13:35:41298 std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
299 resources.push_back(resource);
300 SimulateBlockingPageDone(resources, false);
301 EXPECT_FALSE(IsWhitelisted(resource));
302 waiter.WaitForCallback();
303 EXPECT_TRUE(waiter.callback_called());
304 EXPECT_FALSE(waiter.proceed());
305}
306
dalecurtis6c58ed02016-10-28 23:02:37307namespace {
308
309// A WebContentsDelegate that records whether
310// VisibleSecurityStateChanged() was called.
311class SecurityStateWebContentsDelegate : public content::WebContentsDelegate {
312 public:
313 SecurityStateWebContentsDelegate() {}
314 ~SecurityStateWebContentsDelegate() override {}
315
316 bool visible_security_state_changed() const {
317 return visible_security_state_changed_;
318 }
319
320 void ClearVisibleSecurityStateChanged() {
321 visible_security_state_changed_ = false;
322 }
323
324 // WebContentsDelegate:
325 void VisibleSecurityStateChanged(content::WebContents* source) override {
326 visible_security_state_changed_ = true;
327 }
328
329 private:
330 bool visible_security_state_changed_ = false;
331 DISALLOW_COPY_AND_ASSIGN(SecurityStateWebContentsDelegate);
332};
333
334// A test blocking page that does not create windows.
335class TestSafeBrowsingBlockingPage : public SafeBrowsingBlockingPage {
336 public:
337 TestSafeBrowsingBlockingPage(SafeBrowsingUIManager* manager,
338 content::WebContents* web_contents,
339 const GURL& main_frame_url,
340 const UnsafeResourceList& unsafe_resources)
341 : SafeBrowsingBlockingPage(manager,
342 web_contents,
343 main_frame_url,
344 unsafe_resources) {
345 // Don't delay details at all for the unittest.
346 threat_details_proceed_delay_ms_ = 0;
347 DontCreateViewForTesting();
348 }
349};
350
351// A factory that creates TestSafeBrowsingBlockingPages.
352class TestSafeBrowsingBlockingPageFactory
353 : public SafeBrowsingBlockingPageFactory {
354 public:
355 TestSafeBrowsingBlockingPageFactory() {}
356 ~TestSafeBrowsingBlockingPageFactory() override {}
357
358 SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
359 SafeBrowsingUIManager* delegate,
360 content::WebContents* web_contents,
361 const GURL& main_frame_url,
362 const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
363 override {
364 return new TestSafeBrowsingBlockingPage(delegate, web_contents,
365 main_frame_url, unsafe_resources);
366 }
367};
368
369} // namespace
370
371// Tests that the WebContentsDelegate is notified of a visible security
372// state change when a blocking page is shown for a subresource.
373TEST_F(SafeBrowsingUIManagerTest,
374 VisibleSecurityStateChangedForUnsafeSubresource) {
375 TestSafeBrowsingBlockingPageFactory factory;
376 SafeBrowsingBlockingPage::RegisterFactory(&factory);
377 SecurityStateWebContentsDelegate delegate;
378 web_contents()->SetDelegate(&delegate);
379
380 // Simulate a blocking page showing for an unsafe subresource.
381 SafeBrowsingUIManager::UnsafeResource resource =
382 MakeUnsafeResource(kBadURL, true /* is_subresource */);
383 // Needed for showing the blocking page.
384 resource.threat_source = safe_browsing::ThreatSource::REMOTE;
385 NavigateAndCommit(GURL("http://example.test"));
386
387 delegate.ClearVisibleSecurityStateChanged();
388 EXPECT_FALSE(delegate.visible_security_state_changed());
389 ui_manager()->DisplayBlockingPage(resource);
390 EXPECT_TRUE(delegate.visible_security_state_changed());
391
392 // Simulate proceeding through the blocking page.
393 SafeBrowsingCallbackWaiter waiter;
394 resource.callback =
395 base::Bind(&SafeBrowsingCallbackWaiter::OnBlockingPageDoneOnIO,
396 base::Unretained(&waiter));
397 resource.callback_thread =
398 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO);
399 std::vector<SafeBrowsingUIManager::UnsafeResource> resources;
400 resources.push_back(resource);
401
402 delegate.ClearVisibleSecurityStateChanged();
403 EXPECT_FALSE(delegate.visible_security_state_changed());
404 SimulateBlockingPageDone(resources, true);
405 EXPECT_TRUE(delegate.visible_security_state_changed());
406
407 waiter.WaitForCallback();
408 EXPECT_TRUE(waiter.callback_called());
409 EXPECT_TRUE(waiter.proceed());
410 EXPECT_TRUE(IsWhitelisted(resource));
411}
412
vakh9a474d832015-11-13 01:43:09413} // namespace safe_browsing