blob: 0c68b1b9fec03cf496ee7b0b0de54d3fea93a8a2 [file] [log] [blame]
rdevlin.cronin5f6bebcd2016-09-26 23:11:361// Copyright 2016 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
clamy64040222017-08-03 18:01:475#include "extensions/browser/extension_navigation_throttle.h"
Jinho Bangb5216cec2018-01-17 19:43:116
7#include <memory>
rdevlin.cronin5f6bebcd2016-09-26 23:11:368#include "base/strings/stringprintf.h"
9#include "chrome/test/base/chrome_render_view_host_test_harness.h"
10#include "components/crx_file/id_util.h"
11#include "content/public/browser/content_browser_client.h"
12#include "content/public/browser/navigation_handle.h"
13#include "content/public/common/content_client.h"
Camille Lamyfa5767c2019-01-07 14:53:5914#include "content/public/test/mock_navigation_handle.h"
clamy64040222017-08-03 18:01:4715#include "content/public/test/navigation_simulator.h"
rdevlin.cronin5f6bebcd2016-09-26 23:11:3616#include "content/public/test/test_renderer_host.h"
17#include "content/public/test/web_contents_tester.h"
rdevlin.cronin5f6bebcd2016-09-26 23:11:3618#include "extensions/browser/extension_registry.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/extension_builder.h"
21#include "extensions/common/value_builder.h"
22#include "testing/gtest/include/gtest/gtest.h"
23#include "url/gurl.h"
24
25using content::NavigationThrottle;
26
27namespace extensions {
28
29namespace {
30
31const char kAccessible[] = "accessible.html";
32const char kPrivate[] = "private.html";
33const char kAccessibleDir[] = "accessible_dir/*";
34const char kAccessibleDirResource[] = "accessible_dir/foo.html";
35
36class MockBrowserClient : public content::ContentBrowserClient {
37 public:
38 MockBrowserClient() {}
39 ~MockBrowserClient() override {}
40
41 // Only construct an ExtensionNavigationThrottle so that we can test it in
42 // isolation.
avid6d88b912017-01-13 00:16:0043 std::vector<std::unique_ptr<NavigationThrottle>> CreateThrottlesForNavigation(
rdevlin.cronin5f6bebcd2016-09-26 23:11:3644 content::NavigationHandle* handle) override {
avid6d88b912017-01-13 00:16:0045 std::vector<std::unique_ptr<NavigationThrottle>> throttles;
Jinho Bangb5216cec2018-01-17 19:43:1146 throttles.push_back(std::make_unique<ExtensionNavigationThrottle>(handle));
rdevlin.cronin5f6bebcd2016-09-26 23:11:3647 return throttles;
48 }
49};
50
51} // namespace
52
53class ExtensionNavigationThrottleUnitTest
54 : public ChromeRenderViewHostTestHarness {
55 public:
56 ExtensionNavigationThrottleUnitTest() {}
57 void SetUp() override {
58 ChromeRenderViewHostTestHarness::SetUp();
59 original_client_ = content::SetBrowserClientForTesting(&client_);
60 AddExtension();
61 }
62
63 void TearDown() override {
64 content::SetBrowserClientForTesting(original_client_);
65 ChromeRenderViewHostTestHarness::TearDown();
66 }
67
nick7a719cd2017-05-23 20:51:2668 // Checks that trying to navigate the given |host| to |extension_url| results
69 // in the |expected_will_start_result|, and also that navigating to
Lukasz Anforowiczc8058062019-10-01 20:13:0270 // |extension_url| via http redirect gives the same result.
nick7a719cd2017-05-23 20:51:2671 void CheckTestCase(
72 content::RenderFrameHost* host,
73 const GURL& extension_url,
Lucas Garron75d2c9f92017-09-12 12:28:2174 NavigationThrottle::ThrottleAction expected_will_start_result) {
Camille Lamyfa5767c2019-01-07 14:53:5975 content::MockNavigationHandle test_handle(extension_url, host);
Lukasz Anforowiczfeddf7e32019-09-18 22:13:2376 test_handle.set_initiator_origin(host->GetLastCommittedOrigin());
Camille Lamyfa5767c2019-01-07 14:53:5977 test_handle.set_starting_site_instance(host->GetSiteInstance());
78 auto throttle = std::make_unique<ExtensionNavigationThrottle>(&test_handle);
79
nick7a719cd2017-05-23 20:51:2680 // First subtest: direct navigation to |extension_url|.
Camille Lamyfa5767c2019-01-07 14:53:5981 EXPECT_EQ(expected_will_start_result, throttle->WillStartRequest().action())
nick7a719cd2017-05-23 20:51:2682 << extension_url;
83
Camille Lamyfa5767c2019-01-07 14:53:5984 // Second subtest: server redirect to
nick7a719cd2017-05-23 20:51:2685 // |extension_url|.
86 GURL http_url("https://example.com");
Camille Lamyfa5767c2019-01-07 14:53:5987 test_handle.set_url(http_url);
nick7a719cd2017-05-23 20:51:2688
nick7a719cd2017-05-23 20:51:2689 EXPECT_EQ(NavigationThrottle::PROCEED,
Camille Lamyfa5767c2019-01-07 14:53:5990 throttle->WillStartRequest().action())
nick7a719cd2017-05-23 20:51:2691 << http_url;
Camille Lamyfa5767c2019-01-07 14:53:5992 test_handle.set_url(extension_url);
Lukasz Anforowiczc8058062019-10-01 20:13:0293 EXPECT_EQ(expected_will_start_result,
Camille Lamyfa5767c2019-01-07 14:53:5994 throttle->WillRedirectRequest().action())
nick7a719cd2017-05-23 20:51:2695 << extension_url;
rdevlin.cronin5f6bebcd2016-09-26 23:11:3696 }
97
98 const Extension* extension() { return extension_.get(); }
99 content::WebContentsTester* web_contents_tester() {
100 return content::WebContentsTester::For(web_contents());
101 }
102 content::RenderFrameHostTester* render_frame_host_tester(
103 content::RenderFrameHost* host) {
104 return content::RenderFrameHostTester::For(host);
105 }
106
107 private:
108 // Constructs an extension with accessible.html and accessible_dir/* as
109 // accessible resources.
110 void AddExtension() {
111 DictionaryBuilder manifest;
112 manifest.Set("name", "ext")
113 .Set("description", "something")
114 .Set("version", "0.1")
115 .Set("manifest_version", 2)
116 .Set("web_accessible_resources",
117 ListBuilder().Append(kAccessible).Append(kAccessibleDir).Build());
118 extension_ = ExtensionBuilder()
119 .SetManifest(manifest.Build())
120 .SetID(crx_file::id_util::GenerateId("foo"))
121 .Build();
122 ASSERT_TRUE(extension_);
123 ExtensionRegistry::Get(browser_context())->AddEnabled(extension_.get());
124 }
125
126 scoped_refptr<const Extension> extension_;
127 MockBrowserClient client_;
128 content::ContentBrowserClient* original_client_;
129
130 DISALLOW_COPY_AND_ASSIGN(ExtensionNavigationThrottleUnitTest);
131};
132
133// Tests the basic case of an external web page embedding an extension resource.
134TEST_F(ExtensionNavigationThrottleUnitTest, ExternalWebPage) {
135 web_contents_tester()->NavigateAndCommit(GURL("http://example.com"));
136 content::RenderFrameHost* child =
137 render_frame_host_tester(main_rfh())->AppendChild("child");
138
139 // Only resources specified in web_accessible_resources should be allowed.
140 CheckTestCase(child, extension()->GetResourceURL(kPrivate),
141 NavigationThrottle::BLOCK_REQUEST);
142 CheckTestCase(child, extension()->GetResourceURL(kAccessible),
143 NavigationThrottle::PROCEED);
144 CheckTestCase(child, extension()->GetResourceURL(kAccessibleDirResource),
145 NavigationThrottle::PROCEED);
146}
147
148// Tests that the owning extension can access any of its resources.
149TEST_F(ExtensionNavigationThrottleUnitTest, SameExtension) {
150 web_contents_tester()->NavigateAndCommit(
151 extension()->GetResourceURL("trusted.html"));
152 content::RenderFrameHost* child =
153 render_frame_host_tester(main_rfh())->AppendChild("child");
154
155 // All resources should be allowed.
156 CheckTestCase(child, extension()->GetResourceURL(kPrivate),
157 NavigationThrottle::PROCEED);
158 CheckTestCase(child, extension()->GetResourceURL(kAccessible),
159 NavigationThrottle::PROCEED);
160 CheckTestCase(child, extension()->GetResourceURL(kAccessibleDirResource),
161 NavigationThrottle::PROCEED);
162}
163
rdevlin.cronin5f6bebcd2016-09-26 23:11:36164// Tests that requests to disabled or non-existent extensions are blocked.
nick6cfe5c72017-05-22 22:00:42165TEST_F(ExtensionNavigationThrottleUnitTest, DisabledExtensionChildFrame) {
rdevlin.cronin5f6bebcd2016-09-26 23:11:36166 web_contents_tester()->NavigateAndCommit(GURL("http://example.com"));
167 content::RenderFrameHost* child =
168 render_frame_host_tester(main_rfh())->AppendChild("child");
169
170 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
171 registry->RemoveEnabled(extension()->id());
172 registry->AddDisabled(extension());
173
174 // Since the extension is disabled, all requests should be blocked.
175 CheckTestCase(child, extension()->GetResourceURL(kPrivate),
176 NavigationThrottle::BLOCK_REQUEST);
177 CheckTestCase(child, extension()->GetResourceURL(kAccessible),
178 NavigationThrottle::BLOCK_REQUEST);
179 CheckTestCase(child, extension()->GetResourceURL(kAccessibleDirResource),
180 NavigationThrottle::BLOCK_REQUEST);
181
182 std::string second_id = crx_file::id_util::GenerateId("bar");
183 ASSERT_NE(second_id, extension()->id());
nick6cfe5c72017-05-22 22:00:42184 GURL unknown_url(base::StringPrintf("chrome-extension://%s/accessible.html",
rdevlin.cronin5f6bebcd2016-09-26 23:11:36185 second_id.c_str()));
186 // Requests to non-existent extensions should be blocked.
nick6cfe5c72017-05-22 22:00:42187 CheckTestCase(child, unknown_url, NavigationThrottle::BLOCK_REQUEST);
188
189 // Test blob and filesystem URLs with disabled/unknown extensions.
190 GURL disabled_blob(base::StringPrintf("blob:chrome-extension://%s/SOMEGUID",
191 extension()->id().c_str()));
192 GURL unknown_blob(base::StringPrintf("blob:chrome-extension://%s/SOMEGUID",
193 second_id.c_str()));
194 CheckTestCase(child, disabled_blob, NavigationThrottle::BLOCK_REQUEST);
195 CheckTestCase(child, unknown_blob, NavigationThrottle::BLOCK_REQUEST);
196 GURL disabled_filesystem(
197 base::StringPrintf("filesystem:chrome-extension://%s/temporary/foo.html",
198 extension()->id().c_str()));
199 GURL unknown_filesystem(
200 base::StringPrintf("filesystem:chrome-extension://%s/temporary/foo.html",
201 second_id.c_str()));
202 CheckTestCase(child, disabled_filesystem, NavigationThrottle::BLOCK_REQUEST);
203 CheckTestCase(child, unknown_filesystem, NavigationThrottle::BLOCK_REQUEST);
204}
205
206// Tests that requests to disabled or non-existent extensions are blocked.
207TEST_F(ExtensionNavigationThrottleUnitTest, DisabledExtensionMainFrame) {
208 web_contents_tester()->NavigateAndCommit(GURL("http://example.com"));
209
210 ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context());
211 registry->RemoveEnabled(extension()->id());
212 registry->AddDisabled(extension());
213
214 // Since the extension is disabled, all requests should be blocked.
215 CheckTestCase(main_rfh(), extension()->GetResourceURL(kPrivate),
216 NavigationThrottle::BLOCK_REQUEST);
217 CheckTestCase(main_rfh(), extension()->GetResourceURL(kAccessible),
218 NavigationThrottle::BLOCK_REQUEST);
219 CheckTestCase(main_rfh(), extension()->GetResourceURL(kAccessibleDirResource),
220 NavigationThrottle::BLOCK_REQUEST);
221
222 std::string second_id = crx_file::id_util::GenerateId("bar");
223
224 ASSERT_NE(second_id, extension()->id());
225 GURL unknown_url(base::StringPrintf("chrome-extension://%s/accessible.html",
226 second_id.c_str()));
227 // Requests to non-existent extensions should be blocked.
228 CheckTestCase(main_rfh(), unknown_url, NavigationThrottle::BLOCK_REQUEST);
229
230 // Test blob and filesystem URLs with disabled/unknown extensions.
231 GURL disabled_blob(base::StringPrintf("blob:chrome-extension://%s/SOMEGUID",
232 extension()->id().c_str()));
233 GURL unknown_blob(base::StringPrintf("blob:chrome-extension://%s/SOMEGUID",
234 second_id.c_str()));
235 CheckTestCase(main_rfh(), disabled_blob, NavigationThrottle::BLOCK_REQUEST);
236 CheckTestCase(main_rfh(), unknown_blob, NavigationThrottle::BLOCK_REQUEST);
237 GURL disabled_filesystem(
238 base::StringPrintf("filesystem:chrome-extension://%s/temporary/foo.html",
239 extension()->id().c_str()));
240 GURL unknown_filesystem(
241 base::StringPrintf("filesystem:chrome-extension://%s/temporary/foo.html",
242 second_id.c_str()));
243 CheckTestCase(main_rfh(), disabled_filesystem,
244 NavigationThrottle::BLOCK_REQUEST);
245 CheckTestCase(main_rfh(), unknown_filesystem,
246 NavigationThrottle::BLOCK_REQUEST);
rdevlin.cronin5f6bebcd2016-09-26 23:11:36247}
248
249} // namespace extensions