safe_browsing_browsertest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. // Copyright 2019 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. #include <map>
  5. #include "base/memory/raw_ptr.h"
  6. #include "components/prefs/pref_service.h"
  7. #include "components/safe_browsing/android/safe_browsing_api_handler_bridge.h"
  8. #include "components/safe_browsing/content/browser/base_blocking_page.h"
  9. #include "components/safe_browsing/content/browser/safe_browsing_blocking_page.h"
  10. #include "components/safe_browsing/core/browser/db/v4_protocol_manager_util.h"
  11. #include "components/safe_browsing/core/browser/safe_browsing_token_fetcher.h"
  12. #include "components/security_interstitials/content/security_interstitial_page.h"
  13. #include "components/security_interstitials/content/security_interstitial_tab_helper.h"
  14. #include "components/user_prefs/user_prefs.h"
  15. #include "content/public/browser/browser_context.h"
  16. #include "content/public/browser/browser_task_traits.h"
  17. #include "content/public/browser/browser_thread.h"
  18. #include "content/public/browser/render_process_host.h"
  19. #include "content/public/browser/web_contents.h"
  20. #include "content/public/test/test_utils.h"
  21. #include "google_apis/gaia/gaia_constants.h"
  22. #include "net/dns/mock_host_resolver.h"
  23. #include "net/test/embedded_test_server/embedded_test_server.h"
  24. #include "weblayer/browser/browser_context_impl.h"
  25. #include "weblayer/browser/browser_impl.h"
  26. #include "weblayer/browser/profile_impl.h"
  27. #include "weblayer/browser/safe_browsing/real_time_url_lookup_service_factory.h"
  28. #include "weblayer/browser/tab_impl.h"
  29. #include "weblayer/public/google_account_access_token_fetch_delegate.h"
  30. #include "weblayer/public/navigation.h"
  31. #include "weblayer/public/navigation_controller.h"
  32. #include "weblayer/public/navigation_observer.h"
  33. #include "weblayer/public/profile.h"
  34. #include "weblayer/public/tab.h"
  35. #include "weblayer/shell/browser/shell.h"
  36. #include "weblayer/test/load_completion_observer.h"
  37. #include "weblayer/test/weblayer_browser_test.h"
  38. #include "weblayer/test/weblayer_browser_test_utils.h"
  39. namespace weblayer {
  40. namespace {
  41. // Implementation of GoogleAccountAccessTokenFetchDelegate used to exercise safe
  42. // browsing access token fetches.
  43. class TestAccessTokenFetchDelegate
  44. : public GoogleAccountAccessTokenFetchDelegate {
  45. public:
  46. TestAccessTokenFetchDelegate() = default;
  47. ~TestAccessTokenFetchDelegate() override = default;
  48. // GoogleAccountAccessTokenFetchDelegate:
  49. void FetchAccessToken(const std::set<std::string>& scopes,
  50. OnTokenFetchedCallback callback) override {
  51. has_received_request_ = true;
  52. scopes_from_most_recent_request_ = scopes;
  53. if (should_respond_to_request_) {
  54. std::move(callback).Run("token");
  55. } else {
  56. outstanding_request_ = std::move(callback);
  57. }
  58. }
  59. void OnAccessTokenIdentifiedAsInvalid(const std::set<std::string>& scopes,
  60. const std::string& token) override {
  61. NOTREACHED();
  62. }
  63. void set_should_respond_to_request(bool should_respond) {
  64. should_respond_to_request_ = should_respond;
  65. }
  66. bool has_received_request() { return has_received_request_; }
  67. const std::set<std::string>& scopes_from_most_recent_request() {
  68. return scopes_from_most_recent_request_;
  69. }
  70. private:
  71. bool should_respond_to_request_ = false;
  72. bool has_received_request_ = false;
  73. std::set<std::string> scopes_from_most_recent_request_;
  74. OnTokenFetchedCallback outstanding_request_;
  75. };
  76. // Observer customized for safe browsing navigation failures.
  77. class SafeBrowsingErrorNavigationObserver : public NavigationObserver {
  78. public:
  79. SafeBrowsingErrorNavigationObserver(const GURL& url, Shell* shell)
  80. : url_(url), tab_(shell->tab()) {
  81. tab_->GetNavigationController()->AddObserver(this);
  82. }
  83. ~SafeBrowsingErrorNavigationObserver() override {
  84. tab_->GetNavigationController()->RemoveObserver(this);
  85. }
  86. void NavigationFailed(Navigation* navigation) override {
  87. if (navigation->GetURL() != url_)
  88. return;
  89. EXPECT_EQ(navigation->GetLoadError(),
  90. Navigation::LoadError::kSafeBrowsingError);
  91. run_loop_.Quit();
  92. }
  93. // Begins waiting for a Navigation within |shell_| and to |url_| to fail. In
  94. // the failure callback verifies that the navigation failed with a safe
  95. // browsing error.
  96. void WaitForNavigationFailureWithSafeBrowsingError() { run_loop_.Run(); }
  97. private:
  98. const GURL url_;
  99. raw_ptr<Tab> tab_;
  100. base::RunLoop run_loop_;
  101. };
  102. using SbBridge = safe_browsing::SafeBrowsingApiHandlerBridge;
  103. void RunCallbackOnIOThread(std::unique_ptr<SbBridge::ResponseCallback> callback,
  104. safe_browsing::SBThreatType threat_type,
  105. const safe_browsing::ThreatMetadata& metadata) {
  106. content::GetIOThreadTaskRunner({})->PostTask(
  107. FROM_HERE, base::BindOnce(std::move(*callback), threat_type, metadata));
  108. }
  109. } // namespace
  110. class TestUrlCheckInterceptor : public safe_browsing::UrlCheckInterceptor {
  111. public:
  112. void Add(const GURL& url, safe_browsing::SBThreatType threat_type) {
  113. map_[url] = threat_type;
  114. }
  115. void Clear() { map_.clear(); }
  116. // safe_browsing::UrlCheckInterceptor
  117. void Check(std::unique_ptr<SbBridge::ResponseCallback> callback,
  118. const GURL& url) const override {
  119. RunCallbackOnIOThread(std::move(callback), Find(url),
  120. safe_browsing::ThreatMetadata());
  121. }
  122. ~TestUrlCheckInterceptor() override{};
  123. private:
  124. safe_browsing::SBThreatType Find(const GURL& url) const {
  125. auto it = map_.find(url);
  126. if (it != map_.end())
  127. return it->second;
  128. // If the url is not in the map assume it is safe.
  129. return safe_browsing::SB_THREAT_TYPE_SAFE;
  130. }
  131. std::map<GURL, safe_browsing::SBThreatType> map_;
  132. };
  133. class SafeBrowsingBrowserTest : public WebLayerBrowserTest {
  134. public:
  135. SafeBrowsingBrowserTest()
  136. : url_check_interceptor_(std::make_unique<TestUrlCheckInterceptor>()) {}
  137. SafeBrowsingBrowserTest(const SafeBrowsingBrowserTest&) = delete;
  138. SafeBrowsingBrowserTest& operator=(const SafeBrowsingBrowserTest&) = delete;
  139. ~SafeBrowsingBrowserTest() override = default;
  140. void SetUpOnMainThread() override {
  141. InitializeOnMainThread();
  142. // Safe Browsing is enabled by default
  143. ASSERT_TRUE(GetSafeBrowsingEnabled());
  144. profile()->SetGoogleAccountAccessTokenFetchDelegate(
  145. &access_token_fetch_delegate_);
  146. }
  147. void TearDown() override {
  148. profile()->SetGoogleAccountAccessTokenFetchDelegate(nullptr);
  149. SbBridge::GetInstance().SetInterceptorForTesting(nullptr);
  150. }
  151. void InitializeOnMainThread() {
  152. NavigateAndWaitForCompletion(GURL("about:blank"), shell());
  153. SbBridge::GetInstance().SetInterceptorForTesting(
  154. url_check_interceptor_.get());
  155. // Some tests need to be able to navigate to URLs on domains that are not
  156. // explicitly localhost (e.g., so that realtime URL lookups occur on these
  157. // navigations).
  158. host_resolver()->AddRule("*", "127.0.0.1");
  159. ASSERT_TRUE(embedded_test_server()->Start());
  160. url_ = embedded_test_server()->GetURL("/simple_page.html");
  161. }
  162. void SetSafeBrowsingEnabled(bool value) {
  163. GetProfile()->SetBooleanSetting(SettingType::BASIC_SAFE_BROWSING_ENABLED,
  164. value);
  165. }
  166. void SetRealTimeURLLookupsEnabled(bool value) {
  167. GetProfile()->SetBooleanSetting(
  168. SettingType::REAL_TIME_SAFE_BROWSING_ENABLED, value);
  169. }
  170. void EnableSafeBrowsingAccessTokenFetches() {
  171. RealTimeUrlLookupServiceFactory::GetInstance()
  172. ->set_access_token_fetches_enabled_for_testing();
  173. }
  174. bool GetSafeBrowsingEnabled() {
  175. return GetProfile()->GetBooleanSetting(
  176. SettingType::BASIC_SAFE_BROWSING_ENABLED);
  177. }
  178. void NavigateWithThreatType(const safe_browsing::SBThreatType& threatType,
  179. bool expect_interstitial) {
  180. url_check_interceptor_->Add(url_, threatType);
  181. Navigate(url_, expect_interstitial);
  182. }
  183. void Navigate(const GURL& url, bool expect_interstitial) {
  184. LoadCompletionObserver load_observer(shell());
  185. shell()->tab()->GetNavigationController()->Navigate(url);
  186. load_observer.Wait();
  187. EXPECT_EQ(expect_interstitial, HasInterstitial());
  188. if (expect_interstitial) {
  189. ASSERT_EQ(safe_browsing::SafeBrowsingBlockingPage::kTypeForTesting,
  190. GetSecurityInterstitialPage()->GetTypeForTesting());
  191. EXPECT_TRUE(GetSecurityInterstitialPage()->GetHTMLContents().length() >
  192. 0);
  193. }
  194. }
  195. void NavigateWithSubResourceAndThreatType(
  196. const safe_browsing::SBThreatType& threat_type,
  197. bool expect_interstitial) {
  198. GURL page_with_script_url =
  199. embedded_test_server()->GetURL("/simple_page_with_script.html");
  200. GURL script_url = embedded_test_server()->GetURL("/script.js");
  201. url_check_interceptor_->Add(script_url, threat_type);
  202. Navigate(page_with_script_url, expect_interstitial);
  203. }
  204. protected:
  205. content::WebContents* GetWebContents() {
  206. Tab* tab = shell()->tab();
  207. TabImpl* tab_impl = static_cast<TabImpl*>(tab);
  208. return tab_impl->web_contents();
  209. }
  210. security_interstitials::SecurityInterstitialPage*
  211. GetSecurityInterstitialPage() {
  212. security_interstitials::SecurityInterstitialTabHelper* helper =
  213. security_interstitials::SecurityInterstitialTabHelper::FromWebContents(
  214. GetWebContents());
  215. return helper
  216. ? helper
  217. ->GetBlockingPageForCurrentlyCommittedNavigationForTesting()
  218. : nullptr;
  219. }
  220. bool HasInterstitial() { return GetSecurityInterstitialPage() != nullptr; }
  221. void KillRenderer() {
  222. content::RenderProcessHost* child_process =
  223. static_cast<TabImpl*>(shell()->tab())
  224. ->web_contents()
  225. ->GetPrimaryMainFrame()
  226. ->GetProcess();
  227. content::RenderProcessHostWatcher crash_observer(
  228. child_process,
  229. content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
  230. child_process->Shutdown(0);
  231. crash_observer.Wait();
  232. }
  233. std::unique_ptr<TestUrlCheckInterceptor> url_check_interceptor_;
  234. GURL url_;
  235. ProfileImpl* profile() {
  236. auto* tab_impl = static_cast<TabImpl*>(shell()->tab());
  237. return tab_impl->profile();
  238. }
  239. TestAccessTokenFetchDelegate* access_token_fetch_delegate() {
  240. return &access_token_fetch_delegate_;
  241. }
  242. private:
  243. TestAccessTokenFetchDelegate access_token_fetch_delegate_;
  244. };
  245. class SafeBrowsingDisabledBrowserTest : public SafeBrowsingBrowserTest {
  246. public:
  247. SafeBrowsingDisabledBrowserTest() {}
  248. SafeBrowsingDisabledBrowserTest(const SafeBrowsingDisabledBrowserTest&) =
  249. delete;
  250. SafeBrowsingDisabledBrowserTest& operator=(
  251. const SafeBrowsingDisabledBrowserTest&) = delete;
  252. ~SafeBrowsingDisabledBrowserTest() override = default;
  253. void SetUpOnMainThread() override {
  254. SetSafeBrowsingEnabled(false);
  255. SafeBrowsingBrowserTest::InitializeOnMainThread();
  256. ASSERT_FALSE(GetSafeBrowsingEnabled());
  257. }
  258. };
  259. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  260. DoesNotShowInterstitial_NoRestriction) {
  261. Navigate(url_, false);
  262. }
  263. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, DoesNotShowInterstitial_Safe) {
  264. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_SAFE, false);
  265. }
  266. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  267. DISABLED_ShowsInterstitial_Malware) {
  268. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_MALWARE, true);
  269. }
  270. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  271. DISABLED_ShowsInterstitial_Phishing) {
  272. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, true);
  273. }
  274. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  275. DISABLED_CheckNavigationErrorType) {
  276. auto threat_types = {
  277. safe_browsing::SB_THREAT_TYPE_URL_PHISHING,
  278. safe_browsing::SB_THREAT_TYPE_URL_MALWARE,
  279. safe_browsing::SB_THREAT_TYPE_URL_UNWANTED,
  280. safe_browsing::SB_THREAT_TYPE_BILLING,
  281. };
  282. for (auto threat_type : threat_types) {
  283. SafeBrowsingErrorNavigationObserver observer(url_, shell());
  284. url_check_interceptor_->Clear();
  285. url_check_interceptor_->Add(url_, threat_type);
  286. shell()->tab()->GetNavigationController()->Navigate(url_);
  287. observer.WaitForNavigationFailureWithSafeBrowsingError();
  288. }
  289. }
  290. // Tests below are disabled due to failures on Android.
  291. // See crbug.com/1340200.
  292. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  293. DISABLED_ShowsInterstitial_Unwanted) {
  294. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_UNWANTED, true);
  295. }
  296. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  297. DISABLED_ShowsInterstitial_Billing) {
  298. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_BILLING, true);
  299. }
  300. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  301. DISABLED_ShowsInterstitial_Malware_Subresource) {
  302. NavigateWithSubResourceAndThreatType(
  303. safe_browsing::SB_THREAT_TYPE_URL_MALWARE, true);
  304. }
  305. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  306. DoesNotShowInterstitial_Phishing_disableSB) {
  307. // Test that the browser checks the safe browsing setting for new navigations.
  308. SetSafeBrowsingEnabled(false);
  309. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, false);
  310. }
  311. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  312. DoesNotShowInterstitial_Malware_Subresource_disableSB) {
  313. // Test that new renderer checks the safe browsing setting.
  314. SetSafeBrowsingEnabled(false);
  315. KillRenderer();
  316. NavigateWithSubResourceAndThreatType(
  317. safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
  318. }
  319. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest, CheckSetsPrefs) {
  320. // Check that changing safe browsing setting sets corresponding pref,
  321. // which is persistent.
  322. PrefService* prefs = GetProfile()->GetBrowserContext()->pref_service();
  323. SetSafeBrowsingEnabled(true);
  324. EXPECT_TRUE(prefs->GetBoolean(::prefs::kSafeBrowsingEnabled));
  325. SetSafeBrowsingEnabled(false);
  326. EXPECT_FALSE(prefs->GetBoolean(::prefs::kSafeBrowsingEnabled));
  327. }
  328. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  329. DoesNotShowInterstitial_NoRestriction) {
  330. Navigate(url_, false);
  331. }
  332. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  333. DoesNotShowInterstitial_Safe) {
  334. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_SAFE, false);
  335. }
  336. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  337. DoesNotShowInterstitial_Malware) {
  338. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
  339. }
  340. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  341. DoesNotShowInterstitial_Phishing) {
  342. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_PHISHING, false);
  343. }
  344. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  345. DoesNotShowInterstitial_Unwanted) {
  346. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_URL_UNWANTED, false);
  347. }
  348. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  349. DoesNotShowInterstitial_Billing) {
  350. NavigateWithThreatType(safe_browsing::SB_THREAT_TYPE_BILLING, false);
  351. }
  352. IN_PROC_BROWSER_TEST_F(SafeBrowsingDisabledBrowserTest,
  353. DoesNotShowInterstitial_Malware_Subresource) {
  354. NavigateWithSubResourceAndThreatType(
  355. safe_browsing::SB_THREAT_TYPE_URL_MALWARE, false);
  356. }
  357. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  358. NoAccessTokenFetchWhenSafeBrowsingNotEnabled) {
  359. GURL a_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
  360. NavigateAndWaitForCompletion(a_url, shell()->tab());
  361. EXPECT_FALSE(access_token_fetch_delegate()->has_received_request());
  362. }
  363. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  364. NoAccessTokenFetchInBasicSafeBrowsing) {
  365. SetSafeBrowsingEnabled(true);
  366. GURL a_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
  367. NavigateAndWaitForCompletion(a_url, shell()->tab());
  368. EXPECT_FALSE(access_token_fetch_delegate()->has_received_request());
  369. }
  370. IN_PROC_BROWSER_TEST_F(SafeBrowsingBrowserTest,
  371. NoAccessTokenFetchInRealTimeUrlLookupsUnlessEnabled) {
  372. SetRealTimeURLLookupsEnabled(true);
  373. GURL a_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
  374. NavigateAndWaitForCompletion(a_url, shell()->tab());
  375. EXPECT_FALSE(access_token_fetch_delegate()->has_received_request());
  376. EnableSafeBrowsingAccessTokenFetches();
  377. access_token_fetch_delegate()->set_should_respond_to_request(true);
  378. GURL b_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
  379. NavigateAndWaitForCompletion(a_url, shell()->tab());
  380. std::set<std::string> safe_browsing_scopes = {
  381. GaiaConstants::kChromeSafeBrowsingOAuth2Scope};
  382. EXPECT_TRUE(access_token_fetch_delegate()->has_received_request());
  383. EXPECT_EQ(safe_browsing_scopes,
  384. access_token_fetch_delegate()->scopes_from_most_recent_request());
  385. }
  386. // Tests that even if the embedder does not respond to an access token fetch
  387. // that is made by safe browsing as part of a navigation, the navigation
  388. // completes due to Safe Browsing's timing out the access token fetch.
  389. IN_PROC_BROWSER_TEST_F(
  390. SafeBrowsingBrowserTest,
  391. UnfulfilledAccessTokenFetchTimesOutAndNavigationCompletes) {
  392. SetRealTimeURLLookupsEnabled(true);
  393. EnableSafeBrowsingAccessTokenFetches();
  394. access_token_fetch_delegate()->set_should_respond_to_request(false);
  395. GURL a_url(embedded_test_server()->GetURL("a.com", "/simple_page.html"));
  396. NavigateAndWaitForCompletion(a_url, shell()->tab());
  397. std::set<std::string> safe_browsing_scopes = {
  398. GaiaConstants::kChromeSafeBrowsingOAuth2Scope};
  399. EXPECT_TRUE(access_token_fetch_delegate()->has_received_request());
  400. EXPECT_EQ(safe_browsing_scopes,
  401. access_token_fetch_delegate()->scopes_from_most_recent_request());
  402. }
  403. } // namespace weblayer