blob: e24cede34cbb4f16468c7c66fa2921cf3fc7f924 [file] [log] [blame]
davidben3b8455ae72015-03-11 19:42:191// 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
dchengc963c7142016-04-08 03:55:225#include <memory>
6
davidben3b8455ae72015-03-11 19:42:197#include "base/bind.h"
Nick Peterson87ecb102018-10-16 04:55:018#include "base/json/json_reader.h"
davidben3b8455ae72015-03-11 19:42:199#include "base/run_loop.h"
Devlin Cronind7f58572018-04-19 18:45:4310#include "base/strings/stringprintf.h"
Eric Seckler8652dcd52018-09-20 10:42:2811#include "base/task/post_task.h"
Devlin Cronind7f58572018-04-19 18:45:4312#include "chrome/browser/extensions/extension_apitest.h"
davidben3b8455ae72015-03-11 19:42:1913#include "chrome/browser/extensions/extension_browsertest.h"
Nick Peterson87ecb102018-10-16 04:55:0114#include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
davidben3b8455ae72015-03-11 19:42:1915#include "chrome/browser/profiles/profile.h"
16#include "chrome/browser/profiles/profile_io_data.h"
17#include "chrome/browser/ui/browser.h"
Devlin Cronind7f58572018-04-19 18:45:4318#include "chrome/common/chrome_switches.h"
davidben3b8455ae72015-03-11 19:42:1919#include "chrome/test/base/ui_test_utils.h"
Eric Seckler8652dcd52018-09-20 10:42:2820#include "content/public/browser/browser_task_traits.h"
davidben3b8455ae72015-03-11 19:42:1921#include "content/public/browser/browser_thread.h"
Clark DuVall444729a602018-08-08 23:09:5422#include "content/public/browser/storage_partition.h"
Nick Peterson87ecb102018-10-16 04:55:0123#include "content/public/test/browser_test_utils.h"
Devlin Cronind7f58572018-04-19 18:45:4324#include "extensions/browser/browsertest_util.h"
davidben3b8455ae72015-03-11 19:42:1925#include "extensions/common/extension.h"
Devlin Cronind7f58572018-04-19 18:45:4326#include "extensions/common/extension_urls.h"
27#include "extensions/test/extension_test_message_listener.h"
davidben3b8455ae72015-03-11 19:42:1928#include "extensions/test/result_catcher.h"
Devlin Cronind7f58572018-04-19 18:45:4329#include "extensions/test/test_extension_dir.h"
davidben3b8455ae72015-03-11 19:42:1930#include "net/base/escape.h"
31#include "net/base/url_util.h"
Devlin Cronind7f58572018-04-19 18:45:4332#include "net/dns/mock_host_resolver.h"
davidben3b8455ae72015-03-11 19:42:1933#include "net/ssl/client_cert_store.h"
svaldeza01f7d92015-11-18 17:47:5634#include "net/ssl/ssl_server_config.h"
35#include "net/test/embedded_test_server/embedded_test_server.h"
davidben3b8455ae72015-03-11 19:42:1936#include "url/gurl.h"
37
Devlin Cronind7f58572018-04-19 18:45:4338namespace extensions {
39
davidben3b8455ae72015-03-11 19:42:1940namespace {
41
Devlin Cronind7f58572018-04-19 18:45:4342constexpr const char kWebstoreDomain[] = "cws.com";
43
dchengc963c7142016-04-08 03:55:2244std::unique_ptr<net::ClientCertStore> CreateNullCertStore() {
davidben3b8455ae72015-03-11 19:42:1945 return nullptr;
46}
47
48void InstallNullCertStoreFactoryOnIOThread(
49 content::ResourceContext* resource_context) {
50 ProfileIOData::FromResourceContext(resource_context)
51 ->set_client_cert_store_factory_for_testing(
52 base::Bind(&CreateNullCertStore));
53}
54
55} // namespace
56
57class BackgroundXhrTest : public ExtensionBrowserTest {
58 protected:
59 void RunTest(const std::string& path, const GURL& url) {
Devlin Cronind7f58572018-04-19 18:45:4360 const Extension* extension =
davidben3b8455ae72015-03-11 19:42:1961 LoadExtension(test_data_dir_.AppendASCII("background_xhr"));
62 ASSERT_TRUE(extension);
63
Devlin Cronind7f58572018-04-19 18:45:4364 ResultCatcher catcher;
davidben3b8455ae72015-03-11 19:42:1965 GURL test_url = net::AppendQueryParameter(extension->GetResourceURL(path),
66 "url", url.spec());
67 ui_test_utils::NavigateToURL(browser(), test_url);
Clark DuVall444729a602018-08-08 23:09:5468 content::BrowserContext::GetDefaultStoragePartition(profile())
69 ->FlushNetworkInterfaceForTesting();
Clark DuVall16be2542018-07-23 22:42:4270 constexpr char kSendXHRScript[] = R"(
71 var xhr = new XMLHttpRequest();
72 xhr.open('GET', '%s');
73 xhr.send();
74 domAutomationController.send('');
75 )";
76 browsertest_util::ExecuteScriptInBackgroundPage(
77 profile(), extension->id(),
78 base::StringPrintf(kSendXHRScript, url.spec().c_str()));
davidben3b8455ae72015-03-11 19:42:1979 ASSERT_TRUE(catcher.GetNextResult());
80 }
81};
82
83// Test that fetching a URL using TLS client auth doesn't crash, hang, or
84// prompt.
85IN_PROC_BROWSER_TEST_F(BackgroundXhrTest, TlsClientAuth) {
86 // Install a null ClientCertStore so the client auth prompt isn't bypassed due
87 // to the system certificate store returning no certificates.
88 base::RunLoop loop;
Eric Seckler8652dcd52018-09-20 10:42:2889 base::PostTaskWithTraitsAndReply(
90 FROM_HERE, {content::BrowserThread::IO},
tzik8d880ee2017-04-20 19:46:2491 base::BindOnce(&InstallNullCertStoreFactoryOnIOThread,
92 browser()->profile()->GetResourceContext()),
davidben3b8455ae72015-03-11 19:42:1993 loop.QuitClosure());
94 loop.Run();
95
96 // Launch HTTPS server.
svaldeza01f7d92015-11-18 17:47:5697 net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
98 net::SSLServerConfig ssl_config;
ryanchung987b2ff2016-02-19 00:17:1299 ssl_config.client_cert_type =
100 net::SSLServerConfig::ClientCertType::REQUIRE_CLIENT_CERT;
svaldeza01f7d92015-11-18 17:47:56101 https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK, ssl_config);
102 https_server.ServeFilesFromSourceDirectory("content/test/data");
davidben3b8455ae72015-03-11 19:42:19103 ASSERT_TRUE(https_server.Start());
104
105 ASSERT_NO_FATAL_FAILURE(
svaldeza01f7d92015-11-18 17:47:56106 RunTest("test_tls_client_auth.html", https_server.GetURL("/")));
davidben3b8455ae72015-03-11 19:42:19107}
108
109// Test that fetching a URL using HTTP auth doesn't crash, hang, or prompt.
110IN_PROC_BROWSER_TEST_F(BackgroundXhrTest, HttpAuth) {
svaldeza01f7d92015-11-18 17:47:56111 ASSERT_TRUE(embedded_test_server()->Start());
112 ASSERT_NO_FATAL_FAILURE(RunTest(
113 "test_http_auth.html", embedded_test_server()->GetURL("/auth-basic")));
davidben3b8455ae72015-03-11 19:42:19114}
Devlin Cronind7f58572018-04-19 18:45:43115
Nick Peterson87ecb102018-10-16 04:55:01116class BackgroundXhrWebstoreTest : public ExtensionApiTestWithManagementPolicy {
Devlin Cronind7f58572018-04-19 18:45:43117 public:
118 BackgroundXhrWebstoreTest() = default;
119 ~BackgroundXhrWebstoreTest() override = default;
120
121 void SetUpCommandLine(base::CommandLine* command_line) override {
122 ExtensionApiTest::SetUpCommandLine(command_line);
123 // TODO(devlin): For some reason, trying to fetch an HTTPS url in this test
124 // fails (even when using an HTTPS EmbeddedTestServer). For this reason, we
125 // need to fake the webstore URLs as http versions.
126 command_line->AppendSwitchASCII(
127 ::switches::kAppsGalleryURL,
128 base::StringPrintf("http://%s", kWebstoreDomain));
129 }
130
131 void SetUpOnMainThread() override {
132 ExtensionApiTest::SetUpOnMainThread();
133 host_resolver()->AddRule("*", "127.0.0.1");
134 ASSERT_TRUE(embedded_test_server()->Start());
135 }
136
Nick Peterson87ecb102018-10-16 04:55:01137 bool CanFetch(const Extension* extension, const GURL& url) {
138 content::DOMMessageQueue message_queue;
139 browsertest_util::ExecuteScriptInBackgroundPageNoWait(
140 profile(), extension->id(),
141 base::StringPrintf("canFetch('%s');", url.spec().c_str()));
142 std::string json;
143 EXPECT_TRUE(message_queue.WaitForMessage(&json));
144 base::JSONReader reader(base::JSON_ALLOW_TRAILING_COMMAS);
145 std::unique_ptr<base::Value> value = reader.ReadToValue(json);
146 std::string result;
147 EXPECT_TRUE(value->GetAsString(&result));
148 EXPECT_TRUE(result == "true" || result == "false") << result;
149 return result == "true";
150 }
151
152 const Extension* LoadXhrExtension(const std::string& host) {
153 ExtensionTestMessageListener listener("ready", false);
154 TestExtensionDir test_dir;
155 test_dir.WriteManifest(R"(
156 {
157 "name": "XHR Test",
158 "manifest_version": 2,
159 "version": "0.1",
160 "background": {"scripts": ["background.js"]},
161 "permissions": [")" + host + R"("]
162 })");
163 constexpr char kBackgroundScriptFile[] = R"(
164 function canFetch(url) {
165 console.warn('Fetching: ' + url);
166 fetch(url).then((response) => {
167 domAutomationController.send('true');
168 }).catch((e) => {
169 let message;
170 if (e.message == 'Failed to fetch')
171 message = 'false'
172 else
173 message = 'Unexpected Error: ' + e.message;
174 domAutomationController.send(message);
175 });
176 }
177 chrome.test.sendMessage('ready');)";
178
179 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"),
180 kBackgroundScriptFile);
181 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
182 EXPECT_TRUE(listener.WaitUntilSatisfied());
183 return extension;
184 }
185
Devlin Cronind7f58572018-04-19 18:45:43186 private:
187 DISALLOW_COPY_AND_ASSIGN(BackgroundXhrWebstoreTest);
188};
189
190// Extensions should not be able to XHR to the webstore.
191IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, XHRToWebstore) {
Nick Peterson87ecb102018-10-16 04:55:01192 const Extension* extension = LoadXhrExtension("<all_urls>");
Devlin Cronind7f58572018-04-19 18:45:43193
194 GURL webstore_launch_url = extension_urls::GetWebstoreLaunchURL();
195 GURL webstore_url_to_fetch = embedded_test_server()->GetURL(
196 webstore_launch_url.host(), "/simple.html");
197
Nick Peterson87ecb102018-10-16 04:55:01198 EXPECT_FALSE(CanFetch(extension, webstore_url_to_fetch));
Devlin Cronind7f58572018-04-19 18:45:43199
200 // Sanity check: the extension should be able to fetch google.com.
201 GURL google_url =
202 embedded_test_server()->GetURL("google.com", "/simple.html");
Nick Peterson87ecb102018-10-16 04:55:01203 EXPECT_TRUE(CanFetch(extension, google_url));
204}
205
206// Extensions should not be able to XHR to the webstore regardless of policy.
207IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, XHRToWebstorePolicy) {
208 {
209 ExtensionManagementPolicyUpdater pref(&policy_provider_);
210 pref.AddPolicyAllowedHost(
211 "*", "*://" + extension_urls::GetWebstoreLaunchURL().host());
212 }
213
214 const Extension* extension = LoadXhrExtension("<all_urls>");
215
216 GURL webstore_launch_url = extension_urls::GetWebstoreLaunchURL();
217 GURL webstore_url_to_fetch = embedded_test_server()->GetURL(
218 webstore_launch_url.host(), "/simple.html");
219
220 EXPECT_FALSE(CanFetch(extension, webstore_url_to_fetch));
221
222 // Sanity check: the extension should be able to fetch google.com.
223 GURL google_url =
224 embedded_test_server()->GetURL("google.com", "/simple.html");
225 EXPECT_TRUE(CanFetch(extension, google_url));
226}
227
228// Extensions should not be able to bypass same-origin despite declaring
229// <all_urls> for hosts restricted by enterprise policy.
230IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyBlockedXHR) {
231 {
232 ExtensionManagementPolicyUpdater pref(&policy_provider_);
233 pref.AddPolicyBlockedHost("*", "*://*.example.com");
234 pref.AddPolicyAllowedHost("*", "*://public.example.com");
235 }
236
237 const Extension* extension = LoadXhrExtension("<all_urls>");
238
239 // Should block due to "runtime_blocked_hosts" section of policy.
240 GURL protected_url_to_fetch =
241 embedded_test_server()->GetURL("example.com", "/simple.html");
242 EXPECT_FALSE(CanFetch(extension, protected_url_to_fetch));
243
244 // Should allow due to "runtime_allowed_hosts" section of policy.
245 GURL exempted_url_to_fetch =
246 embedded_test_server()->GetURL("public.example.com", "/simple.html");
247 EXPECT_TRUE(CanFetch(extension, exempted_url_to_fetch));
248}
249
250// Verify that policy blocklists apply to XHRs done from injected scripts.
251IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyContentScriptXHR) {
252 TestExtensionDir test_dir;
253 test_dir.WriteManifest(R"(
254 {
255 "name": "XHR Content Script Test",
256 "manifest_version": 2,
257 "version": "0.1",
258 "permissions": ["<all_urls>", "tabs"],
259 "background": {"scripts": ["background.js"]}
260 })");
261
262 constexpr char kBackgroundScript[] =
263 R"(function canFetch(url) {
264 chrome.tabs.executeScript({code: `
265 fetch("${url}")
266 .then(response => response.text())
267 .then(text => domAutomationController.send('true'))
268 .catch(err => domAutomationController.send('false'));
269 `});
270 }
271 )";
272 test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundScript);
273
274 const Extension* extension = LoadExtension(test_dir.UnpackedPath());
275 ASSERT_TRUE(extension);
276
277 // Navigate to a foo.com page.
278 content::WebContents* web_contents =
279 browser()->tab_strip_model()->GetActiveWebContents();
280 GURL page_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
281 ui_test_utils::NavigateToURL(browser(), page_url);
282 EXPECT_EQ(page_url, web_contents->GetMainFrame()->GetLastCommittedURL());
283
284 GURL example_url =
285 embedded_test_server()->GetURL("example.com", "/simple.html");
286 GURL public_example_url =
287 embedded_test_server()->GetURL("public.example.com", "/simple.html");
288
289 // Sanity Check: Should be able to fetch cross origin.
290 EXPECT_TRUE(CanFetch(extension, example_url));
291 EXPECT_TRUE(CanFetch(extension, public_example_url));
292
293 {
294 ExtensionManagementPolicyUpdater pref(&policy_provider_);
295 pref.AddPolicyBlockedHost("*", "*://*.example.com");
296 pref.AddPolicyAllowedHost("*", "*://public.example.com");
297 }
298
299 // Policies apply to XHR from a content script.
300 EXPECT_FALSE(CanFetch(extension, example_url));
301 EXPECT_TRUE(CanFetch(extension, public_example_url));
302}
303
304// Make sure the blocklist and allowlist update for both Default and Individual
305// scope policies. Testing with all host permissions granted (<all_urls>).
306IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyUpdateXHR) {
307 const Extension* extension = LoadXhrExtension("<all_urls>");
308
309 GURL example_url =
310 embedded_test_server()->GetURL("example.com", "/simple.html");
311 GURL public_example_url =
312 embedded_test_server()->GetURL("public.example.com", "/simple.html");
313
314 // Sanity check: Without restrictions all fetches should work.
315 EXPECT_TRUE(CanFetch(extension, public_example_url));
316 EXPECT_TRUE(CanFetch(extension, example_url));
317
318 {
319 ExtensionManagementPolicyUpdater pref(&policy_provider_);
320 pref.AddPolicyBlockedHost("*", "*://*.example.com");
321 pref.AddPolicyAllowedHost("*", "*://public.example.com");
322 }
323
324 // Default policies propagate.
325 EXPECT_TRUE(CanFetch(extension, public_example_url));
326 EXPECT_FALSE(CanFetch(extension, example_url));
327 {
328 ExtensionManagementPolicyUpdater pref(&policy_provider_);
329 pref.AddPolicyBlockedHost(extension->id(), "*://*.example2.com");
330 pref.AddPolicyAllowedHost(extension->id(), "*://public.example2.com");
331 }
332
333 // Default policies overridden when individual scope policies applied.
334 EXPECT_TRUE(CanFetch(extension, public_example_url));
335 EXPECT_TRUE(CanFetch(extension, example_url));
336
337 GURL example2_url =
338 embedded_test_server()->GetURL("example2.com", "/simple.html");
339 GURL public_example2_url =
340 embedded_test_server()->GetURL("public.example2.com", "/simple.html");
341
342 // Individual scope policies propagate.
343 EXPECT_TRUE(CanFetch(extension, public_example2_url));
344 EXPECT_FALSE(CanFetch(extension, example2_url));
345}
346
347// Make sure the allowlist entries added due to host permissions are removed
348// when a more generic blocklist policy is updated and contains them.
349// This tests the default policy scope update.
350IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyUpdateDefaultXHR) {
351 const Extension* extension = LoadXhrExtension("*://public.example.com/*");
352
353 GURL example_url =
354 embedded_test_server()->GetURL("example.com", "/simple.html");
355 GURL public_example_url =
356 embedded_test_server()->GetURL("public.example.com", "/simple.html");
357
358 // Sanity check: Without restrictions only public.example.com should work.
359 EXPECT_TRUE(CanFetch(extension, public_example_url));
360 EXPECT_FALSE(CanFetch(extension, example_url));
361
362 {
363 ExtensionManagementPolicyUpdater pref(&policy_provider_);
364 pref.AddPolicyBlockedHost("*", "*://*.example.com");
365 }
366
367 // The blocklist of example.com overrides allowlist of public.example.com.
368 EXPECT_FALSE(CanFetch(extension, example_url));
369 EXPECT_FALSE(CanFetch(extension, public_example_url));
370}
371
372// Make sure the allowlist entries added due to host permissions are removed
373// when a more generic blocklist policy is updated and contains them.
374// This tests an individual policy scope update.
375IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyUpdateIndividualXHR) {
376 const Extension* extension = LoadXhrExtension("*://public.example.com/*");
377
378 GURL example_url =
379 embedded_test_server()->GetURL("example.com", "/simple.html");
380 GURL public_example_url =
381 embedded_test_server()->GetURL("public.example.com", "/simple.html");
382
383 // Sanity check: Without restrictions only public.example.com should work.
384 EXPECT_TRUE(CanFetch(extension, public_example_url));
385 EXPECT_FALSE(CanFetch(extension, example_url));
386
387 {
388 ExtensionManagementPolicyUpdater pref(&policy_provider_);
389 pref.AddPolicyBlockedHost(extension->id(), "*://*.example.com");
390 }
391
392 // The blocklist of example.com overrides allowlist of public.example.com.
393 EXPECT_FALSE(CanFetch(extension, example_url));
394 EXPECT_FALSE(CanFetch(extension, public_example_url));
Devlin Cronind7f58572018-04-19 18:45:43395}
396
397} // namespace extensions