summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-28 23:58:54 +0000
committerjstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-28 23:58:54 +0000
commitfdf6801902413c5662d071fba731e013e04f5181 (patch)
treed10a4ff3ed55e7ba76da3f173466b64b6b62c224
parent93939461bcb7d8732d60f1cd70965c25b8617c4a (diff)
downloadchromium_src-fdf6801902413c5662d071fba731e013e04f5181.zip
chromium_src-fdf6801902413c5662d071fba731e013e04f5181.tar.gz
chromium_src-fdf6801902413c5662d071fba731e013e04f5181.tar.bz2
Adds a section to the extension preferences to store the recognized permissions that the user has granted the extension.
Ignores unknown permissions when installing extensions. Disables extension and prompts user to re-enable when unknown permissions become recognized (e.g., through browser upgrade). Allows extensions to remove a permission in one version, then add them back in the next without prompting the user. BUG=42742 TEST=ExtensionsServiceTest, ExtensionPrefsGrantedPermissions, ExtensionTest Review URL: http://codereview.chromium.org/4687005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67500 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/automation/automation_provider.cc2
-rw-r--r--chrome/browser/download/download_util.cc1
-rw-r--r--chrome/browser/extensions/crx_installer.cc3
-rw-r--r--chrome/browser/extensions/crx_installer.h13
-rw-r--r--chrome/browser/extensions/extension_disabled_infobar_delegate.cc4
-rw-r--r--chrome/browser/extensions/extension_prefs.cc152
-rw-r--r--chrome/browser/extensions/extension_prefs.h41
-rw-r--r--chrome/browser/extensions/extension_prefs_unittest.cc122
-rw-r--r--chrome/browser/extensions/extensions_service.cc195
-rw-r--r--chrome/browser/extensions/extensions_service.h19
-rw-r--r--chrome/browser/extensions/extensions_service_unittest.cc314
-rw-r--r--chrome/common/extensions/extension.cc138
-rw-r--r--chrome/common/extensions/extension.h21
-rw-r--r--chrome/common/extensions/extension_manifests_unittest.cc14
-rw-r--r--chrome/common/extensions/extension_unittest.cc101
-rw-r--r--chrome/test/data/extensions/good/Preferences12
-rw-r--r--chrome/test/data/extensions/permissions/unknown.pem16
-rw-r--r--chrome/test/data/extensions/permissions/unknown/manifest.json9
-rw-r--r--chrome/test/data/extensions/permissions/unknown/test.js3
-rw-r--r--chrome/test/live_sync/live_extensions_sync_test_base.cc2
20 files changed, 964 insertions, 218 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc
index bbfb3c5c..231e875 100644
--- a/chrome/browser/automation/automation_provider.cc
+++ b/chrome/browser/automation/automation_provider.cc
@@ -799,7 +799,6 @@ void AutomationProvider::InstallExtension(const FilePath& crx_path,
scoped_refptr<CrxInstaller> installer(
new CrxInstaller(service, NULL)); // silent install, no UI
- installer->set_allow_privilege_increase(true);
installer->InstallCrx(crx_path);
} else {
AutomationMsg_InstallExtension::WriteReplyParams(
@@ -868,7 +867,6 @@ void AutomationProvider::InstallExtensionAndGetHandle(
ExtensionInstallUI* client =
(with_ui ? new ExtensionInstallUI(profile_) : NULL);
scoped_refptr<CrxInstaller> installer(new CrxInstaller(service, client));
- installer->set_allow_privilege_increase(true);
installer->InstallCrx(crx_path);
} else {
AutomationMsg_InstallExtensionAndGetHandle::WriteReplyParams(
diff --git a/chrome/browser/download/download_util.cc b/chrome/browser/download/download_util.cc
index dd4a34c..082845c 100644
--- a/chrome/browser/download/download_util.cc
+++ b/chrome/browser/download/download_util.cc
@@ -252,7 +252,6 @@ void OpenChromeExtension(Profile* profile,
download_item.url(), download_item.referrer_url());
installer->set_original_mime_type(download_item.original_mime_type());
installer->set_apps_require_extension_mime_type(true);
- installer->set_allow_privilege_increase(true);
installer->set_original_url(download_item.url());
installer->set_is_gallery_install(is_gallery_download);
installer->InstallCrx(download_item.full_path());
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 3a9a3df..efe3ad1 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -83,7 +83,6 @@ CrxInstaller::CrxInstaller(ExtensionsService* frontend,
install_source_(Extension::INTERNAL),
extensions_enabled_(frontend->extensions_enabled()),
delete_source_(false),
- allow_privilege_increase_(false),
is_gallery_install_(false),
create_app_shortcut_(false),
frontend_(frontend),
@@ -433,7 +432,7 @@ void CrxInstaller::ReportSuccessFromUIThread() {
// Tell the frontend about the installation and hand off ownership of
// extension_ to it.
- frontend_->OnExtensionInstalled(extension_, allow_privilege_increase_);
+ frontend_->OnExtensionInstalled(extension_);
extension_ = NULL;
// We're done. We don't post any more tasks to ourselves so we are deleted
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index 26bbbd8..c22a4a9 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -96,11 +96,6 @@ class CrxInstaller
bool delete_source() const { return delete_source_; }
void set_delete_source(bool val) { delete_source_ = val; }
- bool allow_privilege_increase() const { return allow_privilege_increase_; }
- void set_allow_privilege_increase(bool val) {
- allow_privilege_increase_ = val;
- }
-
bool allow_silent_install() const { return allow_silent_install_; }
void set_allow_silent_install(bool val) { allow_silent_install_ = val; }
@@ -178,14 +173,6 @@ class CrxInstaller
// to false.
bool delete_source_;
- // Whether privileges should be allowed to silently increaes from any
- // previously installed version of the extension. This is used for things
- // like external extensions, where extensions come with third-party software
- // or are distributed by the network administrator. There is no UI shown
- // for these extensions, so there shouldn't be UI for privilege increase,
- // either. Defaults to false.
- bool allow_privilege_increase_;
-
// Whether the install originated from the gallery.
bool is_gallery_install_;
diff --git a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
index d66a448..354fea9 100644
--- a/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
+++ b/chrome/browser/extensions/extension_disabled_infobar_delegate.cc
@@ -36,9 +36,7 @@ class ExtensionDisabledDialogDelegate
// Overridden from ExtensionInstallUI::Delegate:
virtual void InstallUIProceed() {
- ExtensionPrefs* prefs = service_->extension_prefs();
- prefs->SetDidExtensionEscalatePermissions(extension_, false);
- service_->EnableExtension(extension_->id());
+ service_->GrantPermissionsAndEnableExtension(extension_);
Release();
}
virtual void InstallUIAbort() {
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index 09b7dd6..ec6ada0 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -8,6 +8,7 @@
#include "base/string_number_conversions.h"
#include "base/utf_string_conversions.h"
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
@@ -82,6 +83,14 @@ const char kUpdateUrlData[] = "update_url_data";
// Whether the browser action is visible in the toolbar.
const char kBrowserActionVisible[] = "browser_action_visible";
+// Preferences that hold which permissions the user has granted the extension.
+// We explicitly keep track of these so that extensions can contain unknown
+// permissions, for backwards compatibility reasons, and we can still prompt
+// the user to accept them once recognized.
+const char kPrefGrantedPermissionsAPI[] = "granted_permissions.api";
+const char kPrefGrantedPermissionsHost[] = "granted_permissions.host";
+const char kPrefGrantedPermissionsAll[] = "granted_permissions.full";
+
} // namespace
////////////////////////////////////////////////////////////////////////////////
@@ -113,6 +122,15 @@ static void CleanupBadExtensionKeys(PrefService* prefs) {
prefs->ScheduleSavePersistentPrefs();
}
+static void ExtentToStringSet(const ExtensionExtent& host_extent,
+ std::set<std::string>* result) {
+ ExtensionExtent::PatternList patterns = host_extent.patterns();
+ ExtensionExtent::PatternList::const_iterator i;
+
+ for (i = patterns.begin(); i != patterns.end(); ++i)
+ result->insert(i->GetAsString());
+}
+
} // namespace
ExtensionPrefs::ExtensionPrefs(PrefService* prefs, const FilePath& root_dir)
@@ -218,24 +236,17 @@ DictionaryValue* ExtensionPrefs::CopyCurrentExtensions() {
bool ExtensionPrefs::ReadBooleanFromPref(
DictionaryValue* ext, const std::string& pref_key) {
- if (!ext->HasKey(pref_key)) return false;
bool bool_value = false;
- if (!ext->GetBoolean(pref_key, &bool_value)) {
- NOTREACHED() << "Failed to fetch " << pref_key << " flag.";
- // In case we could not fetch the flag, we treat it as false.
+ if (!ext->GetBoolean(pref_key, &bool_value))
return false;
- }
+
return bool_value;
}
bool ExtensionPrefs::ReadExtensionPrefBoolean(
const std::string& extension_id, const std::string& pref_key) {
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- if (!extensions)
- return false;
-
- DictionaryValue* ext = NULL;
- if (!extensions->GetDictionary(extension_id, &ext)) {
+ DictionaryValue* ext = GetExtensionPref(extension_id);
+ if (!ext) {
// No such extension yet.
return false;
}
@@ -244,29 +255,74 @@ bool ExtensionPrefs::ReadExtensionPrefBoolean(
bool ExtensionPrefs::ReadIntegerFromPref(
DictionaryValue* ext, const std::string& pref_key, int* out_value) {
- if (!ext->HasKey(pref_key)) return false;
- if (!ext->GetInteger(pref_key, out_value)) {
- NOTREACHED() << "Failed to fetch " << pref_key << " flag.";
- // In case we could not fetch the flag, we treat it as false.
+ if (!ext->GetInteger(pref_key, out_value))
return false;
- }
+
return out_value != NULL;
}
bool ExtensionPrefs::ReadExtensionPrefInteger(
const std::string& extension_id, const std::string& pref_key,
int* out_value) {
- const DictionaryValue* extensions = prefs_->GetDictionary(kExtensionsPref);
- if (!extensions)
- return false;
- DictionaryValue* ext = NULL;
- if (!extensions->GetDictionary(extension_id, &ext)) {
+ DictionaryValue* ext = GetExtensionPref(extension_id);
+ if (!ext) {
// No such extension yet.
return false;
}
return ReadIntegerFromPref(ext, pref_key, out_value);
}
+bool ExtensionPrefs::ReadExtensionPrefList(
+ const std::string& extension_id, const std::string& pref_key,
+ ListValue** out_value) {
+ DictionaryValue* ext = GetExtensionPref(extension_id);
+ if (!ext || !ext->GetList(pref_key, out_value))
+ return false;
+
+ return out_value != NULL;
+}
+
+bool ExtensionPrefs::ReadExtensionPrefStringSet(
+ const std::string& extension_id,
+ const std::string& pref_key,
+ std::set<std::string>* result) {
+ ListValue* value = NULL;
+ if (!ReadExtensionPrefList(extension_id, pref_key, &value))
+ return false;
+
+ result->clear();
+
+ for (size_t i = 0; i < value->GetSize(); ++i) {
+ std::string item;
+ if (!value->GetString(i, &item))
+ return false;
+ result->insert(item);
+ }
+
+ return true;
+}
+
+void ExtensionPrefs::AddToExtensionPrefStringSet(
+ const std::string& extension_id,
+ const std::string& pref_key,
+ const std::set<std::string>& added_value) {
+ std::set<std::string> old_value;
+ std::set<std::string> new_value;
+ ReadExtensionPrefStringSet(extension_id, pref_key, &old_value);
+
+ std::set_union(old_value.begin(), old_value.end(),
+ added_value.begin(), added_value.end(),
+ std::inserter(new_value, new_value.end()));
+
+ ListValue* value = new ListValue();
+ for (std::set<std::string>::const_iterator iter = new_value.begin();
+ iter != new_value.end(); ++iter)
+ value->Append(Value::CreateStringValue(*iter));
+
+ UpdateExtensionPref(extension_id, pref_key, value);
+ prefs_->ScheduleSavePersistentPrefs();
+}
+
void ExtensionPrefs::SavePrefsAndNotify() {
prefs_->ScheduleSavePersistentPrefs();
prefs_->pref_notifier()->OnUserPreferenceSet(kExtensionsPref);
@@ -411,6 +467,60 @@ void ExtensionPrefs::SetLastPingDayImpl(const Time& time,
SavePrefsAndNotify();
}
+
+bool ExtensionPrefs::GetGrantedPermissions(
+ const std::string& extension_id,
+ bool* full_access,
+ std::set<std::string>* api_permissions,
+ ExtensionExtent* host_extent) {
+ CHECK(Extension::IdIsValid(extension_id));
+
+ DictionaryValue* ext = GetExtensionPref(extension_id);
+ if (!ext || !ext->GetBoolean(kPrefGrantedPermissionsAll, full_access))
+ return false;
+
+ ReadExtensionPrefStringSet(
+ extension_id, kPrefGrantedPermissionsAPI, api_permissions);
+
+ std::set<std::string> host_permissions;
+ ReadExtensionPrefStringSet(
+ extension_id, kPrefGrantedPermissionsHost, &host_permissions);
+
+ for (std::set<std::string>::iterator i = host_permissions.begin();
+ i != host_permissions.end(); ++i)
+ host_extent->AddPattern(URLPattern(
+ Extension::kValidWebExtentSchemes | UserScript::kValidUserScriptSchemes,
+ *i));
+
+ return true;
+}
+
+void ExtensionPrefs::AddGrantedPermissions(
+ const std::string& extension_id,
+ const bool full_access,
+ const std::set<std::string>& api_permissions,
+ const ExtensionExtent& host_extent) {
+ CHECK(Extension::IdIsValid(extension_id));
+
+ UpdateExtensionPref(extension_id, kPrefGrantedPermissionsAll,
+ Value::CreateBooleanValue(full_access));
+
+ if (!api_permissions.empty()) {
+ AddToExtensionPrefStringSet(
+ extension_id, kPrefGrantedPermissionsAPI, api_permissions);
+ }
+
+ if (!host_extent.is_empty()) {
+ std::set<std::string> host_permissions;
+ ExtentToStringSet(host_extent, &host_permissions);
+
+ AddToExtensionPrefStringSet(
+ extension_id, kPrefGrantedPermissionsHost, host_permissions);
+ }
+
+ SavePrefsAndNotify();
+}
+
Time ExtensionPrefs::LastPingDay(const std::string& extension_id) const {
DCHECK(Extension::IdIsValid(extension_id));
return LastPingDayImpl(GetExtensionPref(extension_id));
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index 0bcd9f9..26e4327 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -123,6 +123,27 @@ class ExtensionPrefs {
// the client's.
void SetLastPingDay(const std::string& extension_id, const base::Time& time);
+ // Gets the permissions (|api_permissions|, |host_extent| and |full_access|)
+ // granted to the extension with |extension_id|. |full_access| will be true
+ // if the extension has all effective permissions (like from an NPAPI plugin).
+ // Returns false if the granted permissions haven't been initialized yet.
+ // TODO(jstritar): Refractor the permissions into a class that encapsulates
+ // all granted permissions, can be initialized from preferences or
+ // a manifest file, and can be compared to each other.
+ bool GetGrantedPermissions(const std::string& extension_id,
+ bool* full_access,
+ std::set<std::string>* api_permissions,
+ ExtensionExtent* host_extent);
+
+ // Adds the specified |api_permissions|, |host_extent| and |full_access|
+ // to the granted permissions for extension with |extension_id|.
+ // |full_access| should be set to true if the extension effectively has all
+ // permissions (such as by having an NPAPI plugin).
+ void AddGrantedPermissions(const std::string& extension_id,
+ const bool full_access,
+ const std::set<std::string>& api_permissions,
+ const ExtensionExtent& host_extent);
+
// Similar to the 2 above, but for the extensions blacklist.
base::Time BlacklistLastPingDay() const;
void SetBlacklistLastPingDay(const base::Time& time);
@@ -222,7 +243,7 @@ class ExtensionPrefs {
void DeleteExtensionPrefs(const std::string& id);
// Reads a boolean pref from |ext| with key |pref_key|.
- // Return false if the value is false or kPrefBlacklist does not exist.
+ // Return false if the value is false or |pref_key| does not exist.
bool ReadBooleanFromPref(DictionaryValue* ext, const std::string& pref_key);
// Reads a boolean pref |pref_key| from extension with id |extension_id|.
@@ -239,6 +260,24 @@ class ExtensionPrefs {
const std::string& pref_key,
int* out_value);
+ // Reads a list pref |pref_key| from extension with id | extension_id|.
+ bool ReadExtensionPrefList(const std::string& extension_id,
+ const std::string& pref_key,
+ ListValue** out_value);
+
+ // Reads a list pref |pref_key| as a string set from the extension with
+ // id |extension_id|.
+ bool ReadExtensionPrefStringSet(const std::string& extension_id,
+ const std::string& pref_key,
+ std::set<std::string>* result);
+
+ // Adds the |added_values| to the value of |pref_key| for the extension
+ // with id |extension_id| (the new value will be the union of the existing
+ // value and |added_values|).
+ void AddToExtensionPrefStringSet(const std::string& extension_id,
+ const std::string& pref_key,
+ const std::set<std::string>& added_values);
+
// Ensures and returns a mutable dictionary for extension |id|'s prefs.
DictionaryValue* GetOrCreateExtensionPref(const std::string& id);
diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc
index 4218790..ec3a33d 100644
--- a/chrome/browser/extensions/extension_prefs_unittest.cc
+++ b/chrome/browser/extensions/extension_prefs_unittest.cc
@@ -19,6 +19,28 @@
using base::Time;
using base::TimeDelta;
+static void AddPattern(ExtensionExtent* extent, const std::string& pattern) {
+ int schemes = URLPattern::SCHEME_ALL;
+ extent->AddPattern(URLPattern(schemes, pattern));
+}
+
+static void AssertEqualExtents(ExtensionExtent* extent1,
+ ExtensionExtent* extent2) {
+ std::vector<URLPattern> patterns1 = extent1->patterns();
+ std::vector<URLPattern> patterns2 = extent2->patterns();
+ std::set<std::string> strings1;
+ EXPECT_EQ(patterns1.size(), patterns2.size());
+
+ for (size_t i = 0; i < patterns1.size(); ++i)
+ strings1.insert(patterns1.at(i).GetAsString());
+
+ std::set<std::string> strings2;
+ for (size_t i = 0; i < patterns2.size(); ++i)
+ strings2.insert(patterns2.at(i).GetAsString());
+
+ EXPECT_EQ(strings1, strings2);
+}
+
// Base class for tests.
class ExtensionPrefsTest : public testing::Test {
public:
@@ -145,6 +167,106 @@ class ExtensionPrefsEscalatePermissions : public ExtensionPrefsTest {
};
TEST_F(ExtensionPrefsEscalatePermissions, EscalatePermissions) {}
+// Tests the AddGrantedPermissions / GetGrantedPermissions functions.
+class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest {
+ public:
+ virtual void Initialize() {
+ extension_id_ = prefs_.AddExtensionAndReturnId("test");
+
+ api_perm_set1_.insert("tabs");
+ api_perm_set1_.insert("bookmarks");
+ api_perm_set1_.insert("something_random");
+
+ api_perm_set2_.insert("history");
+ api_perm_set2_.insert("unknown2");
+
+ AddPattern(&host_perm_set1_, "http://*.google.com/*");
+ AddPattern(&host_perm_set1_, "http://example.com/*");
+
+ AddPattern(&host_perm_set2_, "https://*.google.com/*");
+ // with duplicate:
+ AddPattern(&host_perm_set2_, "http://*.google.com/*");
+
+ std::set_union(api_perm_set1_.begin(), api_perm_set1_.end(),
+ api_perm_set2_.begin(), api_perm_set2_.end(),
+ std::inserter(api_permissions_, api_permissions_.end()));
+
+ AddPattern(&host_permissions_, "http://*.google.com/*");
+ AddPattern(&host_permissions_, "http://example.com/*");
+ AddPattern(&host_permissions_, "https://*.google.com/*");
+
+ std::set<std::string> empty_set;
+ std::set<std::string> api_perms;
+ bool full_access = false;
+ ExtensionExtent host_perms;
+ ExtensionExtent empty_extent;
+
+ // Make sure both granted api and host permissions start empty.
+ EXPECT_FALSE(prefs()->GetGrantedPermissions(
+ extension_id_, &full_access, &api_perms, &host_perms));
+
+ EXPECT_TRUE(api_perms.empty());
+ EXPECT_TRUE(host_perms.is_empty());
+
+ // Add part of the api permissions.
+ prefs()->AddGrantedPermissions(
+ extension_id_, false, api_perm_set1_, empty_extent);
+ EXPECT_TRUE(prefs()->GetGrantedPermissions(
+ extension_id_, &full_access, &api_perms, &host_perms));
+ EXPECT_EQ(api_perm_set1_, api_perms);
+ EXPECT_TRUE(host_perms.is_empty());
+ EXPECT_FALSE(full_access);
+ host_perms.ClearPaths();
+ api_perms.clear();
+
+ // Add part of the host permissions.
+ prefs()->AddGrantedPermissions(
+ extension_id_, false, empty_set, host_perm_set1_);
+ EXPECT_TRUE(prefs()->GetGrantedPermissions(
+ extension_id_, &full_access, &api_perms, &host_perms));
+ EXPECT_FALSE(full_access);
+ EXPECT_EQ(api_perm_set1_, api_perms);
+ AssertEqualExtents(&host_perm_set1_, &host_perms);
+ host_perms.ClearPaths();
+ api_perms.clear();
+
+ // Add the rest of both the api and host permissions.
+ prefs()->AddGrantedPermissions(extension_id_,
+ true,
+ api_perm_set2_,
+ host_perm_set2_);
+
+ EXPECT_TRUE(prefs()->GetGrantedPermissions(
+ extension_id_, &full_access, &api_perms, &host_perms));
+ EXPECT_TRUE(full_access);
+ EXPECT_EQ(api_permissions_, api_perms);
+ AssertEqualExtents(&host_permissions_, &host_perms);
+ }
+
+ virtual void Verify() {
+ std::set<std::string> api_perms;
+ ExtensionExtent host_perms;
+ bool full_access;
+
+ EXPECT_TRUE(prefs()->GetGrantedPermissions(
+ extension_id_, &full_access, &api_perms, &host_perms));
+ EXPECT_EQ(api_permissions_, api_perms);
+ EXPECT_TRUE(full_access);
+ AssertEqualExtents(&host_permissions_, &host_perms);
+ }
+
+ private:
+ std::string extension_id_;
+ std::set<std::string> api_perm_set1_;
+ std::set<std::string> api_perm_set2_;
+ ExtensionExtent host_perm_set1_;
+ ExtensionExtent host_perm_set2_;
+
+
+ std::set<std::string> api_permissions_;
+ ExtensionExtent host_permissions_;
+};
+TEST_F(ExtensionPrefsGrantedPermissions, GrantedPermissions) {}
// Tests the GetVersionString function.
class ExtensionPrefsVersionString : public ExtensionPrefsTest {
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index a675f5b..8284def 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -347,8 +347,9 @@ void ExtensionsServiceBackend::LoadSingleExtension(
// prefs.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(frontend_, &ExtensionsService::OnExtensionInstalled,
- extension, true));
+ NewRunnableMethod(frontend_,
+ &ExtensionsService::OnExtensionInstalled,
+ extension));
}
void ExtensionsServiceBackend::ReportExtensionLoadError(
@@ -644,7 +645,6 @@ void ExtensionsService::InstallExtension(const FilePath& extension_path) {
scoped_refptr<CrxInstaller> installer(
new CrxInstaller(this, // frontend
NULL)); // no client (silent install)
- installer->set_allow_privilege_increase(true);
installer->InstallCrx(extension_path);
}
@@ -943,6 +943,27 @@ void ExtensionsService::DisableExtension(const std::string& extension_id) {
UpdateActiveExtensionsInCrashReporter();
}
+void ExtensionsService::GrantPermissions(const Extension* extension) {
+ CHECK(extension);
+
+ // We only maintain the granted permissions prefs for INTERNAL extensions.
+ CHECK(extension->location() == Extension::INTERNAL);
+
+ ExtensionExtent effective_hosts = extension->GetEffectiveHostPermissions();
+ extension_prefs_->AddGrantedPermissions(extension->id(),
+ extension->HasFullPermissions(),
+ extension->api_permissions(),
+ effective_hosts);
+}
+
+void ExtensionsService::GrantPermissionsAndEnableExtension(
+ const Extension* extension) {
+ CHECK(extension);
+ GrantPermissions(extension);
+ extension_prefs_->SetDidExtensionEscalatePermissions(extension, false);
+ EnableExtension(extension->id());
+}
+
void ExtensionsService::LoadExtension(const FilePath& extension_path) {
BrowserThread::PostTask(
BrowserThread::FILE, FROM_HERE,
@@ -975,7 +996,7 @@ void ExtensionsService::LoadComponentExtensions() {
return;
}
- OnExtensionLoaded(extension, false); // Don't allow privilege increase.
+ OnExtensionLoaded(extension);
}
}
@@ -1120,7 +1141,6 @@ void ExtensionsService::LoadAllExtensions() {
browser_action_count);
}
-
void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
bool write_to_prefs) {
std::string error;
@@ -1147,7 +1167,7 @@ void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
if (write_to_prefs)
extension_prefs_->UpdateManifest(extension);
- OnExtensionLoaded(extension, true);
+ OnExtensionLoaded(extension);
if (Extension::IsExternalLocation(info.extension_location)) {
BrowserThread::PostTask(
@@ -1523,8 +1543,7 @@ void ExtensionsService::OnLoadedInstalledExtensions() {
NotificationService::NoDetails());
}
-void ExtensionsService::OnExtensionLoaded(const Extension* extension,
- bool allow_privilege_increase) {
+void ExtensionsService::OnExtensionLoaded(const Extension* extension) {
// Ensure extension is deleted unless we transfer ownership.
scoped_refptr<const Extension> scoped_extension(extension);
@@ -1535,63 +1554,29 @@ void ExtensionsService::OnExtensionLoaded(const Extension* extension,
if (disabled_extension_paths_.erase(extension->id()) > 0)
EnableExtension(extension->id());
- // TODO(aa): Need to re-evaluate this branch. Does this still make sense now
- // that extensions are enabled by default?
- if (extensions_enabled() ||
- extension->is_theme() ||
- extension->location() == Extension::LOAD ||
- extension->location() == Extension::COMPONENT ||
- Extension::IsExternalLocation(extension->location())) {
- const Extension* old = GetExtensionByIdInternal(extension->id(),
- true, true);
- if (old) {
- // CrxInstaller should have guaranteed that we aren't downgrading.
- CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
-
- bool allow_silent_upgrade =
- allow_privilege_increase || !Extension::IsPrivilegeIncrease(
- old, extension);
-
- // Extensions get upgraded if silent upgrades are allowed, otherwise
- // they get disabled.
- if (allow_silent_upgrade) {
- SetBeingUpgraded(old, true);
- SetBeingUpgraded(extension, true);
- }
-
- // To upgrade an extension in place, unload the old one and
- // then load the new one.
- UnloadExtension(old->id());
- old = NULL;
+ // Check if the extension's privileges have changed and disable the extension
+ // if necessary.
+ DisableIfPrivilegeIncrease(extension);
- if (!allow_silent_upgrade) {
- // Extension has changed permissions significantly. Disable it. We
- // send a notification below.
- extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
- extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
- }
- }
-
- switch (extension_prefs_->GetExtensionState(extension->id())) {
- case Extension::ENABLED:
- extensions_.push_back(scoped_extension);
+ switch (extension_prefs_->GetExtensionState(extension->id())) {
+ case Extension::ENABLED:
+ extensions_.push_back(scoped_extension);
- NotifyExtensionLoaded(extension);
+ NotifyExtensionLoaded(extension);
- ExtensionDOMUI::RegisterChromeURLOverrides(profile_,
- extension->GetChromeURLOverrides());
- break;
- case Extension::DISABLED:
- disabled_extensions_.push_back(scoped_extension);
- NotificationService::current()->Notify(
- NotificationType::EXTENSION_UPDATE_DISABLED,
- Source<Profile>(profile_),
- Details<const Extension>(extension));
- break;
- default:
- NOTREACHED();
- break;
- }
+ ExtensionDOMUI::RegisterChromeURLOverrides(
+ profile_, extension->GetChromeURLOverrides());
+ break;
+ case Extension::DISABLED:
+ disabled_extensions_.push_back(scoped_extension);
+ NotificationService::current()->Notify(
+ NotificationType::EXTENSION_UPDATE_DISABLED,
+ Source<Profile>(profile_),
+ Details<const Extension>(extension));
+ break;
+ default:
+ NOTREACHED();
+ break;
}
SetBeingUpgraded(extension, false);
@@ -1609,6 +1594,86 @@ void ExtensionsService::OnExtensionLoaded(const Extension* extension,
}
}
+void ExtensionsService::DisableIfPrivilegeIncrease(const Extension* extension) {
+ // We keep track of all permissions the user has granted each extension.
+ // This allows extensions to gracefully support backwards compatibility
+ // by including unknown permissions in their manifests. When the user
+ // installs the extension, only the recognized permissions are recorded.
+ // When the unknown permissions become recognized (e.g., through browser
+ // upgrade), we can prompt the user to accept these new permissions.
+ // Extensions can also silently upgrade to less permissions, and then
+ // silently upgrade to a version that adds these permissions back.
+ //
+ // For example, pretend that Chrome 10 includes a permission "omnibox"
+ // for an API that adds suggestions to the omnibox. An extension can
+ // maintain backwards compatibility while still having "omnibox" in the
+ // manifest. If a user installs the extension on Chrome 9, the browser
+ // will record the permissions it recognized, not including "omnibox."
+ // When upgrading to Chrome 10, "omnibox" will be recognized and Chrome
+ // will disable the extension and prompt the user to approve the increase
+ // in privileges. The extension could then release a new version that
+ // removes the "omnibox" permission. When the user upgrades, Chrome will
+ // still remember that "omnibox" had been granted, so that if the
+ // extension once again includes "omnibox" in an upgrade, the extension
+ // can upgrade without requiring this user's approval.
+ const Extension* old = GetExtensionByIdInternal(extension->id(),
+ true, true);
+ bool granted_full_access;
+ std::set<std::string> granted_apis;
+ ExtensionExtent granted_extent;
+
+ bool is_extension_upgrade = old != NULL;
+ bool is_privilege_increase = false;
+
+ // We only record the granted permissions for INTERNAL extensions, since
+ // they can't silently increase privileges.
+ if (extension->location() == Extension::INTERNAL) {
+ // Add all the recognized permissions if the granted permissions list
+ // hasn't been initialized yet.
+ if (!extension_prefs_->GetGrantedPermissions(extension->id(),
+ &granted_full_access,
+ &granted_apis,
+ &granted_extent)) {
+ GrantPermissions(extension);
+ CHECK(extension_prefs_->GetGrantedPermissions(extension->id(),
+ &granted_full_access,
+ &granted_apis,
+ &granted_extent));
+ }
+
+ // Here, we check if an extension's privileges have increased in a manner
+ // that requires the user's approval. This could occur because the browser
+ // upgraded and recognized additional privileges, or an extension upgrades
+ // to a version that requires additional privileges.
+ is_privilege_increase = Extension::IsPrivilegeIncrease(
+ granted_full_access, granted_apis, granted_extent, extension);
+ }
+
+ if (is_extension_upgrade) {
+ // CrxInstaller should have guaranteed that we aren't downgrading.
+ CHECK(extension->version()->CompareTo(*(old->version())) >= 0);
+
+ // Extensions get upgraded if the privileges are allowed to increase or
+ // the privileges haven't increased.
+ if (!is_privilege_increase) {
+ SetBeingUpgraded(old, true);
+ SetBeingUpgraded(extension, true);
+ }
+
+ // To upgrade an extension in place, unload the old one and
+ // then load the new one.
+ UnloadExtension(old->id());
+ old = NULL;
+ }
+
+ // Extension has changed permissions significantly. Disable it. A
+ // notification should be sent by the caller.
+ if (is_privilege_increase) {
+ extension_prefs_->SetExtensionState(extension, Extension::DISABLED);
+ extension_prefs_->SetDidExtensionEscalatePermissions(extension, true);
+ }
+}
+
void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
std::set<std::string> extension_ids;
for (size_t i = 0; i < extensions_.size(); ++i) {
@@ -1620,8 +1685,7 @@ void ExtensionsService::UpdateActiveExtensionsInCrashReporter() {
child_process_logging::SetActiveExtensions(extension_ids);
}
-void ExtensionsService::OnExtensionInstalled(const Extension* extension,
- bool allow_privilege_increase) {
+void ExtensionsService::OnExtensionInstalled(const Extension* extension) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Ensure extension is deleted unless we transfer ownership.
@@ -1763,7 +1827,7 @@ void ExtensionsService::OnExtensionInstalled(const Extension* extension,
}
// Transfer ownership of |extension| to OnExtensionLoaded.
- OnExtensionLoaded(scoped_extension, allow_privilege_increase);
+ OnExtensionLoaded(scoped_extension);
}
const Extension* ExtensionsService::GetExtensionByIdInternal(
@@ -1881,7 +1945,6 @@ void ExtensionsService::OnExternalExtensionFileFound(
NULL)); // no client (silent install)
installer->set_install_source(location);
installer->set_expected_id(id);
- installer->set_allow_privilege_increase(true);
installer->InstallCrx(path);
}
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index f19bb22..c831039 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -277,6 +277,15 @@ class ExtensionsService
void EnableExtension(const std::string& extension_id);
void DisableExtension(const std::string& extension_id);
+ // Updates the |extension|'s granted permissions lists to include all
+ // permissions in the |extension|'s manifest.
+ void GrantPermissions(const Extension* extension);
+
+ // Updates the |extension|'s granted permissions lists to include all
+ // permissions in the |extension|'s manifest and re-enables the
+ // extension.
+ void GrantPermissionsAndEnableExtension(const Extension* extension);
+
// Load the extension from the directory |extension_path|.
void LoadExtension(const FilePath& extension_path);
@@ -351,12 +360,10 @@ class ExtensionsService
virtual void OnLoadedInstalledExtensions();
// Called when an extension has been loaded.
- void OnExtensionLoaded(const Extension* extension,
- bool allow_privilege_increase);
+ void OnExtensionLoaded(const Extension* extension);
// Called by the backend when an extension has been installed.
- void OnExtensionInstalled(const Extension* extension,
- bool allow_privilege_increase);
+ void OnExtensionInstalled(const Extension* extension);
// Called by the backend when an external extension is found.
void OnExternalExtensionFileFound(const std::string& id,
@@ -364,6 +371,10 @@ class ExtensionsService
const FilePath& path,
Extension::Location location);
+ // Checks if the privileges requested by |extension| have increased, and if
+ // so, disables the extension and prompts the user to approve the change.
+ void DisableIfPrivilegeIncrease(const Extension* extension);
+
// Go through each extensions in pref, unload blacklisted extensions
// and update the blacklist state in pref.
virtual void UpdateExtensionBlacklist(
diff --git a/chrome/browser/extensions/extensions_service_unittest.cc b/chrome/browser/extensions/extensions_service_unittest.cc
index e222c27..271309c 100644
--- a/chrome/browser/extensions/extensions_service_unittest.cc
+++ b/chrome/browser/extensions/extensions_service_unittest.cc
@@ -72,6 +72,7 @@ const char* const good_crx = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
const char* const page_action = "obcimlgaoabeegjmmpldobjndiealpln";
const char* const theme_crx = "iamefpfkojoapidjnbafmgkgncegbkad";
const char* const theme2_crx = "pjpgmfcmabopnnfonnhmdjglfpjjfkbf";
+const char* const permissions_crx = "eagpmdpfmaekmmcejjbmjoecnejeiiin";
struct ExtensionsOrder {
bool operator()(const Extension* a, const Extension* b) {
@@ -98,6 +99,28 @@ static std::vector<std::string> GetErrors() {
return ret_val;
}
+static void AddPattern(ExtensionExtent* extent, const std::string& pattern) {
+ int schemes = URLPattern::SCHEME_ALL;
+ extent->AddPattern(URLPattern(schemes, pattern));
+}
+
+static void AssertEqualExtents(ExtensionExtent* extent1,
+ ExtensionExtent* extent2) {
+ std::vector<URLPattern> patterns1 = extent1->patterns();
+ std::vector<URLPattern> patterns2 = extent2->patterns();
+ std::set<std::string> strings1;
+ EXPECT_EQ(patterns1.size(), patterns2.size());
+
+ for (size_t i = 0; i < patterns1.size(); ++i)
+ strings1.insert(patterns1.at(i).GetAsString());
+
+ std::set<std::string> strings2;
+ for (size_t i = 0; i < patterns2.size(); ++i)
+ strings2.insert(patterns2.at(i).GetAsString());
+
+ EXPECT_EQ(strings1, strings2);
+}
+
} // namespace
class MockExtensionProvider : public ExternalExtensionProvider {
@@ -458,21 +481,39 @@ class ExtensionsServiceTest
Extension::Location location);
void PackAndInstallExtension(const FilePath& dir_path,
+ const FilePath& pem_path,
bool should_succeed) {
FilePath crx_path;
ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &crx_path));
crx_path = crx_path.AppendASCII("temp.crx");
- FilePath pem_path = crx_path.DirName().AppendASCII("temp.pem");
+
+ // Use the existing pem key, if provided.
+ FilePath pem_output_path;
+ if (pem_path.value().empty()) {
+ pem_output_path = crx_path.DirName().AppendASCII("temp.pem");
+ ASSERT_TRUE(file_util::Delete(pem_output_path, false));
+ } else {
+ ASSERT_TRUE(file_util::PathExists(pem_path));
+ }
ASSERT_TRUE(file_util::Delete(crx_path, false));
- ASSERT_TRUE(file_util::Delete(pem_path, false));
+
scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
- ASSERT_TRUE(creator->Run(dir_path, crx_path, FilePath(), pem_path));
+ ASSERT_TRUE(creator->Run(dir_path,
+ crx_path,
+ pem_path,
+ pem_output_path));
+
ASSERT_TRUE(file_util::PathExists(crx_path));
InstallExtension(crx_path, should_succeed);
}
+ void PackAndInstallExtension(const FilePath& dir_path,
+ bool should_succeed) {
+ PackAndInstallExtension(dir_path, FilePath(), should_succeed);
+ }
+
void InstallExtension(const FilePath& path,
bool should_succeed) {
ASSERT_TRUE(file_util::PathExists(path));
@@ -654,6 +695,19 @@ class ExtensionsServiceTest
EXPECT_EQ(expected_val, val) << msg;
}
+ void SetPref(const std::string& extension_id,
+ const std::string& pref_path,
+ Value* value,
+ const std::string& msg) {
+ const DictionaryValue* dict =
+ profile_->GetPrefs()->GetMutableDictionary("extensions.settings");
+ ASSERT_TRUE(dict != NULL) << msg;
+ DictionaryValue* pref = NULL;
+ ASSERT_TRUE(dict->GetDictionary(extension_id, &pref)) << msg;
+ EXPECT_TRUE(pref != NULL) << msg;
+ pref->Set(pref_path, value);
+ }
+
void SetPrefInteg(const std::string& extension_id,
const std::string& pref_path,
int value) {
@@ -664,13 +718,46 @@ class ExtensionsServiceTest
msg += " = ";
msg += base::IntToString(value);
+ SetPref(extension_id, pref_path, Value::CreateIntegerValue(value), msg);
+ }
+
+ void SetPrefBool(const std::string& extension_id,
+ const std::string& pref_path,
+ bool value) {
+ std::string msg = " while setting: ";
+ msg += extension_id + " " + pref_path;
+ msg += " = ";
+ msg += (value ? "true" : "false");
+
+ SetPref(extension_id, pref_path, Value::CreateBooleanValue(value), msg);
+ }
+
+ void ClearPref(const std::string& extension_id,
+ const std::string& pref_path) {
+ std::string msg = " while clearing: ";
+ msg += extension_id + " " + pref_path;
+
const DictionaryValue* dict =
profile_->GetPrefs()->GetMutableDictionary("extensions.settings");
ASSERT_TRUE(dict != NULL) << msg;
DictionaryValue* pref = NULL;
ASSERT_TRUE(dict->GetDictionary(extension_id, &pref)) << msg;
EXPECT_TRUE(pref != NULL) << msg;
- pref->SetInteger(pref_path, value);
+ pref->Remove(pref_path, NULL);
+ }
+
+ void SetPrefStringSet(const std::string& extension_id,
+ const std::string& pref_path,
+ const std::set<std::string>& value) {
+ std::string msg = " while setting: ";
+ msg += extension_id + " " + pref_path;
+
+ ListValue* list_value = new ListValue();
+ for (std::set<std::string>::const_iterator iter = value.begin();
+ iter != value.end(); ++iter)
+ list_value->Append(Value::CreateStringValue(*iter));
+
+ SetPref(extension_id, pref_path, list_value, msg);
}
protected:
@@ -1007,6 +1094,225 @@ TEST_F(ExtensionsServiceTest, InstallUserScript) {
ExtensionErrorReporter::GetInstance()->ClearErrors();
}
+// This tests that the granted permissions preferences are correctly set when
+// installing an extension.
+TEST_F(ExtensionsServiceTest, GrantedPermissions) {
+ InitializeEmptyExtensionsService();
+ FilePath path;
+ FilePath pem_path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+ path = path.AppendASCII("extensions")
+ .AppendASCII("permissions");
+
+ pem_path = path.AppendASCII("unknown.pem");
+ path = path.AppendASCII("unknown");
+
+ ASSERT_TRUE(file_util::PathExists(pem_path));
+ ASSERT_TRUE(file_util::PathExists(path));
+
+ ExtensionPrefs* prefs = service_->extension_prefs();
+
+ std::set<std::string> expected_api_perms;
+ std::set<std::string> known_api_perms;
+ bool full_access;
+ ExtensionExtent expected_host_perms;
+ ExtensionExtent known_host_perms;
+
+ // Make sure there aren't any granted permissions before the
+ // extension is installed.
+ EXPECT_FALSE(prefs->GetGrantedPermissions(
+ permissions_crx, &full_access, &known_api_perms, &known_host_perms));
+ EXPECT_TRUE(known_api_perms.empty());
+ EXPECT_TRUE(known_host_perms.is_empty());
+
+ PackAndInstallExtension(path, pem_path, true);
+
+ EXPECT_EQ(0u, GetErrors().size());
+ ASSERT_EQ(1u, service_->extensions()->size());
+ std::string extension_id = service_->extensions()->at(0)->id();
+ EXPECT_EQ(permissions_crx, extension_id);
+
+
+ // Verify that the valid API permissions have been recognized.
+ expected_api_perms.insert("tabs");
+
+ AddPattern(&expected_host_perms, "http://*.google.com/*");
+ AddPattern(&expected_host_perms, "https://*.google.com/*");
+ AddPattern(&expected_host_perms, "http://www.example.com/*");
+
+ EXPECT_TRUE(prefs->GetGrantedPermissions(extension_id,
+ &full_access,
+ &known_api_perms,
+ &known_host_perms));
+
+ EXPECT_EQ(expected_api_perms, known_api_perms);
+ EXPECT_FALSE(full_access);
+ AssertEqualExtents(&expected_host_perms, &known_host_perms);
+}
+
+
+// Tests that the granted permissions full_access bit gets set correctly when
+// an extension contains an NPAPI plugin.
+TEST_F(ExtensionsServiceTest, GrantedFullAccessPermissions) {
+ InitializeEmptyExtensionsService();
+
+ FilePath path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+ path = path.AppendASCII("extensions")
+ .AppendASCII("good")
+ .AppendASCII("Extensions")
+ .AppendASCII("hpiknbiabeeppbpihjehijgoemciehgk")
+ .AppendASCII("2");
+
+ ASSERT_TRUE(file_util::PathExists(path));
+
+ PackAndInstallExtension(path, true);
+
+ EXPECT_EQ(0u, GetErrors().size());
+ EXPECT_EQ(1u, service_->extensions()->size());
+ const Extension* extension = service_->extensions()->at(0);
+ std::string extension_id = extension->id();
+ ExtensionPrefs* prefs = service_->extension_prefs();
+
+ bool full_access;
+ std::set<std::string> api_permissions;
+ ExtensionExtent host_permissions;
+ EXPECT_TRUE(prefs->GetGrantedPermissions(
+ extension_id, &full_access, &api_permissions, &host_permissions));
+
+ EXPECT_TRUE(full_access);
+ EXPECT_TRUE(api_permissions.empty());
+ EXPECT_TRUE(host_permissions.is_empty());
+}
+
+// Tests that the extension is disabled when permissions are missing from
+// the extension's granted permissions preferences. (This simulates updating
+// the browser to a version which recognizes more permissions).
+TEST_F(ExtensionsServiceTest, GrantedAPIAndHostPermissions) {
+ InitializeEmptyExtensionsService();
+
+ FilePath path;
+ ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
+ path = path.AppendASCII("extensions")
+ .AppendASCII("permissions")
+ .AppendASCII("unknown");
+
+ ASSERT_TRUE(file_util::PathExists(path));
+
+ PackAndInstallExtension(path, true);
+
+ EXPECT_EQ(0u, GetErrors().size());
+ EXPECT_EQ(1u, service_->extensions()->size());
+ const Extension* extension = service_->extensions()->at(0);
+ std::string extension_id = extension->id();
+
+ ExtensionPrefs* prefs = service_->extension_prefs();
+
+ std::set<std::string> expected_api_permissions;
+ ExtensionExtent expected_host_permissions;
+
+ expected_api_permissions.insert("tabs");
+ AddPattern(&expected_host_permissions, "http://*.google.com/*");
+ AddPattern(&expected_host_permissions, "https://*.google.com/*");
+ AddPattern(&expected_host_permissions, "http://www.example.com/*");
+
+ std::set<std::string> api_permissions;
+ std::set<std::string> host_permissions;
+
+ // Test that the extension is disabled when an API permission is missing from
+ // the extension's granted api permissions preference. (This simulates
+ // updating the browser to a version which recognizes a new API permission).
+ host_permissions.insert("http://*.google.com/*");
+ host_permissions.insert("https://*.google.com/*");
+ host_permissions.insert("http://www.example.com/*");
+
+ SetPrefBool(extension_id, "granted_permissions.initialized", true);
+ SetPrefStringSet(extension_id, "granted_permissions.api", api_permissions);
+ SetPrefStringSet(extension_id, "granted_permissions.host", host_permissions);
+
+ service_->ReloadExtensions();
+
+ ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::DISABLED);
+ ASSERT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id));
+
+ // Now grant and re-enable the extension, making sure the prefs are updated.
+ service_->GrantPermissionsAndEnableExtension(extension);
+
+ ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED);
+ ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
+
+ std::set<std::string> current_api_permissions;
+ ExtensionExtent current_host_permissions;
+ bool current_full_access;
+
+ ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id,
+ &current_full_access,
+ &current_api_permissions,
+ &current_host_permissions));
+
+ ASSERT_FALSE(current_full_access);
+ ASSERT_EQ(expected_api_permissions, current_api_permissions);
+ AssertEqualExtents(&expected_host_permissions, &current_host_permissions);
+
+ // Tests that the extension is disabled when a host permission is missing from
+ // the extension's granted host permissions preference. (This simulates
+ // updating the browser to a version which recognizes additional host
+ // permissions).
+ api_permissions.clear();
+ host_permissions.clear();
+ current_api_permissions.clear();
+ current_host_permissions.ClearPaths();
+
+ api_permissions.insert("tabs");
+ host_permissions.insert("http://*.google.com/*");
+ host_permissions.insert("https://*.google.com/*");
+
+ SetPrefBool(extension_id, "granted_permissions.initialized", true);
+ SetPrefStringSet(extension_id, "granted_permissions.api", api_permissions);
+ SetPrefStringSet(extension_id, "granted_permissions.host", host_permissions);
+
+ service_->ReloadExtensions();
+
+ ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::DISABLED);
+ ASSERT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id));
+
+ // Now grant and re-enable the extension, making sure the prefs are updated.
+ service_->GrantPermissionsAndEnableExtension(extension);
+
+ ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED);
+ ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
+
+ ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id,
+ &current_full_access,
+ &current_api_permissions,
+ &current_host_permissions));
+
+ ASSERT_FALSE(current_full_access);
+ ASSERT_EQ(expected_api_permissions, current_api_permissions);
+ AssertEqualExtents(&expected_host_permissions, &current_host_permissions);
+
+ // Tests that the granted permissions preferences are initialized when
+ // migrating from the old pref schema.
+ current_api_permissions.clear();
+ current_host_permissions.ClearPaths();
+
+ ClearPref(extension_id, "granted_permissions");
+
+ service_->ReloadExtensions();
+
+ ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED);
+ ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id));
+
+ ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id,
+ &current_full_access,
+ &current_api_permissions,
+ &current_host_permissions));
+
+ ASSERT_FALSE(current_full_access);
+ ASSERT_EQ(expected_api_permissions, current_api_permissions);
+ AssertEqualExtents(&expected_host_permissions, &current_host_permissions);
+}
+
// Test Packaging and installing an extension.
TEST_F(ExtensionsServiceTest, PackExtension) {
InitializeEmptyExtensionsService();
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc
index ba316b8..4323e6e 100644
--- a/chrome/common/extensions/extension.cc
+++ b/chrome/common/extensions/extension.cc
@@ -71,9 +71,6 @@ static void ConvertHexadecimalToIDAlphabet(std::string* id) {
}
}
-const int kValidWebExtentSchemes =
- URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
-
// These keys are allowed by all crx files (apps, extensions, themes, etc).
static const char* kBaseCrxKeys[] = {
keys::kCurrentLocale,
@@ -250,6 +247,9 @@ const size_t Extension::kNumHostedAppPermissions =
// We purposefully don't put this into kPermissionNames.
const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage";
+const int Extension::kValidWebExtentSchemes =
+ URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS;
+
//
// Extension
//
@@ -1079,52 +1079,60 @@ bool Extension::FormatPEMForFileOutput(const std::string input,
}
// static
-// TODO(aa): A problem with this code is that we silently allow upgrades to
-// extensions that require less permissions than the current version, but then
-// we don't silently allow them to go back. In order to fix this, we would need
-// to remember the max set of permissions we ever granted a single extension.
-bool Extension::IsPrivilegeIncrease(const Extension* old_extension,
+bool Extension::IsPrivilegeIncrease(const bool granted_full_access,
+ const std::set<std::string>& granted_apis,
+ const ExtensionExtent& granted_extent,
const Extension* new_extension) {
- // If the old extension had native code access, we don't need to go any
- // further. Things can't get any worse.
- if (old_extension->plugins().size() > 0)
+ // If the extension had native code access, we don't need to go any further.
+ // Things can't get any worse.
+ if (granted_full_access)
return false;
// Otherwise, if the new extension has a plugin, it's a privilege increase.
- if (new_extension->plugins().size() > 0)
+ if (new_extension->HasFullPermissions())
return true;
- // If we are increasing the set of hosts we have access to (not
- // counting scheme differences), it's a privilege increase.
- if (!old_extension->HasEffectiveAccessToAllHosts()) {
+ // If the extension hadn't been granted access to all hosts in the past, then
+ // see if the extension requires more host permissions.
+ if (!HasEffectiveAccessToAllHosts(granted_extent, granted_apis)) {
if (new_extension->HasEffectiveAccessToAllHosts())
return true;
- // TODO(erikkay) This will trip when you add a new distinct hostname,
- // but we should unique based on RCD as well. crbug.com/57042
- std::vector<std::string> old_hosts = old_extension->GetDistinctHosts();
- std::vector<std::string> new_hosts = new_extension->GetDistinctHosts();
+ const ExtensionExtent new_extent =
+ new_extension->GetEffectiveHostPermissions();
+ std::vector<std::string> new_hosts =
+ GetDistinctHosts(new_extent.patterns());
+ std::vector<std::string> old_hosts =
+ GetDistinctHosts(granted_extent.patterns());
+
std::set<std::string> old_hosts_set(old_hosts.begin(), old_hosts.end());
std::set<std::string> new_hosts_set(new_hosts.begin(), new_hosts.end());
- std::set<std::string> new_only;
+ std::set<std::string> new_hosts_only;
+
std::set_difference(new_hosts_set.begin(), new_hosts_set.end(),
- old_hosts_set.begin(), old_hosts_set.end(),
- std::inserter(new_only, new_only.end()));
- if (new_only.size())
+ old_hosts_set.begin(), old_hosts_set.end(),
+ std::inserter(new_hosts_only, new_hosts_only.end()));
+
+ if (new_hosts_only.size())
return true;
}
- std::set<string16> old_messages =
- old_extension->GetSimplePermissionMessages();
- std::set<string16> new_messages =
- new_extension->GetSimplePermissionMessages();
- std::set<string16> new_only;
- std::set_difference(new_messages.begin(), new_messages.end(),
- old_messages.begin(), old_messages.end(),
- std::inserter(new_only, new_only.end()));
+ std::set<std::string> new_apis = new_extension->api_permissions();
+ std::set<std::string> new_apis_only;
+ std::set_difference(new_apis.begin(), new_apis.end(),
+ granted_apis.begin(), granted_apis.end(),
+ std::inserter(new_apis_only, new_apis_only.end()));
- // If there are any new permission messages, then it's an increase.
- if (!new_only.empty())
+ // Ignore API permissions that don't require user approval when deciding if
+ // an extension has increased its privileges.
+ size_t new_api_count = 0;
+ for (std::set<std::string>::iterator i = new_apis_only.begin();
+ i != new_apis_only.end(); ++i) {
+ if (GetPermissionMessageId(*i))
+ new_api_count++;
+ }
+
+ if (new_api_count)
return true;
return false;
@@ -1706,29 +1714,31 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key,
}
}
- // Otherwise, it's a host pattern permission.
+ // Check if it's a host pattern permission.
URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ?
URLPattern::SCHEME_ALL :
(UserScript::kValidUserScriptSchemes |
URLPattern::SCHEME_CHROMEUI) & ~URLPattern::SCHEME_FILE);
- if (URLPattern::PARSE_SUCCESS != pattern.Parse(permission_str)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPermission, base::IntToString(i));
- return false;
- }
+ if (URLPattern::PARSE_SUCCESS == pattern.Parse(permission_str)) {
+ if (!CanSpecifyHostPermission(pattern)) {
+ *error = ExtensionErrorUtils::FormatErrorMessage(
+ errors::kInvalidPermissionScheme, base::IntToString(i));
+ return false;
+ }
- if (!CanSpecifyHostPermission(pattern)) {
- *error = ExtensionErrorUtils::FormatErrorMessage(
- errors::kInvalidPermissionScheme, base::IntToString(i));
- return false;
- }
+ // The path component is not used for host permissions, so we force it
+ // to match all paths.
+ pattern.set_path("/*");
- // The path component is not used for host permissions, so we force it to
- // match all paths.
- pattern.set_path("/*");
+ host_permissions_.push_back(pattern);
+ }
- host_permissions_.push_back(pattern);
+ // If it's not a host permission, then it's probably an unknown API
+ // permission. Do not throw an error so extensions can retain
+ // backwards compatability (http://crbug.com/42742).
+ // TODO(jstritar): We can improve error messages by adding better
+ // validation of API permissions here.
}
}
@@ -2122,32 +2132,34 @@ bool Extension::CanExecuteScriptOnPage(
return false;
}
-bool Extension::HasEffectiveAccessToAllHosts() const {
+// static
+bool Extension::HasEffectiveAccessToAllHosts(
+ const ExtensionExtent& effective_host_permissions,
+ const std::set<std::string>& api_permissions) {
// Some APIs effectively grant access to every site. New ones should be
// added here. (I'm looking at you, network API)
- if (HasApiPermission(kProxyPermission))
+ if (HasApiPermission(api_permissions, kProxyPermission))
return true;
- for (URLPatternList::const_iterator host = host_permissions().begin();
- host != host_permissions().end(); ++host) {
+ const URLPatternList patterns = effective_host_permissions.patterns();
+ for (URLPatternList::const_iterator host = patterns.begin();
+ host != patterns.end(); ++host) {
if (host->match_subdomains() && host->host().empty())
return true;
}
- for (UserScriptList::const_iterator content_script =
- content_scripts().begin();
- content_script != content_scripts().end(); ++content_script) {
- UserScript::PatternList::const_iterator pattern =
- content_script->url_patterns().begin();
- for (; pattern != content_script->url_patterns().end(); ++pattern) {
- if (pattern->match_subdomains() && pattern->host().empty())
- return true;
- }
- }
-
return false;
}
+bool Extension::HasEffectiveAccessToAllHosts() const {
+ return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(),
+ api_permissions());
+}
+
+bool Extension::HasFullPermissions() const {
+ return plugins().size() > 0;
+}
+
bool Extension::IsAPIPermission(const std::string& str) const {
for (size_t i = 0; i < Extension::kNumPermissions; ++i) {
if (str == Extension::kPermissions[i].name) {
diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h
index 13b7cc1..29bc888 100644
--- a/chrome/common/extensions/extension.h
+++ b/chrome/common/extensions/extension.h
@@ -163,6 +163,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// still accepted as meaning the same thing as kUnlimitedStoragePermission.
static const char kOldUnlimitedStoragePermission[];
+ // Valid schemes for web extent URLPatterns.
+ static const int kValidWebExtentSchemes;
+
// Returns true if the string is one of the known hosted app permissions (see
// kHostedAppPermissionNames).
static bool IsHostedAppPermission(const std::string& permission);
@@ -250,8 +253,11 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
std::string* output, bool is_public);
// Determine whether |new_extension| has increased privileges compared to
- // |old_extension|.
- static bool IsPrivilegeIncrease(const Extension* old_extension,
+ // its previously granted permissions, specified by |granted_apis|,
+ // |granted_extent| and |granted_full_access|.
+ static bool IsPrivilegeIncrease(const bool granted_full_access,
+ const std::set<std::string>& granted_apis,
+ const ExtensionExtent& granted_extent,
const Extension* new_extension);
// Given an extension and icon size, read it if present and decode it into
@@ -301,6 +307,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
static bool HasApiPermission(const std::set<std::string>& api_permissions,
const std::string& function_name);
+ // Whether the |effective_host_permissions| and |api_permissions| include
+ // effective access to all hosts. See the non-static version of the method
+ // for more details.
+ static bool HasEffectiveAccessToAllHosts(
+ const ExtensionExtent& effective_host_permissions,
+ const std::set<std::string>& api_permissions);
+
bool HasApiPermission(const std::string& function_name) const {
return HasApiPermission(this->api_permissions(), function_name);
}
@@ -325,6 +338,10 @@ class Extension : public base::RefCountedThreadSafe<Extension> {
// network, etc.)
bool HasEffectiveAccessToAllHosts() const;
+ // Whether the extension effectively has all permissions (for example, by
+ // having an NPAPI plugin).
+ bool HasFullPermissions() const;
+
// Returns the Homepage URL for this extension. If homepage_url was not
// specified in the manifest, this returns the Google Gallery URL. For
// third-party extensions, this returns a blank GURL.
diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc
index 3a89166..446ff31 100644
--- a/chrome/common/extensions/extension_manifests_unittest.cc
+++ b/chrome/common/extensions/extension_manifests_unittest.cc
@@ -294,7 +294,7 @@ TEST_F(ExtensionManifestTest, OptionsPageInApps) {
errors::kInvalidOptionsPageExpectUrlInPackage);
}
-TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) {
+TEST_F(ExtensionManifestTest, AllowUnrecognizedPermissions) {
std::string error;
scoped_ptr<DictionaryValue> manifest(
LoadManifestFile("valid_app.json", &error));
@@ -308,13 +308,11 @@ TEST_F(ExtensionManifestTest, DisallowExtensionPermissions) {
permissions->Clear();
permissions->Append(p);
std::string message_name = base::StringPrintf("permission-%s", name);
- if (Extension::IsHostedAppPermission(name)) {
- scoped_refptr<Extension> extension;
- extension = LoadAndExpectSuccess(manifest.get(), message_name);
- } else {
- LoadAndExpectError(manifest.get(), message_name,
- errors::kInvalidPermission);
- }
+
+ // Extensions are allowed to contain unrecognized API permissions,
+ // so there shouldn't be any errors.
+ scoped_refptr<Extension> extension;
+ extension = LoadAndExpectSuccess(manifest.get(), message_name);
}
}
diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc
index 013fce7..07bd6c6 100644
--- a/chrome/common/extensions/extension_unittest.cc
+++ b/chrome/common/extensions/extension_unittest.cc
@@ -48,6 +48,11 @@ void CompareLists(const std::vector<std::string>& expected,
}
}
+static void AddPattern(ExtensionExtent* extent, const std::string& pattern) {
+ int schemes = URLPattern::SCHEME_ALL;
+ extent->AddPattern(URLPattern(schemes, pattern));
+}
+
}
class ExtensionTest : public testing::Test {
@@ -252,9 +257,10 @@ TEST(ExtensionTest, InitFromValueInvalid) {
EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission));
+ // We allow unknown API permissions, so this will be valid until we better
+ // distinguish between API and host permissions.
permissions->Set(0, Value::CreateStringValue("www.google.com"));
- EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error));
- EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission));
+ EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error));
// Multiple page actions are not allowed.
input_value.reset(static_cast<DictionaryValue*>(valid_value->DeepCopy()));
@@ -327,10 +333,11 @@ TEST(ExtensionTest, InitFromValueValid) {
ListValue* permissions = new ListValue;
permissions->Set(0, Value::CreateStringValue("file:///C:/foo.txt"));
input_value.Set(keys::kPermissions, permissions);
- EXPECT_FALSE(extension.InitFromValue(input_value, false, &error));
- EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission));
+
+ // We allow unknown API permissions, so this will be valid until we better
+ // distinguish between API and host permissions.
+ EXPECT_TRUE(extension.InitFromValue(input_value, false, &error));
input_value.Remove(keys::kPermissions, NULL);
- error.clear();
// Test with an options page.
input_value.SetString(keys::kOptionsPage, "options.html");
@@ -816,29 +823,60 @@ TEST(ExtensionTest, EffectiveHostPermissions) {
TEST(ExtensionTest, IsPrivilegeIncrease) {
const struct {
const char* base_name;
+ // Increase these sizes if you have more than 10.
+ const char* granted_apis[10];
+ const char* granted_hosts[10];
+ bool full_access;
bool expect_success;
} kTests[] = {
- { "allhosts1", false }, // all -> all
- { "allhosts2", false }, // all -> one
- { "allhosts3", true }, // one -> all
- { "hosts1", false }, // http://a,http://b -> http://a,http://b
- { "hosts2", false }, // http://a,http://b -> https://a,http://*.b
- { "hosts3", false }, // http://a,http://b -> http://a
- { "hosts4", true }, // http://a -> http://a,http://b
- { "hosts5", false }, // http://a,b,c -> http://a,b,c + https://a,b,c
- { "hosts6", false }, // http://a.com -> http://a.com + http://a.co.uk
- { "permissions1", false }, // tabs -> tabs
- { "permissions2", true }, // tabs -> tabs,bookmarks
- { "permissions3", true }, // http://a -> http://a,tabs
- { "permissions5", true }, // bookmarks -> bookmarks,history
+ { "allhosts1", {NULL}, {"http://*/", NULL}, false,
+ false }, // all -> all
+ { "allhosts2", {NULL}, {"http://*/", NULL}, false,
+ false }, // all -> one
+ { "allhosts3", {NULL}, {NULL}, false, true }, // one -> all
+ { "hosts1", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ false }, // http://a,http://b -> http://a,http://b
+ { "hosts2", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ false }, // http://a,http://b -> https://a,http://*.b
+ { "hosts3", {NULL},
+ {"http://www.google.com/", "http://www.reddit.com/", NULL}, false,
+ false }, // http://a,http://b -> http://a
+ { "hosts4", {NULL},
+ {"http://www.google.com/", NULL}, false,
+ true }, // http://a -> http://a,http://b
+ { "hosts5", {"tabs", "notifications", NULL},
+ {"http://*.example.com/", "http://*.example.com/*",
+ "http://*.example.co.uk/*", "http://*.example.com.au/*",
+ NULL}, false,
+ false }, // http://a,b,c -> http://a,b,c + https://a,b,c
+ { "hosts6", {"tabs", "notifications", NULL},
+ {"http://*.example.com/", "http://*.example.com/*", NULL}, false,
+ false }, // http://a.com -> http://a.com + http://a.co.uk
+ { "permissions1", {"tabs", NULL},
+ {NULL}, false, false }, // tabs -> tabs
+ { "permissions2", {"tabs", NULL},
+ {NULL}, false, true }, // tabs -> tabs,bookmarks
+ { "permissions3", {NULL},
+ {"http://*/*", NULL},
+ false, true }, // http://a -> http://a,tabs
+ { "permissions5", {"bookmarks", NULL},
+ {NULL}, false, true }, // bookmarks -> bookmarks,history
#if !defined(OS_CHROMEOS) // plugins aren't allowed in ChromeOS
- { "permissions4", false }, // plugin -> plugin,tabs
- { "plugin1", false }, // plugin -> plugin
- { "plugin2", false }, // plugin -> none
- { "plugin3", true }, // none -> plugin
+ { "permissions4", {NULL},
+ {NULL}, true, false }, // plugin -> plugin,tabs
+ { "plugin1", {NULL},
+ {NULL}, true, false }, // plugin -> plugin
+ { "plugin2", {NULL},
+ {NULL}, true, false }, // plugin -> none
+ { "plugin3", {NULL},
+ {NULL}, false, true }, // none -> plugin
#endif
- { "storage", false }, // none -> storage
- { "notifications", false } // none -> notifications
+ { "storage", {NULL},
+ {NULL}, false, false }, // none -> storage
+ { "notifications", {NULL},
+ {NULL}, false, false } // none -> notifications
};
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) {
@@ -849,13 +887,22 @@ TEST(ExtensionTest, IsPrivilegeIncrease) {
LoadManifest("allow_silent_upgrade",
std::string(kTests[i].base_name) + "_new.json"));
- EXPECT_TRUE(old_extension.get()) << kTests[i].base_name << "_old.json";
+ std::set<std::string> granted_apis;
+ for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j)
+ granted_apis.insert(kTests[i].granted_apis[j]);
+
+ ExtensionExtent granted_hosts;
+ for (size_t j = 0; kTests[i].granted_hosts[j] != NULL; ++j)
+ AddPattern(&granted_hosts, kTests[i].granted_hosts[j]);
+
EXPECT_TRUE(new_extension.get()) << kTests[i].base_name << "_new.json";
- if (!old_extension.get() || !new_extension.get())
+ if (!new_extension.get())
continue;
EXPECT_EQ(kTests[i].expect_success,
- Extension::IsPrivilegeIncrease(old_extension.get(),
+ Extension::IsPrivilegeIncrease(kTests[i].full_access,
+ granted_apis,
+ granted_hosts,
new_extension.get()))
<< kTests[i].base_name;
}
diff --git a/chrome/test/data/extensions/good/Preferences b/chrome/test/data/extensions/good/Preferences
index fc5524c..2d6f0bf 100644
--- a/chrome/test/data/extensions/good/Preferences
+++ b/chrome/test/data/extensions/good/Preferences
@@ -4,6 +4,11 @@
"behllobkkfkfnphdnhnkndlbkcpglgmj": {
"location": 1,
"path": "behllobkkfkfnphdnhnkndlbkcpglgmj/1.0.0.0",
+ "granted_permissions": {
+ "full": false,
+ "api": ["tabs"],
+ "host": ["file://*", "http://*.google.com/*", "https://*.google.com/*", "http://*.news.com/*"]
+ },
"state": 1,
"allowFileAccess": true,
"manifest": {
@@ -34,6 +39,10 @@
"bjafgdebaacbbbecmhlhpofkepfkgcpa": {
"location": 1,
"path": "bjafgdebaacbbbecmhlhpofkepfkgcpa/1.0",
+ "granted_permissions": {
+ "full": false,
+ "api": [ "tabs" ]
+ },
"state": 1,
"manifest": {
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRS2GUBOUAO5VZ2CMRId/eRR8/e9V42nUvY5XG+0sZ+JDHEjIQdq8qQy7HqdqEpCXKPMSPuMiC2t2HE9/hpL89SblNn3mwYPtSJGQdZvAzuv6SB0oA6jZ66V7+h/k0noGD3Tcu+Ko/vfkt5wCx2uHVK29k5JR/vGr0klaoVezGlwIDAQAB",
@@ -46,6 +55,9 @@
"hpiknbiabeeppbpihjehijgoemciehgk": {
"location": 1,
"path": "hpiknbiabeeppbpihjehijgoemciehgk/2",
+ "granted_permissions": {
+ "full": true
+ },
"state": 1,
"manifest": {
"key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1nrgIE4OIQez0DVtc3JPR5O69s0XrH7TYC/xUC2e7Cla6eZldsA1PCWzLacimqtwfu7ljGXslk0HffkWNNou28Ip07KIC5oQHGEieAkNdPd3Pfi7QcAz+D0/xNPxuMtTKyuptWNtb2TTWD0MG7IdLHbMYFO6avkZtP+ldiKqxOwIDAQAB",
diff --git a/chrome/test/data/extensions/permissions/unknown.pem b/chrome/test/data/extensions/permissions/unknown.pem
new file mode 100644
index 0000000..e6f2e9f
--- /dev/null
+++ b/chrome/test/data/extensions/permissions/unknown.pem
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALhtQMz6RmotoLKif
+r1jhcO1jXcesy7d0cdRwxcl5KhgPkWH789iV5uLAHqHTW0SMPyxJGtjGvH8dn18S3
+1NjjviezsDRxGX5vKcCBfdfF6HCqGowD0qj4+qIixKNjdZ1nz62pyLAk1jls5zGoF
+t1TnoNhkZN+d8Jn7bmS/FAgURAgMBAAECgYA84UPc5llMP5emoGF6H1pOyqXSRr4b
+oHnZdDIUeBvNQUgNJ9yP9wS4geA8kmGKudCV+dvt3x9L9m0e62L4EBv9NLWIbsbwP
+eWcG62w0YxM4SW5ZPHv9mWtjxaIbhyMPJawowbGCWUf8dIuoK7UyGOmN7Nqav6WqT
+FD/LQFWenygQJBAO2KkiqlItIuU5uen1a1ilXpm6M82cCwi/6lZVMtnGH5hbxZhU+
+Mg7npOzm6PtWBkWyafWOf+naHQz0RlEmXtJkCQQDGwhIKOKDsauaBohlHK/NX71FL
+7cgubN8eq8LEtQj02LcKWbJ7cMEt1Ho43JwoeVoDOJAPg5m3+SbE/NOeo6c5AkA7Z
+/piG+Z3dgqG0sa6orA0CDZaPq+elwiL6MVMZg3EGuktT54dZODUr9WV/FQWhU7fQE
+u3jJzQ48cXELxrIzuRAkEAw/06rLmKdZY0FkAxjOzZ2Cw0jhq6+oyAt03HkRCy9D1
++wMYSKYbV9ss+ejOQCJkhGWv9Ik8ylScqA7ULN7NIgQJAZdPhs18Z+Nh6KTb4eUhF
+UZIlZhPjU/2zTeSoUS5XXPcfTEICYwEv08++UDbJ89pzQZgl/nIZM9CT1B8u6uG2K
+A==
+-----END PRIVATE KEY-----
diff --git a/chrome/test/data/extensions/permissions/unknown/manifest.json b/chrome/test/data/extensions/permissions/unknown/manifest.json
new file mode 100644
index 0000000..227b8e3
--- /dev/null
+++ b/chrome/test/data/extensions/permissions/unknown/manifest.json
@@ -0,0 +1,9 @@
+{
+ "content_scripts": [ {
+ "js": [ "test.js" ],
+ "matches": [ "http://*.google.com/*", "https://*.google.com/*", "http://www.example.com/*" ]
+ } ],
+ "name": "Unknown Permissions Test",
+ "permissions": [ "tabs", "blah" ],
+ "version": "1"
+}
diff --git a/chrome/test/data/extensions/permissions/unknown/test.js b/chrome/test/data/extensions/permissions/unknown/test.js
new file mode 100644
index 0000000..0a77ac4
--- /dev/null
+++ b/chrome/test/data/extensions/permissions/unknown/test.js
@@ -0,0 +1,3 @@
+// place holder...
+var x = 0;
+x = x + 1; \ No newline at end of file
diff --git a/chrome/test/live_sync/live_extensions_sync_test_base.cc b/chrome/test/live_sync/live_extensions_sync_test_base.cc
index 1eb040e..ce4396e 100644
--- a/chrome/test/live_sync/live_extensions_sync_test_base.cc
+++ b/chrome/test/live_sync/live_extensions_sync_test_base.cc
@@ -103,7 +103,7 @@ void LiveExtensionsSyncTestBase::InstallExtension(
Profile* profile, scoped_refptr<Extension> extension) {
CHECK(profile);
CHECK(extension.get());
- profile->GetExtensionsService()->OnExtensionInstalled(extension, true);
+ profile->GetExtensionsService()->OnExtensionInstalled(extension);
}
void LiveExtensionsSyncTestBase::InstallAllPendingExtensions(