blob: 20497429f161ad47258579f437098446550b9c70 [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() {
[email protected]023b3d12013-12-23 18:46:49100 base::DictionaryValue manifest;
[email protected]93ac047a2012-12-13 02:53:49101 manifest.SetString("name", "WebStore");
102 manifest.SetString("version", "1");
103 manifest.SetString("icons.16", "webstore_icon_16.png");
104
[email protected]650b2d52013-02-10 03:41:45105 base::FilePath path;
[email protected]93ac047a2012-12-13 02:53:49106 EXPECT_TRUE(PathService::Get(chrome::DIR_RESOURCES, &path));
107 path = path.AppendASCII("web_store");
108
109 std::string error;
110 scoped_refptr<Extension> extension(
[email protected]1d5e58b2013-01-31 08:41:40111 Extension::Create(path, Manifest::COMPONENT, manifest,
[email protected]93ac047a2012-12-13 02:53:49112 Extension::NO_FLAGS, &error));
113 EXPECT_TRUE(extension.get()) << error;
114 return extension;
115}
116
[email protected]6f7d7062013-06-04 03:49:33117scoped_refptr<Extension> CreateTestResponseHeaderExtension() {
[email protected]023b3d12013-12-23 18:46:49118 base::DictionaryValue manifest;
[email protected]6f7d7062013-06-04 03:49:33119 manifest.SetString("name", "An extension with web-accessible resources");
120 manifest.SetString("version", "2");
121
Jinho Bangb5216cec2018-01-17 19:43:11122 auto web_accessible_list = std::make_unique<base::ListValue>();
[email protected]6f7d7062013-06-04 03:49:33123 web_accessible_list->AppendString("test.dat");
vabr9984ea62017-04-10 11:33:49124 manifest.Set("web_accessible_resources", std::move(web_accessible_list));
[email protected]6f7d7062013-06-04 03:49:33125
asargenta093ec32016-02-13 01:36:43126 base::FilePath path = GetTestPath("response_headers");
[email protected]6f7d7062013-06-04 03:49:33127
128 std::string error;
129 scoped_refptr<Extension> extension(
130 Extension::Create(path, Manifest::UNPACKED, manifest,
131 Extension::NO_FLAGS, &error));
132 EXPECT_TRUE(extension.get()) << error;
133 return extension;
134}
135
Chris Mumford8f812662018-02-22 00:27:57136// Helper function to create a |ResourceRequest| for testing purposes.
137network::ResourceRequest CreateResourceRequest(const std::string& method,
138 ResourceType resource_type,
139 const GURL& url) {
140 network::ResourceRequest request;
141 request.method = method;
142 request.url = url;
143 request.site_for_cookies = url; // bypass third-party cookie blocking.
144 request.request_initiator =
145 url::Origin::Create(url); // ensure initiator set.
146 request.referrer_policy = content::Referrer::GetDefaultReferrerPolicy();
147 request.resource_type = resource_type;
148 request.is_main_frame = resource_type == content::RESOURCE_TYPE_MAIN_FRAME;
149 request.allow_download = true;
150 return request;
151}
152
153// The result of either a URLRequest of a URLLoader response (but not both)
154// depending on the on test type.
155class GetResult {
156 public:
157 GetResult(std::unique_ptr<net::URLRequest> request, int result)
158 : request_(std::move(request)), result_(result) {}
159 GetResult(const network::ResourceResponseHead& response, int result)
160 : resource_response_(response), result_(result) {}
161 GetResult(GetResult&& other)
162 : request_(std::move(other.request_)), result_(other.result_) {}
163 ~GetResult() = default;
164
165 std::string GetResponseHeaderByName(const std::string& name) const {
166 std::string value;
167 if (request_)
168 request_->GetResponseHeaderByName(name, &value);
169 else if (resource_response_.headers)
170 resource_response_.headers->GetNormalizedHeader(name, &value);
171 return value;
172 }
173
174 int result() const { return result_; }
175
176 private:
177 std::unique_ptr<net::URLRequest> request_;
178 const network::ResourceResponseHead resource_response_;
179 int result_;
180
181 DISALLOW_COPY_AND_ASSIGN(GetResult);
182};
183
jamescook8816ae52014-09-05 17:02:37184} // namespace
185
186// This test lives in src/chrome instead of src/extensions because it tests
187// functionality delegated back to Chrome via ChromeExtensionsBrowserClient.
lfg048201a2014-09-16 19:09:36188// See chrome/browser/extensions/chrome_url_request_util.cc.
Chris Mumford8f812662018-02-22 00:27:57189class ExtensionProtocolsTest
190 : public testing::Test,
191 public testing::WithParamInterface<RequestHandlerType> {
[email protected]5e212ed2012-03-21 23:29:15192 public:
lazyboyd6dbb262017-03-30 00:57:30193 ExtensionProtocolsTest()
[email protected]1791e6c92014-04-11 08:29:01194 : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
Chris Mumford8f812662018-02-22 00:27:57195 rvh_test_enabler_(new content::RenderViewHostTestEnabler()),
[email protected]1791e6c92014-04-11 08:29:01196 old_factory_(NULL),
197 resource_context_(&test_url_request_context_) {}
[email protected]5e212ed2012-03-21 23:29:15198
dcheng72191812014-10-28 20:49:56199 void SetUp() override {
[email protected]06492ed2013-03-24 22:13:14200 testing::Test::SetUp();
lazyboyd6dbb262017-03-30 00:57:30201 testing_profile_ = TestingProfile::Builder().Build();
Chris Mumford8f812662018-02-22 00:27:57202 contents_.reset(CreateTestWebContents());
[email protected]38427a12013-11-09 17:34:20203 extension_info_map_ = new InfoMap();
Chris Mumford8f812662018-02-22 00:27:57204 old_factory_ = resource_context_.GetRequestContext()->job_factory();
lazyboyd6dbb262017-03-30 00:57:30205
206 // Set up content verification.
207 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
208 command_line->AppendSwitchASCII(
209 switches::kExtensionContentVerification,
210 switches::kExtensionContentVerificationEnforce);
211 content_verifier_ = new ContentVerifier(
Chris Mumford8f812662018-02-22 00:27:57212 browser_context(),
213 std::make_unique<ChromeContentVerifierDelegate>(browser_context()));
lazyboyd6dbb262017-03-30 00:57:30214 extension_info_map_->SetContentVerifier(content_verifier_.get());
[email protected]5e212ed2012-03-21 23:29:15215 }
216
dcheng72191812014-10-28 20:49:56217 void TearDown() override {
Chris Mumford8f812662018-02-22 00:27:57218 loader_factory_.reset();
219 resource_context_.GetRequestContext()->set_job_factory(old_factory_);
lazyboyd6dbb262017-03-30 00:57:30220 content_verifier_->Shutdown();
[email protected]5e212ed2012-03-21 23:29:15221 }
222
[email protected]1791e6c92014-04-11 08:29:01223 void SetProtocolHandler(bool is_incognito) {
Chris Mumford8f812662018-02-22 00:27:57224 switch (request_handler()) {
225 case RequestHandlerType::kURLLoader:
226 loader_factory_ = extensions::CreateExtensionNavigationURLLoaderFactory(
227 main_rfh(), extension_info_map_.get());
228 break;
229 case RequestHandlerType::kURLRequest:
230 job_factory_.SetProtocolHandler(
231 kExtensionScheme, CreateExtensionProtocolHandler(
232 is_incognito, extension_info_map_.get()));
233 resource_context_.GetRequestContext()->set_job_factory(&job_factory_);
234 break;
235 }
236 testing_profile_->ForceIncognito(is_incognito);
[email protected]93ac047a2012-12-13 02:53:49237 }
238
Chris Mumford8f812662018-02-22 00:27:57239 GetResult RequestOrLoad(const GURL& url, ResourceType resource_type) {
240 switch (request_handler()) {
241 case RequestHandlerType::kURLLoader:
242 return LoadURL(url, resource_type);
243 case RequestHandlerType::kURLRequest:
244 return RequestURL(url, resource_type);
245 }
246 NOTREACHED();
247 return GetResult(nullptr, net::ERR_FAILED);
248 }
249
250 void AddExtension(const scoped_refptr<const Extension>& extension,
251 bool incognito_enabled,
252 bool notifications_disabled) {
253 extension_info_map_->AddExtension(extension.get(), base::Time::Now(),
254 incognito_enabled,
255 notifications_disabled);
256 if (request_handler() == RequestHandlerType::kURLLoader) {
257 EXPECT_TRUE(extension_registry()->AddEnabled(extension));
258 ExtensionPrefs::Get(browser_context())
259 ->SetIsIncognitoEnabled(extension->id(), incognito_enabled);
260 }
261 }
262
263 void RemoveExtension(const scoped_refptr<const Extension>& extension,
264 const UnloadedExtensionReason reason) {
265 extension_info_map_->RemoveExtension(extension->id(), reason);
266 if (request_handler() == RequestHandlerType::kURLLoader) {
267 EXPECT_TRUE(extension_registry()->RemoveEnabled(extension->id()));
268 if (reason == UnloadedExtensionReason::DISABLE)
269 EXPECT_TRUE(extension_registry()->AddDisabled(extension));
270 }
271 }
272
273 // Helper method to create a URL request/loader, call RequestOrLoad on it, and
274 // return the result. If |extension| hasn't already been added to
275 // |extension_info_map_|, this will add it.
276 GetResult DoRequestOrLoad(const scoped_refptr<Extension> extension,
277 const std::string& relative_path) {
278 if (!extension_info_map_->extensions().Contains(extension->id())) {
279 AddExtension(extension.get(),
280 /*incognito_enabled=*/false,
281 /*notifications_disabled=*/false);
282 }
283 return RequestOrLoad(extension->GetResourceURL(relative_path),
284 content::RESOURCE_TYPE_MAIN_FRAME);
285 }
286
287 ExtensionRegistry* extension_registry() {
288 return ExtensionRegistry::Get(browser_context());
289 }
290
291 content::BrowserContext* browser_context() { return testing_profile_.get(); }
292
293 protected:
294 scoped_refptr<ContentVerifier> content_verifier_;
295
296 private:
297 GetResult LoadURL(const GURL& url, ResourceType resource_type) {
298 constexpr int32_t kRoutingId = 81;
299 constexpr int32_t kRequestId = 28;
300
301 network::mojom::URLLoaderPtr loader;
302 network::TestURLLoaderClient client;
303 loader_factory_->CreateLoaderAndStart(
304 mojo::MakeRequest(&loader), kRoutingId, kRequestId,
305 network::mojom::kURLLoadOptionNone,
306 CreateResourceRequest("GET", resource_type, url),
307 client.CreateInterfacePtr(),
308 net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS));
309
310 client.RunUntilComplete();
311 return GetResult(client.response_head(),
312 client.completion_status().error_code);
313 }
314
315 GetResult RequestURL(const GURL& url, ResourceType resource_type) {
316 auto request = resource_context_.GetRequestContext()->CreateRequest(
317 url, net::DEFAULT_PRIORITY, &test_delegate_,
318 TRAFFIC_ANNOTATION_FOR_TESTS);
319
gabf9d15582014-11-13 16:40:15320 content::ResourceRequestInfo::AllocateForTesting(
Chris Mumford8f812662018-02-22 00:27:57321 request.get(), resource_type, &resource_context_,
megjabloncaf312f2017-01-12 18:47:49322 /*render_process_id=*/-1,
323 /*render_view_id=*/-1,
324 /*render_frame_id=*/-1,
325 /*is_main_frame=*/resource_type == content::RESOURCE_TYPE_MAIN_FRAME,
megjabloncaf312f2017-01-12 18:47:49326 /*allow_download=*/true,
Jian Li18173422017-11-08 03:00:02327 /*is_async=*/false, content::PREVIEWS_OFF,
328 /*navigation_ui_data*/ nullptr);
[email protected]5e212ed2012-03-21 23:29:15329 request->Start();
fdoraycb32419d2016-06-23 15:52:55330 base::RunLoop().Run();
Chris Mumford8f812662018-02-22 00:27:57331 return GetResult(std::move(request), test_delegate_.request_status());
[email protected]5e212ed2012-03-21 23:29:15332 }
333
Chris Mumford8f812662018-02-22 00:27:57334 content::WebContents* CreateTestWebContents() {
335 auto site_instance = content::SiteInstance::Create(browser_context());
336 return content::WebContentsTester::CreateTestWebContents(
337 browser_context(), std::move(site_instance));
asargenta093ec32016-02-13 01:36:43338 }
339
Chris Mumford8f812662018-02-22 00:27:57340 content::WebContents* web_contents() { return contents_.get(); }
341
342 content::RenderFrameHost* main_rfh() {
343 return web_contents()->GetMainFrame();
344 }
345
346 RequestHandlerType request_handler() const { return GetParam(); }
347
[email protected]ec04d3f2013-06-06 21:31:39348 content::TestBrowserThreadBundle thread_bundle_;
Chris Mumford8f812662018-02-22 00:27:57349 std::unique_ptr<content::RenderViewHostTestEnabler> rvh_test_enabler_;
[email protected]9d5730b2012-08-24 17:42:49350 net::URLRequestJobFactoryImpl job_factory_;
[email protected]5e212ed2012-03-21 23:29:15351 const net::URLRequestJobFactory* old_factory_;
Chris Mumford8f812662018-02-22 00:27:57352 std::unique_ptr<network::mojom::URLLoaderFactory> loader_factory_;
[email protected]37ac95b2013-07-23 23:39:35353 net::TestURLRequestContext test_url_request_context_;
[email protected]5e212ed2012-03-21 23:29:15354 content::MockResourceContext resource_context_;
lazyboyd6dbb262017-03-30 00:57:30355 std::unique_ptr<TestingProfile> testing_profile_;
Chris Mumford8f812662018-02-22 00:27:57356 net::TestDelegate test_delegate_;
357 std::unique_ptr<content::WebContents> contents_;
358 scoped_refptr<InfoMap> extension_info_map_;
[email protected]5e212ed2012-03-21 23:29:15359};
360
361// Tests that making a chrome-extension request in an incognito context is
362// only allowed under the right circumstances (if the extension is allowed
363// in incognito, and it's either a non-main-frame request or a split-mode
364// extension).
Chris Mumford8f812662018-02-22 00:27:57365TEST_P(ExtensionProtocolsTest, IncognitoRequest) {
[email protected]93ac047a2012-12-13 02:53:49366 // Register an incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01367 SetProtocolHandler(true);
[email protected]93ac047a2012-12-13 02:53:49368
[email protected]5e212ed2012-03-21 23:29:15369 struct TestCase {
370 // Inputs.
371 std::string name;
372 bool incognito_split_mode;
373 bool incognito_enabled;
374
375 // Expected results.
376 bool should_allow_main_frame_load;
377 bool should_allow_sub_frame_load;
378 } cases[] = {
379 {"spanning disabled", false, false, false, false},
380 {"split disabled", true, false, false, false},
nasko5cf9d452016-06-01 05:34:56381 {"spanning enabled", false, true, false, false},
382 {"split enabled", true, true, true, false},
[email protected]5e212ed2012-03-21 23:29:15383 };
384
viettrungluu9e65ad12014-10-16 04:22:26385 for (size_t i = 0; i < arraysize(cases); ++i) {
[email protected]5e212ed2012-03-21 23:29:15386 scoped_refptr<Extension> extension =
387 CreateTestExtension(cases[i].name, cases[i].incognito_split_mode);
Chris Mumford8f812662018-02-22 00:27:57388 AddExtension(extension, cases[i].incognito_enabled, false);
[email protected]5e212ed2012-03-21 23:29:15389
390 // First test a main frame request.
391 {
392 // It doesn't matter that the resource doesn't exist. If the resource
naskob9164c42016-06-07 01:21:35393 // is blocked, we should see BLOCKED_BY_CLIENT. Otherwise, the request
[email protected]5e212ed2012-03-21 23:29:15394 // should just fail because the file doesn't exist.
Chris Mumford8f812662018-02-22 00:27:57395 auto get_result = RequestOrLoad(extension->GetResourceURL("404.html"),
396 content::RESOURCE_TYPE_MAIN_FRAME);
[email protected]5e212ed2012-03-21 23:29:15397
398 if (cases[i].should_allow_main_frame_load) {
Chris Mumford8f812662018-02-22 00:27:57399 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, get_result.result())
maksim.sisov1b83bb72016-10-07 06:07:23400 << cases[i].name;
[email protected]5e212ed2012-03-21 23:29:15401 } else {
Chris Mumford8f812662018-02-22 00:27:57402 EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, get_result.result())
naskob9164c42016-06-07 01:21:35403 << cases[i].name;
[email protected]5e212ed2012-03-21 23:29:15404 }
405 }
406
John Abd-El-Malek2305cdc2018-02-14 20:26:28407 // Subframe navigation requests are blocked in ExtensionNavigationThrottle
408 // which isn't added in this unit test. This is tested in an integration
409 // test in ExtensionResourceRequestPolicyTest.IframeNavigateToInaccessible.
Chris Mumford8f812662018-02-22 00:27:57410 RemoveExtension(extension, UnloadedExtensionReason::UNINSTALL);
[email protected]5e212ed2012-03-21 23:29:15411 }
412}
413
Chris Mumford8f812662018-02-22 00:27:57414void CheckForContentLengthHeader(const GetResult& get_result) {
415 std::string content_length = get_result.GetResponseHeaderByName(
416 net::HttpRequestHeaders::kContentLength);
417
[email protected]774cebd2013-09-26 04:55:01418 EXPECT_FALSE(content_length.empty());
419 int length_value = 0;
420 EXPECT_TRUE(base::StringToInt(content_length, &length_value));
421 EXPECT_GT(length_value, 0);
422}
423
[email protected]93ac047a2012-12-13 02:53:49424// Tests getting a resource for a component extension works correctly, both when
425// the extension is enabled and when it is disabled.
Chris Mumford8f812662018-02-22 00:27:57426TEST_P(ExtensionProtocolsTest, ComponentResourceRequest) {
[email protected]93ac047a2012-12-13 02:53:49427 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01428 SetProtocolHandler(false);
[email protected]93ac047a2012-12-13 02:53:49429
430 scoped_refptr<Extension> extension = CreateWebStoreExtension();
Chris Mumford8f812662018-02-22 00:27:57431 AddExtension(extension, false, false);
[email protected]93ac047a2012-12-13 02:53:49432
433 // First test it with the extension enabled.
434 {
Chris Mumford8f812662018-02-22 00:27:57435 auto get_result =
436 RequestOrLoad(extension->GetResourceURL("webstore_icon_16.png"),
437 content::RESOURCE_TYPE_MEDIA);
438 EXPECT_EQ(net::OK, get_result.result());
439 CheckForContentLengthHeader(get_result);
440 EXPECT_EQ("image/png", get_result.GetResponseHeaderByName(
441 net::HttpRequestHeaders::kContentType));
[email protected]93ac047a2012-12-13 02:53:49442 }
443
444 // And then test it with the extension disabled.
Chris Mumford8f812662018-02-22 00:27:57445 RemoveExtension(extension, UnloadedExtensionReason::DISABLE);
[email protected]93ac047a2012-12-13 02:53:49446 {
Chris Mumford8f812662018-02-22 00:27:57447 auto get_result =
448 RequestOrLoad(extension->GetResourceURL("webstore_icon_16.png"),
449 content::RESOURCE_TYPE_MEDIA);
450 EXPECT_EQ(net::OK, get_result.result());
451 CheckForContentLengthHeader(get_result);
452 EXPECT_EQ("image/png", get_result.GetResponseHeaderByName(
453 net::HttpRequestHeaders::kContentType));
[email protected]93ac047a2012-12-13 02:53:49454 }
455}
456
[email protected]6f7d7062013-06-04 03:49:33457// Tests that a URL request for resource from an extension returns a few
458// expected response headers.
Chris Mumford8f812662018-02-22 00:27:57459TEST_P(ExtensionProtocolsTest, ResourceRequestResponseHeaders) {
[email protected]6f7d7062013-06-04 03:49:33460 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01461 SetProtocolHandler(false);
[email protected]6f7d7062013-06-04 03:49:33462
463 scoped_refptr<Extension> extension = CreateTestResponseHeaderExtension();
Chris Mumford8f812662018-02-22 00:27:57464 AddExtension(extension, false, false);
[email protected]6f7d7062013-06-04 03:49:33465
466 {
Chris Mumford8f812662018-02-22 00:27:57467 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
468 content::RESOURCE_TYPE_MEDIA);
469 EXPECT_EQ(net::OK, get_result.result());
[email protected]6f7d7062013-06-04 03:49:33470
471 // Check that cache-related headers are set.
Chris Mumford8f812662018-02-22 00:27:57472 std::string etag = get_result.GetResponseHeaderByName("ETag");
brettw66d1b81b2015-07-06 19:29:40473 EXPECT_TRUE(base::StartsWith(etag, "\"", base::CompareCase::SENSITIVE));
474 EXPECT_TRUE(base::EndsWith(etag, "\"", base::CompareCase::SENSITIVE));
[email protected]6f7d7062013-06-04 03:49:33475
Chris Mumford8f812662018-02-22 00:27:57476 std::string revalidation_header =
477 get_result.GetResponseHeaderByName("cache-control");
[email protected]6f7d7062013-06-04 03:49:33478 EXPECT_EQ("no-cache", revalidation_header);
479
480 // We set test.dat as web-accessible, so it should have a CORS header.
Chris Mumford8f812662018-02-22 00:27:57481 std::string access_control =
482 get_result.GetResponseHeaderByName("Access-Control-Allow-Origin");
[email protected]6f7d7062013-06-04 03:49:33483 EXPECT_EQ("*", access_control);
484 }
485}
486
[email protected]b109bdd2013-11-04 18:08:43487// Tests that a URL request for main frame or subframe from an extension
488// succeeds, but subresources fail. See http://crbug.com/312269.
Chris Mumford8f812662018-02-22 00:27:57489TEST_P(ExtensionProtocolsTest, AllowFrameRequests) {
[email protected]b109bdd2013-11-04 18:08:43490 // Register a non-incognito extension protocol handler.
[email protected]1791e6c92014-04-11 08:29:01491 SetProtocolHandler(false);
[email protected]b109bdd2013-11-04 18:08:43492
493 scoped_refptr<Extension> extension = CreateTestExtension("foo", false);
Chris Mumford8f812662018-02-22 00:27:57494 AddExtension(extension, false, false);
[email protected]b109bdd2013-11-04 18:08:43495
nasko5cf9d452016-06-01 05:34:56496 // All MAIN_FRAME requests should succeed. SUB_FRAME requests that are not
Chris Mumford8f812662018-02-22 00:27:57497 // explicitly listed in web_accessible_resources or same-origin to the parent
nasko5cf9d452016-06-01 05:34:56498 // should not succeed.
[email protected]b109bdd2013-11-04 18:08:43499 {
Chris Mumford8f812662018-02-22 00:27:57500 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
501 content::RESOURCE_TYPE_MAIN_FRAME);
502 EXPECT_EQ(net::OK, get_result.result());
[email protected]b109bdd2013-11-04 18:08:43503 }
John Abd-El-Malek2305cdc2018-02-14 20:26:28504
505 // Subframe navigation requests are blocked in ExtensionNavigationThrottle
506 // which isn't added in this unit test. This is tested in an integration test
507 // in ExtensionResourceRequestPolicyTest.IframeNavigateToInaccessible.
[email protected]b109bdd2013-11-04 18:08:43508
509 // And subresource types, such as media, should fail.
510 {
Chris Mumford8f812662018-02-22 00:27:57511 auto get_result = RequestOrLoad(extension->GetResourceURL("test.dat"),
512 content::RESOURCE_TYPE_MEDIA);
513 EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, get_result.result());
[email protected]b109bdd2013-11-04 18:08:43514 }
515}
516
Chris Mumford8f812662018-02-22 00:27:57517TEST_P(ExtensionProtocolsTest, MetadataFolder) {
asargenta093ec32016-02-13 01:36:43518 SetProtocolHandler(false);
519
520 base::FilePath extension_dir = GetTestPath("metadata_folder");
521 std::string error;
522 scoped_refptr<Extension> extension =
523 file_util::LoadExtension(extension_dir, Manifest::INTERNAL,
524 Extension::NO_FLAGS, &error);
525 ASSERT_NE(extension.get(), nullptr) << "error: " << error;
526
527 // Loading "/test.html" should succeed.
Chris Mumford8f812662018-02-22 00:27:57528 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, "test.html").result());
asargenta093ec32016-02-13 01:36:43529
530 // Loading "/_metadata/verified_contents.json" should fail.
531 base::FilePath relative_path =
532 base::FilePath(kMetadataFolder).Append(kVerifiedContentsFilename);
533 EXPECT_TRUE(base::PathExists(extension_dir.Append(relative_path)));
Chris Mumford8f812662018-02-22 00:27:57534 EXPECT_NE(net::OK,
535 DoRequestOrLoad(extension, relative_path.AsUTF8Unsafe()).result());
asargenta093ec32016-02-13 01:36:43536
537 // Loading "/_metadata/a.txt" should also fail.
538 relative_path = base::FilePath(kMetadataFolder).AppendASCII("a.txt");
539 EXPECT_TRUE(base::PathExists(extension_dir.Append(relative_path)));
Chris Mumford8f812662018-02-22 00:27:57540 EXPECT_NE(net::OK,
541 DoRequestOrLoad(extension, relative_path.AsUTF8Unsafe()).result());
asargenta093ec32016-02-13 01:36:43542}
543
lazyboyd6dbb262017-03-30 00:57:30544// Tests that unreadable files and deleted files correctly go through
545// ContentVerifyJob.
Chris Mumford8f812662018-02-22 00:27:57546TEST_P(ExtensionProtocolsTest, VerificationSeenForFileAccessErrors) {
lazyboyd6dbb262017-03-30 00:57:30547 SetProtocolHandler(false);
548
Istiaque Ahmed72816eaa2018-01-30 22:37:06549 // Unzip extension containing verification hashes to a temporary directory.
lazyboyd6dbb262017-03-30 00:57:30550 base::ScopedTempDir temp_dir;
551 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
Istiaque Ahmed72816eaa2018-01-30 22:37:06552 base::FilePath unzipped_path = temp_dir.GetPath();
553 scoped_refptr<Extension> extension =
554 content_verifier_test_utils::UnzipToDirAndLoadExtension(
555 GetContentVerifierTestPath().AppendASCII("source.zip"),
556 unzipped_path);
lazyboyd6dbb262017-03-30 00:57:30557 ASSERT_TRUE(extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06558 ExtensionId extension_id = extension->id();
lazyboyd6dbb262017-03-30 00:57:30559
Istiaque Ahmed72816eaa2018-01-30 22:37:06560 const std::string kJs("1024.js");
561 base::FilePath kRelativePath(FILE_PATH_LITERAL("1024.js"));
lazyboyd6dbb262017-03-30 00:57:30562
Istiaque Ahmed72816eaa2018-01-30 22:37:06563 // Valid and readable 1024.js.
564 {
565 TestContentVerifyJobObserver observer(extension_id, kRelativePath);
lazyboyd6dbb262017-03-30 00:57:30566
Chris Mumford8f812662018-02-22 00:27:57567 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06568 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
569 content::RunAllPendingInMessageLoop();
570
Chris Mumford8f812662018-02-22 00:27:57571 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36572 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06573 }
574
575 // chmod -r 1024.js.
576 {
577 TestContentVerifyJobObserver observer(extension->id(), kRelativePath);
578 base::FilePath file_path = unzipped_path.AppendASCII(kJs);
579 ASSERT_TRUE(base::MakeFileUnreadable(file_path));
Chris Mumford8f812662018-02-22 00:27:57580 EXPECT_EQ(net::ERR_ACCESS_DENIED, DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36581 EXPECT_EQ(ContentVerifyJob::HASH_MISMATCH, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06582 // NOTE: In production, hash mismatch would have disabled |extension|, but
583 // since UnzipToDirAndLoadExtension() doesn't add the extension to
584 // ExtensionRegistry, ChromeContentVerifierDelegate won't disable it.
585 // TODO(lazyboy): We may want to update this to more closely reflect the
586 // real flow.
587 }
588
589 // Delete 1024.js.
590 {
591 TestContentVerifyJobObserver observer(extension_id, kRelativePath);
592 base::FilePath file_path = unzipped_path.AppendASCII(kJs);
593 ASSERT_TRUE(base::DieFileDie(file_path, false));
Chris Mumford8f812662018-02-22 00:27:57594 EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
595 DoRequestOrLoad(extension, kJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36596 EXPECT_EQ(ContentVerifyJob::HASH_MISMATCH, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06597 }
lazyboyd6dbb262017-03-30 00:57:30598}
599
lazyboye83ab9c62017-03-30 03:18:26600// Tests that zero byte files correctly go through ContentVerifyJob.
Chris Mumford8f812662018-02-22 00:27:57601TEST_P(ExtensionProtocolsTest, VerificationSeenForZeroByteFile) {
lazyboye83ab9c62017-03-30 03:18:26602 SetProtocolHandler(false);
603
Istiaque Ahmed72816eaa2018-01-30 22:37:06604 const std::string kEmptyJs("empty.js");
lazyboye83ab9c62017-03-30 03:18:26605 base::ScopedTempDir temp_dir;
606 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
Istiaque Ahmed72816eaa2018-01-30 22:37:06607 base::FilePath unzipped_path = temp_dir.GetPath();
lazyboye83ab9c62017-03-30 03:18:26608
Istiaque Ahmed72816eaa2018-01-30 22:37:06609 scoped_refptr<Extension> extension =
610 content_verifier_test_utils::UnzipToDirAndLoadExtension(
611 GetContentVerifierTestPath().AppendASCII("source.zip"),
612 unzipped_path);
613 ASSERT_TRUE(extension.get());
614
615 base::FilePath kRelativePath(FILE_PATH_LITERAL("empty.js"));
616 ExtensionId extension_id = extension->id();
617
618 // Sanity check empty.js.
619 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
lazyboye83ab9c62017-03-30 03:18:26620 int64_t foo_file_size = -1;
Istiaque Ahmed72816eaa2018-01-30 22:37:06621 ASSERT_TRUE(base::GetFileSize(file_path, &foo_file_size));
lazyboye83ab9c62017-03-30 03:18:26622 ASSERT_EQ(0, foo_file_size);
623
Istiaque Ahmed72816eaa2018-01-30 22:37:06624 // Request empty.js.
625 {
626 TestContentVerifyJobObserver observer(extension_id, kRelativePath);
lazyboye83ab9c62017-03-30 03:18:26627
Chris Mumford8f812662018-02-22 00:27:57628 content_verifier_->OnExtensionLoaded(browser_context(), extension.get());
Istiaque Ahmed72816eaa2018-01-30 22:37:06629 // Wait for PostTask to ContentVerifierIOData::AddData() to finish.
630 content::RunAllPendingInMessageLoop();
lazyboye83ab9c62017-03-30 03:18:26631
Chris Mumford8f812662018-02-22 00:27:57632 EXPECT_EQ(net::OK, DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36633 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06634 }
635
636 // chmod -r empty.js.
637 // Unreadable empty file doesn't generate hash mismatch. Note that this is the
638 // current behavior of ContentVerifyJob.
639 // TODO(lazyboy): The behavior is probably incorrect.
640 {
641 TestContentVerifyJobObserver observer(extension->id(), kRelativePath);
642 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
643 ASSERT_TRUE(base::MakeFileUnreadable(file_path));
Chris Mumford8f812662018-02-22 00:27:57644 EXPECT_EQ(net::ERR_ACCESS_DENIED,
645 DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36646 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06647 }
648
649 // rm empty.js.
650 // Deleted empty file doesn't generate hash mismatch. Note that this is the
651 // current behavior of ContentVerifyJob.
652 // TODO(lazyboy): The behavior is probably incorrect.
653 {
654 TestContentVerifyJobObserver observer(extension_id, kRelativePath);
655 base::FilePath file_path = unzipped_path.AppendASCII(kEmptyJs);
656 ASSERT_TRUE(base::DieFileDie(file_path, false));
Chris Mumford8f812662018-02-22 00:27:57657 EXPECT_EQ(net::ERR_FILE_NOT_FOUND,
658 DoRequestOrLoad(extension, kEmptyJs).result());
Istiaque Ahmed1dec1d42018-02-02 00:01:36659 EXPECT_EQ(ContentVerifyJob::NONE, observer.WaitForJobFinished());
Istiaque Ahmed72816eaa2018-01-30 22:37:06660 }
lazyboye83ab9c62017-03-30 03:18:26661}
662
Devlin Croninc3f88072018-01-30 02:10:11663// Tests that mime types are properly set for returned extension resources.
Chris Mumford8f812662018-02-22 00:27:57664TEST_P(ExtensionProtocolsTest, MimeTypesForKnownFiles) {
Devlin Croninc3f88072018-01-30 02:10:11665 // Register a non-incognito extension protocol handler.
666 SetProtocolHandler(false);
667
668 TestExtensionDir test_dir;
669 constexpr char kManifest[] = R"(
670 {
671 "name": "Test Ext",
672 "description": "A test extension",
673 "manifest_version": 2,
674 "version": "0.1",
675 "web_accessible_resources": ["*"]
676 })";
677 test_dir.WriteManifest(kManifest);
678 std::unique_ptr<base::DictionaryValue> manifest =
679 base::DictionaryValue::From(base::test::ParseJson(kManifest));
680 ASSERT_TRUE(manifest);
681
682 test_dir.WriteFile(FILE_PATH_LITERAL("json_file.json"), "{}");
683 test_dir.WriteFile(FILE_PATH_LITERAL("js_file.js"), "function() {}");
684
685 base::FilePath unpacked_path = test_dir.UnpackedPath();
686 ASSERT_TRUE(base::PathExists(unpacked_path.AppendASCII("json_file.json")));
687 std::string error;
688 scoped_refptr<const Extension> extension =
689 ExtensionBuilder()
690 .SetManifest(std::move(manifest))
691 .SetPath(unpacked_path)
692 .SetLocation(Manifest::INTERNAL)
693 .Build();
694 ASSERT_TRUE(extension);
695
Chris Mumford8f812662018-02-22 00:27:57696 AddExtension(extension.get(), false, false);
Devlin Croninc3f88072018-01-30 02:10:11697
698 struct {
699 const char* file_name;
700 const char* expected_mime_type;
701 } test_cases[] = {
702 {"json_file.json", "application/json"},
703 {"js_file.js", "application/javascript"},
704 };
705
706 for (const auto& test_case : test_cases) {
707 SCOPED_TRACE(test_case.file_name);
Chris Mumford8f812662018-02-22 00:27:57708 auto result = RequestOrLoad(extension->GetResourceURL(test_case.file_name),
709 content::RESOURCE_TYPE_SUB_RESOURCE);
710 EXPECT_EQ(
711 test_case.expected_mime_type,
712 result.GetResponseHeaderByName(net::HttpRequestHeaders::kContentType));
Devlin Croninc3f88072018-01-30 02:10:11713 }
714}
715
Chris Mumford8f812662018-02-22 00:27:57716INSTANTIATE_TEST_CASE_P(Extensions,
717 ExtensionProtocolsTest,
718 ::testing::ValuesIn(kTestModes));
719
[email protected]702d8b42013-02-27 20:55:50720} // namespace extensions