blob: 4ee5a54666ddfef7c8f62e2d8f26f074a8bf67f9 [file] [log] [blame]
[email protected]78cd68e2014-05-22 20:33:521// 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"
[email protected]b33c8c22014-05-29 19:51:0810#include "chrome/browser/extensions/extension_util.h"
[email protected]23a85362014-07-07 23:26:1911#include "chrome/browser/extensions/permissions_updater.h"
[email protected]78cd68e2014-05-22 20:33:5212#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 "content/public/browser/navigation_controller.h"
16#include "content/public/browser/navigation_entry.h"
17#include "content/public/browser/web_contents.h"
18#include "extensions/browser/extension_registry.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/extension_builder.h"
21#include "extensions/common/feature_switch.h"
[email protected]b33c8c22014-05-29 19:51:0822#include "extensions/common/id_util.h"
[email protected]78cd68e2014-05-22 20:33:5223#include "extensions/common/manifest.h"
[email protected]23a85362014-07-07 23:26:1924#include "extensions/common/user_script.h"
[email protected]78cd68e2014-05-22 20:33:5225#include "extensions/common/value_builder.h"
26
27namespace extensions {
28
29namespace {
30
31const 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.
40class 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
[email protected]e1670582014-08-15 23:05:4148 // Reloads |extension_| by removing it from the registry and recreating it.
49 const Extension* ReloadExtension();
50
[email protected]23a85362014-07-07 23:26:1951 // 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);
[email protected]78cd68e2014-05-22 20:33:5257
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
[email protected]23a85362014-07-07 23:26:1961 ActiveScriptController* controller() const {
62 return active_script_controller_;
63 }
[email protected]78cd68e2014-05-22 20:33:5264
65 private:
[email protected]23a85362014-07-07 23:26:1966 // Returns a closure to use as a script execution for a given extension.
67 base::Closure GetExecutionCallbackForExtension(
68 const std::string& extension_id);
69
[email protected]78cd68e2014-05-22 20:33:5270 // 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_;
[email protected]e1670582014-08-15 23:05:4184
85 scoped_refptr<const Extension> extension_;
[email protected]78cd68e2014-05-22 20:33:5286};
87
88ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
89 : feature_override_(FeatureSwitch::scripts_require_action(),
90 FeatureSwitch::OVERRIDE_ENABLED),
91 active_script_controller_(NULL) {
92}
93
94ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
95}
96
97const Extension* ActiveScriptControllerUnitTest::AddExtension() {
[email protected]b33c8c22014-05-29 19:51:0898 const std::string kId = id_util::GenerateId("all_hosts_extension");
[email protected]e1670582014-08-15 23:05:4199 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();
[email protected]78cd68e2014-05-22 20:33:52111
[email protected]e1670582014-08-15 23:05:41112 ExtensionRegistry::Get(profile())->AddEnabled(extension_);
113 PermissionsUpdater(profile()).InitializePermissions(extension_);
114 return extension_;
115}
116
117const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
118 ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
119 return AddExtension();
[email protected]78cd68e2014-05-22 20:33:52120}
121
[email protected]23a85362014-07-07 23:26:19122bool 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
132void ActiveScriptControllerUnitTest::RequestInjection(
133 const Extension* extension) {
134 controller()->RequestScriptInjectionForTesting(
135 extension,
136 GetExecutionCallbackForExtension(extension->id()));
[email protected]78cd68e2014-05-22 20:33:52137}
138
139size_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
[email protected]23a85362014-07-07 23:26:19148base::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
[email protected]78cd68e2014-05-22 20:33:52157void ActiveScriptControllerUnitTest::IncrementExecutionCount(
158 const std::string& extension_id) {
159 ++extension_executions_[extension_id];
160}
161
162void ActiveScriptControllerUnitTest::SetUp() {
163 ChromeRenderViewHostTestHarness::SetUp();
164
165 TabHelper::CreateForWebContents(web_contents());
166 TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
167 // None of these should ever be NULL.
168 DCHECK(tab_helper);
169 DCHECK(tab_helper->location_bar_controller());
170 active_script_controller_ =
171 tab_helper->location_bar_controller()->active_script_controller();
172 DCHECK(active_script_controller_);
173}
174
175// Test that extensions with all_hosts require permission to execute, and, once
176// that permission is granted, do execute.
177TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
178 const Extension* extension = AddExtension();
179 ASSERT_TRUE(extension);
180
181 NavigateAndCommit(GURL("https://www.google.com"));
182
183 // Ensure that there aren't any executions pending.
184 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
185 ASSERT_FALSE(controller()->GetActionForExtension(extension));
186
187 // Since the extension requests all_hosts, we should require user consent.
[email protected]23a85362014-07-07 23:26:19188 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]78cd68e2014-05-22 20:33:52189
190 // Request an injection. There should be an action visible, but no executions.
[email protected]23a85362014-07-07 23:26:19191 RequestInjection(extension);
[email protected]78cd68e2014-05-22 20:33:52192 EXPECT_TRUE(controller()->GetActionForExtension(extension));
193 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
194
195 // Click to accept the extension executing.
196 controller()->OnClicked(extension);
197
198 // The extension should execute, and the action should go away.
199 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
200 EXPECT_FALSE(controller()->GetActionForExtension(extension));
201
202 // Since we already executed on the given page, we shouldn't need permission
203 // for a second time.
[email protected]23a85362014-07-07 23:26:19204 EXPECT_FALSE(RequiresUserConsent(extension));
[email protected]78cd68e2014-05-22 20:33:52205
[email protected]4b8d1c62014-08-16 01:22:21206 // Reloading and same-origin navigations shouldn't clear those permissions,
207 // and we shouldn't require user constent again.
[email protected]78cd68e2014-05-22 20:33:52208 Reload();
[email protected]4b8d1c62014-08-16 01:22:21209 EXPECT_FALSE(RequiresUserConsent(extension));
210 NavigateAndCommit(GURL("https://www.google.com/foo"));
211 EXPECT_FALSE(RequiresUserConsent(extension));
212 NavigateAndCommit(GURL("https://www.google.com/bar"));
213 EXPECT_FALSE(RequiresUserConsent(extension));
214
215 // Cross-origin navigations should clear permissions.
216 NavigateAndCommit(GURL("https://otherdomain.google.com"));
[email protected]23a85362014-07-07 23:26:19217 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]78cd68e2014-05-22 20:33:52218
219 // Grant access.
[email protected]23a85362014-07-07 23:26:19220 RequestInjection(extension);
[email protected]78cd68e2014-05-22 20:33:52221 controller()->OnClicked(extension);
222 EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
223 EXPECT_FALSE(controller()->GetActionForExtension(extension));
224
225 // Navigating to another site should also clear the permissions.
226 NavigateAndCommit(GURL("https://www.foo.com"));
[email protected]23a85362014-07-07 23:26:19227 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]78cd68e2014-05-22 20:33:52228}
229
230// Test that injections that are not executed by the time the user navigates are
231// ignored and never execute.
232TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
233 const Extension* extension = AddExtension();
234 ASSERT_TRUE(extension);
235
236 NavigateAndCommit(GURL("https://www.google.com"));
237
238 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
239
240 // Request an injection. There should be an action visible, but no executions.
[email protected]23a85362014-07-07 23:26:19241 RequestInjection(extension);
[email protected]78cd68e2014-05-22 20:33:52242 EXPECT_TRUE(controller()->GetActionForExtension(extension));
243 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
244
[email protected]8d5cb212014-06-04 09:00:39245 // Reload. This should remove the pending injection, and we should not
[email protected]78cd68e2014-05-22 20:33:52246 // execute anything.
[email protected]8d5cb212014-06-04 09:00:39247 Reload();
[email protected]78cd68e2014-05-22 20:33:52248 EXPECT_FALSE(controller()->GetActionForExtension(extension));
249 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
250
251 // Request and accept a new injection.
[email protected]23a85362014-07-07 23:26:19252 RequestInjection(extension);
[email protected]78cd68e2014-05-22 20:33:52253 controller()->OnClicked(extension);
254
255 // The extension should only have executed once, even though a grand total
256 // of two executions were requested.
257 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
258 EXPECT_FALSE(controller()->GetActionForExtension(extension));
259}
260
261// Test that queueing multiple pending injections, and then accepting, triggers
262// them all.
263TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
264 const Extension* extension = AddExtension();
265 ASSERT_TRUE(extension);
266 NavigateAndCommit(GURL("https://www.google.com"));
267
268 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
269
270 const size_t kNumInjections = 3u;
271 // Queue multiple pending injections.
[email protected]23a85362014-07-07 23:26:19272 for (size_t i = 0u; i < kNumInjections; ++i)
273 RequestInjection(extension);
274
[email protected]78cd68e2014-05-22 20:33:52275 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
276
277 controller()->OnClicked(extension);
278
279 // All pending injections should have executed.
280 EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
281 EXPECT_FALSE(controller()->GetActionForExtension(extension));
282}
283
284TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
285 const Extension* extension = AddExtension();
286 NavigateAndCommit(GURL("https://www.google.com"));
287
288 ActiveTabPermissionGranter* active_tab_permission_granter =
289 TabHelper::FromWebContents(web_contents())
290 ->active_tab_permission_granter();
291 ASSERT_TRUE(active_tab_permission_granter);
292 // Grant the extension active tab permissions. This normally happens, e.g.,
293 // if the user clicks on a browser action.
294 active_tab_permission_granter->GrantIfRequested(extension);
295
296 // Since we have active tab permissions, we shouldn't need user consent
297 // anymore.
[email protected]23a85362014-07-07 23:26:19298 EXPECT_FALSE(RequiresUserConsent(extension));
[email protected]78cd68e2014-05-22 20:33:52299
[email protected]4b8d1c62014-08-16 01:22:21300 // Reloading and other same-origin navigations maintain the permission to
301 // execute.
[email protected]8d5cb212014-06-04 09:00:39302 Reload();
[email protected]4b8d1c62014-08-16 01:22:21303 EXPECT_FALSE(RequiresUserConsent(extension));
304 NavigateAndCommit(GURL("https://www.google.com/foo"));
305 EXPECT_FALSE(RequiresUserConsent(extension));
306 NavigateAndCommit(GURL("https://www.google.com/bar"));
307 EXPECT_FALSE(RequiresUserConsent(extension));
308
309 // Navigating to a different origin will require user consent again.
310 NavigateAndCommit(GURL("https://yahoo.com"));
311 EXPECT_TRUE(RequiresUserConsent(extension));
312
313 // Back to the original origin should also re-require constent.
314 NavigateAndCommit(GURL("https://www.google.com"));
[email protected]23a85362014-07-07 23:26:19315 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]11814f52014-05-23 06:50:35316
[email protected]23a85362014-07-07 23:26:19317 RequestInjection(extension);
[email protected]11814f52014-05-23 06:50:35318 EXPECT_TRUE(controller()->GetActionForExtension(extension));
319 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
320
321 // Grant active tab.
322 active_tab_permission_granter->GrantIfRequested(extension);
323
324 // The pending injections should have run since active tab permission was
325 // granted.
326 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
327 EXPECT_FALSE(controller()->GetActionForExtension(extension));
[email protected]78cd68e2014-05-22 20:33:52328}
329
[email protected]b33c8c22014-05-29 19:51:08330TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
331 const Extension* extension = AddExtension();
332 ASSERT_TRUE(extension);
333
334 NavigateAndCommit(GURL("https://www.google.com"));
[email protected]23a85362014-07-07 23:26:19335 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]b33c8c22014-05-29 19:51:08336
337 // Enable the extension on all urls.
338 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
339
[email protected]23a85362014-07-07 23:26:19340 EXPECT_FALSE(RequiresUserConsent(extension));
[email protected]b33c8c22014-05-29 19:51:08341 // This should carry across navigations, and websites.
342 NavigateAndCommit(GURL("http://www.foo.com"));
[email protected]23a85362014-07-07 23:26:19343 EXPECT_FALSE(RequiresUserConsent(extension));
[email protected]b33c8c22014-05-29 19:51:08344
345 // Turning off the preference should have instant effect.
346 util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
[email protected]23a85362014-07-07 23:26:19347 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]b33c8c22014-05-29 19:51:08348
349 // And should also persist across navigations and websites.
350 NavigateAndCommit(GURL("http://www.bar.com"));
[email protected]23a85362014-07-07 23:26:19351 EXPECT_TRUE(RequiresUserConsent(extension));
[email protected]b33c8c22014-05-29 19:51:08352}
353
[email protected]e1670582014-08-15 23:05:41354TEST_F(ActiveScriptControllerUnitTest, TestAlwaysRun) {
355 const Extension* extension = AddExtension();
356 ASSERT_TRUE(extension);
357
358 NavigateAndCommit(GURL("https://www.google.com/?gws_rd=ssl"));
359
360 // Ensure that there aren't any executions pending.
361 ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
362 ASSERT_FALSE(controller()->GetActionForExtension(extension));
363
364 // Since the extension requests all_hosts, we should require user consent.
365 EXPECT_TRUE(RequiresUserConsent(extension));
366
367 // Request an injection. There should be an action visible, but no executions.
368 RequestInjection(extension);
369 EXPECT_TRUE(controller()->GetActionForExtension(extension));
370 EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
371
372 // Allow the extension to always run on this origin.
373 controller()->AlwaysRunOnVisibleOrigin(extension);
374
375 // The extension should execute, and the action should go away.
376 EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
377 EXPECT_FALSE(controller()->GetActionForExtension(extension));
378
379 // Since we already executed on the given page, we shouldn't need permission
380 // for a second time.
381 EXPECT_FALSE(RequiresUserConsent(extension));
382
383 // Navigating to another site that hasn't been granted a persisted permission
384 // should necessitate user consent.
385 NavigateAndCommit(GURL("https://www.foo.com/bar"));
386 EXPECT_TRUE(RequiresUserConsent(extension));
387
388 // We shouldn't need user permission upon returning to the original origin.
389 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
390 EXPECT_FALSE(RequiresUserConsent(extension));
391
392 // Reloading the extension should not clear any granted host permissions.
393 extension = ReloadExtension();
394 Reload();
395 EXPECT_FALSE(RequiresUserConsent(extension));
396
397 // Different host...
398 NavigateAndCommit(GURL("https://www.foo.com/bar"));
399 EXPECT_TRUE(RequiresUserConsent(extension));
400 // Different scheme...
401 NavigateAndCommit(GURL("http://www.google.com/foo/bar"));
402 EXPECT_TRUE(RequiresUserConsent(extension));
403 // Different subdomain...
404 NavigateAndCommit(GURL("https://en.google.com/foo/bar"));
405 EXPECT_TRUE(RequiresUserConsent(extension));
406 // Only the "always run" origin should be allowed to run without user consent.
407 NavigateAndCommit(GURL("https://www.google.com/foo/bar"));
408 EXPECT_FALSE(RequiresUserConsent(extension));
409}
410
[email protected]78cd68e2014-05-22 20:33:52411} // namespace extensions