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
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
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
61  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
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() {
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
117const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
118  ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
119  return AddExtension();
120}
121
122bool 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()));
137}
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
148base::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
157void 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  // 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.
175TEST_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.
231TEST_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.
262TEST_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
283TEST_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
329TEST_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
353TEST_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