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/tab_helper.h"
     12 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
     13 #include "chrome/test/base/testing_profile.h"
     14 #include "content/public/browser/navigation_controller.h"
     15 #include "content/public/browser/navigation_entry.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "extensions/browser/extension_registry.h"
     18 #include "extensions/common/extension.h"
     19 #include "extensions/common/extension_builder.h"
     20 #include "extensions/common/feature_switch.h"
     21 #include "extensions/common/id_util.h"
     22 #include "extensions/common/manifest.h"
     23 #include "extensions/common/value_builder.h"
     24 
     25 namespace extensions {
     26 
     27 namespace {
     28 
     29 const char kAllHostsPermission[] = "*://*/*";
     30 
     31 }  // namespace
     32 
     33 // Unittests for the ActiveScriptController mostly test the internal logic
     34 // of the controller itself (when to allow/deny extension script injection).
     35 // Testing real injection is allowed/denied as expected (i.e., that the
     36 // ActiveScriptController correctly interfaces in the system) is done in the
     37 // ActiveScriptControllerBrowserTests.
     38 class ActiveScriptControllerUnitTest : public ChromeRenderViewHostTestHarness {
     39  protected:
     40   ActiveScriptControllerUnitTest();
     41   virtual ~ActiveScriptControllerUnitTest();
     42 
     43   // Creates an extension with all hosts permission and adds it to the registry.
     44   const Extension* AddExtension();
     45 
     46   // Returns the current page id.
     47   int GetPageId();
     48 
     49   // Returns a closure to use as a script execution for a given extension.
     50   base::Closure GetExecutionCallbackForExtension(
     51       const std::string& extension_id);
     52 
     53   // Returns the number of times a given extension has had a script execute.
     54   size_t GetExecutionCountForExtension(const std::string& extension_id) const;
     55 
     56   ActiveScriptController* controller() { return active_script_controller_; }
     57 
     58  private:
     59   // Increment the number of executions for the given |extension_id|.
     60   void IncrementExecutionCount(const std::string& extension_id);
     61 
     62   virtual void SetUp() OVERRIDE;
     63 
     64   // Since ActiveScriptController's behavior is behind a flag, override the
     65   // feature switch.
     66   FeatureSwitch::ScopedOverride feature_override_;
     67 
     68   // The associated ActiveScriptController.
     69   ActiveScriptController* active_script_controller_;
     70 
     71   // The map of observed executions, keyed by extension id.
     72   std::map<std::string, int> extension_executions_;
     73 };
     74 
     75 ActiveScriptControllerUnitTest::ActiveScriptControllerUnitTest()
     76     : feature_override_(FeatureSwitch::scripts_require_action(),
     77                         FeatureSwitch::OVERRIDE_ENABLED),
     78       active_script_controller_(NULL) {
     79 }
     80 
     81 ActiveScriptControllerUnitTest::~ActiveScriptControllerUnitTest() {
     82 }
     83 
     84 const Extension* ActiveScriptControllerUnitTest::AddExtension() {
     85   const std::string kId = id_util::GenerateId("all_hosts_extension");
     86   scoped_refptr<const Extension> extension =
     87       ExtensionBuilder()
     88           .SetManifest(
     89               DictionaryBuilder()
     90                   .Set("name", "all_hosts_extension")
     91                   .Set("description", "an extension")
     92                   .Set("manifest_version", 2)
     93                   .Set("version", "1.0.0")
     94                   .Set("permissions",
     95                        ListBuilder().Append(kAllHostsPermission)))
     96           .SetLocation(Manifest::INTERNAL)
     97           .SetID(kId)
     98           .Build();
     99 
    100   ExtensionRegistry::Get(profile())->AddEnabled(extension);
    101   return extension;
    102 }
    103 
    104 int ActiveScriptControllerUnitTest::GetPageId() {
    105   content::NavigationEntry* navigation_entry =
    106       web_contents()->GetController().GetVisibleEntry();
    107   DCHECK(navigation_entry);  // This should never be NULL.
    108   return navigation_entry->GetPageID();
    109 }
    110 
    111 base::Closure ActiveScriptControllerUnitTest::GetExecutionCallbackForExtension(
    112     const std::string& extension_id) {
    113   // We use base unretained here, but if this ever gets executed outside of
    114   // this test's lifetime, we have a major problem anyway.
    115   return base::Bind(&ActiveScriptControllerUnitTest::IncrementExecutionCount,
    116                     base::Unretained(this),
    117                     extension_id);
    118 }
    119 
    120 size_t ActiveScriptControllerUnitTest::GetExecutionCountForExtension(
    121     const std::string& extension_id) const {
    122   std::map<std::string, int>::const_iterator iter =
    123       extension_executions_.find(extension_id);
    124   if (iter != extension_executions_.end())
    125     return iter->second;
    126   return 0u;
    127 }
    128 
    129 void ActiveScriptControllerUnitTest::IncrementExecutionCount(
    130     const std::string& extension_id) {
    131   ++extension_executions_[extension_id];
    132 }
    133 
    134 void ActiveScriptControllerUnitTest::SetUp() {
    135   ChromeRenderViewHostTestHarness::SetUp();
    136 
    137   TabHelper::CreateForWebContents(web_contents());
    138   TabHelper* tab_helper = TabHelper::FromWebContents(web_contents());
    139   // None of these should ever be NULL.
    140   DCHECK(tab_helper);
    141   DCHECK(tab_helper->location_bar_controller());
    142   active_script_controller_ =
    143       tab_helper->location_bar_controller()->active_script_controller();
    144   DCHECK(active_script_controller_);
    145 }
    146 
    147 // Test that extensions with all_hosts require permission to execute, and, once
    148 // that permission is granted, do execute.
    149 TEST_F(ActiveScriptControllerUnitTest, RequestPermissionAndExecute) {
    150   const Extension* extension = AddExtension();
    151   ASSERT_TRUE(extension);
    152 
    153   NavigateAndCommit(GURL("https://www.google.com"));
    154 
    155   // Ensure that there aren't any executions pending.
    156   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    157   ASSERT_FALSE(controller()->GetActionForExtension(extension));
    158 
    159   // Since the extension requests all_hosts, we should require user consent.
    160   EXPECT_TRUE(
    161       controller()->RequiresUserConsentForScriptInjection(extension));
    162 
    163   // Request an injection. There should be an action visible, but no executions.
    164   controller()->RequestScriptInjection(
    165       extension,
    166       GetPageId(),
    167       GetExecutionCallbackForExtension(extension->id()));
    168   EXPECT_TRUE(controller()->GetActionForExtension(extension));
    169   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    170 
    171   // Click to accept the extension executing.
    172   controller()->OnClicked(extension);
    173 
    174   // The extension should execute, and the action should go away.
    175   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
    176   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    177 
    178   // Since we already executed on the given page, we shouldn't need permission
    179   // for a second time.
    180   EXPECT_FALSE(
    181       controller()->RequiresUserConsentForScriptInjection(extension));
    182 
    183   // Reloading should clear those permissions, and we should again require user
    184   // consent.
    185   Reload();
    186   EXPECT_TRUE(
    187       controller()->RequiresUserConsentForScriptInjection(extension));
    188 
    189   // Grant access.
    190   controller()->RequestScriptInjection(
    191       extension,
    192       GetPageId(),
    193       GetExecutionCallbackForExtension(extension->id()));
    194   controller()->OnClicked(extension);
    195   EXPECT_EQ(2u, GetExecutionCountForExtension(extension->id()));
    196   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    197 
    198   // Navigating to another site should also clear the permissions.
    199   NavigateAndCommit(GURL("https://www.foo.com"));
    200   EXPECT_TRUE(
    201       controller()->RequiresUserConsentForScriptInjection(extension));
    202 }
    203 
    204 // Test that injections that are not executed by the time the user navigates are
    205 // ignored and never execute.
    206 TEST_F(ActiveScriptControllerUnitTest, PendingInjectionsRemovedAtNavigation) {
    207   const Extension* extension = AddExtension();
    208   ASSERT_TRUE(extension);
    209 
    210   NavigateAndCommit(GURL("https://www.google.com"));
    211 
    212   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    213 
    214   // Request an injection. There should be an action visible, but no executions.
    215   controller()->RequestScriptInjection(
    216       extension,
    217       GetPageId(),
    218       GetExecutionCallbackForExtension(extension->id()));
    219   EXPECT_TRUE(controller()->GetActionForExtension(extension));
    220   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    221 
    222   // Reload. This should remove the pending injection, and we should not
    223   // execute anything.
    224   Reload();
    225   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    226   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    227 
    228   // Request and accept a new injection.
    229   controller()->RequestScriptInjection(
    230       extension,
    231       GetPageId(),
    232       GetExecutionCallbackForExtension(extension->id()));
    233   controller()->OnClicked(extension);
    234 
    235   // The extension should only have executed once, even though a grand total
    236   // of two executions were requested.
    237   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
    238   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    239 }
    240 
    241 // Test that queueing multiple pending injections, and then accepting, triggers
    242 // them all.
    243 TEST_F(ActiveScriptControllerUnitTest, MultiplePendingInjection) {
    244   const Extension* extension = AddExtension();
    245   ASSERT_TRUE(extension);
    246   NavigateAndCommit(GURL("https://www.google.com"));
    247 
    248   ASSERT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    249 
    250   const size_t kNumInjections = 3u;
    251   // Queue multiple pending injections.
    252   for (size_t i = 0u; i < kNumInjections; ++i) {
    253     controller()->RequestScriptInjection(
    254         extension,
    255         GetPageId(),
    256         GetExecutionCallbackForExtension(extension->id()));
    257   }
    258   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    259 
    260   controller()->OnClicked(extension);
    261 
    262   // All pending injections should have executed.
    263   EXPECT_EQ(kNumInjections, GetExecutionCountForExtension(extension->id()));
    264   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    265 }
    266 
    267 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsUseActiveTabPermissions) {
    268   const Extension* extension = AddExtension();
    269   NavigateAndCommit(GURL("https://www.google.com"));
    270 
    271   ActiveTabPermissionGranter* active_tab_permission_granter =
    272       TabHelper::FromWebContents(web_contents())
    273           ->active_tab_permission_granter();
    274   ASSERT_TRUE(active_tab_permission_granter);
    275   // Grant the extension active tab permissions. This normally happens, e.g.,
    276   // if the user clicks on a browser action.
    277   active_tab_permission_granter->GrantIfRequested(extension);
    278 
    279   // Since we have active tab permissions, we shouldn't need user consent
    280   // anymore.
    281   EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension));
    282 
    283   // Also test that granting active tab runs any pending tasks.
    284   Reload();
    285   // Navigating should mean we need permission again.
    286   EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension));
    287 
    288   controller()->RequestScriptInjection(
    289       extension,
    290       GetPageId(),
    291       GetExecutionCallbackForExtension(extension->id()));
    292   EXPECT_TRUE(controller()->GetActionForExtension(extension));
    293   EXPECT_EQ(0u, GetExecutionCountForExtension(extension->id()));
    294 
    295   // Grant active tab.
    296   active_tab_permission_granter->GrantIfRequested(extension);
    297 
    298   // The pending injections should have run since active tab permission was
    299   // granted.
    300   EXPECT_EQ(1u, GetExecutionCountForExtension(extension->id()));
    301   EXPECT_FALSE(controller()->GetActionForExtension(extension));
    302 }
    303 
    304 TEST_F(ActiveScriptControllerUnitTest, ActiveScriptsCanHaveAllUrlsPref) {
    305   const Extension* extension = AddExtension();
    306   ASSERT_TRUE(extension);
    307 
    308   NavigateAndCommit(GURL("https://www.google.com"));
    309   EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension));
    310 
    311   // Enable the extension on all urls.
    312   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), true);
    313 
    314   EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension));
    315   // This should carry across navigations, and websites.
    316   NavigateAndCommit(GURL("http://www.foo.com"));
    317   EXPECT_FALSE(controller()->RequiresUserConsentForScriptInjection(extension));
    318 
    319   // Turning off the preference should have instant effect.
    320   util::SetAllowedScriptingOnAllUrls(extension->id(), profile(), false);
    321   EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension));
    322 
    323   // And should also persist across navigations and websites.
    324   NavigateAndCommit(GURL("http://www.bar.com"));
    325   EXPECT_TRUE(controller()->RequiresUserConsentForScriptInjection(extension));
    326 }
    327 
    328 }  // namespace extensions
    329