1 // Copyright 2014 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
5 #include <map>
6
7 #include "base/values.h"
8 #include "chrome/browser/extensions/active_script_controller.h"
9 #include "chrome/browser/extensions/active_tab_permission_granter.h"
10 #include "chrome/browser/extensions/extension_util.h"
11 #include "chrome/browser/extensions/permissions_updater.h"
12 #include "chrome/browser/extensions/tab_helper.h"
13 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "components/crx_file/id_util.h"
16 #include "content/public/browser/navigation_controller.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/web_contents.h"
19 #include "extensions/browser/extension_registry.h"
20 #include "extensions/common/extension.h"
21 #include "extensions/common/extension_builder.h"
22 #include "extensions/common/feature_switch.h"
23 #include "extensions/common/manifest.h"
24 #include "extensions/common/user_script.h"
25 #include "extensions/common/value_builder.h"
26
27 namespace extensions {
28
29 namespace {
30
31 const char kAllHostsPermission[] = "*://*/*";
32
33 } // namespace
34
35 // Unittests for the ActiveScriptController mostly test the internal logic
36 // of the controller itself (when to allow/deny extension script injection).
37 // Testing real injection is allowed/denied as expected (i.e., that the
38 // ActiveScriptController correctly interfaces in the system) is done in the
39 // ActiveScriptControllerBrowserTests.
40 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
41 protected:
42 ActiveScriptControllerUnitTest();
43 virtual ~ActiveScriptControllerUnitTest();
44
45 // Creates an extension with all hosts permission and adds it to the registry.
46 const Extension* AddExtension();
47
48 // Reloads |extension_| by removing it from the registry and recreating it.
49 const Extension* ReloadExtension();
50
51 // Returns true if the |extension| requires user consent before injecting
52 // a script.
53 bool RequiresUserConsent(const Extension* extension) const;
54
55 // Request an injection for the given |extension|.
56 void RequestInjection(const Extension* extension);
57
58 // Returns the number of times a given extension has had a script execute.
59 size_t GetExecutionCountForExtension(const std::string& extension_id) const;
60
controller() const61 ActiveScriptController* controller() const {
62 return active_script_controller_;
63 }
64
65 private:
66 // Returns a closure to use as a script execution for a given extension.
67 base::Closure GetExecutionCallbackForExtension(
68 const std::string& extension_id);
69
70 // Increment the number of executions for the given |extension_id|.
71 void IncrementExecutionCount(const std::string& extension_id);
72
73 virtual void SetUp() OVERRIDE;
74
75 // Since ActiveScriptController's behavior is behind a flag, override the
76 // feature switch.
77 FeatureSwitch::ScopedOverride feature_override_;
78
79 // The associated ActiveScriptController.
80 ActiveScriptController* active_script_controller_;
81
82 // The map of observed executions, keyed by extension id.
83 std::map<std::string, int> extension_executions_;
84
85 scoped_refptr<const Extension> extension_;
86 };
87
ActiveScriptControllerUnitTest()88 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
89 : feature_override_(FeatureSwitch::scripts_require_action(),
90 FeatureSwitch::OVERRIDE_ENABLED),
91 active_script_controller_(NULL) {
92 }
93
~ActiveScriptControllerUnitTest()94 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
95 }
96
AddExtension()97 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
98 const std::string kId = crx_file::id_util::GenerateId("all_hosts_extension");
99 extension_ = ExtensionBuilder()
100 .SetManifest(
101 DictionaryBuilder()
102 .Set("name", "all_hosts_extension")
103 .Set("description", "an extension")
104 .Set("manifest_version", 2)
105 .Set("version", "1.0.0")
106 .Set("permissions",
107 ListBuilder().Append(kAllHostsPermission)))
108 .SetLocation(Manifest::INTERNAL)
109 .SetID(kId)
110 .Build();
111
112 ExtensionRegistry::Get(profile())->AddEnabled(extension_);
113 PermissionsUpdater(profile()).InitializePermissions(extension_.get());
114 return extension_.get();
115 }
116
ReloadExtension()117 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
118 ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
119 return AddExtension();
120 }
121
RequiresUserConsent(const Extension * extension) const122 bool ActiveScriptControllerUnitTest::RequiresUserConsent(
123 const Extension* extension) const {
124 PermissionsData::AccessType access_type =
125 controller()->RequiresUserConsentForScriptInjectionForTesting(
126 extension, UserScript::PROGRAMMATIC_SCRIPT);
127 // We should never downright refuse access in these tests.
128 DCHECK_NE(PermissionsData::ACCESS_DENIED, access_type);
129 return access_type == PermissionsData::ACCESS_WITHHELD;
130 }
131
RequestInjection(const Extension * extension)132 void ActiveScriptControllerUnitTest::RequestInjection(
133 const Extension* extension) {
134 controller()->RequestScriptInjectionForTesting(
135 extension,
136 GetExecutionCallbackForExtension(extension->id()));
137 }
138
GetExecutionCountForExtension(const std::string & extension_id) const139 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
140 const std::string& extension_id) const {
141 std::map<std::string, int>::const_iterator iter =
142 extension_executions_.find(extension_id);
143 if (iter != extension_executions_.end())
144 return iter->second;
145 return 0u;
146 }
147
GetExecutionCallbackForExtension(const std::string & extension_id)148 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
149 const std::string& extension_id) {
150 // We use base unretained here, but if this ever gets executed outside of
151 // this test's lifetime, we have a major problem anyway.
152 return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
153 base::Unretained(this),
154 extension_id);
155 }
156
IncrementExecutionCount(const std::string & extension_id)157 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
158 const std::string& extension_id) {
159 ++extension_executions_[extension_id];
160 }
161
SetUp()162 void ActiveScriptControllerUnitTest::SetUp() {
163 ChromeRenderViewHostTestHarness::SetUp();
164
165 TabHelper::CreateForWebContents(web_contents());
166 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
167 // These should never be NULL.
168 DCHECK(tab_helper);
169 active_script_controller_ = tab_helper->active_script_controller();
170 DCHECK(active_script_controller_);
171 }
172
173 // Test that extensions with all_hosts require permission to execute, and, once
174 // that permission is granted, do execute.
TEST_F(ActiveScriptControllerUnitTest,RequestPermissionAndExecute)175 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
176 const Extension* extension = AddExtension();
177 ASSERT_TRUE(extension);
178
179 NavigateAndCommit(GURL("https://www.google.com"));
180
181 // Ensure that there aren't any executions pending.
182 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
183 ASSERT_FALSE(controller()->WantsToRun(extension));
184
185 // Since the extension requests all_hosts, we should require user consent.
186 EXPECT_TRUE(RequiresUserConsent(extension));
187
188 // Request an injection. The extension should want to run, but should not have
189 // executed.
190 RequestInjection(extension);
191 EXPECT_TRUE(controller()->WantsToRun(extension));
192 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
193
194 // Click to accept the extension executing.
195 controller()->OnClicked(extension);
196
197 // The extension should execute, and the extension shouldn't want to run.
198 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
199 EXPECT_FALSE(controller()->WantsToRun(extension));
200
201 // Since we already executed on the given page, we shouldn't need permission
202 // for a second time.
203 EXPECT_FALSE(RequiresUserConsent(extension));
204
205 // Reloading and same-origin navigations shouldn't clear those permissions,
206 // and we shouldn't require user constent again.
207 Reload();
208 EXPECT_FALSE(RequiresUserConsent(extension));
209 NavigateAndCommit(GURL("https://www.google.com/foo"));
210 EXPECT_FALSE(RequiresUserConsent(extension));
211 NavigateAndCommit(GURL("https://www.google.com/bar"));
212 EXPECT_FALSE(RequiresUserConsent(extension));
213
214 // Cross-origin navigations should clear permissions.
215 NavigateAndCommit(GURL("https://otherdomain.google.com"));
216 EXPECT_TRUE(RequiresUserConsent(extension));
217
218 // Grant access.
219 RequestInjection(extension);
220 controller()->OnClicked(extension);
221 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
222 EXPECT_FALSE(controller()->WantsToRun(extension));
223
224 // Navigating to another site should also clear the permissions.
225 NavigateAndCommit(GURL("https://www.foo.com"));
226 EXPECT_TRUE(RequiresUserConsent(extension));
227 }
228
229 // Test that injections that are not executed by the time the user navigates are
230 // ignored and never execute.
TEST_F(ActiveScriptControllerUnitTest,PendingInjectionsRemovedAtNavigation)231 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
232 const Extension* extension = AddExtension();
233 ASSERT_TRUE(extension);
234
235 NavigateAndCommit(GURL("https://www.google.com"));
236
237 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
238
239 // Request an injection. The extension should want to run, but not execute.
240 RequestInjection(extension);
241 EXPECT_TRUE(controller()->WantsToRun(extension));
242 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
243
244 // Reload. This should remove the pending injection, and we should not
245 // execute anything.
246 Reload();
247 EXPECT_FALSE(controller()->WantsToRun(extension));
248 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
249
250 // Request and accept a new injection.
251 RequestInjection(extension);
252 controller()->OnClicked(extension);
253
254 // The extension should only have executed once, even though a grand total
255 // of two executions were requested.
256 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
257 EXPECT_FALSE(controller()->WantsToRun(extension));
258 }
259
260 // Test that queueing multiple pending injections, and then accepting, triggers
261 // them all.
TEST_F(ActiveScriptControllerUnitTest,MultiplePendingInjection)262 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
263 const Extension* extension = AddExtension();
264 ASSERT_TRUE(extension);
265 NavigateAndCommit(GURL("https://www.google.com"));
266
267 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
268
269 const size_t kNumInjections = 3u;
270 // Queue multiple pending injections.
271 for (size_t i = 0u; i < kNumInjections; ++i)
272 RequestInjection(extension);
273
274 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
275
276 controller()->OnClicked(extension);
277
278 // All pending injections should have executed.
279 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
280 EXPECT_FALSE(controller()->WantsToRun(extension));
281 }
282
TEST_F(ActiveScriptControllerUnitTest,ActiveScriptsUseActiveTabPermissions)283 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
284 const Extension* extension = AddExtension();
285 NavigateAndCommit(GURL("https://www.google.com"));
286
287 ActiveTabPermissionGranter* active_tab_permission_granter =
288 TabHelper::FromWebContents(web_contents())
289 ->active_tab_permission_granter();
290 ASSERT_TRUE(active_tab_permission_granter);
291 // Grant the extension active tab permissions. This normally happens, e.g.,
292 // if the user clicks on a browser action.
293 active_tab_permission_granter->GrantIfRequested(extension);
294
295 // Since we have active tab permissions, we shouldn't need user consent
296 // anymore.
297 EXPECT_FALSE(RequiresUserConsent(extension));
298
299 // Reloading and other same-origin navigations maintain the permission to
300 // execute.
301 Reload();
302 EXPECT_FALSE(RequiresUserConsent(extension));
303 NavigateAndCommit(GURL("https://www.google.com/foo"));
304 EXPECT_FALSE(RequiresUserConsent(extension));
305 NavigateAndCommit(GURL("https://www.google.com/bar"));
306 EXPECT_FALSE(RequiresUserConsent(extension));
307
308 // Navigating to a different origin will require user consent again.
309 NavigateAndCommit(GURL("https://yahoo.com"));
310 EXPECT_TRUE(RequiresUserConsent(extension));
311
312 // Back to the original origin should also re-require constent.
313 NavigateAndCommit(GURL("https://www.google.com"));
314 EXPECT_TRUE(RequiresUserConsent(extension));
315
316 RequestInjection(extension);
317 EXPECT_TRUE(controller()->WantsToRun(extension));
318 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
319
320 // Grant active tab.
321 active_tab_permission_granter->GrantIfRequested(extension);
322
323 // The pending injections should have run since active tab permission was
324 // granted.
325 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
326 EXPECT_FALSE(controller()->WantsToRun(extension));
327 }
328
TEST_F(ActiveScriptControllerUnitTest,ActiveScriptsCanHaveAllUrlsPref)329 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
330 const Extension* extension = AddExtension();
331 ASSERT_TRUE(extension);
332
333 NavigateAndCommit(GURL("https://www.google.com"));
334 EXPECT_TRUE(RequiresUserConsent(extension));
335
336 // Enable the extension on all urls.
337 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
338
339 EXPECT_FALSE(RequiresUserConsent(extension));
340 // This should carry across navigations, and websites.
341 NavigateAndCommit(GURL("http://www.foo.com"));
342 EXPECT_FALSE(RequiresUserConsent(extension));
343
344 // Turning off the preference should have instant effect.
345 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
346 EXPECT_TRUE(RequiresUserConsent(extension));
347
348 // And should also persist across navigations and websites.
349 NavigateAndCommit(GURL("http://www.bar.com"));
350 EXPECT_TRUE(RequiresUserConsent(extension));
351 }
352
TEST_F(ActiveScriptControllerUnitTest,TestAlwaysRun)353 TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
354 const Extension* extension = AddExtension();
355 ASSERT_TRUE(extension);
356
357 NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
358
359 // Ensure that there aren't any executions pending.
360 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
361 ASSERT_FALSE(controller()->WantsToRun(extension));
362
363 // Since the extension requests all_hosts, we should require user consent.
364 EXPECT_TRUE(RequiresUserConsent(extension));
365
366 // Request an injection. The extension should want to run, but not execute.
367 RequestInjection(extension);
368 EXPECT_TRUE(controller()->WantsToRun(extension));
369 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
370
371 // Allow the extension to always run on this origin.
372 controller()->AlwaysRunOnVisibleOrigin(extension);
373
374 // The extension should execute, and the extension shouldn't want to run.
375 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
376 EXPECT_FALSE(controller()->WantsToRun(extension));
377
378 // Since we already executed on the given page, we shouldn't need permission
379 // for a second time.
380 EXPECT_FALSE(RequiresUserConsent(extension));
381
382 // Navigating to another site that hasn't been granted a persisted permission
383 // should necessitate user consent.
384 NavigateAndCommit(GURL("https://www.foo.com/bar"));
385 EXPECT_TRUE(RequiresUserConsent(extension));
386
387 // We shouldn't need user permission upon returning to the original origin.
388 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
389 EXPECT_FALSE(RequiresUserConsent(extension));
390
391 // Reloading the extension should not clear any granted host permissions.
392 extension = ReloadExtension();
393 Reload();
394 EXPECT_FALSE(RequiresUserConsent(extension));
395
396 // Different host...
397 NavigateAndCommit(GURL("https://www.foo.com/bar"));
398 EXPECT_TRUE(RequiresUserConsent(extension));
399 // Different scheme...
400 NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
401 EXPECT_TRUE(RequiresUserConsent(extension));
402 // Different subdomain...
403 NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
404 EXPECT_TRUE(RequiresUserConsent(extension));
405 // Only the "always run" origin should be allowed to run without user consent.
406 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
407 EXPECT_FALSE(RequiresUserConsent(extension));
408 }
409
410 } // namespace extensions
411