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/message_loop/message_loop.h" 6 #include "base/values.h" 7 #include "chrome/browser/content_settings/cookie_settings.h" 8 #include "chrome/browser/extensions/extension_special_storage_policy.h" 9 #include "chrome/test/base/testing_profile.h" 10 #include "components/content_settings/core/common/content_settings.h" 11 #include "components/content_settings/core/common/content_settings_types.h" 12 #include "content/public/test/test_browser_thread.h" 13 #include "extensions/common/extension.h" 14 #include "extensions/common/extension_set.h" 15 #include "extensions/common/manifest.h" 16 #include "extensions/common/manifest_constants.h" 17 #include "testing/gtest/include/gtest/gtest.h" 18 19 using content::BrowserThread; 20 using extensions::Extension; 21 using extensions::ExtensionSet; 22 using extensions::Manifest; 23 using storage::SpecialStoragePolicy; 24 25 typedef SpecialStoragePolicy::StoragePolicy StoragePolicy; 26 27 namespace keys = extensions::manifest_keys; 28 29 class ExtensionSpecialStoragePolicyTest : public testing::Test { 30 protected: 31 class PolicyChangeObserver : public SpecialStoragePolicy::Observer { 32 public: 33 PolicyChangeObserver() 34 : expected_type_(NOTIFICATION_TYPE_NONE), 35 expected_change_flags_(0) { 36 } 37 38 virtual void OnGranted(const GURL& origin, 39 int change_flags) OVERRIDE { 40 EXPECT_EQ(expected_type_, NOTIFICATION_TYPE_GRANT); 41 EXPECT_EQ(expected_origin_, origin); 42 EXPECT_EQ(expected_change_flags_, change_flags); 43 expected_type_ = NOTIFICATION_TYPE_NONE; 44 } 45 46 virtual void OnRevoked(const GURL& origin, 47 int change_flags) OVERRIDE { 48 EXPECT_EQ(expected_type_, NOTIFICATION_TYPE_REVOKE); 49 EXPECT_EQ(expected_origin_, origin); 50 EXPECT_EQ(expected_change_flags_, change_flags); 51 expected_type_ = NOTIFICATION_TYPE_NONE; 52 } 53 54 virtual void OnCleared() OVERRIDE { 55 EXPECT_EQ(expected_type_, NOTIFICATION_TYPE_CLEAR); 56 expected_type_ = NOTIFICATION_TYPE_NONE; 57 } 58 59 void ExpectGrant(const std::string& extension_id, 60 int change_flags) { 61 expected_type_ = NOTIFICATION_TYPE_GRANT; 62 expected_origin_ = Extension::GetBaseURLFromExtensionId(extension_id); 63 expected_change_flags_ = change_flags; 64 } 65 66 void ExpectRevoke(const std::string& extension_id, 67 int change_flags) { 68 expected_type_ = NOTIFICATION_TYPE_REVOKE; 69 expected_origin_ = Extension::GetBaseURLFromExtensionId(extension_id); 70 expected_change_flags_ = change_flags; 71 } 72 73 void ExpectClear() { 74 expected_type_ = NOTIFICATION_TYPE_CLEAR; 75 } 76 77 bool IsCompleted() { 78 return expected_type_ == NOTIFICATION_TYPE_NONE; 79 } 80 81 private: 82 enum { 83 NOTIFICATION_TYPE_NONE, 84 NOTIFICATION_TYPE_GRANT, 85 NOTIFICATION_TYPE_REVOKE, 86 NOTIFICATION_TYPE_CLEAR, 87 } expected_type_; 88 89 GURL expected_origin_; 90 int expected_change_flags_; 91 92 DISALLOW_COPY_AND_ASSIGN(PolicyChangeObserver); 93 }; 94 95 virtual void SetUp() OVERRIDE { 96 policy_ = new ExtensionSpecialStoragePolicy(NULL); 97 } 98 99 scoped_refptr<Extension> CreateProtectedApp() { 100 #if defined(OS_WIN) 101 base::FilePath path(FILE_PATH_LITERAL("c:\\foo")); 102 #elif defined(OS_POSIX) 103 base::FilePath path(FILE_PATH_LITERAL("/foo")); 104 #endif 105 base::DictionaryValue manifest; 106 manifest.SetString(keys::kName, "Protected"); 107 manifest.SetString(keys::kVersion, "1"); 108 manifest.SetString(keys::kLaunchWebURL, "http://explicit/protected/start"); 109 base::ListValue* list = new base::ListValue(); 110 list->Append(new base::StringValue("http://explicit/protected")); 111 list->Append(new base::StringValue("*://*.wildcards/protected")); 112 manifest.Set(keys::kWebURLs, list); 113 std::string error; 114 scoped_refptr<Extension> protected_app = Extension::Create( 115 path, Manifest::INVALID_LOCATION, manifest, 116 Extension::NO_FLAGS, &error); 117 EXPECT_TRUE(protected_app.get()) << error; 118 return protected_app; 119 } 120 121 scoped_refptr<Extension> CreateUnlimitedApp() { 122 #if defined(OS_WIN) 123 base::FilePath path(FILE_PATH_LITERAL("c:\\bar")); 124 #elif defined(OS_POSIX) 125 base::FilePath path(FILE_PATH_LITERAL("/bar")); 126 #endif 127 base::DictionaryValue manifest; 128 manifest.SetString(keys::kName, "Unlimited"); 129 manifest.SetString(keys::kVersion, "1"); 130 manifest.SetString(keys::kLaunchWebURL, "http://explicit/unlimited/start"); 131 base::ListValue* list = new base::ListValue(); 132 list->Append(new base::StringValue("unlimitedStorage")); 133 manifest.Set(keys::kPermissions, list); 134 list = new base::ListValue(); 135 list->Append(new base::StringValue("http://explicit/unlimited")); 136 list->Append(new base::StringValue("*://*.wildcards/unlimited")); 137 manifest.Set(keys::kWebURLs, list); 138 std::string error; 139 scoped_refptr<Extension> unlimited_app = Extension::Create( 140 path, Manifest::INVALID_LOCATION, manifest, 141 Extension::NO_FLAGS, &error); 142 EXPECT_TRUE(unlimited_app.get()) << error; 143 return unlimited_app; 144 } 145 146 scoped_refptr<Extension> CreateRegularApp() { 147 #if defined(OS_WIN) 148 base::FilePath path(FILE_PATH_LITERAL("c:\\app")); 149 #elif defined(OS_POSIX) 150 base::FilePath path(FILE_PATH_LITERAL("/app")); 151 #endif 152 base::DictionaryValue manifest; 153 manifest.SetString(keys::kName, "App"); 154 manifest.SetString(keys::kVersion, "1"); 155 manifest.SetString(keys::kPlatformAppBackgroundPage, "background.html"); 156 std::string error; 157 scoped_refptr<Extension> app = Extension::Create( 158 path, Manifest::INVALID_LOCATION, manifest, 159 Extension::NO_FLAGS, &error); 160 EXPECT_TRUE(app.get()) << error; 161 return app; 162 } 163 164 // Verifies that the set of extensions protecting |url| is *exactly* equal to 165 // |expected_extensions|. Pass in an empty set to verify that an origin is not 166 // protected. 167 void ExpectProtectedBy(const ExtensionSet& expected_extensions, 168 const GURL& url) { 169 const ExtensionSet* extensions = policy_->ExtensionsProtectingOrigin(url); 170 EXPECT_EQ(expected_extensions.size(), extensions->size()); 171 for (ExtensionSet::const_iterator it = expected_extensions.begin(); 172 it != expected_extensions.end(); ++it) { 173 EXPECT_TRUE(extensions->Contains((*it)->id())) 174 << "Origin " << url << "not protected by extension ID " 175 << (*it)->id(); 176 } 177 } 178 179 scoped_refptr<ExtensionSpecialStoragePolicy> policy_; 180 }; 181 182 TEST_F(ExtensionSpecialStoragePolicyTest, EmptyPolicy) { 183 const GURL kHttpUrl("http://foo"); 184 const GURL kExtensionUrl("chrome-extension://bar"); 185 scoped_refptr<Extension> app(CreateRegularApp()); 186 187 EXPECT_FALSE(policy_->IsStorageUnlimited(kHttpUrl)); 188 EXPECT_FALSE(policy_->IsStorageUnlimited(kHttpUrl)); // test cached result 189 EXPECT_FALSE(policy_->IsStorageUnlimited(kExtensionUrl)); 190 EXPECT_FALSE(policy_->IsStorageUnlimited(app->url())); 191 ExtensionSet empty_set; 192 ExpectProtectedBy(empty_set, kHttpUrl); 193 194 // This one is just based on the scheme. 195 EXPECT_TRUE(policy_->IsStorageProtected(kExtensionUrl)); 196 EXPECT_TRUE(policy_->IsStorageProtected(app->url())); 197 } 198 199 TEST_F(ExtensionSpecialStoragePolicyTest, AppWithProtectedStorage) { 200 scoped_refptr<Extension> extension(CreateProtectedApp()); 201 policy_->GrantRightsForExtension(extension.get()); 202 ExtensionSet protecting_extensions; 203 protecting_extensions.Insert(extension); 204 ExtensionSet empty_set; 205 206 EXPECT_FALSE(policy_->IsStorageUnlimited(extension->url())); 207 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("http://explicit/"))); 208 ExpectProtectedBy(protecting_extensions, GURL("http://explicit/")); 209 ExpectProtectedBy(protecting_extensions, GURL("http://explicit:6000/")); 210 ExpectProtectedBy(protecting_extensions, GURL("http://foo.wildcards/")); 211 ExpectProtectedBy(protecting_extensions, GURL("https://bar.wildcards/")); 212 ExpectProtectedBy(empty_set, GURL("http://not_listed/")); 213 214 policy_->RevokeRightsForExtension(extension.get()); 215 ExpectProtectedBy(empty_set, GURL("http://explicit/")); 216 ExpectProtectedBy(empty_set, GURL("http://foo.wildcards/")); 217 ExpectProtectedBy(empty_set, GURL("https://bar.wildcards/")); 218 } 219 220 TEST_F(ExtensionSpecialStoragePolicyTest, AppWithUnlimitedStorage) { 221 scoped_refptr<Extension> extension(CreateUnlimitedApp()); 222 policy_->GrantRightsForExtension(extension.get()); 223 ExtensionSet protecting_extensions; 224 protecting_extensions.Insert(extension); 225 ExtensionSet empty_set; 226 227 ExpectProtectedBy(protecting_extensions, GURL("http://explicit/")); 228 ExpectProtectedBy(protecting_extensions, GURL("http://explicit:6000/")); 229 ExpectProtectedBy(protecting_extensions, GURL("https://foo.wildcards/")); 230 ExpectProtectedBy(protecting_extensions, GURL("https://foo.wildcards/")); 231 ExpectProtectedBy(protecting_extensions, GURL("http://bar.wildcards/")); 232 ExpectProtectedBy(empty_set, GURL("http://not_listed/")); 233 EXPECT_TRUE(policy_->IsStorageUnlimited(extension->url())); 234 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("http://explicit/"))); 235 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("http://explicit:6000/"))); 236 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("https://foo.wildcards/"))); 237 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("https://bar.wildcards/"))); 238 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("http://not_listed/"))); 239 240 policy_->RevokeRightsForExtension(extension.get()); 241 ExpectProtectedBy(empty_set, GURL("http://explicit/")); 242 ExpectProtectedBy(empty_set, GURL("https://foo.wildcards/")); 243 ExpectProtectedBy(empty_set, GURL("https://foo.wildcards/")); 244 ExpectProtectedBy(empty_set, GURL("http://bar.wildcards/")); 245 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("http://explicit/"))); 246 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("https://foo.wildcards/"))); 247 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("https://bar.wildcards/"))); 248 } 249 250 TEST_F(ExtensionSpecialStoragePolicyTest, CanQueryDiskSize) { 251 const GURL kHttpUrl("http://foo"); 252 const GURL kExtensionUrl("chrome-extension://bar"); 253 scoped_refptr<Extension> regular_app(CreateRegularApp()); 254 scoped_refptr<Extension> protected_app(CreateProtectedApp()); 255 scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp()); 256 policy_->GrantRightsForExtension(regular_app.get()); 257 policy_->GrantRightsForExtension(protected_app.get()); 258 policy_->GrantRightsForExtension(unlimited_app.get()); 259 260 EXPECT_FALSE(policy_->CanQueryDiskSize(kHttpUrl)); 261 EXPECT_FALSE(policy_->CanQueryDiskSize(kExtensionUrl)); 262 EXPECT_TRUE(policy_->CanQueryDiskSize(regular_app->url())); 263 EXPECT_TRUE(policy_->CanQueryDiskSize(protected_app->url())); 264 EXPECT_TRUE(policy_->CanQueryDiskSize(unlimited_app->url())); 265 } 266 267 TEST_F(ExtensionSpecialStoragePolicyTest, HasIsolatedStorage) { 268 const GURL kHttpUrl("http://foo"); 269 const GURL kExtensionUrl("chrome-extension://bar"); 270 scoped_refptr<Extension> app(CreateRegularApp()); 271 policy_->GrantRightsForExtension(app.get()); 272 273 EXPECT_FALSE(policy_->HasIsolatedStorage(kHttpUrl)); 274 EXPECT_FALSE(policy_->HasIsolatedStorage(kExtensionUrl)); 275 EXPECT_TRUE(policy_->HasIsolatedStorage(app->url())); 276 } 277 278 TEST_F(ExtensionSpecialStoragePolicyTest, OverlappingApps) { 279 scoped_refptr<Extension> protected_app(CreateProtectedApp()); 280 scoped_refptr<Extension> unlimited_app(CreateUnlimitedApp()); 281 policy_->GrantRightsForExtension(protected_app.get()); 282 policy_->GrantRightsForExtension(unlimited_app.get()); 283 ExtensionSet protecting_extensions; 284 ExtensionSet empty_set; 285 protecting_extensions.Insert(protected_app); 286 protecting_extensions.Insert(unlimited_app); 287 288 ExpectProtectedBy(protecting_extensions, GURL("http://explicit/")); 289 ExpectProtectedBy(protecting_extensions, GURL("http://explicit:6000/")); 290 ExpectProtectedBy(protecting_extensions, GURL("https://foo.wildcards/")); 291 ExpectProtectedBy(protecting_extensions, GURL("https://foo.wildcards/")); 292 ExpectProtectedBy(protecting_extensions, GURL("http://bar.wildcards/")); 293 ExpectProtectedBy(empty_set, GURL("http://not_listed/")); 294 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("http://explicit/"))); 295 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("http://explicit:6000/"))); 296 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("https://foo.wildcards/"))); 297 EXPECT_TRUE(policy_->IsStorageUnlimited(GURL("https://bar.wildcards/"))); 298 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("http://not_listed/"))); 299 300 policy_->RevokeRightsForExtension(unlimited_app.get()); 301 protecting_extensions.Remove(unlimited_app->id()); 302 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("http://explicit/"))); 303 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("https://foo.wildcards/"))); 304 EXPECT_FALSE(policy_->IsStorageUnlimited(GURL("https://bar.wildcards/"))); 305 ExpectProtectedBy(protecting_extensions, GURL("http://explicit/")); 306 ExpectProtectedBy(protecting_extensions, GURL("http://foo.wildcards/")); 307 ExpectProtectedBy(protecting_extensions, GURL("https://bar.wildcards/")); 308 309 policy_->RevokeRightsForExtension(protected_app.get()); 310 ExpectProtectedBy(empty_set, GURL("http://explicit/")); 311 ExpectProtectedBy(empty_set, GURL("http://foo.wildcards/")); 312 ExpectProtectedBy(empty_set, GURL("https://bar.wildcards/")); 313 } 314 315 TEST_F(ExtensionSpecialStoragePolicyTest, HasSessionOnlyOrigins) { 316 base::MessageLoop message_loop; 317 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 318 319 TestingProfile profile; 320 CookieSettings* cookie_settings = 321 CookieSettings::Factory::GetForProfile(&profile).get(); 322 policy_ = new ExtensionSpecialStoragePolicy(cookie_settings); 323 324 EXPECT_FALSE(policy_->HasSessionOnlyOrigins()); 325 326 // The default setting can be session-only. 327 cookie_settings->SetDefaultCookieSetting(CONTENT_SETTING_SESSION_ONLY); 328 EXPECT_TRUE(policy_->HasSessionOnlyOrigins()); 329 330 cookie_settings->SetDefaultCookieSetting(CONTENT_SETTING_ALLOW); 331 EXPECT_FALSE(policy_->HasSessionOnlyOrigins()); 332 333 // Or the session-onlyness can affect individual origins. 334 ContentSettingsPattern pattern = 335 ContentSettingsPattern::FromString("pattern.com"); 336 337 cookie_settings->SetCookieSetting(pattern, 338 ContentSettingsPattern::Wildcard(), 339 CONTENT_SETTING_SESSION_ONLY); 340 341 EXPECT_TRUE(policy_->HasSessionOnlyOrigins()); 342 343 // Clearing an origin-specific rule. 344 cookie_settings->ResetCookieSetting(pattern, 345 ContentSettingsPattern::Wildcard()); 346 347 EXPECT_FALSE(policy_->HasSessionOnlyOrigins()); 348 } 349 350 TEST_F(ExtensionSpecialStoragePolicyTest, NotificationTest) { 351 base::MessageLoop message_loop; 352 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); 353 content::TestBrowserThread io_thread(BrowserThread::IO, &message_loop); 354 355 PolicyChangeObserver observer; 356 policy_->AddObserver(&observer); 357 358 scoped_refptr<Extension> apps[] = { 359 CreateProtectedApp(), 360 CreateUnlimitedApp(), 361 }; 362 363 int change_flags[] = { 364 SpecialStoragePolicy::STORAGE_PROTECTED, 365 366 SpecialStoragePolicy::STORAGE_PROTECTED | 367 SpecialStoragePolicy::STORAGE_UNLIMITED, 368 }; 369 370 ASSERT_EQ(arraysize(apps), arraysize(change_flags)); 371 for (size_t i = 0; i < arraysize(apps); ++i) { 372 SCOPED_TRACE(testing::Message() << "i: " << i); 373 observer.ExpectGrant(apps[i]->id(), change_flags[i]); 374 policy_->GrantRightsForExtension(apps[i].get()); 375 message_loop.RunUntilIdle(); 376 EXPECT_TRUE(observer.IsCompleted()); 377 } 378 379 for (size_t i = 0; i < arraysize(apps); ++i) { 380 SCOPED_TRACE(testing::Message() << "i: " << i); 381 policy_->GrantRightsForExtension(apps[i].get()); 382 message_loop.RunUntilIdle(); 383 EXPECT_TRUE(observer.IsCompleted()); 384 } 385 386 for (size_t i = 0; i < arraysize(apps); ++i) { 387 SCOPED_TRACE(testing::Message() << "i: " << i); 388 observer.ExpectRevoke(apps[i]->id(), change_flags[i]); 389 policy_->RevokeRightsForExtension(apps[i].get()); 390 message_loop.RunUntilIdle(); 391 EXPECT_TRUE(observer.IsCompleted()); 392 } 393 394 for (size_t i = 0; i < arraysize(apps); ++i) { 395 SCOPED_TRACE(testing::Message() << "i: " << i); 396 policy_->RevokeRightsForExtension(apps[i].get()); 397 message_loop.RunUntilIdle(); 398 EXPECT_TRUE(observer.IsCompleted()); 399 } 400 401 observer.ExpectClear(); 402 policy_->RevokeRightsForAllExtensions(); 403 message_loop.RunUntilIdle(); 404 EXPECT_TRUE(observer.IsCompleted()); 405 406 policy_->RemoveObserver(&observer); 407 } 408