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