Home | History | Annotate | Download | only in extensions
      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 
     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 
     88 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
     89     : feature_override_(FeatureSwitch::scripts_require_action(),
     90                         FeatureSwitch::OVERRIDE_ENABLED),
     91       active_script_controller_(NULL) {
     92 }
     93 
     94 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
     95 }
     96 
     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 
    117 const Extension* ActiveScriptControllerUnitTest::ReloadExtension() {
    118   ExtensionRegistry::Get(profile())->RemoveEnabled(extension_->id());
    119   return AddExtension();
    120 }
    121 
    122 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 
    132 void ActiveScriptControllerUnitTest::RequestInjection(
    133     const Extension* extension) {
    134   controller()->RequestScriptInjectionForTesting(
    135       extension,
    136       GetExecutionCallbackForExtension(extension->id()));
    137 }
    138 
    139 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 
    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 
    157 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
    158     const std::string& extension_id) {
    159   ++extension_executions_[extension_id];
    160 }
    161 
    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.
    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.
    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.
    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 
    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 
    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 
    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