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
19using content::BrowserThread;
20using extensions::Extension;
21using extensions::ExtensionSet;
22using extensions::Manifest;
23using storage::SpecialStoragePolicy;
24
25typedef SpecialStoragePolicy::StoragePolicy StoragePolicy;
26
27namespace keys = extensions::manifest_keys;
28
29class 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
182TEST_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
199TEST_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
220TEST_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
250TEST_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
267TEST_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
278TEST_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
315TEST_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
350TEST_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