| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <string> |
| |
| #include "base/containers/contains.h" |
| #include "base/functional/bind.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/apps/app_service/app_launch_params.h" |
| #include "chrome/browser/apps/app_service/app_service_proxy_factory.h" |
| #include "chrome/browser/policy/url_blocking_policy_test_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_commands.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "components/policy/core/common/policy_map.h" |
| #include "components/policy/core/common/policy_pref_names.h" |
| #include "components/policy/core/common/policy_types.h" |
| #include "components/policy/policy_constants.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/test/browser_test.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "net/test/embedded_test_server/http_request.h" |
| #include "net/test/embedded_test_server/http_response.h" |
| #include "url/gurl.h" |
| |
| #if !BUILDFLAG(IS_MAC) |
| #include "extensions/browser/app_window/app_window.h" |
| #include "ui/base/window_open_disposition.h" |
| #endif |
| |
| using content::BrowserThread; |
| |
| namespace policy { |
| |
| namespace { |
| |
| // Verifies that the given url |spec| can be opened. This assumes that |spec| |
| // points at empty.html in the test data dir. |
| void CheckCanOpenURL(Browser* browser, const std::string& spec) { |
| GURL url(spec); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url)); |
| content::WebContents* contents = |
| browser->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_EQ(url, contents->GetLastCommittedURL()); |
| |
| std::u16string blocked_page_title; |
| if (url.has_host()) { |
| blocked_page_title = base::UTF8ToUTF16(url.host()); |
| } else { |
| // Local file paths show the full URL. |
| blocked_page_title = base::UTF8ToUTF16(url.spec()); |
| } |
| EXPECT_NE(blocked_page_title, contents->GetTitle()); |
| } |
| |
| void CheckCanOpenViewSourceURL(Browser* browser, const std::string& spec) { |
| GURL view_source_url("view-source:" + spec); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, view_source_url)); |
| content::WebContents* contents = |
| browser->tab_strip_model()->GetActiveWebContents(); |
| EXPECT_EQ(view_source_url, contents->GetLastCommittedURL()); |
| } |
| |
| // Handler for embedded http-server, returns a small page with javascript |
| // variable and a link to increment it. It's for JavascriptBlocklistable test. |
| std::unique_ptr<net::test_server::HttpResponse> JSIncrementerPageHandler( |
| const net::test_server::HttpRequest& request) { |
| if (request.relative_url != "/test.html") { |
| return nullptr; |
| } |
| |
| std::unique_ptr<net::test_server::BasicHttpResponse> http_response( |
| new net::test_server::BasicHttpResponse()); |
| http_response->set_code(net::HTTP_OK); |
| http_response->set_content( |
| "<head><script type=\"text/javascript\">\n" |
| "<!--\n" |
| "var value = 1;" |
| "var increment = function() {" |
| " value = value + 1;" |
| "};\n" |
| "//-->\n" |
| "</script></head><body>" |
| "<a id='link' href=\"javascript:increment();\">click</a>" |
| "</body>"); |
| http_response->set_content_type("text/html"); |
| return http_response; |
| } |
| |
| // Fetch value from page generated by JSIncrementerPageHandler. |
| int JSIncrementerFetch(content::WebContents* contents) { |
| return content::EvalJs(contents, "value;").ExtractInt(); |
| } |
| |
| } // namespace |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklist) { |
| // Checks that URLs can be blocklisted, and that exceptions can be made to |
| // the blocklist. |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const std::string kURLS[] = { |
| embedded_test_server()->GetURL("aaa.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("bbb.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("sub.bbb.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("bbb.com", "/policy/blank.html").spec(), |
| embedded_test_server()->GetURL("bbb.com.", "/policy/blank.html").spec(), |
| }; |
| |
| // Verify that "bbb.com" opens before applying the blocklist. |
| CheckCanOpenURL(browser(), kURLS[1]); |
| |
| // Set a blocklist. |
| base::Value::List blocklist; |
| blocklist.Append("bbb.com"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(std::move(blocklist)), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| // All bbb.com URLs are blocked, and "aaa.com" is still unblocked. |
| CheckCanOpenURL(browser(), kURLS[0]); |
| for (size_t i = 1; i < std::size(kURLS); ++i) |
| CheckURLIsBlocked(browser(), kURLS[i]); |
| |
| // Allowlist some sites of bbb.com. |
| base::Value::List allowlist; |
| allowlist.Append("sub.bbb.com"); |
| allowlist.Append("bbb.com/policy"); |
| policies.Set(key::kURLAllowlist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(allowlist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| CheckURLIsBlocked(browser(), kURLS[1]); |
| CheckCanOpenURL(browser(), kURLS[2]); |
| CheckCanOpenURL(browser(), kURLS[3]); |
| CheckCanOpenURL(browser(), kURLS[4]); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistViewSource) { |
| // Checks that blocklisted urls are blocked when accessed by via view-source:, |
| // and that blocklisting view-source:* blocks all view-source urls. |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const std::string kURL_A = |
| embedded_test_server()->GetURL("aaa.com", "/empty.html").spec(); |
| const std::string kURL_B = |
| embedded_test_server()->GetURL("bbb.com", "/empty.html").spec(); |
| |
| // Ensure that no urls are blocked by default. |
| CheckCanOpenURL(browser(), kURL_A); |
| CheckCanOpenURL(browser(), kURL_B); |
| CheckCanOpenViewSourceURL(browser(), kURL_A); |
| CheckCanOpenViewSourceURL(browser(), kURL_B); |
| |
| // Block bbb.com urls. |
| base::Value::List blocklist; |
| blocklist.Append("bbb.com"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| // Verify that blocking bbb.com also blocks view-source:bbb.com. |
| CheckURLIsBlocked(browser(), kURL_B); |
| CheckViewSourceURLIsBlocked(browser(), kURL_B); |
| |
| // Block all view-source urls. |
| blocklist.Append("view-source:*"); |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| // Verify that blocking view-source:* blocks view-source:aaa.com but does not |
| // block http://aaa.com. |
| CheckViewSourceURLIsBlocked(browser(), kURL_A); |
| CheckCanOpenURL(browser(), kURL_A); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistNonStandardScheme) { |
| // Checks that non-standard schemes can be blocklisted, and that the blocking |
| // page mentions the URL's scheme. |
| const std::string kURL = "mailto:nobody"; |
| |
| // Block mailto: urls. |
| base::Value::List blocklist; |
| blocklist.Append("mailto:*"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| // Ensure the URL is blocked. |
| CheckURLIsBlocked(browser(), kURL); |
| |
| // Ensure the blocking page mentions the scheme. |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| std::string result = |
| content::EvalJs(contents, "document.body.textContent;").ExtractString(); |
| EXPECT_THAT(result, testing::HasSubstr("mailto")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistIncognito) { |
| // Checks that URLs can be blocklisted, and that exceptions can be made to |
| // the blocklist. |
| |
| Browser* incognito_browser = |
| OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| const std::string kURLS[] = { |
| embedded_test_server()->GetURL("aaa.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("bbb.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("sub.bbb.com", "/empty.html").spec(), |
| embedded_test_server()->GetURL("bbb.com", "/policy/blank.html").spec(), |
| embedded_test_server()->GetURL("bbb.com.", "/policy/blank.html").spec(), |
| }; |
| |
| // Verify that "bbb.com" opens before applying the blocklist. |
| CheckCanOpenURL(incognito_browser, kURLS[1]); |
| |
| // Set a blocklist. |
| base::Value::List blocklist; |
| blocklist.Append("bbb.com"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| // All bbb.com URLs are blocked, and "aaa.com" is still unblocked. |
| CheckCanOpenURL(incognito_browser, kURLS[0]); |
| for (size_t i = 1; i < std::size(kURLS); ++i) |
| CheckURLIsBlocked(incognito_browser, kURLS[i]); |
| |
| // Allowlist some sites of bbb.com. |
| base::Value::List allowlist; |
| allowlist.Append("sub.bbb.com"); |
| allowlist.Append("bbb.com/policy"); |
| policies.Set(key::kURLAllowlist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(allowlist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| CheckURLIsBlocked(incognito_browser, kURLS[1]); |
| CheckCanOpenURL(incognito_browser, kURLS[2]); |
| CheckCanOpenURL(incognito_browser, kURLS[3]); |
| CheckCanOpenURL(incognito_browser, kURLS[4]); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistAndAllowlist) { |
| // Regression test for http://crbug.com/755256. Blocklisting * and |
| // allowlisting an origin should work. |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| base::Value::List blocklist; |
| blocklist.Append("*"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| |
| base::Value::List allowlist; |
| allowlist.Append("aaa.com"); |
| policies.Set(key::kURLAllowlist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(allowlist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| CheckCanOpenURL( |
| browser(), |
| embedded_test_server()->GetURL("aaa.com", "/empty.html").spec()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistSubresources) { |
| // Checks that an image with a blocklisted URL is loaded, but an iframe with a |
| // blocklisted URL is not. |
| |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL main_url = |
| embedded_test_server()->GetURL("/policy/denylist-subresources.html"); |
| GURL image_url = embedded_test_server()->GetURL("/policy/pixel.png"); |
| GURL subframe_url = embedded_test_server()->GetURL("/policy/blank.html"); |
| |
| // Set a blocklist containing the image and the iframe which are used by the |
| // main document. |
| base::Value::List blocklist; |
| blocklist.Append(image_url.spec().c_str()); |
| blocklist.Append(subframe_url.spec().c_str()); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| EXPECT_EQ("success", content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "imageLoadResult")); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), main_url)); |
| EXPECT_EQ("error", content::EvalJs( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "iframeLoadResult")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistClientRedirect) { |
| // Checks that a client side redirect to a blocklisted URL is blocked. |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL redirected_url = |
| embedded_test_server()->GetURL("/policy/denylist-redirect.html"); |
| GURL first_url = embedded_test_server()->GetURL("/client-redirect?" + |
| redirected_url.spec()); |
| |
| // There are two navigations: one when loading client-redirect.html and |
| // another when the document redirects using http-equiv="refresh". |
| ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), |
| first_url, 2); |
| EXPECT_EQ(u"Redirected!", |
| browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); |
| |
| base::Value::List blocklist; |
| blocklist.Append(redirected_url.spec().c_str()); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url)); |
| content::WaitForLoadStop( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_NE(u"Redirected!", |
| browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, URLBlocklistServerRedirect) { |
| // Checks that a server side redirect to a blocklisted URL is blocked. |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| GURL redirected_url = |
| embedded_test_server()->GetURL("/policy/denylist-redirect.html"); |
| GURL first_url = embedded_test_server()->GetURL("/server-redirect?" + |
| redirected_url.spec()); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url)); |
| content::WaitForLoadStop( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_EQ(u"Redirected!", |
| browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); |
| |
| base::Value::List blocklist; |
| blocklist.Append(redirected_url.spec().c_str()); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), first_url)); |
| content::WaitForLoadStop( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| EXPECT_NE(u"Redirected!", |
| browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, FileURLBlocklist) { |
| // Check that FileURLs can be blocklisted and DisabledSchemes works together |
| // with URLblocklisting and URLallowlisting. |
| |
| base::FilePath test_path; |
| GetTestDataDirectory(&test_path); |
| const std::string base_path = "file://" + test_path.AsUTF8Unsafe() + "/"; |
| const std::string folder_path = base_path + "apptest/"; |
| const std::string file_path1 = base_path + "title1.html"; |
| const std::string file_path2 = folder_path + "basic.html"; |
| |
| CheckCanOpenURL(browser(), file_path1); |
| CheckCanOpenURL(browser(), file_path2); |
| |
| // Set a blocklist for all the files. |
| base::Value::List blocklist; |
| blocklist.Append("file://*"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| CheckURLIsBlocked(browser(), file_path1); |
| CheckURLIsBlocked(browser(), file_path2); |
| |
| // Replace the URLblocklist with disabling the file scheme. |
| blocklist.EraseValue(base::Value("file://*")); |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| |
| EXPECT_FALSE(base::Contains(prefs->GetList(policy_prefs::kUrlBlocklist), |
| (base::Value("file://*")))); |
| |
| base::Value::List disabledscheme; |
| disabledscheme.Append("file"); |
| policies.Set(key::kDisabledSchemes, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(disabledscheme.Clone()), |
| nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| EXPECT_TRUE(base::Contains(prefs->GetList(policy_prefs::kUrlBlocklist), |
| base::Value("file://*"))); |
| |
| // Allowlist one folder and blocklist an another just inside. |
| base::Value::List allowlist; |
| allowlist.Append(base_path); |
| policies.Set(key::kURLAllowlist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(allowlist.Clone()), nullptr); |
| blocklist.Append(folder_path); |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| CheckCanOpenURL(browser(), file_path1); |
| CheckURLIsBlocked(browser(), file_path2); |
| } |
| |
| // Tests that javascript-links are handled properly according to blocklist |
| // settings, bug crbug/913334. |
| IN_PROC_BROWSER_TEST_F(UrlBlockingPolicyTest, JavascriptBlocklistable) { |
| embedded_test_server()->RegisterRequestHandler( |
| base::BindRepeating(&JSIncrementerPageHandler)); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| content::WebContents* contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_TRUE(ui_test_utils::NavigateToURL( |
| browser(), embedded_test_server()->GetURL("/test.html"))); |
| |
| EXPECT_EQ(JSIncrementerFetch(contents), 1); |
| |
| // Without blocklist policy value is incremented properly. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL("javascript:increment()"), |
| WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NO_WAIT); |
| |
| EXPECT_EQ(JSIncrementerFetch(contents), 2); |
| |
| // Create and apply a policy. |
| base::Value::List blocklist; |
| blocklist.Append("javascript://*"); |
| PolicyMap policies; |
| policies.Set(key::kURLBlocklist, POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER, |
| POLICY_SOURCE_CLOUD, base::Value(blocklist.Clone()), nullptr); |
| UpdateProviderPolicy(policies); |
| FlushBlocklistPolicy(); |
| |
| // After applying policy javascript URLs don't work any more, value leaves |
| // unchanged. |
| ui_test_utils::NavigateToURLWithDisposition( |
| browser(), GURL("javascript:increment()"), |
| WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NO_WAIT); |
| EXPECT_EQ(JSIncrementerFetch(contents), 2); |
| |
| // But in-page links still work even if they are javascript-links. |
| EXPECT_TRUE( |
| content::ExecJs(contents, "document.getElementById('link').click();")); |
| EXPECT_EQ(JSIncrementerFetch(contents), 3); |
| } |
| |
| } // namespace policy |