1// Copyright (c) 2012 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 "base/files/file_path.h"
6#include "base/files/scoped_temp_dir.h"
7#include "base/threading/sequenced_worker_pool.h"
8#include "chrome/app/chrome_command_ids.h"
9#include "chrome/browser/extensions/extension_browsertest.h"
10#include "chrome/browser/extensions/extension_service.h"
11#include "chrome/browser/extensions/extension_sync_service.h"
12#include "chrome/browser/extensions/updater/extension_updater.h"
13#include "chrome/browser/profiles/profile.h"
14#include "chrome/browser/ui/browser.h"
15#include "chrome/browser/ui/global_error/global_error.h"
16#include "chrome/browser/ui/global_error/global_error_service.h"
17#include "chrome/browser/ui/global_error/global_error_service_factory.h"
18#include "chrome/common/chrome_switches.h"
19#include "content/public/browser/browser_thread.h"
20#include "content/public/test/test_utils.h"
21#include "extensions/browser/extension_prefs.h"
22#include "extensions/browser/extension_registry.h"
23#include "extensions/browser/extension_system.h"
24#include "extensions/common/extension.h"
25#include "net/url_request/test_url_request_interceptor.h"
26#include "net/url_request/url_fetcher.h"
27#include "sync/protocol/extension_specifics.pb.h"
28#include "sync/protocol/sync.pb.h"
29
30using content::BrowserThread;
31using extensions::Extension;
32using extensions::ExtensionRegistry;
33using extensions::ExtensionPrefs;
34
35class ExtensionDisabledGlobalErrorTest : public ExtensionBrowserTest {
36 protected:
37  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
38    ExtensionBrowserTest::SetUpCommandLine(command_line);
39    command_line->AppendSwitchASCII(switches::kAppsGalleryUpdateURL,
40                                    "http://localhost/autoupdate/updates.xml");
41  }
42
43  virtual void SetUpOnMainThread() OVERRIDE {
44    EXPECT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
45    service_ = extensions::ExtensionSystem::Get(
46        browser()->profile())->extension_service();
47    registry_ = ExtensionRegistry::Get(browser()->profile());
48    const base::FilePath test_dir =
49        test_data_dir_.AppendASCII("permissions_increase");
50    const base::FilePath pem_path = test_dir.AppendASCII("permissions.pem");
51    path_v1_ = PackExtensionWithOptions(
52        test_dir.AppendASCII("v1"),
53        scoped_temp_dir_.path().AppendASCII("permissions1.crx"),
54        pem_path,
55        base::FilePath());
56    path_v2_ = PackExtensionWithOptions(
57        test_dir.AppendASCII("v2"),
58        scoped_temp_dir_.path().AppendASCII("permissions2.crx"),
59        pem_path,
60        base::FilePath());
61    path_v3_ = PackExtensionWithOptions(
62        test_dir.AppendASCII("v3"),
63        scoped_temp_dir_.path().AppendASCII("permissions3.crx"),
64        pem_path,
65        base::FilePath());
66  }
67
68  // Returns the ExtensionDisabledGlobalError, if present.
69  // Caution: currently only supports one error at a time.
70  GlobalError* GetExtensionDisabledGlobalError() {
71    return GlobalErrorServiceFactory::GetForProfile(browser()->profile())->
72        GetGlobalErrorByMenuItemCommandID(IDC_EXTENSION_DISABLED_FIRST);
73  }
74
75  // Install the initial version, which should happen just fine.
76  const Extension* InstallIncreasingPermissionExtensionV1() {
77    size_t size_before = registry_->enabled_extensions().size();
78    const Extension* extension = InstallExtension(path_v1_, 1);
79    if (!extension)
80      return NULL;
81    if (registry_->enabled_extensions().size() != size_before + 1)
82      return NULL;
83    return extension;
84  }
85
86  // Upgrade to a version that wants more permissions. We should disable the
87  // extension and prompt the user to reenable.
88  const Extension* UpdateIncreasingPermissionExtension(
89      const Extension* extension,
90      const base::FilePath& crx_path,
91      int expected_change) {
92    size_t size_before = registry_->enabled_extensions().size();
93    if (UpdateExtension(extension->id(), crx_path, expected_change))
94      return NULL;
95    content::RunAllBlockingPoolTasksUntilIdle();
96    EXPECT_EQ(size_before + expected_change,
97              registry_->enabled_extensions().size());
98    if (registry_->disabled_extensions().size() != 1u)
99      return NULL;
100
101    return registry_->disabled_extensions().begin()->get();
102  }
103
104  // Helper function to install an extension and upgrade it to a version
105  // requiring additional permissions. Returns the new disabled Extension.
106  const Extension* InstallAndUpdateIncreasingPermissionsExtension() {
107    const Extension* extension = InstallIncreasingPermissionExtensionV1();
108    extension = UpdateIncreasingPermissionExtension(extension, path_v2_, -1);
109    return extension;
110  }
111
112  ExtensionService* service_;
113  ExtensionRegistry* registry_;
114  base::ScopedTempDir scoped_temp_dir_;
115  base::FilePath path_v1_;
116  base::FilePath path_v2_;
117  base::FilePath path_v3_;
118};
119
120// Tests the process of updating an extension to one that requires higher
121// permissions, and accepting the permissions.
122IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, AcceptPermissions) {
123  const Extension* extension = InstallAndUpdateIncreasingPermissionsExtension();
124  ASSERT_TRUE(extension);
125  ASSERT_TRUE(GetExtensionDisabledGlobalError());
126  const size_t size_before = registry_->enabled_extensions().size();
127
128  service_->GrantPermissionsAndEnableExtension(extension);
129  EXPECT_EQ(size_before + 1, registry_->enabled_extensions().size());
130  EXPECT_EQ(0u, registry_->disabled_extensions().size());
131  ASSERT_FALSE(GetExtensionDisabledGlobalError());
132}
133
134// Tests uninstalling an extension that was disabled due to higher permissions.
135IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, Uninstall) {
136  const Extension* extension = InstallAndUpdateIncreasingPermissionsExtension();
137  ASSERT_TRUE(extension);
138  ASSERT_TRUE(GetExtensionDisabledGlobalError());
139  const size_t size_before = registry_->enabled_extensions().size();
140
141  UninstallExtension(extension->id());
142  EXPECT_EQ(size_before, registry_->enabled_extensions().size());
143  EXPECT_EQ(0u, registry_->disabled_extensions().size());
144  ASSERT_FALSE(GetExtensionDisabledGlobalError());
145}
146
147// Tests that no error appears if the user disabled the extension.
148IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, UserDisabled) {
149  const Extension* extension = InstallIncreasingPermissionExtensionV1();
150  DisableExtension(extension->id());
151  extension = UpdateIncreasingPermissionExtension(extension, path_v2_, 0);
152  ASSERT_FALSE(GetExtensionDisabledGlobalError());
153}
154
155// Test that no error appears if the disable reason is unknown
156// (but probably was by the user).
157IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest,
158                       UnknownReasonSamePermissions) {
159  const Extension* extension = InstallIncreasingPermissionExtensionV1();
160  DisableExtension(extension->id());
161  // Clear disable reason to simulate legacy disables.
162  ExtensionPrefs::Get(browser()->profile())
163      ->ClearDisableReasons(extension->id());
164  // Upgrade to version 2. Infer from version 1 having the same permissions
165  // granted by the user that it was disabled by the user.
166  extension = UpdateIncreasingPermissionExtension(extension, path_v2_, 0);
167  ASSERT_TRUE(extension);
168  ASSERT_FALSE(GetExtensionDisabledGlobalError());
169}
170
171// Test that an error appears if the disable reason is unknown
172// (but probably was for increased permissions).
173IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest,
174                       UnknownReasonHigherPermissions) {
175  const Extension* extension = InstallAndUpdateIncreasingPermissionsExtension();
176  // Clear disable reason to simulate legacy disables.
177  ExtensionPrefs::Get(service_->profile())
178      ->ClearDisableReasons(extension->id());
179  // We now have version 2 but only accepted permissions for version 1.
180  GlobalError* error = GetExtensionDisabledGlobalError();
181  ASSERT_TRUE(error);
182  // Also, remove the upgrade error for version 2.
183  GlobalErrorServiceFactory::GetForProfile(browser()->profile())->
184      RemoveGlobalError(error);
185  delete error;
186  // Upgrade to version 3, with even higher permissions. Infer from
187  // version 2 having higher-than-granted permissions that it was disabled
188  // for permissions increase.
189  extension = UpdateIncreasingPermissionExtension(extension, path_v3_, 0);
190  ASSERT_TRUE(extension);
191  ASSERT_TRUE(GetExtensionDisabledGlobalError());
192}
193
194// Test that an error appears if the extension gets disabled because a
195// version with higher permissions was installed by sync.
196IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest,
197                       HigherPermissionsFromSync) {
198  // Get data for extension v2 (disabled) into sync.
199  const Extension* extension = InstallAndUpdateIncreasingPermissionsExtension();
200  std::string extension_id = extension->id();
201  ExtensionSyncService* sync_service = ExtensionSyncService::Get(
202      browser()->profile());
203  extensions::ExtensionSyncData sync_data =
204      sync_service->GetExtensionSyncData(*extension);
205  UninstallExtension(extension_id);
206  extension = NULL;
207
208  // Install extension v1.
209  InstallIncreasingPermissionExtensionV1();
210
211  // Note: This interceptor gets requests on the IO thread.
212  net::LocalHostTestURLRequestInterceptor interceptor(
213      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
214      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
215          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
216  net::URLFetcher::SetEnableInterceptionForTests(true);
217  interceptor.SetResponseIgnoreQuery(
218      GURL("http://localhost/autoupdate/updates.xml"),
219      test_data_dir_.AppendASCII("permissions_increase")
220                    .AppendASCII("updates.xml"));
221  interceptor.SetResponseIgnoreQuery(
222      GURL("http://localhost/autoupdate/v2.crx"),
223      scoped_temp_dir_.path().AppendASCII("permissions2.crx"));
224
225  extensions::ExtensionUpdater::CheckParams params;
226  service_->updater()->set_default_check_params(params);
227
228  // Sync is replacing an older version, so it pends.
229  EXPECT_FALSE(sync_service->ProcessExtensionSyncData(sync_data));
230
231  WaitForExtensionInstall();
232  content::RunAllBlockingPoolTasksUntilIdle();
233
234  extension = service_->GetExtensionById(extension_id, true);
235  ASSERT_TRUE(extension);
236  EXPECT_EQ("2", extension->VersionString());
237  EXPECT_EQ(1u, registry_->disabled_extensions().size());
238  EXPECT_EQ(Extension::DISABLE_PERMISSIONS_INCREASE,
239            ExtensionPrefs::Get(service_->profile())
240                ->GetDisableReasons(extension_id));
241  EXPECT_TRUE(GetExtensionDisabledGlobalError());
242}
243
244// Test that an error appears if an extension gets installed server side.
245IN_PROC_BROWSER_TEST_F(ExtensionDisabledGlobalErrorTest, RemoteInstall) {
246  static const char* extension_id = "pgdpcfcocojkjfbgpiianjngphoopgmo";
247  ExtensionSyncService* sync_service =
248      ExtensionSyncService::Get(browser()->profile());
249
250  // Note: This interceptor gets requests on the IO thread.
251  net::LocalHostTestURLRequestInterceptor interceptor(
252      BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
253      BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior(
254          base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
255  net::URLFetcher::SetEnableInterceptionForTests(true);
256  interceptor.SetResponseIgnoreQuery(
257      GURL("http://localhost/autoupdate/updates.xml"),
258      test_data_dir_.AppendASCII("permissions_increase")
259          .AppendASCII("updates.xml"));
260  interceptor.SetResponseIgnoreQuery(
261      GURL("http://localhost/autoupdate/v2.crx"),
262      scoped_temp_dir_.path().AppendASCII("permissions2.crx"));
263
264  extensions::ExtensionUpdater::CheckParams params;
265  service_->updater()->set_default_check_params(params);
266
267  sync_pb::EntitySpecifics specifics;
268  specifics.mutable_extension()->set_id(extension_id);
269  specifics.mutable_extension()->set_enabled(false);
270  specifics.mutable_extension()->set_remote_install(true);
271  specifics.mutable_extension()->set_update_url(
272      "http://localhost/autoupdate/updates.xml");
273  specifics.mutable_extension()->set_version("2");
274  syncer::SyncData sync_data =
275      syncer::SyncData::CreateRemoteData(1234567,
276                                         specifics,
277                                         base::Time::Now(),
278                                         syncer::AttachmentIdList(),
279                                         syncer::AttachmentServiceProxy());
280  // Sync is installing a new extension, so it pends.
281  EXPECT_FALSE(sync_service->ProcessExtensionSyncData(
282      extensions::ExtensionSyncData(sync_data)));
283
284  WaitForExtensionInstall();
285  content::RunAllBlockingPoolTasksUntilIdle();
286
287  const Extension* extension = service_->GetExtensionById(extension_id, true);
288  ASSERT_TRUE(extension);
289  EXPECT_EQ("2", extension->VersionString());
290  EXPECT_EQ(1u, registry_->disabled_extensions().size());
291  EXPECT_EQ(Extension::DISABLE_REMOTE_INSTALL,
292            ExtensionPrefs::Get(service_->profile())
293                ->GetDisableReasons(extension_id));
294  EXPECT_TRUE(GetExtensionDisabledGlobalError());
295}
296