blob: d97cdbcd749010ff4a5bc90e1214754b85d1190c [file] [log] [blame]
[email protected]1791e6c92014-04-11 08:29:011// Copyright 2014 The Chromium Authors. All rights reserved.
[email protected]5e212ed2012-03-21 23:29:152// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
avia2f4804a2015-12-24 23:11:135#include <stddef.h>
6
dchengc963c7142016-04-08 03:55:227#include <memory>
[email protected]5e212ed2012-03-21 23:29:158#include <string>
vabr9984ea62017-04-10 11:33:499#include <utility>
[email protected]5e212ed2012-03-21 23:29:1510
lazyboyd6dbb262017-03-30 00:57:3011#include "base/command_line.h"
thestigc9e38a22014-09-13 01:02:1112#include "base/files/file_util.h"
avia2f4804a2015-12-24 23:11:1313#include "base/macros.h"
fdoraycb32419d2016-06-23 15:52:5514#include "base/run_loop.h"
[email protected]774cebd2013-09-26 04:55:0115#include "base/strings/string_number_conversions.h"
[email protected]00e7bef2013-06-10 20:35:1716#include "base/strings/string_util.h"
lazyboyd6dbb262017-03-30 00:57:3017#include "base/test/test_file_util.h"
Devlin Croninc3f88072018-01-30 02:10:1118#include "base/test/values_test_util.h"
[email protected]06492ed2013-03-24 22:13:1419#include "base/values.h"
lazyboyd6dbb262017-03-30 00:57:3020#include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
[email protected]93ac047a2012-12-13 02:53:4921#include "chrome/common/chrome_paths.h"
lazyboyd6dbb262017-03-30 00:57:3022#include "chrome/common/chrome_switches.h"
23#include "chrome/test/base/testing_profile.h"
24#include "components/crx_file/id_util.h"
[email protected]5e212ed2012-03-21 23:29:1525#include "content/public/browser/resource_request_info.h"
megjabloncaf312f2017-01-12 18:47:4926#include "content/public/common/previews_state.h"
[email protected]08a932d52012-06-03 21:42:1227#include "content/public/test/mock_resource_context.h"
[email protected]ec04d3f2013-06-06 21:31:3928#include "content/public/test/test_browser_thread_bundle.h"
Chris Mumford8f812662018-02-22 00:27:5729#include "content/public/test/test_renderer_host.h"
lazyboyd6dbb262017-03-30 00:57:3030#include "content/public/test/test_utils.h"
Chris Mumford8f812662018-02-22 00:27:5731#include "content/public/test/web_contents_tester.h"
lazyboyd6dbb262017-03-30 00:57:3032#include "extensions/browser/content_verifier.h"
Istiaque Ahmed72816eaa2018-01-30 22:37:0633#include "extensions/browser/content_verifier/test_utils.h"
Chris Mumford8f812662018-02-22 00:27:5734#include "extensions/browser/extension_prefs.h"
[email protected]1791e6c92014-04-11 08:29:0135#include "extensions/browser/extension_protocols.h"
Chris Mumford8f812662018-02-22 00:27:5736#include "extensions/browser/extension_registry.h"
[email protected]38427a12013-11-09 17:34:2037#include "extensions/browser/info_map.h"
[email protected]885c0e92012-11-13 20:27:4238#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4139#include "extensions/common/extension.h"
lazyboyd6dbb262017-03-30 00:57:3040#include "extensions/common/extension_builder.h"
Istiaque Ahmed72816eaa2018-01-30 22:37:0641#include "extensions/common/extension_paths.h"
asargenta093ec32016-02-13 01:36:4342#include "extensions/common/file_util.h"
Devlin Croninc3f88072018-01-30 02:10:1143#include "extensions/test/test_extension_dir.h"
[email protected]2ca01e52013-10-31 22:05:1944#include "net/base/request_priority.h"
rhalavati04b93382017-04-07 19:00:5445#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
[email protected]5e212ed2012-03-21 23:29:1546#include "net/url_request/url_request.h"
[email protected]9d5730b2012-08-24 17:42:4947#include "net/url_request/url_request_job_factory_impl.h"
[email protected]5e212ed2012-03-21 23:29:1548#include "net/url_request/url_request_status.h"
49#include "net/url_request/url_request_test_util.h"
Chris Mumford8f812662018-02-22 00:27:5750#include "services/network/test/test_url_loader_client.h"
[email protected]5e212ed2012-03-21 23:29:1551#include "testing/gtest/include/gtest/gtest.h"
52
[email protected]7491ad02014-07-05 19:10:0753using content::ResourceType;
Chris Mumford8f812662018-02-22 00:27:5754using extensions::ExtensionRegistry;
55using network::mojom::URLLoader;
[email protected]7491ad02014-07-05 19:10:0756
[email protected]702d8b42013-02-27 20:55:5057namespace extensions {
jamescook8816ae52014-09-05 17:02:3758namespace {
[email protected]5e212ed2012-03-21 23:29:1559
Chris Mumford8f812662018-02-22 00:27:5760enum class RequestHandlerType {
61 kURLLoader,
62 kURLRequest,
63};
64
65const RequestHandlerType kTestModes[] = {RequestHandlerType::kURLLoader,
66 RequestHandlerType::kURLRequest};
67
asargenta093ec32016-02-13 01:36:4368base::FilePath GetTestPath(const std::string& name) {
69 base::FilePath path;
70 EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
71 return path.AppendASCII("extensions").AppendASCII(name);
72}
73
Istiaque Ahmed72816eaa2018-01-30 22:37:0674base::FilePath GetContentVerifierTestPath() {
75 base::FilePath path;
76 EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &path));
77 return path.AppendASCII("content_hash_fetcher")
78 .AppendASCII("different_sized_files");
lazyboyd6dbb262017-03-30 00:57:3079}
80
[email protected]5e212ed2012-03-21 23:29:1581scoped_refptr<Extension> CreateTestExtension(const std::string& name,
82 bool incognito_split_mode) {
[email protected]023b3d12013-12-23 18:46:4983 base::DictionaryValue manifest;
[email protected]5e212ed2012-03-21 23:29:1584 manifest.SetString("name", name);
85 manifest.SetString("version", "1");
[email protected]b109bdd2013-11-04 18:08:4386 manifest.SetInteger("manifest_version", 2);
[email protected]5e212ed2012-03-21 23:29:1587 manifest.SetString("incognito", incognito_split_mode ? "split" : "spanning");
88
asargenta093ec32016-02-13 01:36:4389 base::FilePath path = GetTestPath("response_headers");
[email protected]5e212ed2012-03-21 23:29:1590
91 std::string error;
92 scoped_refptr<Extension> extension(
[email protected]1d5e58b2013-01-31 08:41:4093 Extension::Create(path, Manifest::INTERNAL, manifest,
[email protected]ed3b9b12012-05-31 18:37:5194 Extension::NO_FLAGS, &error));
[email protected]5e212ed2012-03-21 23:29:1595 EXPECT_TRUE(extension.get()) << error;
96 return extension;
97}
98
[email protected]93ac047a2012-12-13 02:53:4999scoped_refptr<Extension> CreateWebStoreExtension() {
Devlin Croninf43c3312018-03-27 19:54:17100 std::unique_ptr<base::DictionaryValue> manifest =
101 DictionaryBuilder()
102 .Set("name", "WebStore")
103 .Set("version", "1")
104 .Set("manifest_version", 2)
105 .Set("icons",
106 DictionaryBuilder().Set("16", "webstore_icon_16.png").Build())
107 .Set("web_accessible_resources",
108 ListBuilder().Append("webstore_icon_16.png").Build())
109 .Build();
[email protected]93ac047a2012-12-13 02:53:49110
[email protected]650b2d52013-02-10 03:41:45111 base::FilePath path;
[email protected]93ac047a2012-12-13 02:53:49112 EXPECT_TRUE(PathService::Get(chrome::DIR_RESOURCES, &path));
113 path = path.AppendASCII("web_store");
114
115 std::string error;
Devlin Croninf43c3312018-03-27 19:54:17116 scoped_refptr<Extension> extension(Extension::Create(
117 path, Manifest::COMPONENT, *manifest, Extension::NO_FLAGS, &error));
[email protected]93ac047a2012-12-13 02:53:49118 EXPECT_TRUE(extension.get()) << error;
119 return extension;
120}
121
[email protected]6f7d7062013-06-04 03:49:33122scoped_refptr<Extension> CreateTestResponseHeaderExtension() {
[email protected]023b3d12013-12-23 18:46:49123 base::DictionaryValue manifest;
[email protected]6f7d7062013-06-04 03:49:33124 manifest.SetString("name", "An extension with web-accessible resources");
125 manifest.SetString("version", "2");
126
Jinho Bangb5216cec2018-01-17 19:43:11127 auto web_accessible_list = std::make_unique<base::ListValue>();
[email protected]6f7d7062013-06-04 03:49:33128 web_accessible_list->AppendString("test.dat");
vabr9984ea62017-04-10 11:33:49129 manifest.Set("web_accessible_resources", std::move(web_accessible_list));
[email protected]6f7d7062013-06-04 03:49:33130
asargenta093ec32016-02-13 01:36:43131 base::FilePath path = GetTestPath("response_headers");
[email protected]6f7d7062013-06-04 03:49:33132
133 std::string error;
134 scoped_refptr<Extension> extension(
135 Extension::Create(path, Manifest::UNPACKED, manifest,
136 Extension::NO_FLAGS, &error));
137 EXPECT_TRUE(extension.get()) << error;
138 return extension;
139}
140
Chris Mumford8f812662018-02-22 00:27:57141// Helper function to create a |ResourceRequest| for testing purposes.
142network::ResourceRequest CreateResourceRequest(const std::string& method,
143 ResourceType resource_type,
144 const GURL& url) {
145 network::ResourceRequest request;
146 request.method = method;
147 request.url = url;
148 request.site_for_cookies = url; // bypass third-party cookie blocking.
149 request.request_initiator =
150 url::Origin::Create(url); // ensure initiator set.
151 request.referrer_policy = content::Referrer::GetDefaultReferrerPolicy();
152 request.resource_type = resource_type;
153 request.is_main_frame = resource_type == content::RESOURCE_TYPE_MAIN_FRAME;
154 request.allow_download = true;
155 return request;
156}
157
158// The result of either a URLRequest of a URLLoader response (but not both)
159// depending on the on test type.
160class GetResult {
161 public:
162 GetResult(std::unique_ptr<net::URLRequest> request, int result)
163 : request_(std::move(request)), result_(result) {}
164 GetResult(const network::ResourceResponseHead& response, int result)
165 : resource_response_(response), result_(result) {}
166 GetResult(GetResult&& other)
167 : request_(std::move(other.request_)), result_(other.result_) {}
168 ~GetResult() = default;
169
170 std::string GetResponseHeaderByName(const std::string& name) const {
171 std::string value;
172 if (request_)
173 request_->GetResponseHeaderByName(name, &value);
174 else if (resource_response_.headers)
175 resource_response_.headers->GetNormalizedHeader(name, &value);
176 return value;
177 }
178
179 int result() const { return result_; }
180
181 private:
182 std::unique_ptr<net::URLRequest> request_;
183 const network::ResourceResponseHead resource_response_;
184 int result_;
185
186 DISALLOW_COPY_AND_ASSIGN(GetResult);
187};
188
jamescook8816ae52014-09-05 17:02:37189} // namespace
190
191// This test lives in src/chrome instead of src/extensions because it tests
192// functionality delegated back to Chrome via ChromeExtensionsBrowserClient.
lfg048201a2014-09-16 19:09:36193// See chrome/browser/extensions/chrome_url_request_util.cc.
Chris Mumford8f812662018-02-22 00:27:57194class ExtensionProtocolsTest
195 : public testing::Test,
196 public testing::WithParamInterface<RequestHandlerType> {
[email protected]5e212ed2012-03-21 23:29:15197 public:
lazyboyd6dbb262017-03-30 00:57:30198 ExtensionProtocolsTest()
[email protected]1791e6c92014-04-11 08:29:01199 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
Chris Mumford8f812662018-02-22 00:27:57200 rvh_test_enabler_(new content::RenderViewHostTestEnabler()),
[email protected]1791e6c92014-04-11 08:29:01201 old_factory_(NULL),
202 resource_context_(&test_url_request_context_) {}
[email protected]5e212ed2012-03-21 23:29:15203
dcheng72191812014-10-28 20:49:56204 void SetUp() override {
[email protected]06492ed2013-03-24 22:13:14205 testing::Test::SetUp();
lazyboyd6dbb262017-03-30 00:57:30206 testing_profile_ = TestingProfile::Builder().Build();
Chris Mumford8f812662018-02-22 00:27:57207 contents_.reset(CreateTestWebContents());
[email protected]38427a12013-11-09 17:34:20208 extension_info_map_ = new InfoMap();
Chris Mumford8f812662018-02-22 00:27:57209 old_factory_ = resource_context_.GetRequestContext()->job_factory();
lazyboyd6dbb262017-03-30 00:57:30210
211 // Set up content verification.
212 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
213 command_line->AppendSwitchASCII(
214 switches::kExtensionContentVerification,
215 switches::kExtensionContentVerificationEnforce);
216 content_verifier_ = new ContentVerifier(
Chris Mumford8f812662018-02-22 00:27:57217 browser_context(),
218 std::make_unique<ChromeContentVerifierDelegate>(browser_context()));
lazyboyd6dbb262017-03-30 00:57:30219 extension_info_map_->SetContentVerifier(content_verifier_.get());
[email protected]5e212ed2012-03-21 23:29:15220 }
221
dcheng72191812014-10-28 20:49:56222 void TearDown() override {
Chris Mumford8f812662018-02-22 00:27:57223 loader_factory_.reset();
224 resource_context_.GetRequestContext()->set_job_factory(old_factory_);
lazyboyd6dbb262017-03-30 00:57:30225 content_verifier_->Shutdown();
[email protected]5e212ed2012-03-21 23:29:15226 }
227
[email protected]1791e6c92014-04-11 08:29:01228 void SetProtocolHandler(bool is_incognito) {
Chris Mumford8f812662018-02-22 00:27:57229 switch (request_handler()) {
230 case RequestHandlerType::kURLLoader:
231 loader_factory_ = extensions::CreateExtensionNavigationURLLoaderFactory(
232 main_rfh(), extension_info_map_.get());
233 break;
234 case RequestHandlerType::kURLRequest:
235 job_factory_.SetProtocolHandler(
236 kExtensionScheme, CreateExtensionProtocolHandler(
237 is_incognito, extension_info_map_.get()));
238 resource_context_.GetRequestContext()->set_job_factory(&job_factory_);
239 break;
240 }
241 testing_profile_->ForceIncognito(is_incognito);
[email protected]93ac047a2012-12-13 02:53:49242 }
243
Chris Mumford8f812662018-02-22 00:27:57244 GetResult RequestOrLoad(const GURL& url, ResourceType resource_type) {
245 switch (request_handler()) {
246 case RequestHandlerType::kURLLoader:
247 return LoadURL(url, resource_type);
248 case RequestHandlerType::kURLRequest:
249 return RequestURL(url, resource_type);
250 }
251 NOTREACHED();
252 return GetResult(nullptr, net::ERR_FAILED);
253 }
254
255 void AddExtension(const scoped_refptr<const Extension>& extension,
256 bool incognito_enabled,
257 bool notifications_disabled) {
258 extension_info_map_->AddExtension(extension.get(), base::Time::Now(),
259 incognito_enabled,
260 notifications_disabled);
261 if (request_handler() == RequestHandlerType::kURLLoader) {
262 EXPECT_TRUE(extension_registry()->AddEnabled(extension));
263 ExtensionPrefs::Get(browser_context())
264 ->SetIsIncognitoEnabled(extension->id(), incognito_enabled);
265 }
266 }
267
268 void RemoveExtension(const scoped_refptr<const Extension>& extension,
269 const UnloadedExtensionReason reason) {
270 extension_info_map_->RemoveExtension(extension->id(), reason);
271 if (request_handler() == RequestHandlerType::kURLLoader) {
272 EXPECT_TRUE(extension_registry()->RemoveEnabled(extension->id()));
273 if (reason == UnloadedExtensionReason::DISABLE)
274 EXPECT_TRUE(extension_registry()->AddDisabled(extension));
275 }
276 }
277
278 // Helper method to create a URL request/loader, call RequestOrLoad on it, and
279 // return the result. If |extension| hasn't already been added to
280 // |extension_info_map_|, this will add it.
281 GetResult DoRequestOrLoad(const scoped_refptr<Extension> extension,
282 const std::string& relative_path) {
283 if (!extension_info_map_->extensions().Contains(extension->id())) {
284 AddExtension(extension.get(),
285 /*incognito_enabled=*/false,
286 /*notifications_disabled=*/false);
287 }
288 return RequestOrLoad(extension->GetResourceURL(relative_path),
289 content::RESOURCE_TYPE_MAIN_FRAME);
290 }
291
292 ExtensionRegistry* extension_registry() {
293 return ExtensionRegistry::Get(browser_context());
294 }
295
296 content::BrowserContext* browser_context() { return testing_profile_.get(); }
297
298 protected:
299 scoped_refptr<ContentVerifier> content_verifier_;
300
301 private:
302 GetResult LoadURL(const GURL& url, ResourceType resource_type) {
303 constexpr int32_t kRoutingId = 81;
304 constexpr int32_t kRequestId = 28;
305
306 network::mojom::URLLoaderPtr loader;
307 network::TestURLLoaderClient client;
308 loader_factory_->CreateLoaderAndStart(
309 mojo::MakeRequest(&loader), kRoutingId, kRequestId,
310 network::mojom::kURLLoadOptionNone,
311 CreateResourceRequest("GET", resource_type, url),
312 client.CreateInterfacePtr(),
313 net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
314
315 client.RunUntilComplete();
316 return GetResult(client.response_head(),
317 client.completion_status().error_code);
318 }
319
320 GetResult RequestURL(const GURL& url, ResourceType resource_type) {
321 auto request = resource_context_.GetRequestContext()->CreateRequest(
322 url, net::DEFAULT_PRIORITY, &test_delegate_,
323 TRAFFIC_ANNOTATION_FOR_TESTS);
324
gabf9d15582014-11-13 16:40:15325 content::ResourceRequestInfo::AllocateForTesting(
Chris Mumford8f812662018-02-22 00:27:57326 request.get(), resource_type, &resource_context_,
megjabloncaf312f2017-01-12 18:47:49327 /*render_process_id=*/-1,
328 /*render_view_id=*/-1,
329 /*render_frame_id=*/-1,
330 /*is_main_frame=*/resource_type == content::RESOURCE_TYPE_MAIN_FRAME,
megjabloncaf312f2017-01-12 18:47:49331 /*allow_download=*/true,
Jian Li18173422017-11-08 03:00:02332 /*is_async=*/false, content::PREVIEWS_OFF,
333 /*navigation_ui_data*/ nullptr);
[email protected]5e212ed2012-03-21 23:29:15334 request->Start();
fdoraycb32419d2016-06-23 15:52:55335 base::RunLoop().Run();
Chris Mumford8f812662018-02-22 00:27:57336 return GetResult(std::move(request), test_delegate_.request_status());
[email protected]5e212ed2012-03-21 23:29:15337 }
338
Chris Mumford8f812662018-02-22 00:27:57339 content::WebContents* CreateTestWebContents() {
340 auto site_instance = content::SiteInstance::Create(browser_context());
341 return content::WebContentsTester::CreateTestWebContents(
342 browser_context(), std::move(site_instance));
asargenta093ec32016-02-13 01:36:43343 }
344
Chris Mumford8f812662018-02-22 00:27:57345 content::WebContents* web_contents() { return contents_.get(); }
346
347 content::RenderFrameHost* main_rfh() {
348 return web_contents()->GetMainFrame();
349 }
350
351 RequestHandlerType request_handler() const { return GetParam(); }
352
[email protected]ec04d3f2013-06-06 21:31:39353 content::TestBrowserThreadBundle thread_bundle_;
Chris Mumford8f812662018-02-22 00:27:57354 std::unique_ptr<content::RenderViewHostTestEnabler> rvh_test_enabler_;
[email protected]9d5730b2012-08-24 17:42:49355 net::URLRequestJobFactoryImpl job_factory_;
[email protected]5e212ed2012-03-21 23:29:15356 const net::URLRequestJobFactory* old_factory_;
Chris Mumford8f812662018-02-22 00:27:57357 std::unique_ptr<network::mojom::URLLoaderFactory> loader_factory_;
[email protected]37ac95b2013-07-23 23:39:35358 net::TestURLRequestContext test_url_request_context_;
[email protected]5e212ed2012-03-21 23:29:15359 content::MockResourceContext resource_context_;
lazyboyd6dbb262017-03-30 00:57:30360 std::unique_ptr<TestingProfile> testing_profile_;
Chris Mumford8f812662018-02-22 00:27:57361 net::TestDelegate test_delegate_;
362 std::unique_ptr<content::WebContents> contents_;
363 scoped_refptr<InfoMap> extension_info_map_;
[email protected]5e212ed2012-03-21 23:29:15364};
365
366// Tests that making a chrome-extension request in an incognito context is
367// only allowed under the right circumstances (if the extension is allowed
368// in incognito, and it's either a non-main-frame request or a split-mode
369// extension).
Chris Mumford8f812662018-02-22 00:27:57370TEST_P(ExtensionProtocolsTest, IncognitoRequest) {
[email protected]93ac047a2012-12-13 02:53:49371 // Register an incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01372 SetProtocolHandler(true);
[email protected]93ac047a2012-12-13 02:53:49373
[email protected]5e212ed2012-03-21 23:29:15374 struct TestCase {
375 // Inputs.
376 std::string name;
377 bool incognito_split_mode;
378 bool incognito_enabled;
379
380 // Expected results.
381 bool should_allow_main_frame_load;
382 bool should_allow_sub_frame_load;
383 } cases[] = {
384 {"spanning disabled", false, false, false, false},
385 {"split disabled", true, false, false, false},
nasko5cf9d452016-06-01 05:34:56386 {"spanning enabled", false, true, false, false},
387 {"split enabled", true, true, true, false},
[email protected]5e212ed2012-03-21 23:29:15388 };
389
viettrungluu9e65ad12014-10-16 04:22:26390 for (size_t i = 0; i < arraysize(cases); ++i) {
[email protected]5e212ed2012-03-21 23:29:15391 scoped_refptr<Extension> extension =
392 CreateTestExtension(cases[i].name, cases[i].incognito_split_mode);
Chris Mumford8f812662018-02-22 00:27:57393 AddExtension(extension, cases[i].incognito_enabled, false);
[email protected]5e212ed2012-03-21 23:29:15394
395 // First test a main frame request.
396 {
397 // It doesn't matter that the resource doesn't exist. If the resource
naskob9164c42016-06-07 01:21:35398 // is blocked, we should see BLOCKED_BY_CLIENT. Otherwise, the request
[email protected]5e212ed2012-03-21 23:29:15399 // should just fail because the file doesn't exist.
Chris Mumford8f812662018-02-22 00:27:57400 auto get_result = RequestOrLoad(extension->GetResourceURL("404.html"),
401 content::RESOURCE_TYPE_MAIN_FRAME);
[email protected]5e212ed2012-03-21 23:29:15402
403 if (cases[i].should_allow_main_frame_load) {
Chris Mumford8f812662018-02-22 00:27:57404 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, get_result.result())
maksim.sisov1b83bb72016-10-07 06:07:23405 << cases[i].name;
[email protected]5e212ed2012-03-21 23:29:15406 } else {
Chris Mumford8f812662018-02-22 00:27:57407 EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, get_result.result())
naskob9164c42016-06-07 01:21:35408 << cases[i].name;
[email protected]5e212ed2012-03-21 23:29:15409 }
410 }
411
John Abd-El-Malek2305cdc2018-02-14 20:26:28412 // Subframe navigation requests are blocked in ExtensionNavigationThrottle
413 // which isn't added in this unit test. This is tested in an integration
414 // test in ExtensionResourceRequestPolicyTest.IframeNavigateToInaccessible.
Chris Mumford8f812662018-02-22 00:27:57415 RemoveExtension(extension, UnloadedExtensionReason::UNINSTALL);
[email protected]5e212ed2012-03-21 23:29:15416 }
417}
418
Chris Mumford8f812662018-02-22 00:27:57419void CheckForContentLengthHeader(const GetResult& get_result) {
420 std::string content_length = get_result.GetResponseHeaderByName(
421 net::HttpRequestHeaders::kContentLength);
422
[email protected]774cebd2013-09-26 04:55:01423 EXPECT_FALSE(content_length.empty());
424 int length_value = 0;
425 EXPECT_TRUE(base::StringToInt(content_length, &length_value));
426 EXPECT_GT(length_value, 0);
427}
428
[email protected]93ac047a2012-12-13 02:53:49429// Tests getting a resource for a component extension works correctly, both when
430// the extension is enabled and when it is disabled.
Chris Mumford8f812662018-02-22 00:27:57431TEST_P(ExtensionProtocolsTest, ComponentResourceRequest) {
[email protected]93ac047a2012-12-13 02:53:49432 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01433 SetProtocolHandler(false);
[email protected]93ac047a2012-12-13 02:53:49434
435 scoped_refptr<Extension> extension = CreateWebStoreExtension();
Chris Mumford8f812662018-02-22 00:27:57436 AddExtension(extension, false, false);
[email protected]93ac047a2012-12-13 02:53:49437
438 // First test it with the extension enabled.
439 {
Chris Mumford8f812662018-02-22 00:27:57440 auto get_result =
441 RequestOrLoad(extension->GetResourceURL("webstore_icon_16.png"),
442 content::RESOURCE_TYPE_MEDIA);
443 EXPECT_EQ(net::OK, get_result.result());
444 CheckForContentLengthHeader(get_result);
445 EXPECT_EQ("image/png", get_result.GetResponseHeaderByName(
446 net::HttpRequestHeaders::kContentType));
[email protected]93ac047a2012-12-13 02:53:49447 }
448
449 // And then test it with the extension disabled.
Chris Mumford8f812662018-02-22 00:27:57450 RemoveExtension(extension, UnloadedExtensionReason::DISABLE);
[email protected]93ac047a2012-12-13 02:53:49451 {
Chris Mumford8f812662018-02-22 00:27:57452 auto get_result =
453 RequestOrLoad(extension->GetResourceURL("webstore_icon_16.png"),
454 content::RESOURCE_TYPE_MEDIA);
455 EXPECT_EQ(net::OK, get_result.result());
456 CheckForContentLengthHeader(get_result);
457 EXPECT_EQ("image/png", get_result.GetResponseHeaderByName(
458 net::HttpRequestHeaders::kContentType));
[email protected]93ac047a2012-12-13 02:53:49459 }
460}
461
[email protected]6f7d7062013-06-04 03:49:33462// Tests that a URL request for resource from an extension returns a few
463// expected response headers.
Chris Mumford8f812662018-02-22 00:27:57464TEST_P(ExtensionProtocolsTest, ResourceRequestResponseHeaders) {
[email protected]6f7d7062013-06-04 03:49:33465 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01466 SetProtocolHandler(false);
[email protected]6f7d7062013-06-04 03:49:33467
468 scoped_refptr<Extension> extension = CreateTestResponseHeaderExtension();
Chris Mumford8f812662018-02-22 00:27:57469 AddExtension(extension, false, false);
[email protected]6f7d7062013-06-04 03:49:33470
471 {
Chris Mumford8f812662018-02-22 00:27:57472 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
473 content::RESOURCE_TYPE_MEDIA);
474 EXPECT_EQ(net::OK, get_result.result());
[email protected]6f7d7062013-06-04 03:49:33475
476 // Check that cache-related headers are set.
Chris Mumford8f812662018-02-22 00:27:57477 std::string etag = get_result.GetResponseHeaderByName("ETag");
brettw66d1b81b2015-07-06 19:29:40478 EXPECT_TRUE(base::StartsWith(etag, "\"", base::CompareCase::SENSITIVE));
479 EXPECT_TRUE(base::EndsWith(etag, "\"", base::CompareCase::SENSITIVE));
[email protected]6f7d7062013-06-04 03:49:33480
Chris Mumford8f812662018-02-22 00:27:57481 std::string revalidation_header =
482 get_result.GetResponseHeaderByName("cache-control");
[email protected]6f7d7062013-06-04 03:49:33483 EXPECT_EQ("no-cache", revalidation_header);
484
485 // We set test.dat as web-accessible, so it should have a CORS header.
Chris Mumford8f812662018-02-22 00:27:57486 std::string access_control =
487 get_result.GetResponseHeaderByName("Access-Control-Allow-Origin");
[email protected]6f7d7062013-06-04 03:49:33488 EXPECT_EQ("*", access_control);
489 }
490}
491
[email protected]b109bdd2013-11-04 18:08:43492// Tests that a URL request for main frame or subframe from an extension
493// succeeds, but subresources fail. See http://crbug.com/312269.
Chris Mumford8f812662018-02-22 00:27:57494TEST_P(ExtensionProtocolsTest, AllowFrameRequests) {
[email protected]b109bdd2013-11-04 18:08:43495 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01496 SetProtocolHandler(false);
[email protected]b109bdd2013-11-04 18:08:43497
498 scoped_refptr<Extension> extension = CreateTestExtension("foo", false);
Chris Mumford8f812662018-02-22 00:27:57499 AddExtension(extension, false, false);
[email protected]b109bdd2013-11-04 18:08:43500
nasko5cf9d452016-06-01 05:34:56501 // All MAIN_FRAME requests should succeed. SUB_FRAME requests that are not
Chris Mumford8f812662018-02-22 00:27:57502 // explicitly listed in web_accessible_resources or same-origin to the parent
nasko5cf9d452016-06-01 05:34:56503 // should not succeed.
[email protected]b109bdd2013-11-04 18:08:43504 {
Chris Mumford8f812662018-02-22 00:27:57505 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
506 content::RESOURCE_TYPE_MAIN_FRAME);
507 EXPECT_EQ(net::OK, get_result.result());
[email protected]b109bdd2013-11-04 18:08:43508 }
John Abd-El-Malek2305cdc2018-02-14 20:26:28509
510 // Subframe navigation requests are blocked in ExtensionNavigationThrottle
511 // which isn't added in this unit test. This is tested in an integration test
512 // in ExtensionResourceRequestPolicyTest.IframeNavigateToInaccessible.
[email protected]b109bdd2013-11-04 18:08:43513
514 // And subresource types, such as media, should fail.
515 {
Chris Mumford8f812662018-02-22 00:27:57516 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
517 content::RESOURCE_TYPE_MEDIA);
518 EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, get_result.result());
[email protected]b109bdd2013-11-04 18:08:43519 }
520}
521
Chris Mumford8f812662018-02-22 00:27:57522TEST_P(ExtensionProtocolsTest, MetadataFolder) {
asargenta093ec32016-02-13 01:36:43523 SetProtocolHandler(false);
524
525 base::FilePath extension_dir = GetTestPath("metadata_folder");
526 std::string error;
527 scoped_refptr<Extension> extension =
528 file_util::LoadExtension(extension_dir, Manifest::INTERNAL,
529 Extension::NO_FLAGS, &error);
530 ASSERT_NE(extension.get(), nullptr) << "error: " << error;
531
532 // Loading "/test.html" should succeed.
Chris Mumford8f812662018-02-22 00:27:57533 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, "test.html").result());
asargenta093ec32016-02-13 01:36:43534
535 // Loading "/_metadata/verified_contents.json" should fail.
536 base::FilePath relative_path =
537 base::FilePath(kMetadataFolder).Append(kVerifiedContentsFilename);
538 EXPECT_TRUE(base::PathExists(extension_dir.Append(relative_path)));
Chris Mumford8f812662018-02-22 00:27:57539 EXPECT_NE(net::OK,
540 DoRequestOrLoad(extension, relative_path.AsUTF8Unsafe()).result());
asargenta093ec32016-02-13 01:36:43541
542 // Loading "/_metadata/a.txt" should also fail.
543 relative_path = base::FilePath(kMetadataFolder).AppendASCII("a.txt");
544 EXPECT_TRUE(base::PathExists(extension_dir.Append(relative_path)));
Chris Mumford8f812662018-02-22 00:27:57545 EXPECT_NE(net::OK,
546 DoRequestOrLoad(extension, relative_path.AsUTF8Unsafe()).result());
asargenta093ec32016-02-13 01:36:43547}
548
lazyboyd6dbb262017-03-30 00:57:30549// Tests that unreadable files and deleted files correctly go through
550// ContentVerifyJob.
Chris Mumford8f812662018-02-22 00:27:57551TEST_P(ExtensionProtocolsTest, VerificationSeenForFileAccessErrors) {
lazyboyd6dbb262017-03-30 00:57:30552 SetProtocolHandler(false);
553
Istiaque Ahmed72816eaa2018-01-30 22:37:06554 // Unzip extension containing verification hashes to a temporary directory.
lazyboyd6dbb262017-03-30 00:57:30555 base::ScopedTempDir temp_dir;
556 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
Istiaque Ahmed72816eaa2018-01-30 22:37:06557 base::FilePath unzipped_path = temp_dir.GetPath();
558 scoped_refptr<Extension> extension =
559 content_verifier_test_utils::UnzipToDirAndLoadExtension(
560 GetContentVerifierTestPath().AppendASCII("source.zip"),
561 unzipped_path);
lazyboyd6dbb262017-03-30 00:57:30562 ASSERT_TRUE(extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06563 ExtensionId extension_id = extension->id();
lazyboyd6dbb262017-03-30 00:57:30564
Istiaque Ahmed72816eaa2018-01-30 22:37:06565 const std::string kJs("1024.js");
566 base::FilePath kRelativePath(FILE_PATH_LITERAL("1024.js"));
lazyboyd6dbb262017-03-30 00:57:30567
Istiaque Ahmed72816eaa2018-01-30 22:37:06568 // Valid and readable 1024.js.
569 {
Istiaque Ahmed584fe142018-03-13 09:19:04570 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
lazyboyd6dbb262017-03-30 00:57:30571
Chris Mumford8f812662018-02-22 00:27:57572 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06573 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
574 content::RunAllPendingInMessageLoop();
575
Chris Mumford8f812662018-02-22 00:27:57576 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36577 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06578 }
579
580 // chmod -r 1024.js.
581 {
Istiaque Ahmed584fe142018-03-13 09:19:04582 TestContentVerifySingleJobObserver observer(extension->id(), kRelativePath);
Istiaque Ahmed72816eaa2018-01-30 22:37:06583 base::FilePath file_path = unzipped_path.AppendASCII(kJs);
584 ASSERT_TRUE(base::MakeFileUnreadable(file_path));
Chris Mumford8f812662018-02-22 00:27:57585 EXPECT_EQ(net::ERR_ACCESS_DENIED, DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36586 EXPECT_EQ(ContentVerifyJob::HASH_MISMATCH, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06587 // NOTE: In production, hash mismatch would have disabled |extension|, but
588 // since UnzipToDirAndLoadExtension() doesn't add the extension to
589 // ExtensionRegistry, ChromeContentVerifierDelegate won't disable it.
590 // TODO(lazyboy): We may want to update this to more closely reflect the
591 // real flow.
592 }
593
594 // Delete 1024.js.
595 {
Istiaque Ahmed584fe142018-03-13 09:19:04596 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
Istiaque Ahmed72816eaa2018-01-30 22:37:06597 base::FilePath file_path = unzipped_path.AppendASCII(kJs);
598 ASSERT_TRUE(base::DieFileDie(file_path, false));
Chris Mumford8f812662018-02-22 00:27:57599 EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
600 DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36601 EXPECT_EQ(ContentVerifyJob::HASH_MISMATCH, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06602 }
lazyboyd6dbb262017-03-30 00:57:30603}
604
lazyboye83ab9c62017-03-30 03:18:26605// Tests that zero byte files correctly go through ContentVerifyJob.
Chris Mumford8f812662018-02-22 00:27:57606TEST_P(ExtensionProtocolsTest, VerificationSeenForZeroByteFile) {
lazyboye83ab9c62017-03-30 03:18:26607 SetProtocolHandler(false);
608
Istiaque Ahmed72816eaa2018-01-30 22:37:06609 const std::string kEmptyJs("empty.js");
lazyboye83ab9c62017-03-30 03:18:26610 base::ScopedTempDir temp_dir;
611 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
Istiaque Ahmed72816eaa2018-01-30 22:37:06612 base::FilePath unzipped_path = temp_dir.GetPath();
lazyboye83ab9c62017-03-30 03:18:26613
Istiaque Ahmed72816eaa2018-01-30 22:37:06614 scoped_refptr<Extension> extension =
615 content_verifier_test_utils::UnzipToDirAndLoadExtension(
616 GetContentVerifierTestPath().AppendASCII("source.zip"),
617 unzipped_path);
618 ASSERT_TRUE(extension.get());
619
620 base::FilePath kRelativePath(FILE_PATH_LITERAL("empty.js"));
621 ExtensionId extension_id = extension->id();
622
623 // Sanity check empty.js.
624 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
lazyboye83ab9c62017-03-30 03:18:26625 int64_t foo_file_size = -1;
Istiaque Ahmed72816eaa2018-01-30 22:37:06626 ASSERT_TRUE(base::GetFileSize(file_path, &foo_file_size));
lazyboye83ab9c62017-03-30 03:18:26627 ASSERT_EQ(0, foo_file_size);
628
Istiaque Ahmed72816eaa2018-01-30 22:37:06629 // Request empty.js.
630 {
Istiaque Ahmed584fe142018-03-13 09:19:04631 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
lazyboye83ab9c62017-03-30 03:18:26632
Chris Mumford8f812662018-02-22 00:27:57633 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06634 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
635 content::RunAllPendingInMessageLoop();
lazyboye83ab9c62017-03-30 03:18:26636
Chris Mumford8f812662018-02-22 00:27:57637 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36638 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06639 }
640
641 // chmod -r empty.js.
642 // Unreadable empty file doesn't generate hash mismatch. Note that this is the
643 // current behavior of ContentVerifyJob.
644 // TODO(lazyboy): The behavior is probably incorrect.
645 {
Istiaque Ahmed584fe142018-03-13 09:19:04646 TestContentVerifySingleJobObserver observer(extension->id(), kRelativePath);
Istiaque Ahmed72816eaa2018-01-30 22:37:06647 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
648 ASSERT_TRUE(base::MakeFileUnreadable(file_path));
Chris Mumford8f812662018-02-22 00:27:57649 EXPECT_EQ(net::ERR_ACCESS_DENIED,
650 DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36651 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06652 }
653
654 // rm empty.js.
655 // Deleted empty file doesn't generate hash mismatch. Note that this is the
656 // current behavior of ContentVerifyJob.
657 // TODO(lazyboy): The behavior is probably incorrect.
658 {
Istiaque Ahmed584fe142018-03-13 09:19:04659 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
Istiaque Ahmed72816eaa2018-01-30 22:37:06660 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
661 ASSERT_TRUE(base::DieFileDie(file_path, false));
Chris Mumford8f812662018-02-22 00:27:57662 EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
663 DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36664 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06665 }
lazyboye83ab9c62017-03-30 03:18:26666}
667
proberge4f9644c2018-03-27 23:01:54668TEST_P(ExtensionProtocolsTest, VerifyScriptListedAsIcon) {
669 SetProtocolHandler(false);
670
671 const std::string kBackgroundJs("background.js");
672 base::ScopedTempDir temp_dir;
673 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
674 base::FilePath unzipped_path = temp_dir.GetPath();
675
676 base::FilePath path;
677 EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &path));
678
679 scoped_refptr<Extension> extension =
680 content_verifier_test_utils::UnzipToDirAndLoadExtension(
681 path.AppendASCII("content_hash_fetcher")
682 .AppendASCII("manifest_mislabeled_script")
683 .AppendASCII("source.zip"),
684 unzipped_path);
685 ASSERT_TRUE(extension.get());
686
687 base::FilePath kRelativePath(FILE_PATH_LITERAL("background.js"));
688 ExtensionId extension_id = extension->id();
689
690 // Request background.js.
691 {
692 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
693
694 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
695 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
696 base::RunLoop().RunUntilIdle();
697
698 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kBackgroundJs).result());
699 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
700 }
701
702 // Modify background.js and request it.
703 {
704 base::FilePath file_path = unzipped_path.AppendASCII("background.js");
705 const std::string content = "new content";
706 EXPECT_NE(base::WriteFile(file_path, content.c_str(), content.size()), -1);
707 TestContentVerifySingleJobObserver observer(extension_id, kRelativePath);
708
709 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
710 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
711 base::RunLoop().RunUntilIdle();
712
713 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kBackgroundJs).result());
714 EXPECT_EQ(ContentVerifyJob::HASH_MISMATCH, observer.WaitForJobFinished());
715 }
716}
717
Devlin Croninc3f88072018-01-30 02:10:11718// Tests that mime types are properly set for returned extension resources.
Chris Mumford8f812662018-02-22 00:27:57719TEST_P(ExtensionProtocolsTest, MimeTypesForKnownFiles) {
Devlin Croninc3f88072018-01-30 02:10:11720 // Register a non-incognito extension protocol handler.
721 SetProtocolHandler(false);
722
723 TestExtensionDir test_dir;
724 constexpr char kManifest[] = R"(
725 {
726 "name": "Test Ext",
727 "description": "A test extension",
728 "manifest_version": 2,
729 "version": "0.1",
730 "web_accessible_resources": ["*"]
731 })";
732 test_dir.WriteManifest(kManifest);
733 std::unique_ptr<base::DictionaryValue> manifest =
734 base::DictionaryValue::From(base::test::ParseJson(kManifest));
735 ASSERT_TRUE(manifest);
736
737 test_dir.WriteFile(FILE_PATH_LITERAL("json_file.json"), "{}");
738 test_dir.WriteFile(FILE_PATH_LITERAL("js_file.js"), "function() {}");
739
740 base::FilePath unpacked_path = test_dir.UnpackedPath();
741 ASSERT_TRUE(base::PathExists(unpacked_path.AppendASCII("json_file.json")));
742 std::string error;
743 scoped_refptr<const Extension> extension =
744 ExtensionBuilder()
745 .SetManifest(std::move(manifest))
746 .SetPath(unpacked_path)
747 .SetLocation(Manifest::INTERNAL)
748 .Build();
749 ASSERT_TRUE(extension);
750
Chris Mumford8f812662018-02-22 00:27:57751 AddExtension(extension.get(), false, false);
Devlin Croninc3f88072018-01-30 02:10:11752
753 struct {
754 const char* file_name;
755 const char* expected_mime_type;
756 } test_cases[] = {
757 {"json_file.json", "application/json"},
758 {"js_file.js", "application/javascript"},
759 };
760
761 for (const auto& test_case : test_cases) {
762 SCOPED_TRACE(test_case.file_name);
Chris Mumford8f812662018-02-22 00:27:57763 auto result = RequestOrLoad(extension->GetResourceURL(test_case.file_name),
764 content::RESOURCE_TYPE_SUB_RESOURCE);
765 EXPECT_EQ(
766 test_case.expected_mime_type,
767 result.GetResponseHeaderByName(net::HttpRequestHeaders::kContentType));
Devlin Croninc3f88072018-01-30 02:10:11768 }
769}
770
Chris Mumford8f812662018-02-22 00:27:57771INSTANTIATE_TEST_CASE_P(Extensions,
772 ExtensionProtocolsTest,
773 ::testing::ValuesIn(kTestModes));
774
[email protected]702d8b42013-02-27 20:55:50775} // namespace extensions