diff options
author | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-23 19:02:52 +0000 |
---|---|---|
committer | jstritar@chromium.org <jstritar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-06-23 19:02:52 +0000 |
commit | 0d3e4a22b373147c6144b57fefdf4012823e9150 (patch) | |
tree | 6388b5763a9c434ba6c79daff407b0e4e11d3572 | |
parent | e31440ac4b5ff347768ade5e2cd6e42720234ca0 (diff) | |
download | chromium_src-0d3e4a22b373147c6144b57fefdf4012823e9150.zip chromium_src-0d3e4a22b373147c6144b57fefdf4012823e9150.tar.gz chromium_src-0d3e4a22b373147c6144b57fefdf4012823e9150.tar.bz2 |
Start refractoring extension permissions into ExtensionPermissionSet.
BUG=84507
TEST=*Extension*
Review URL: http://codereview.chromium.org/7003098
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90244 0039d316-1c4b-4281-b951-d872f2087c98
37 files changed, 2722 insertions, 1531 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 4f61688..06e73e3 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -808,6 +808,8 @@ void AutomationProvider::InstallExtensionAndGetHandle( ExtensionInstallUI* client = (with_ui ? new ExtensionInstallUI(profile_) : NULL); scoped_refptr<CrxInstaller> installer(service->MakeCrxInstaller(client)); + if (!with_ui) + installer->set_allow_silent_install(true); installer->set_install_cause(extension_misc::INSTALL_CAUSE_AUTOMATION); installer->InstallCrx(crx_path); } else { diff --git a/chrome/browser/automation/testing_automation_provider.cc b/chrome/browser/automation/testing_automation_provider.cc index 5fe2218..b7b83ad 100644 --- a/chrome/browser/automation/testing_automation_provider.cc +++ b/chrome/browser/automation/testing_automation_provider.cc @@ -4015,7 +4015,7 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) { if (effective_perm) pattern_list = ext->GetEffectiveHostPermissions().patterns(); else - pattern_list = ext->host_permissions(); + pattern_list = ext->permission_set()->explicit_hosts().patterns(); ListValue* permissions = new ListValue; for (URLPatternList::const_iterator perm = pattern_list.begin(); @@ -4028,7 +4028,7 @@ ListValue* GetHostPermissions(const Extension* ext, bool effective_perm) { ListValue* GetAPIPermissions(const Extension* ext) { ListValue* permissions = new ListValue; - std::set<std::string> perm_list = ext->api_permissions(); + std::set<std::string> perm_list = ext->permission_set()->GetAPIsAsStrings(); for (std::set<std::string>::const_iterator perm = perm_list.begin(); perm != perm_list.end(); ++perm) { permissions->Append(new StringValue(perm->c_str())); diff --git a/chrome/browser/background/background_application_list_model.cc b/chrome/browser/background/background_application_list_model.cc index f9092c1..d8512bc 100644 --- a/chrome/browser/background/background_application_list_model.cc +++ b/chrome/browser/background/background_application_list_model.cc @@ -99,11 +99,6 @@ void GetServiceApplications(ExtensionService* service, ExtensionNameComparator(collator.get())); } -bool HasBackgroundAppPermission( - const std::set<std::string>& api_permissions) { - return Extension::HasApiPermission( - api_permissions, Extension::kBackgroundPermission); -} } // namespace void @@ -250,7 +245,7 @@ int BackgroundApplicationListModel::GetPosition( // static bool BackgroundApplicationListModel::IsBackgroundApp( const Extension& extension) { - return HasBackgroundAppPermission(extension.api_permissions()); + return extension.HasAPIPermission(ExtensionAPIPermission::kBackground); } void BackgroundApplicationListModel::Observe( diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index dfde2e9..8d72958 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -484,7 +484,7 @@ WebKit::WebNotificationPresenter::Permission const Extension* extension = io_data->GetExtensionInfoMap()->extensions().GetByURL(source_url); if (extension && - extension->HasApiPermission(Extension::kNotificationPermission)) { + extension->HasAPIPermission(ExtensionAPIPermission::kNotification)) { return WebKit::WebNotificationPresenter::PermissionAllowed; } @@ -547,7 +547,7 @@ bool ChromeContentBrowserClient::CanCreateWindow( const Extension* extension = io_data->GetExtensionInfoMap()->extensions().GetByURL(source_url); return (extension && - extension->HasApiPermission(Extension::kBackgroundPermission)); + extension->HasAPIPermission(ExtensionAPIPermission::kBackground)); } return true; } diff --git a/chrome/browser/extensions/convert_web_app_browsertest.cc b/chrome/browser/extensions/convert_web_app_browsertest.cc index e4985df..e923e90 100644 --- a/chrome/browser/extensions/convert_web_app_browsertest.cc +++ b/chrome/browser/extensions/convert_web_app_browsertest.cc @@ -70,11 +70,11 @@ IN_PROC_BROWSER_TEST_F(ExtensionFromWebAppTest, Basic) { EXPECT_EQ(extension_misc::LAUNCH_PANEL, installed_extension_->launch_container()); - ASSERT_EQ(2u, installed_extension_->api_permissions().size()); - EXPECT_TRUE(installed_extension_->api_permissions().find("geolocation") != - installed_extension_->api_permissions().end()); - EXPECT_TRUE(installed_extension_->api_permissions().find("notifications") != - installed_extension_->api_permissions().end()); + ASSERT_EQ(2u, installed_extension_->permission_set()->apis().size()); + EXPECT_TRUE(installed_extension_->HasAPIPermission( + ExtensionAPIPermission::kGeolocation)); + EXPECT_TRUE(installed_extension_->HasAPIPermission( + ExtensionAPIPermission::kNotification)); ASSERT_EQ(3u, installed_extension_->icons().map().size()); EXPECT_EQ("icons/16.png", installed_extension_->icons().Get( diff --git a/chrome/browser/extensions/convert_web_app_unittest.cc b/chrome/browser/extensions/convert_web_app_unittest.cc index 551a923..4aafc4f 100644 --- a/chrome/browser/extensions/convert_web_app_unittest.cc +++ b/chrome/browser/extensions/convert_web_app_unittest.cc @@ -123,9 +123,9 @@ TEST(ExtensionFromWebApp, Basic) { EXPECT_EQ(UTF16ToUTF8(web_app.title), extension->name()); EXPECT_EQ(UTF16ToUTF8(web_app.description), extension->description()); EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL()); - EXPECT_EQ(2u, extension->api_permissions().size()); - EXPECT_TRUE(extension->HasApiPermission("geolocation")); - EXPECT_TRUE(extension->HasApiPermission("notifications")); + EXPECT_EQ(2u, extension->permission_set()->apis().size()); + EXPECT_TRUE(extension->HasAPIPermission("geolocation")); + EXPECT_TRUE(extension->HasAPIPermission("notifications")); ASSERT_EQ(1u, extension->web_extent().patterns().size()); EXPECT_EQ("http://aaronboodman.com/gearpad/*", extension->web_extent().patterns()[0].GetAsString()); @@ -167,7 +167,7 @@ TEST(ExtensionFromWebApp, Minimal) { EXPECT_EQ("", extension->description()); EXPECT_EQ(web_app.app_url, extension->GetFullLaunchURL()); EXPECT_EQ(0u, extension->icons().map().size()); - EXPECT_EQ(0u, extension->api_permissions().size()); + EXPECT_EQ(0u, extension->permission_set()->apis().size()); ASSERT_EQ(1u, extension->web_extent().patterns().size()); EXPECT_EQ("*://aaronboodman.com/*", extension->web_extent().patterns()[0].GetAsString()); diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index e772a0f..7f76e1d 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -546,6 +546,13 @@ void CrxInstaller::ReportSuccessFromUIThread() { if (client_) client_->OnInstallSuccess(extension_.get(), install_icon_.get()); + // We update the extension's granted permissions if the user already approved + // the install (client_ is non NULL), or we are allowed to install this + // silently. We only track granted permissions for INTERNAL extensions. + if ((client_ || allow_silent_install_) && + extension_->location() == Extension::INTERNAL) + frontend_weak_->GrantPermissions(extension_); + // Tell the frontend about the installation and hand off ownership of // extension_ to it. frontend_weak_->OnExtensionInstalled(extension_); diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 2c1fe40..92aa0f7 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -563,7 +563,7 @@ ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction( return NULL; } - if (!extension->HasApiPermission(params.name)) { + if (!extension->HasAPIPermission(params.name)) { LOG(ERROR) << "Extension " << extension->id() << " does not have " << "permission to function: " << params.name; SendAccessDenied(ipc_sender, routing_id, params.request_id); diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc index a355673..5df0bd1 100644 --- a/chrome/browser/extensions/extension_info_map_unittest.cc +++ b/chrome/browser/extensions/extension_info_map_unittest.cc @@ -138,19 +138,19 @@ TEST_F(ExtensionInfoMapTest, CheckPermissions) { const Extension* match = info_map->extensions().GetByURL( app->GetResourceURL("a.html")); EXPECT_TRUE(match && - match->HasApiPermission(Extension::kNotificationPermission)); + match->HasAPIPermission(ExtensionAPIPermission::kNotification)); match = info_map->extensions().GetByURL(app_url); EXPECT_TRUE(match && - match->HasApiPermission(Extension::kNotificationPermission)); + match->HasAPIPermission(ExtensionAPIPermission::kNotification)); EXPECT_FALSE(match && - match->HasApiPermission(Extension::kTabPermission)); + match->HasAPIPermission(ExtensionAPIPermission::kTab)); // The extension should have the tabs permission. match = info_map->extensions().GetByURL(extension->GetResourceURL("a.html")); EXPECT_TRUE(match && - match->HasApiPermission(Extension::kTabPermission)); + match->HasAPIPermission(ExtensionAPIPermission::kTab)); EXPECT_FALSE(match && - match->HasApiPermission(Extension::kNotificationPermission)); + match->HasAPIPermission(ExtensionAPIPermission::kNotification)); // Random URL should not have any permissions. match = info_map->extensions().GetByURL(GURL("http://evil.com/a.html")); diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index 0053b1d..b67b03f 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -91,7 +91,8 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, info->Set("icons", icon_list); } - const std::set<std::string> perms = extension.api_permissions(); + const std::set<std::string> perms = + extension.permission_set()->GetAPIsAsStrings(); ListValue* permission_list = new ListValue(); if (!perms.empty()) { std::set<std::string>::const_iterator perms_iter; @@ -105,7 +106,8 @@ static DictionaryValue* CreateExtensionInfo(const Extension& extension, ListValue* host_permission_list = new ListValue(); if (!extension.is_hosted_app()) { // Skip host permissions for hosted apps. - const URLPatternList host_perms = extension.host_permissions(); + const URLPatternList host_perms = + extension.permission_set()->explicit_hosts().patterns(); if (!host_perms.empty()) { URLPatternList::const_iterator host_perms_iter; for (host_perms_iter = host_perms.begin(); diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc index d80f37c..e7f8004 100644 --- a/chrome/browser/extensions/extension_management_browsertest.cc +++ b/chrome/browser/extensions/extension_management_browsertest.cc @@ -124,6 +124,18 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, InstallThenCancel) { "1.0")); } +IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, InstallRequiresConfirm) { + // Installing the extension without an auto confirming UI should fail + // since good.crx has permissions that require approval. + ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx"), 0)); + UninstallExtension("ldnnhddmnhbkjipkidpdiheffobcpfmf"); + + // And the install should succeed when the permissions are accepted. + ASSERT_TRUE(InstallExtensionWithUIAutoConfirm( + test_data_dir_.AppendASCII("good.crx"), 1, browser()->profile())); + UninstallExtension("ldnnhddmnhbkjipkidpdiheffobcpfmf"); +} + // Tests that installing and uninstalling extensions don't crash with an // incognito window open. IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, Incognito) { @@ -133,7 +145,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, Incognito) { ui_test_utils::OpenURLOffTheRecord(browser()->profile(), GURL(chrome::kChromeUIExtensionsURL)); - ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx"), 1)); + ASSERT_TRUE(InstallExtensionWithUIAutoConfirm( + test_data_dir_.AppendASCII("good.crx"), 1, browser()->profile())); UninstallExtension("ldnnhddmnhbkjipkidpdiheffobcpfmf"); } diff --git a/chrome/browser/extensions/extension_preference_api.cc b/chrome/browser/extensions/extension_preference_api.cc index 1fc7813..21b1f71 100644 --- a/chrome/browser/extensions/extension_preference_api.cc +++ b/chrome/browser/extensions/extension_preference_api.cc @@ -20,6 +20,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/extensions/extension_error_utils.h" +#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/pref_names.h" #include "content/common/notification_type.h" #include "content/common/notification_service.h" @@ -29,7 +30,7 @@ namespace { struct PrefMappingEntry { const char* extension_pref; const char* browser_pref; - const char* permission; + ExtensionAPIPermission::ID permission; }; const char kNotControllable[] = "not_controllable"; @@ -46,19 +47,19 @@ const char kOnPrefChangeFormat[] = "types.ChromeSetting.%s.onChange"; PrefMappingEntry kPrefMapping[] = { { "thirdPartyCookiesAllowed", prefs::kBlockThirdPartyCookies, - Extension::kContentSettingsPermission + ExtensionAPIPermission::kContentSettings }, { "referrersEnabled", prefs::kEnableReferrers, - Extension::kContentSettingsPermission + ExtensionAPIPermission::kContentSettings }, { "hyperlinkAuditingEnabled", prefs::kEnableHyperlinkAuditing, - Extension::kContentSettingsPermission + ExtensionAPIPermission::kContentSettings }, { "proxy", prefs::kProxy, - Extension::kProxyPermission + ExtensionAPIPermission::kProxy }, }; @@ -130,9 +131,8 @@ class PrefMapping { bool FindBrowserPrefForExtensionPref(const std::string& extension_pref, std::string* browser_pref, - std::string* permission) { - std::map<std::string, std::pair<std::string, std::string> >::iterator it = - mapping_.find(extension_pref); + ExtensionAPIPermission::ID* permission) { + PrefMap::iterator it = mapping_.find(extension_pref); if (it != mapping_.end()) { *browser_pref = it->second.first; *permission = it->second.second; @@ -143,9 +143,8 @@ class PrefMapping { bool FindEventForBrowserPref(const std::string& browser_pref, std::string* event_name, - std::string* permission) { - std::map<std::string, std::pair<std::string, std::string> >::iterator it = - event_mapping_.find(browser_pref); + ExtensionAPIPermission::ID* permission) { + PrefMap::iterator it = event_mapping_.find(browser_pref); if (it != event_mapping_.end()) { *event_name = it->second.first; *permission = it->second.second; @@ -198,11 +197,15 @@ class PrefMapping { transformers_[browser_pref] = transformer; } + typedef std::map<std::string, + std::pair<std::string, ExtensionAPIPermission::ID> > + PrefMap; + // Mapping from extension pref keys to browser pref keys and permissions. - std::map<std::string, std::pair<std::string, std::string> > mapping_; + PrefMap mapping_; // Mapping from browser pref keys to extension event names and permissions. - std::map<std::string, std::pair<std::string, std::string> > event_mapping_; + PrefMap event_mapping_; // Mapping from browser pref keys to transformers. std::map<std::string, PrefTransformerInterface*> transformers_; @@ -248,7 +251,7 @@ void ExtensionPreferenceEventRouter::OnPrefChanged( bool incognito = (pref_service != profile_->GetPrefs()); std::string event_name; - std::string permission; + ExtensionAPIPermission::ID permission = ExtensionAPIPermission::kInvalid; bool rv = PrefMapping::GetInstance()->FindEventForBrowserPref( browser_pref, &event_name, &permission); DCHECK(rv); @@ -278,7 +281,7 @@ void ExtensionPreferenceEventRouter::OnPrefChanged( std::string extension_id = (*it)->id(); // TODO(bauerb): Only iterate over registered event listeners. if (router->ExtensionHasEventListener(extension_id, event_name) && - (*it)->HasApiPermission(permission) && + (*it)->HasAPIPermission(permission) && (!incognito || extension_service->CanCrossIncognito(*it))) { std::string level_of_control = GetLevelOfControl(profile_, extension_id, browser_pref, incognito); @@ -323,11 +326,11 @@ bool GetPreferenceFunction::RunImpl() { PrefService* prefs = incognito ? profile_->GetOffTheRecordPrefs() : profile_->GetPrefs(); std::string browser_pref; - std::string permission; + ExtensionAPIPermission::ID permission = ExtensionAPIPermission::kInvalid; EXTENSION_FUNCTION_VALIDATE( PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( pref_key, &browser_pref, &permission)); - if (!GetExtension()->HasApiPermission(permission)) { + if (!GetExtension()->HasAPIPermission(permission)) { error_ = ExtensionErrorUtils::FormatErrorMessage( keys::kPermissionErrorMessage, pref_key); return false; @@ -397,11 +400,11 @@ bool SetPreferenceFunction::RunImpl() { } std::string browser_pref; - std::string permission; + ExtensionAPIPermission::ID permission = ExtensionAPIPermission::kInvalid; EXTENSION_FUNCTION_VALIDATE( PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( pref_key, &browser_pref, &permission)); - if (!GetExtension()->HasApiPermission(permission)) { + if (!GetExtension()->HasAPIPermission(permission)) { error_ = ExtensionErrorUtils::FormatErrorMessage( keys::kPermissionErrorMessage, pref_key); return false; @@ -461,11 +464,11 @@ bool ClearPreferenceFunction::RunImpl() { } std::string browser_pref; - std::string permission; + ExtensionAPIPermission::ID permission = ExtensionAPIPermission::kInvalid; EXTENSION_FUNCTION_VALIDATE( PrefMapping::GetInstance()->FindBrowserPrefForExtensionPref( pref_key, &browser_pref, &permission)); - if (!GetExtension()->HasApiPermission(permission)) { + if (!GetExtension()->HasAPIPermission(permission)) { error_ = ExtensionErrorUtils::FormatErrorMessage( keys::kPermissionErrorMessage, pref_key); return false; diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index b901ba5..d7b63dc 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -67,7 +67,6 @@ const char kIdleInstallInfoCrxPath[] = "crx_path"; const char kIdleInstallInfoVersion[] = "version"; const char kIdleInstallInfoFetchTime[] = "fetch_time"; - // A preference that, if true, will allow this extension to run in incognito // mode. const char kPrefIncognitoEnabled[] = "incognito"; @@ -103,9 +102,15 @@ const char kBrowserActionVisible[] = "browser_action_visible"; // 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"; +const char kPrefGrantedAPIs[] = "granted_permissions.api"; +const char kPrefGrantedExplicitHosts[] = "granted_permissions.explicit_host"; +const char kPrefGrantedScriptableHosts[] = + "granted_permissions.scriptable_host"; + +// The preference names for the old granted permissions scheme. +const char kPrefOldGrantedFullAccess[] = "granted_permissions.full"; +const char kPrefOldGrantedHosts[] = "granted_permissions.host"; +const char kPrefOldGrantedAPIs[] = "granted_permissions.api"; // A preference that indicates when an extension was installed. const char kPrefInstallTime[] = "install_time"; @@ -256,15 +261,6 @@ static void CleanupBadExtensionKeys(const FilePath& root_dir, } } -static void ExtentToStringSet(const URLPatternSet& host_extent, - std::set<std::string>* result) { - URLPatternList patterns = host_extent.patterns(); - URLPatternList::const_iterator i; - - for (i = patterns.begin(); i != patterns.end(); ++i) - result->insert(i->GetAsString()); -} - } // namespace ExtensionPrefs::ExtensionPrefs( @@ -438,42 +434,46 @@ bool ExtensionPrefs::ReadExtensionPrefList( return true; } -bool ExtensionPrefs::ReadExtensionPrefStringSet( +bool ExtensionPrefs::ReadExtensionPrefURLPatternSet( const std::string& extension_id, const std::string& pref_key, - std::set<std::string>* result) { + URLPatternSet* result, + int valid_schemes) { const ListValue* value = NULL; if (!ReadExtensionPrefList(extension_id, pref_key, &value)) return false; - result->clear(); + result->ClearPatterns(); + bool allow_file_access = AllowFileAccess(extension_id); for (size_t i = 0; i < value->GetSize(); ++i) { std::string item; if (!value->GetString(i, &item)) return false; - result->insert(item); + URLPattern pattern(valid_schemes); + if (pattern.Parse(item, URLPattern::PARSE_LENIENT) != + URLPattern::PARSE_SUCCESS) { + NOTREACHED(); + return false; + } + if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) { + pattern.set_valid_schemes( + pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); + } + result->AddPattern(pattern); } return true; } -void ExtensionPrefs::AddToExtensionPrefStringSet( +void ExtensionPrefs::SetExtensionPrefURLPatternSet( 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.begin())); - + const URLPatternSet& new_value) { 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)); + for (URLPatternList::const_iterator i = new_value.patterns().begin(); + i != new_value.patterns().end(); ++i) + value->AppendIfNotPresent(Value::CreateStringValue(i->GetAsString())); UpdateExtensionPref(extension_id, pref_key, value); } @@ -671,72 +671,132 @@ void ExtensionPrefs::SetActiveBit(const std::string& extension_id, Value::CreateBooleanValue(active)); } -bool ExtensionPrefs::GetGrantedPermissions( - const std::string& extension_id, - bool* full_access, - std::set<std::string>* api_permissions, - URLPatternSet* host_extent) { - CHECK(Extension::IdIsValid(extension_id)); +void ExtensionPrefs::MigratePermissions(const ExtensionIdSet& extension_ids) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionIdSet::const_iterator ext_id = extension_ids.begin(); + ext_id != extension_ids.end(); ++ext_id) { - const DictionaryValue* ext = GetExtensionPref(extension_id); - if (!ext || !ext->GetBoolean(kPrefGrantedPermissionsAll, full_access)) - return false; + // An extension's granted permissions need to be migrated if the + // full_access bit is present. This bit was always present in the previous + // scheme and is never present now. + bool full_access; + const DictionaryValue* ext = GetExtensionPref(*ext_id); + if (!ext || !ext->GetBoolean(kPrefOldGrantedFullAccess, &full_access)) + continue; - ReadExtensionPrefStringSet( - extension_id, kPrefGrantedPermissionsAPI, api_permissions); + // Remove the full access bit (empty list will get trimmed). + UpdateExtensionPref( + *ext_id, kPrefOldGrantedFullAccess, new ListValue()); + + // Add the plugin permission if the full access bit was set. + if (full_access) { + ListValue* apis = NULL; + ListValue* new_apis = NULL; + + if (ext->GetList(kPrefGrantedAPIs, &apis)) + new_apis = apis->DeepCopy(); + else + new_apis = new ListValue(); + + std::string plugin_name = info->GetByID( + ExtensionAPIPermission::kPlugin)->name(); + new_apis->Append(Value::CreateStringValue(plugin_name)); + UpdateExtensionPref(*ext_id, kPrefGrantedAPIs, new_apis); + } - std::set<std::string> host_permissions; - ReadExtensionPrefStringSet( - extension_id, kPrefGrantedPermissionsHost, &host_permissions); - bool allow_file_access = AllowFileAccess(extension_id); + // The granted permissions originally only held the effective hosts, + // which are a combination of host and user script host permissions. + // We now maintain these lists separately. For migration purposes, it + // does not matter how we treat the old effective hosts as long as the + // new effective hosts will be the same, so we move them to explicit + // host permissions. + ListValue* hosts; + if (ext->GetList(kPrefOldGrantedHosts, &hosts)) { + UpdateExtensionPref( + *ext_id, kPrefGrantedExplicitHosts, hosts->DeepCopy()); + + // We can get rid of the old one by setting it to an empty list. + UpdateExtensionPref(*ext_id, kPrefOldGrantedHosts, new ListValue()); + } + } +} - // The granted host permissions contain hosts from the manifest's - // "permissions" array and from the content script "matches" arrays, - // so the URLPattern needs to accept valid schemes from both types. - for (std::set<std::string>::iterator i = host_permissions.begin(); - i != host_permissions.end(); ++i) { - URLPattern pattern( - Extension::kValidHostPermissionSchemes | - UserScript::kValidUserScriptSchemes); - - // Parse without strict checks, so that new strict checks do not - // fail on a pattern in an installed extension. - if (URLPattern::PARSE_SUCCESS != pattern.Parse( - *i, URLPattern::PARSE_LENIENT)) { - NOTREACHED(); // Corrupt prefs? Hand editing? - } else { - if (!allow_file_access && pattern.MatchesScheme(chrome::kFileScheme)) { - pattern.set_valid_schemes( - pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); +ExtensionPermissionSet* ExtensionPrefs::GetGrantedPermissions( + const std::string& extension_id) { + CHECK(Extension::IdIsValid(extension_id)); + + const DictionaryValue* ext = GetExtensionPref(extension_id); + if (!ext) + return NULL; + + // Retrieve the API permissions. + ExtensionAPIPermissionSet apis; + const ListValue* api_values = NULL; + if (ReadExtensionPrefList(extension_id, kPrefGrantedAPIs, &api_values)) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (size_t i = 0; i < api_values->GetSize(); ++i) { + std::string permission_name; + if (api_values->GetString(i, &permission_name)) { + ExtensionAPIPermission *permission = info->GetByName(permission_name); + if (permission) + apis.insert(permission->id()); } - host_extent->AddPattern(pattern); } } - return true; + // Retrieve the explicit host permissions. + URLPatternSet explicit_hosts; + ReadExtensionPrefURLPatternSet( + extension_id, kPrefGrantedExplicitHosts, + &explicit_hosts, Extension::kValidHostPermissionSchemes); + + // Retrieve the scriptable host permissions. + URLPatternSet scriptable_hosts; + ReadExtensionPrefURLPatternSet( + extension_id, kPrefGrantedScriptableHosts, + &scriptable_hosts, UserScript::kValidUserScriptSchemes); + + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); } void ExtensionPrefs::AddGrantedPermissions( const std::string& extension_id, - const bool full_access, - const std::set<std::string>& api_permissions, - const URLPatternSet& host_extent) { + const ExtensionPermissionSet* permissions) { CHECK(Extension::IdIsValid(extension_id)); - UpdateExtensionPref(extension_id, kPrefGrantedPermissionsAll, - Value::CreateBooleanValue(full_access)); - - if (!api_permissions.empty()) { - AddToExtensionPrefStringSet( - extension_id, kPrefGrantedPermissionsAPI, api_permissions); + scoped_ptr<ExtensionPermissionSet> granted_permissions( + GetGrantedPermissions(extension_id)); + + // The new granted permissions are the union of the already granted + // permissions and the newly granted permissions. + scoped_ptr<ExtensionPermissionSet> new_perms( + ExtensionPermissionSet::CreateUnion( + permissions, granted_permissions.get())); + + // Set the API permissions. + ListValue* api_values = new ListValue(); + ExtensionAPIPermissionSet apis = new_perms->apis(); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = apis.begin(); + i != apis.end(); ++i) { + ExtensionAPIPermission* perm = info->GetByID(*i); + if (perm) + api_values->Append(Value::CreateStringValue(perm->name())); } + UpdateExtensionPref(extension_id, kPrefGrantedAPIs, api_values); - if (!host_extent.is_empty()) { - std::set<std::string> host_permissions; - ExtentToStringSet(host_extent, &host_permissions); + // Set the explicit host permissions. + if (!new_perms->explicit_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + kPrefGrantedExplicitHosts, + new_perms->explicit_hosts()); + } - AddToExtensionPrefStringSet( - extension_id, kPrefGrantedPermissionsHost, host_permissions); + // Set the scriptable host permissions. + if (!new_perms->scriptable_hosts().is_empty()) { + SetExtensionPrefURLPatternSet(extension_id, + kPrefGrantedScriptableHosts, + new_perms->scriptable_hosts()); } } @@ -1432,6 +1492,8 @@ void ExtensionPrefs::InitPrefStore() { } FixMissingPrefs(extension_ids); + MigratePermissions(extension_ids); + // Store extension controlled preference values in the // |extension_pref_value_map_|, which then informs the subscribers // (ExtensionPrefStores) about the winning values. @@ -1489,7 +1551,6 @@ void ExtensionPrefs::InitPrefStore() { extension_pref_value_map_->NotifyInitializationCompleted(); } - void ExtensionPrefs::SetExtensionControlledPref( const std::string& extension_id, const std::string& pref_key, diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h index 2ea50e4..096d861 100644 --- a/chrome/browser/extensions/extension_prefs.h +++ b/chrome/browser/extensions/extension_prefs.h @@ -173,26 +173,17 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer { bool GetActiveBit(const std::string& extension_id); void SetActiveBit(const std::string& extension_id, bool active); - // 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): Refactor 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, - URLPatternSet* 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). + // Returns the granted permission set for the extension with |extension_id|, + // and NULL if no preferences were found for |extension_id|. + // This passes ownership of the returned set to the caller. + ExtensionPermissionSet* GetGrantedPermissions( + const std::string& extension_id); + + // Adds |permissions| to the granted permissions set for the extension with + // |extension_id|. The new granted permissions set will be the union of + // |permissions| and the already granted permissions. void AddGrantedPermissions(const std::string& extension_id, - const bool full_access, - const std::set<std::string>& api_permissions, - const URLPatternSet& host_extent); + const ExtensionPermissionSet* permissions); // Returns true if the user enabled this extension to be loaded in incognito // mode. @@ -393,23 +384,23 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer { const std::string& pref_key, int* out_value); - // Reads a list pref |pref_key| from extension with id | extension_id|. + // Reads a list pref |pref_key| from extension with id |extension_id|. bool ReadExtensionPrefList(const std::string& extension_id, const std::string& pref_key, const 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); + // Interprets the list pref, |pref_key| in |extension_id|'s preferences, as a + // URLPatternSet. The |valid_schemes| specify how to parse the URLPatterns. + bool ReadExtensionPrefURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + URLPatternSet* result, + int valid_schemes); - // 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); + // Converts |new_value| to a list of strings and sets the |pref_key| pref + // belonging to |extension_id|. + void SetExtensionPrefURLPatternSet(const std::string& extension_id, + const std::string& pref_key, + const URLPatternSet& new_value); // Returns a dictionary for extension |id|'s prefs or NULL if it doesn't // exist. @@ -440,6 +431,9 @@ class ExtensionPrefs : public ExtensionContentSettingsStore::Observer { // pref store. void InitPrefStore(); + // Migrates the permissions data in the pref store. + void MigratePermissions(const ExtensionIdSet& extension_ids); + // The pref service specific to this set of extension prefs. Owned by profile. PrefService* prefs_; diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index 673044a..68faeb1 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -14,6 +14,7 @@ #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_permission_set.h" #include "content/browser/browser_thread.h" #include "content/common/notification_details.h" #include "content/common/notification_observer_mock.h" @@ -43,13 +44,13 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) { extent->AddPattern(URLPattern(schemes, pattern)); } -static void AssertEqualExtents(URLPatternSet* extent1, - URLPatternSet* extent2) { - URLPatternList patterns1 = extent1->patterns(); - URLPatternList patterns2 = extent2->patterns(); - std::set<std::string> strings1; +static void AssertEqualExtents(const URLPatternSet& extent1, + const URLPatternSet& extent2) { + URLPatternList patterns1 = extent1.patterns(); + URLPatternList patterns2 = extent2.patterns(); EXPECT_EQ(patterns1.size(), patterns2.size()); + std::set<std::string> strings1; for (size_t i = 0; i < patterns1.size(); ++i) strings1.insert(patterns1.at(i).GetAsString()); @@ -204,100 +205,135 @@ class ExtensionPrefsGrantedPermissions : public ExtensionPrefsTest { 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_set1_.insert(ExtensionAPIPermission::kTab); + api_perm_set1_.insert(ExtensionAPIPermission::kBookmark); - api_perm_set2_.insert("history"); - api_perm_set2_.insert("unknown2"); + api_perm_set2_.insert(ExtensionAPIPermission::kHistory); - AddPattern(&host_perm_set1_, "http://*.google.com/*"); - AddPattern(&host_perm_set1_, "http://example.com/*"); - AddPattern(&host_perm_set1_, "chrome://favicon/*"); + AddPattern(&ehost_perm_set1_, "http://*.google.com/*"); + AddPattern(&ehost_perm_set1_, "http://example.com/*"); + AddPattern(&ehost_perm_set1_, "chrome://favicon/*"); - AddPattern(&host_perm_set2_, "https://*.google.com/*"); + AddPattern(&ehost_perm_set2_, "https://*.google.com/*"); // with duplicate: - AddPattern(&host_perm_set2_, "http://*.google.com/*"); + AddPattern(&ehost_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_.begin())); + AddPattern(&shost_perm_set1_, "http://reddit.com/r/test/*"); + AddPattern(&shost_perm_set2_, "http://reddit.com/r/test/*"); + AddPattern(&shost_perm_set2_, "http://somesite.com/*"); + AddPattern(&shost_perm_set2_, "http://example.com/*"); + + ExtensionAPIPermissionSet expected_apis = api_perm_set1_; + + AddPattern(&ehost_permissions_, "http://*.google.com/*"); + AddPattern(&ehost_permissions_, "http://example.com/*"); + AddPattern(&ehost_permissions_, "chrome://favicon/*"); + AddPattern(&ehost_permissions_, "https://*.google.com/*"); - AddPattern(&host_permissions_, "http://*.google.com/*"); - AddPattern(&host_permissions_, "http://example.com/*"); - AddPattern(&host_permissions_, "chrome://favicon/*"); - AddPattern(&host_permissions_, "https://*.google.com/*"); + AddPattern(&shost_permissions_, "http://reddit.com/r/test/*"); + AddPattern(&shost_permissions_, "http://somesite.com/*"); + AddPattern(&shost_permissions_, "http://example.com/*"); - std::set<std::string> empty_set; - std::set<std::string> api_perms; - bool full_access = false; - URLPatternSet host_perms; + ExtensionAPIPermissionSet empty_set; URLPatternSet empty_extent; + scoped_ptr<ExtensionPermissionSet> permissions; + scoped_ptr<ExtensionPermissionSet> granted_permissions; // Make sure both granted api and host permissions start empty. - EXPECT_FALSE(prefs()->GetGrantedPermissions( - extension_id_, &full_access, &api_perms, &host_perms)); + granted_permissions.reset( + prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_TRUE(granted_permissions->IsEmpty()); - EXPECT_TRUE(api_perms.empty()); - EXPECT_TRUE(host_perms.is_empty()); + permissions.reset(new ExtensionPermissionSet( + api_perm_set1_, empty_extent, empty_extent)); // 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.ClearPatterns(); - 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.ClearPatterns(); - 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); + prefs()->AddGrantedPermissions(extension_id_, permissions.get()); + granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_TRUE(granted_permissions.get()); + EXPECT_FALSE(granted_permissions->IsEmpty()); + EXPECT_EQ(expected_apis, granted_permissions->apis()); + EXPECT_TRUE(granted_permissions->effective_hosts().is_empty()); + EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); + granted_permissions.reset(); + + // Add part of the explicit host permissions. + permissions.reset(new ExtensionPermissionSet( + empty_set, ehost_perm_set1_, empty_extent)); + prefs()->AddGrantedPermissions(extension_id_, permissions.get()); + granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_FALSE(granted_permissions->IsEmpty()); + EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); + EXPECT_EQ(expected_apis, granted_permissions->apis()); + AssertEqualExtents(ehost_perm_set1_, + granted_permissions->explicit_hosts()); + AssertEqualExtents(ehost_perm_set1_, + granted_permissions->effective_hosts()); + + // Add part of the scriptable host permissions. + permissions.reset(new ExtensionPermissionSet( + empty_set, empty_extent, shost_perm_set1_)); + prefs()->AddGrantedPermissions(extension_id_, permissions.get()); + granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_FALSE(granted_permissions->IsEmpty()); + EXPECT_FALSE(granted_permissions->HasEffectiveFullAccess()); + EXPECT_EQ(expected_apis, granted_permissions->apis()); + AssertEqualExtents(ehost_perm_set1_, + granted_permissions->explicit_hosts()); + AssertEqualExtents(shost_perm_set1_, + granted_permissions->scriptable_hosts()); + URLPatternSet::CreateUnion(ehost_perm_set1_, shost_perm_set1_, + &effective_permissions_); + AssertEqualExtents(effective_permissions_, + granted_permissions->effective_hosts()); + + // Add the rest of both the permissions. + permissions.reset(new ExtensionPermissionSet( + api_perm_set2_, ehost_perm_set2_, shost_perm_set2_)); + + std::set_union(expected_apis.begin(), expected_apis.end(), + api_perm_set2_.begin(), api_perm_set2_.end(), + std::inserter(api_permissions_, api_permissions_.begin())); + + prefs()->AddGrantedPermissions(extension_id_, permissions.get()); + granted_permissions.reset(prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_TRUE(granted_permissions.get()); + EXPECT_FALSE(granted_permissions->IsEmpty()); + EXPECT_EQ(api_permissions_, granted_permissions->apis()); + AssertEqualExtents(ehost_permissions_, + granted_permissions->explicit_hosts()); + AssertEqualExtents(shost_permissions_, + granted_permissions->scriptable_hosts()); + effective_permissions_.ClearPatterns(); + URLPatternSet::CreateUnion(ehost_permissions_, shost_permissions_, + &effective_permissions_); + AssertEqualExtents(effective_permissions_, + granted_permissions->effective_hosts()); } virtual void Verify() { - std::set<std::string> api_perms; - URLPatternSet 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); + scoped_ptr<ExtensionPermissionSet> permissions( + prefs()->GetGrantedPermissions(extension_id_)); + EXPECT_TRUE(permissions.get()); + EXPECT_FALSE(permissions->HasEffectiveFullAccess()); + EXPECT_EQ(api_permissions_, permissions->apis()); + AssertEqualExtents(ehost_permissions_, permissions->explicit_hosts()); + AssertEqualExtents(shost_permissions_, permissions->scriptable_hosts()); } private: std::string extension_id_; - std::set<std::string> api_perm_set1_; - std::set<std::string> api_perm_set2_; - URLPatternSet host_perm_set1_; - URLPatternSet host_perm_set2_; - - - std::set<std::string> api_permissions_; - URLPatternSet host_permissions_; + ExtensionAPIPermissionSet api_perm_set1_; + ExtensionAPIPermissionSet api_perm_set2_; + URLPatternSet ehost_perm_set1_; + URLPatternSet ehost_perm_set2_; + URLPatternSet shost_perm_set1_; + URLPatternSet shost_perm_set2_; + + ExtensionAPIPermissionSet api_permissions_; + URLPatternSet ehost_permissions_; + URLPatternSet shost_permissions_; + URLPatternSet effective_permissions_; }; TEST_F(ExtensionPrefsGrantedPermissions, GrantedPermissions) {} diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index b5b7ae9..a8973f6 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -747,6 +747,8 @@ bool ExtensionService::UpdateExtension( installer->set_install_source(pending_extension_info.install_source()); else if (extension) installer->set_install_source(extension->location()); + if (pending_extension_info.install_silently()) + installer->set_allow_silent_install(true); installer->set_delete_source(true); installer->set_original_url(download_url); installer->set_install_cause(extension_misc::INSTALL_CAUSE_UPDATE); @@ -965,11 +967,8 @@ void ExtensionService::GrantPermissions(const Extension* extension) { // We only maintain the granted permissions prefs for INTERNAL extensions. CHECK_EQ(Extension::INTERNAL, extension->location()); - URLPatternSet effective_hosts = extension->GetEffectiveHostPermissions(); extension_prefs_->AddGrantedPermissions(extension->id(), - extension->HasFullPermissions(), - extension->api_permissions(), - effective_hosts); + extension->permission_set()); } void ExtensionService::GrantPermissionsAndEnableExtension( @@ -1208,18 +1207,17 @@ void ExtensionService::RecordPermissionMessagesHistogram( base::Histogram* counter = base::LinearHistogram::FactoryGet( histogram, 1, - Extension::PermissionMessage::ID_ENUM_BOUNDARY, - Extension::PermissionMessage::ID_ENUM_BOUNDARY + 1, + ExtensionPermissionMessage::kEnumBoundary, + ExtensionPermissionMessage::kEnumBoundary + 1, base::Histogram::kUmaTargetedHistogramFlag); - std::vector<Extension::PermissionMessage> permissions = - e->GetPermissionMessages(); + ExtensionPermissionMessages permissions = e->GetPermissionMessages(); if (permissions.empty()) { - counter->Add(Extension::PermissionMessage::ID_NONE); + counter->Add(ExtensionPermissionMessage::kNone); } else { - std::vector<Extension::PermissionMessage>::iterator it; - for (it = permissions.begin(); it != permissions.end(); ++it) - counter->Add(it->message_id()); + for (ExtensionPermissionMessages::iterator it = permissions.begin(); + it != permissions.end(); ++it) + counter->Add(it->id()); } } @@ -1938,10 +1936,6 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) { // can upgrade without requiring this user's approval. const Extension* old = GetExtensionByIdInternal(extension->id(), true, true, false); - bool granted_full_access; - std::set<std::string> granted_apis; - URLPatternSet granted_extent; - bool is_extension_upgrade = old != NULL; bool is_privilege_increase = false; @@ -1950,23 +1944,16 @@ void ExtensionService::DisableIfPrivilegeIncrease(const Extension* extension) { 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)); - } + scoped_ptr<ExtensionPermissionSet> granted_permissions( + extension_prefs_->GetGrantedPermissions(extension->id())); + CHECK(granted_permissions.get()); // 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); + is_privilege_increase = + granted_permissions->HasLessPrivilegesThan(extension->permission_set()); } if (is_extension_upgrade) { @@ -2023,7 +2010,6 @@ void ExtensionService::OnLoadSingleExtension(const Extension* extension) { prompt->ShowPrompt(); return; // continues in SimpleExtensionLoadPrompt::InstallUI* } - OnExtensionInstalled(extension); } diff --git a/chrome/browser/extensions/extension_service_unittest.cc b/chrome/browser/extensions/extension_service_unittest.cc index 6a28563..8008828 100644 --- a/chrome/browser/extensions/extension_service_unittest.cc +++ b/chrome/browser/extensions/extension_service_unittest.cc @@ -114,10 +114,10 @@ static void AddPattern(URLPatternSet* extent, const std::string& pattern) { extent->AddPattern(URLPattern(schemes, pattern)); } -static void AssertEqualExtents(URLPatternSet* extent1, - URLPatternSet* extent2) { - URLPatternList patterns1 = extent1->patterns(); - URLPatternList patterns2 = extent2->patterns(); +static void AssertEqualExtents(const URLPatternSet& extent1, + const URLPatternSet& extent2) { + URLPatternList patterns1 = extent1.patterns(); + URLPatternList patterns2 = extent2.patterns(); std::set<std::string> strings1; EXPECT_EQ(patterns1.size(), patterns2.size()); @@ -569,7 +569,6 @@ class ExtensionServiceTest pem_output_path)); ASSERT_TRUE(file_util::PathExists(crx_path)); - InstallCrx(crx_path, should_succeed); } @@ -585,8 +584,9 @@ class ExtensionServiceTest void StartCrxInstall(const FilePath& crx_path) { ASSERT_TRUE(file_util::PathExists(crx_path)) << "Path does not exist: "<< crx_path.value().c_str(); - // no client (silent install) - scoped_refptr<CrxInstaller> installer(service_->MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer( + service_->MakeCrxInstaller(NULL)); + installer->set_allow_silent_install(true); installer->InstallCrx(crx_path); } @@ -1033,7 +1033,8 @@ TEST_F(ExtensionServiceTest, LoadAllExtensionsFromDirectorySuccess) { extension->path().AppendASCII("js_files").AppendASCII("script3.js"); ASSERT_TRUE(file_util::AbsolutePath(&expected_path)); EXPECT_TRUE(resource10.ComparePathWithDefault(expected_path)); - const URLPatternList permissions = extension->host_permissions(); + const URLPatternList permissions = + extension->permission_set()->explicit_hosts().patterns(); ASSERT_EQ(2u, permissions.size()); EXPECT_EQ("http://*.google.com/*", permissions[0].GetAsString()); EXPECT_EQ("https://*.google.com/*", permissions[1].GetAsString()); @@ -1336,8 +1337,9 @@ TEST_F(ExtensionServiceTest, InstallUserScript) { .AppendASCII("user_script_basic.user.js"); ASSERT_TRUE(file_util::PathExists(path)); - // Pass NULL to install silently. - scoped_refptr<CrxInstaller> installer(service_->MakeCrxInstaller(NULL)); + scoped_refptr<CrxInstaller> installer( + service_->MakeCrxInstaller(NULL)); + installer->set_allow_silent_install(true); installer->InstallUserScript( path, GURL("http://www.aaronboodman.com/scripts/user_script_basic.user.js")); @@ -1371,18 +1373,14 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) { ExtensionPrefs* prefs = service_->extension_prefs(); - std::set<std::string> expected_api_perms; - std::set<std::string> known_api_perms; - bool full_access; + ExtensionAPIPermissionSet expected_api_perms; URLPatternSet expected_host_perms; - URLPatternSet 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()); + scoped_ptr<ExtensionPermissionSet> known_perms( + prefs->GetGrantedPermissions(permissions_crx)); + EXPECT_FALSE(known_perms.get()); PackAndInstallCrx(path, pem_path, true); @@ -1391,23 +1389,20 @@ TEST_F(ExtensionServiceTest, GrantedPermissions) { 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"); + expected_api_perms.insert(ExtensionAPIPermission::kTab); AddPattern(&expected_host_perms, "http://*.google.com/*"); AddPattern(&expected_host_perms, "https://*.google.com/*"); AddPattern(&expected_host_perms, "http://*.google.com.hk/*"); 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); + known_perms.reset(prefs->GetGrantedPermissions(extension_id)); + EXPECT_TRUE(known_perms.get()); + EXPECT_FALSE(known_perms->IsEmpty()); + EXPECT_EQ(expected_api_perms, known_perms->apis()); + EXPECT_FALSE(known_perms->HasEffectiveFullAccess()); + AssertEqualExtents(expected_host_perms, known_perms->effective_hosts()); } #if !defined(OS_CHROMEOS) @@ -1424,24 +1419,22 @@ TEST_F(ExtensionServiceTest, GrantedFullAccessPermissions) { .AppendASCII("2"); ASSERT_TRUE(file_util::PathExists(path)); - PackAndInstallCrx(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; - URLPatternSet host_permissions; - EXPECT_TRUE(prefs->GetGrantedPermissions( - extension_id, &full_access, &api_permissions, &host_permissions)); + scoped_ptr<ExtensionPermissionSet> permissions( + prefs->GetGrantedPermissions(extension_id)); + EXPECT_FALSE(permissions->IsEmpty()); + EXPECT_TRUE(permissions->HasEffectiveFullAccess()); + EXPECT_FALSE(permissions->apis().empty()); + EXPECT_TRUE(permissions->HasAPIPermission(ExtensionAPIPermission::kPlugin)); - EXPECT_TRUE(full_access); - EXPECT_TRUE(api_permissions.empty()); - EXPECT_TRUE(host_permissions.is_empty()); + // Full access implies full host access too... + EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); } #endif @@ -1466,23 +1459,22 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { ExtensionPrefs* prefs = service_->extension_prefs(); - std::set<std::string> expected_api_permissions; + ExtensionAPIPermissionSet expected_api_permissions; URLPatternSet expected_host_permissions; - expected_api_permissions.insert("tabs"); + expected_api_permissions.insert(ExtensionAPIPermission::kTab); AddPattern(&expected_host_permissions, "http://*.google.com/*"); AddPattern(&expected_host_permissions, "https://*.google.com/*"); AddPattern(&expected_host_permissions, "http://*.google.com.hk/*"); 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). - SetPrefStringSet(extension_id, "granted_permissions.api", api_permissions); - + SetPref(extension_id, "granted_permissions.api", + new ListValue(), "granted_permissions.api"); service_->ReloadExtensions(); EXPECT_EQ(1u, service_->disabled_extensions()->size()); @@ -1497,35 +1489,33 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED); ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); - std::set<std::string> current_api_permissions; - URLPatternSet current_host_permissions; - bool current_full_access; - - ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id, - ¤t_full_access, - ¤t_api_permissions, - ¤t_host_permissions)); - - ASSERT_FALSE(current_full_access); - ASSERT_EQ(expected_api_permissions, current_api_permissions); - AssertEqualExtents(&expected_host_permissions, ¤t_host_permissions); + scoped_ptr<ExtensionPermissionSet> current_perms( + prefs->GetGrantedPermissions(extension_id)); + ASSERT_TRUE(current_perms.get()); + ASSERT_FALSE(current_perms->IsEmpty()); + ASSERT_FALSE(current_perms->HasEffectiveFullAccess()); + ASSERT_EQ(expected_api_permissions, current_perms->apis()); + AssertEqualExtents(expected_host_permissions, + current_perms->effective_hosts()); // 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.ClearPatterns(); + current_perms.reset(); - api_permissions.insert("tabs"); host_permissions.insert("http://*.google.com/*"); host_permissions.insert("https://*.google.com/*"); host_permissions.insert("http://*.google.com.hk/*"); - SetPrefStringSet(extension_id, "granted_permissions.api", api_permissions); - SetPrefStringSet(extension_id, "granted_permissions.host", host_permissions); + ListValue* api_permissions = new ListValue(); + api_permissions->Append( + Value::CreateIntegerValue(ExtensionAPIPermission::kTab)); + SetPref(extension_id, "granted_permissions.api", + api_permissions, "granted_permissions.api"); + SetPrefStringSet( + extension_id, "granted_permissions.scriptable_host", host_permissions); service_->ReloadExtensions(); @@ -1541,38 +1531,13 @@ TEST_F(ExtensionServiceTest, GrantedAPIAndHostPermissions) { ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED); ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); - ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id, - ¤t_full_access, - ¤t_api_permissions, - ¤t_host_permissions)); - - ASSERT_FALSE(current_full_access); - ASSERT_EQ(expected_api_permissions, current_api_permissions); - AssertEqualExtents(&expected_host_permissions, ¤t_host_permissions); - - // Tests that the granted permissions preferences are initialized when - // migrating from the old pref schema. - current_api_permissions.clear(); - current_host_permissions.ClearPatterns(); - - ClearPref(extension_id, "granted_permissions"); - - service_->ReloadExtensions(); - - EXPECT_EQ(1u, service_->extensions()->size()); - extension = service_->extensions()->at(0); - - ASSERT_TRUE(prefs->GetExtensionState(extension_id) == Extension::ENABLED); - ASSERT_FALSE(prefs->DidExtensionEscalatePermissions(extension_id)); - - ASSERT_TRUE(prefs->GetGrantedPermissions(extension_id, - ¤t_full_access, - ¤t_api_permissions, - ¤t_host_permissions)); - - ASSERT_FALSE(current_full_access); - ASSERT_EQ(expected_api_permissions, current_api_permissions); - AssertEqualExtents(&expected_host_permissions, ¤t_host_permissions); + current_perms.reset(prefs->GetGrantedPermissions(extension_id)); + ASSERT_TRUE(current_perms.get()); + ASSERT_FALSE(current_perms->IsEmpty()); + ASSERT_FALSE(current_perms->HasEffectiveFullAccess()); + ASSERT_EQ(expected_api_permissions, current_perms->apis()); + AssertEqualExtents(expected_host_permissions, + current_perms->effective_hosts()); } // Test Packaging and installing an extension. @@ -1834,8 +1799,8 @@ TEST_F(ExtensionServiceTest, InstallAppsWithUnlimtedStorage) { ASSERT_EQ(1u, service_->extensions()->size()); const Extension* extension = service_->extensions()->at(0); const std::string id1 = extension->id(); - EXPECT_TRUE(extension->HasApiPermission( - Extension::kUnlimitedStoragePermission)); + EXPECT_TRUE(extension->HasAPIPermission( + ExtensionAPIPermission::kUnlimitedStorage)); EXPECT_TRUE(extension->web_extent().MatchesURL( extension->GetFullLaunchURL())); const GURL origin1(extension->GetFullLaunchURL().GetOrigin()); @@ -1848,8 +1813,8 @@ TEST_F(ExtensionServiceTest, InstallAppsWithUnlimtedStorage) { ASSERT_EQ(2u, service_->extensions()->size()); extension = service_->extensions()->at(1); const std::string id2 = extension->id(); - EXPECT_TRUE(extension->HasApiPermission( - Extension::kUnlimitedStoragePermission)); + EXPECT_TRUE(extension->HasAPIPermission( + ExtensionAPIPermission::kUnlimitedStorage)); EXPECT_TRUE(extension->web_extent().MatchesURL( extension->GetFullLaunchURL())); const GURL origin2(extension->GetFullLaunchURL().GetOrigin()); diff --git a/chrome/browser/extensions/extension_special_storage_policy.cc b/chrome/browser/extensions/extension_special_storage_policy.cc index fd3ccc4..9c95c1c 100644 --- a/chrome/browser/extensions/extension_special_storage_policy.cc +++ b/chrome/browser/extensions/extension_special_storage_policy.cc @@ -34,16 +34,18 @@ void ExtensionSpecialStoragePolicy::GrantRightsForExtension( const Extension* extension) { DCHECK(extension); if (!extension->is_hosted_app() && - !extension->HasApiPermission(Extension::kUnlimitedStoragePermission) && - !extension->HasApiPermission(Extension::kFileBrowserHandlerPermission)) { + !extension->HasAPIPermission( + ExtensionAPIPermission::kUnlimitedStorage) && + !extension->HasAPIPermission( + ExtensionAPIPermission::kFileBrowserHandler)) { return; } base::AutoLock locker(lock_); if (extension->is_hosted_app()) protected_apps_.Add(extension); - if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) + if (extension->HasAPIPermission(ExtensionAPIPermission::kUnlimitedStorage)) unlimited_extensions_.Add(extension); - if (extension->HasApiPermission(Extension::kFileBrowserHandlerPermission)) + if (extension->HasAPIPermission(ExtensionAPIPermission::kFileBrowserHandler)) file_handler_extensions_.Add(extension); } @@ -51,16 +53,18 @@ void ExtensionSpecialStoragePolicy::RevokeRightsForExtension( const Extension* extension) { DCHECK(extension); if (!extension->is_hosted_app() && - !extension->HasApiPermission(Extension::kUnlimitedStoragePermission) && - !extension->HasApiPermission(Extension::kFileBrowserHandlerPermission)) { + !extension->HasAPIPermission( + ExtensionAPIPermission::kUnlimitedStorage) && + !extension->HasAPIPermission( + ExtensionAPIPermission::kFileBrowserHandler)) { return; } base::AutoLock locker(lock_); if (extension->is_hosted_app()) protected_apps_.Remove(extension); - if (extension->HasApiPermission(Extension::kUnlimitedStoragePermission)) + if (extension->HasAPIPermission(ExtensionAPIPermission::kUnlimitedStorage)) unlimited_extensions_.Remove(extension); - if (extension->HasApiPermission(Extension::kFileBrowserHandlerPermission)) + if (extension->HasAPIPermission(ExtensionAPIPermission::kFileBrowserHandler)) file_handler_extensions_.Remove(extension); } diff --git a/chrome/browser/extensions/extension_tabs_module.cc b/chrome/browser/extensions/extension_tabs_module.cc index bfe2e70..dbe4b30 100644 --- a/chrome/browser/extensions/extension_tabs_module.cc +++ b/chrome/browser/extensions/extension_tabs_module.cc @@ -548,8 +548,8 @@ bool CreateWindowFunction::RunImpl() { window_type = Browser::TYPE_POPUP; app_name = GetExtension()->id(); } else if (type_str == keys::kWindowTypeValuePanel) { - if (GetExtension()->HasApiPermission( - Extension::kExperimentalPermission)) { + if (GetExtension()->HasAPIPermission( + ExtensionAPIPermission::kExperimental)) { window_type = Browser::TYPE_PANEL; app_name = GetExtension()->id(); } else { diff --git a/chrome/browser/geolocation/chrome_geolocation_permission_context.cc b/chrome/browser/geolocation/chrome_geolocation_permission_context.cc index 9b3bd60..8b8a455 100644 --- a/chrome/browser/geolocation/chrome_geolocation_permission_context.cc +++ b/chrome/browser/geolocation/chrome_geolocation_permission_context.cc @@ -517,7 +517,7 @@ void ChromeGeolocationPermissionContext::RequestGeolocationPermission( const Extension* ext = extensions->GetExtensionByURL(requesting_frame); if (!ext) ext = extensions->GetExtensionByWebExtent(requesting_frame); - if (ext && ext->HasApiPermission(Extension::kGeolocationPermission)) { + if (ext && ext->HasAPIPermission(ExtensionAPIPermission::kGeolocation)) { ExtensionProcessManager* epm = profile_->GetExtensionProcessManager(); RenderProcessHost* process = epm->GetExtensionProcess(requesting_frame); if (process && process->id() == render_process_id) { diff --git a/chrome/browser/profiles/profile.cc b/chrome/browser/profiles/profile.cc index 5c31834..bceb2a3 100644 --- a/chrome/browser/profiles/profile.cc +++ b/chrome/browser/profiles/profile.cc @@ -478,7 +478,8 @@ class OffTheRecordProfileImpl : public Profile, const Extension* installed_app = GetExtensionService()-> GetInstalledAppForRenderer(renderer_child_id); if (installed_app != NULL && installed_app->is_storage_isolated() && - installed_app->HasApiPermission(Extension::kExperimentalPermission)) { + installed_app->HasAPIPermission( + ExtensionAPIPermission::kExperimental)) { return GetRequestContextForIsolatedApp(installed_app->id()); } } diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 8718711..6b92e21 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -82,6 +82,7 @@ #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/json_pref_store.h" #include "chrome/common/pref_names.h" #include "chrome/common/render_messages.h" @@ -883,7 +884,8 @@ net::URLRequestContextGetter* ProfileImpl::GetRequestContextForRenderProcess( const Extension* installed_app = extension_service_-> GetInstalledAppForRenderer(renderer_child_id); if (installed_app != NULL && installed_app->is_storage_isolated() && - installed_app->HasApiPermission(Extension::kExperimentalPermission)) { + installed_app->HasAPIPermission( + ExtensionAPIPermission::kExperimental)) { return GetRequestContextForIsolatedApp(installed_app->id()); } } diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc index a43e231..ad64d4f 100644 --- a/chrome/browser/renderer_host/chrome_render_message_filter.cc +++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc @@ -466,7 +466,7 @@ void ChromeRenderMessageFilter::OnCanTriggerClipboardRead(const GURL& url, const Extension* extension = context->extension_info_map()->extensions().GetByURL(url); *allowed = extension && - extension->HasApiPermission(Extension::kClipboardReadPermission); + extension->HasAPIPermission(ExtensionAPIPermission::kClipboardRead); } void ChromeRenderMessageFilter::OnCanTriggerClipboardWrite(const GURL& url, @@ -479,7 +479,7 @@ void ChromeRenderMessageFilter::OnCanTriggerClipboardWrite(const GURL& url, context->extension_info_map()->extensions().GetByURL(url); *allowed = url.SchemeIs(chrome::kExtensionScheme) || (extension && - extension->HasApiPermission(Extension::kClipboardWritePermission)); + extension->HasAPIPermission(ExtensionAPIPermission::kClipboardWrite)); } void ChromeRenderMessageFilter::OnClearPredictorCache(int* result) { diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index d35e37b..d0eef11 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -176,6 +176,8 @@ 'common/extensions/extension_message_bundle.h', 'common/extensions/extension_messages.cc', 'common/extensions/extension_messages.h', + 'common/extensions/extension_permission_set.cc', + 'common/extensions/extension_permission_set.h', 'common/extensions/extension_resource.cc', 'common/extensions/extension_resource.h', 'common/extensions/extension_set.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 05e6d1a..2901662 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1889,6 +1889,7 @@ 'common/extensions/extension_localization_peer_unittest.cc', 'common/extensions/extension_manifests_unittest.cc', 'common/extensions/extension_message_bundle_unittest.cc', + 'common/extensions/extension_permission_set_unittest.cc', 'common/extensions/extension_resource_unittest.cc', 'common/extensions/extension_set_unittest.cc', 'common/extensions/extension_unittest.cc', diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 3a47efe..e98c5f0 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -98,50 +98,6 @@ bool IsBaseCrxKey(const std::string& key) { return false; } -// Constant used to represent an undefined l10n message id. -const int kUndefinedMessageId = -1; - -// Names of API modules that do not require a permission. -const char kBrowserActionModuleName[] = "browserAction"; -const char kBrowserActionsModuleName[] = "browserActions"; -const char kDevToolsModuleName[] = "devtools"; -const char kExtensionModuleName[] = "extension"; -const char kI18NModuleName[] = "i18n"; -const char kOmniboxModuleName[] = "omnibox"; -const char kPageActionModuleName[] = "pageAction"; -const char kPageActionsModuleName[] = "pageActions"; -const char kTestModuleName[] = "test"; -const char kTypesModuleName[] = "types"; - -// Names of modules that can be used without listing it in the permissions -// section of the manifest. -const char* kNonPermissionModuleNames[] = { - kBrowserActionModuleName, - kBrowserActionsModuleName, - kDevToolsModuleName, - kExtensionModuleName, - kI18NModuleName, - kOmniboxModuleName, - kPageActionModuleName, - kPageActionsModuleName, - kTestModuleName, - kTypesModuleName -}; -const size_t kNumNonPermissionModuleNames = - arraysize(kNonPermissionModuleNames); - -// Names of functions (within modules requiring permissions) that can be used -// without asking for the module permission. In other words, functions you can -// use with no permissions specified. -const char* kNonPermissionFunctionNames[] = { - "tabs.create", - "tabs.onRemoved", - "tabs.remove", - "tabs.update", -}; -const size_t kNumNonPermissionFunctionNames = - arraysize(kNonPermissionFunctionNames); - // A singleton object containing global data needed by the extension objects. class ExtensionConfig { public: @@ -149,25 +105,14 @@ class ExtensionConfig { return Singleton<ExtensionConfig>::get(); } - Extension::PermissionMessage::MessageId GetPermissionMessageId( - const std::string& permission) { - return Extension::kPermissions[permission_map_[permission]].message_id; - } - Extension::ScriptingWhitelist* whitelist() { return &scripting_whitelist_; } private: friend struct DefaultSingletonTraits<ExtensionConfig>; - ExtensionConfig() { - for (size_t i = 0; i < Extension::kNumPermissions; ++i) - permission_map_[Extension::kPermissions[i].name] = i; - }; - + ExtensionConfig() { } ~ExtensionConfig() { } - std::map<const std::string, size_t> permission_map_; - // A whitelist of extensions that can script anywhere. Do not add to this // list (except in tests) without consulting the Extensions team first. // Note: Component extensions have this right implicitly and do not need to be @@ -175,10 +120,6 @@ class ExtensionConfig { Extension::ScriptingWhitelist scripting_whitelist_; }; -// Aliased to kTabPermission for purposes of API checks, but not allowed -// in the permissions field of the manifest. -static const char kWindowPermission[] = "windows"; - // Rank extension locations in a way that allows // Extension::GetHigherPriorityLocation() to compare locations. // An extension installed from two locations will have the location @@ -264,90 +205,6 @@ const int Extension::kPageActionIconMaxSize = 19; const int Extension::kBrowserActionIconMaxSize = 19; const int Extension::kSidebarIconMaxSize = 16; -// Explicit permissions -- permission declaration required. -const char Extension::kBackgroundPermission[] = "background"; -const char Extension::kBookmarkPermission[] = "bookmarks"; -const char Extension::kClipboardReadPermission[] = "clipboardRead"; -const char Extension::kClipboardWritePermission[] = "clipboardWrite"; -const char Extension::kContextMenusPermission[] = "contextMenus"; -const char Extension::kContentSettingsPermission[] = "contentSettings"; -const char Extension::kCookiePermission[] = "cookies"; -const char Extension::kChromePrivatePermission[] = "chromePrivate"; -const char Extension::kChromeosInfoPrivatePermission[] = "chromeosInfoPrivate"; -const char Extension::kDebuggerPermission[] = "debugger"; -const char Extension::kExperimentalPermission[] = "experimental"; -const char Extension::kFileBrowserHandlerPermission[] = "fileBrowserHandler"; -const char Extension::kFileBrowserPrivatePermission[] = "fileBrowserPrivate"; -const char Extension::kGeolocationPermission[] = "geolocation"; -const char Extension::kHistoryPermission[] = "history"; -const char Extension::kIdlePermission[] = "idle"; -const char Extension::kManagementPermission[] = "management"; -const char Extension::kMediaPlayerPrivatePermission[] = "mediaPlayerPrivate"; -const char Extension::kNotificationPermission[] = "notifications"; -const char Extension::kProxyPermission[] = "proxy"; -const char Extension::kTabPermission[] = "tabs"; -const char Extension::kUnlimitedStoragePermission[] = "unlimitedStorage"; -const char Extension::kWebstorePrivatePermission[] = "webstorePrivate"; -const char Extension::kWebSocketProxyPrivatePermission[] = - "webSocketProxyPrivate"; - -// In general, all permissions should have an install message. -// See ExtensionsTest.PermissionMessages for an explanation of each -// exception. -const Extension::Permission Extension::kPermissions[] = { - { kBackgroundPermission, PermissionMessage::ID_NONE }, - { kBookmarkPermission, PermissionMessage::ID_BOOKMARKS }, - { kChromePrivatePermission, PermissionMessage::ID_NONE }, - { kChromeosInfoPrivatePermission, PermissionMessage::ID_NONE }, - { kClipboardReadPermission, PermissionMessage::ID_CLIPBOARD }, - { kClipboardWritePermission, PermissionMessage::ID_NONE }, - { kContentSettingsPermission, PermissionMessage::ID_NONE }, - { kContextMenusPermission, PermissionMessage::ID_NONE }, - { kCookiePermission, PermissionMessage::ID_NONE }, - { kDebuggerPermission, PermissionMessage::ID_DEBUGGER }, - { kExperimentalPermission, PermissionMessage::ID_NONE }, - { kFileBrowserHandlerPermission, PermissionMessage::ID_NONE }, - { kFileBrowserPrivatePermission, PermissionMessage::ID_NONE }, - { kGeolocationPermission, PermissionMessage::ID_GEOLOCATION }, - { kHistoryPermission, PermissionMessage::ID_BROWSING_HISTORY }, - { kIdlePermission, PermissionMessage::ID_NONE }, - { kManagementPermission, PermissionMessage::ID_MANAGEMENT }, - { kMediaPlayerPrivatePermission, PermissionMessage::ID_NONE }, - { kNotificationPermission, PermissionMessage::ID_NONE }, - { kProxyPermission, PermissionMessage::ID_NONE }, - { kTabPermission, PermissionMessage::ID_TABS }, - { kUnlimitedStoragePermission, PermissionMessage::ID_NONE }, - { kWebSocketProxyPrivatePermission, PermissionMessage::ID_NONE }, - { kWebstorePrivatePermission, PermissionMessage::ID_NONE }, -}; -const size_t Extension::kNumPermissions = arraysize(Extension::kPermissions); - -const char* const Extension::kHostedAppPermissionNames[] = { - Extension::kBackgroundPermission, - Extension::kChromePrivatePermission, - Extension::kClipboardReadPermission, - Extension::kClipboardWritePermission, - Extension::kExperimentalPermission, - Extension::kGeolocationPermission, - Extension::kNotificationPermission, - Extension::kUnlimitedStoragePermission, - Extension::kWebstorePrivatePermission, -}; -const size_t Extension::kNumHostedAppPermissions = - arraysize(Extension::kHostedAppPermissionNames); - -const char* const Extension::kComponentPrivatePermissionNames[] = { - Extension::kFileBrowserPrivatePermission, - Extension::kWebstorePrivatePermission, - Extension::kMediaPlayerPrivatePermission, - Extension::kChromeosInfoPrivatePermission, -}; -const size_t Extension::kNumComponentPrivatePermissions = - arraysize(Extension::kComponentPrivatePermissionNames); - -// We purposefully don't put this into kPermissionNames. -const char Extension::kOldUnlimitedStoragePermission[] = "unlimited_storage"; - const int Extension::kValidWebExtentSchemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS; @@ -364,84 +221,6 @@ Extension::InputComponentInfo::InputComponentInfo() Extension::InputComponentInfo::~InputComponentInfo() {} // -// PermissionMessage -// - -// static -Extension::PermissionMessage Extension::PermissionMessage::CreateFromMessageId( - Extension::PermissionMessage::MessageId message_id) { - DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); - if (message_id <= ID_NONE) - return PermissionMessage(message_id, string16()); - - string16 message = l10n_util::GetStringUTF16(kMessageIds[message_id]); - return PermissionMessage(message_id, message); -} - -// static -Extension::PermissionMessage Extension::PermissionMessage::CreateFromHostList( - const std::vector<std::string>& hosts) { - CHECK(hosts.size() > 0); - - MessageId message_id; - string16 message; - switch (hosts.size()) { - case 1: - message_id = ID_HOSTS_1; - message = l10n_util::GetStringFUTF16(kMessageIds[message_id], - UTF8ToUTF16(hosts[0])); - break; - case 2: - message_id = ID_HOSTS_2; - message = l10n_util::GetStringFUTF16(kMessageIds[message_id], - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1])); - break; - case 3: - message_id = ID_HOSTS_3; - message = l10n_util::GetStringFUTF16(kMessageIds[message_id], - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1]), - UTF8ToUTF16(hosts[2])); - break; - default: - message_id = ID_HOSTS_4_OR_MORE; - message = l10n_util::GetStringFUTF16( - kMessageIds[message_id], - UTF8ToUTF16(hosts[0]), - UTF8ToUTF16(hosts[1]), - base::IntToString16(hosts.size() - 2)); - break; - } - - return PermissionMessage(message_id, message); -} - -Extension::PermissionMessage::PermissionMessage( - Extension::PermissionMessage::MessageId message_id, string16 message) - : message_id_(message_id), - message_(message) { -} - -const int Extension::PermissionMessage::kMessageIds[] = { - kUndefinedMessageId, // "unknown" - kUndefinedMessageId, // "none" - IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, - IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION, - IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, - IDS_EXTENSION_PROMPT_WARNING_TABS, - IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, - IDS_EXTENSION_PROMPT_WARNING_DEBUGGER, - IDS_EXTENSION_PROMPT_WARNING_1_HOST, - IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS, - IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS, - IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD -}; - -// // Extension // @@ -491,145 +270,12 @@ Extension::Location Extension::GetHigherPriorityLocation( return (loc1_rank > loc2_rank ? loc1 : loc2 ); } -// static -Extension::PermissionMessage::MessageId Extension::GetPermissionMessageId( - const std::string& permission) { - return ExtensionConfig::GetInstance()->GetPermissionMessageId(permission); -} - -Extension::PermissionMessages Extension::GetPermissionMessages() const { - PermissionMessages messages; - if (!plugins().empty()) { - messages.push_back(PermissionMessage::CreateFromMessageId( - PermissionMessage::ID_FULL_ACCESS)); - return messages; - } - - if (HasEffectiveAccessToAllHosts()) { - messages.push_back(PermissionMessage::CreateFromMessageId( - PermissionMessage::ID_HOSTS_ALL)); - } else { - std::vector<std::string> hosts = GetDistinctHostsForDisplay( - GetEffectiveHostPermissions().patterns()); - if (!hosts.empty()) - messages.push_back(PermissionMessage::CreateFromHostList(hosts)); - } - - std::set<PermissionMessage> simple_msgs = GetSimplePermissionMessages(); - messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); - - return messages; +ExtensionPermissionMessages Extension::GetPermissionMessages() const { + return permission_set_->GetPermissionMessages(); } std::vector<string16> Extension::GetPermissionMessageStrings() const { - std::vector<string16> messages; - PermissionMessages permissions = GetPermissionMessages(); - for (PermissionMessages::const_iterator i = permissions.begin(); - i != permissions.end(); ++i) - messages.push_back(i->message()); - return messages; -} - -std::set<Extension::PermissionMessage> - Extension::GetSimplePermissionMessages() const { - std::set<PermissionMessage> messages; - std::set<std::string>::const_iterator i; - for (i = api_permissions().begin(); i != api_permissions().end(); ++i) { - PermissionMessage::MessageId message_id = GetPermissionMessageId(*i); - DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); - if (message_id > PermissionMessage::ID_NONE) - messages.insert(PermissionMessage::CreateFromMessageId(message_id)); - } - return messages; -} - -// static -std::vector<std::string> Extension::GetDistinctHostsForDisplay( - const URLPatternList& list) { - return GetDistinctHosts(list, true); -} - -// static -bool Extension::IsElevatedHostList( - const URLPatternList& old_list, const URLPatternList& new_list) { - // TODO(jstritar): This is overly conservative with respect to subdomains. - // For example, going from *.google.com to www.google.com will be - // considered an elevation, even though it is not (http://crbug.com/65337). - - std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false); - std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false); - - 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_hosts_only; - - std::set_difference(new_hosts_set.begin(), new_hosts_set.end(), - old_hosts_set.begin(), old_hosts_set.end(), - std::inserter(new_hosts_only, new_hosts_only.begin())); - - return !new_hosts_only.empty(); -} - -// Helper for GetDistinctHosts(): com > net > org > everything else. -static bool RcdBetterThan(const std::string& a, const std::string& b) { - if (a == b) - return false; - if (a == "com") - return true; - if (a == "net") - return b != "com"; - if (a == "org") - return b != "com" && b != "net"; - return false; -} - -// static -std::vector<std::string> Extension::GetDistinctHosts( - const URLPatternList& host_patterns, - bool include_rcd) { - // Use a vector to preserve order (also faster than a map on small sets). - // Each item is a host split into two parts: host without RCDs and - // current best RCD. - typedef std::vector<std::pair<std::string, std::string> > HostVector; - HostVector hosts_best_rcd; - for (size_t i = 0; i < host_patterns.size(); ++i) { - std::string host = host_patterns[i].host(); - - // Add the subdomain wildcard back to the host, if necessary. - if (host_patterns[i].match_subdomains()) - host = "*." + host; - - // If the host has an RCD, split it off so we can detect duplicates. - std::string rcd; - size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( - host, false); - if (reg_len && reg_len != std::string::npos) { - if (include_rcd) // else leave rcd empty - rcd = host.substr(host.size() - reg_len); - host = host.substr(0, host.size() - reg_len); - } - - // Check if we've already seen this host. - HostVector::iterator it = hosts_best_rcd.begin(); - for (; it != hosts_best_rcd.end(); ++it) { - if (it->first == host) - break; - } - // If this host was found, replace the RCD if this one is better. - if (it != hosts_best_rcd.end()) { - if (include_rcd && RcdBetterThan(rcd, it->second)) - it->second = rcd; - } else { // Previously unseen host, append it. - hosts_best_rcd.push_back(std::make_pair(host, rcd)); - } - } - - // Build up the final vector by concatenating hosts and RCDs. - std::vector<std::string> distinct_hosts; - for (HostVector::iterator it = hosts_best_rcd.begin(); - it != hosts_best_rcd.end(); ++it) - distinct_hosts.push_back(it->first + it->second); - return distinct_hosts; + return permission_set_->GetWarningMessages(); } FilePath Extension::MaybeNormalizePath(const FilePath& path) { @@ -648,16 +294,6 @@ FilePath Extension::MaybeNormalizePath(const FilePath& path) { #endif } -// static -bool Extension::IsHostedAppPermission(const std::string& str) { - for (size_t i = 0; i < Extension::kNumHostedAppPermissions; ++i) { - if (str == Extension::kHostedAppPermissionNames[i]) { - return true; - } - } - return false; -} - const std::string Extension::VersionString() const { return version()->GetString(); } @@ -1624,55 +1260,6 @@ bool Extension::FormatPEMForFileOutput(const std::string& input, } // static -bool Extension::IsPrivilegeIncrease(const bool granted_full_access, - const std::set<std::string>& granted_apis, - const URLPatternSet& granted_extent, - const Extension* new_extension) { - // 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->HasFullPermissions()) - return true; - - // 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; - - const URLPatternSet new_extent = - new_extension->GetEffectiveHostPermissions(); - - if (IsElevatedHostList(granted_extent.patterns(), new_extent.patterns())) - return true; - } - - 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.begin())); - - // 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) { - DCHECK_GT(PermissionMessage::ID_NONE, PermissionMessage::ID_UNKNOWN); - if (GetPermissionMessageId(*i) > PermissionMessage::ID_NONE) - new_api_count++; - } - - if (new_api_count) - return true; - - return false; -} - -// static void Extension::DecodeIcon(const Extension* extension, Icons icon_size, scoped_ptr<SkBitmap>* result) { @@ -1739,6 +1326,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, (flags & STRICT_ERROR_CHECKS ? URLPattern::PARSE_STRICT : URLPattern::PARSE_LENIENT); + // Initialize permissions with an empty, default permission set. + permission_set_.reset(new ExtensionPermissionSet()); + if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; if (!source.GetString(keys::kPublicKey, @@ -2257,6 +1847,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, } } + ExtensionAPIPermissionSet api_permissions; + URLPatternSet host_permissions; + // Initialize the permissions (optional). if (source.HasKey(keys::kPermissions)) { ListValue* permissions = NULL; @@ -2274,10 +1867,13 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, return false; } + ExtensionAPIPermission* permission = + ExtensionPermissionsInfo::GetInstance()->GetByName(permission_str); + // Only COMPONENT extensions can use private APIs. // TODO(asargent) - We want a more general purpose mechanism for this, // and better error messages. (http://crbug.com/54013) - if (!IsComponentOnlyPermission(permission_str) + if (!IsComponentOnlyPermission(permission) #ifndef NDEBUG && !CommandLine::ForCurrentProcess()->HasSwitch( switches::kExposePrivateExtensionApi) @@ -2286,31 +1882,27 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, continue; } - // Remap the old unlimited storage permission name. - if (permission_str == kOldUnlimitedStoragePermission) - permission_str = kUnlimitedStoragePermission; - if (web_extent().is_empty() || location() == Extension::COMPONENT) { // Check if it's a module permission. If so, enable that permission. - if (IsAPIPermission(permission_str)) { + if (permission != NULL) { // Only allow the experimental API permission if the command line // flag is present, or if the extension is a component of Chrome. - if (IsDisallowedExperimentalPermission(permission_str) && + if (IsDisallowedExperimentalPermission(permission->id()) && location() != Extension::COMPONENT) { *error = errors::kExperimentalFlagRequired; return false; } - api_permissions_.insert(permission_str); + api_permissions.insert(permission->id()); continue; } } else { // Hosted apps only get access to a subset of the valid permissions. - if (IsHostedAppPermission(permission_str)) { - if (IsDisallowedExperimentalPermission(permission_str)) { + if (permission != NULL && permission->is_hosted_app()) { + if (IsDisallowedExperimentalPermission(permission->id())) { *error = errors::kExperimentalFlagRequired; return false; } - api_permissions_.insert(permission_str); + api_permissions.insert(permission->id()); continue; } } @@ -2340,7 +1932,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, pattern.valid_schemes() & ~URLPattern::SCHEME_FILE); } - host_permissions_.push_back(pattern); + host_permissions.AddPattern(pattern); } // If it's not a host permission, then it's probably an unknown API @@ -2365,8 +1957,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, if (is_hosted_app()) { // Make sure "background" permission is set. - if (api_permissions_.find(kBackgroundPermission) == - api_permissions_.end()) { + if (!api_permissions.count(ExtensionAPIPermission::kBackground)) { *error = errors::kBackgroundPermissionNeeded; return false; } @@ -2605,7 +2196,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, *error = errors::kInvalidDevToolsPage; return false; } - if (!HasApiPermission(Extension::kExperimentalPermission)) { + if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) { *error = errors::kDevToolsExperimental; return false; } @@ -2619,7 +2210,7 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, *error = errors::kInvalidSidebar; return false; } - if (!HasApiPermission(Extension::kExperimentalPermission)) { + if (!api_permissions.count(ExtensionAPIPermission::kExperimental)) { *error = errors::kSidebarExperimental; return false; } @@ -2705,7 +2296,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, int flags, return false; } - InitEffectiveHostPermissions(); + permission_set_.reset( + new ExtensionPermissionSet(this, api_permissions, host_permissions)); // Although |source| is passed in as a const, it's still possible to modify // it. This is dangerous since the utility process re-uses |source| after @@ -2896,91 +2488,37 @@ bool Extension::CanSpecifyHostPermission(const URLPattern& pattern) const { return true; } -// static -bool Extension::HasApiPermission( - const std::set<std::string>& api_permissions, - const std::string& function_name) { - std::string permission_name = function_name; - - for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { - if (permission_name == kNonPermissionFunctionNames[i]) - return true; - } - - // See if this is a function or event name first and strip out the package. - // Functions will be of the form package.function - // Events will be of the form package/id or package.optional.stuff - size_t separator = function_name.find_first_of("./"); - if (separator != std::string::npos) - permission_name = function_name.substr(0, separator); - - // windows and tabs are the same permission. - if (permission_name == kWindowPermission) - permission_name = Extension::kTabPermission; - - if (api_permissions.count(permission_name)) - return true; - - for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) { - if (permission_name == kNonPermissionModuleNames[i]) { - return true; - } - } - - return false; +bool Extension::HasAPIPermission( + ExtensionAPIPermission::ID permission) const { + return permission_set()->HasAPIPermission(permission); } -bool Extension::HasHostPermission(const GURL& url) const { - for (URLPatternList::const_iterator host = host_permissions().begin(); - host != host_permissions().end(); ++host) { - // Non-component extensions can only access chrome://favicon and no other - // chrome:// scheme urls. - if (url.SchemeIs(chrome::kChromeUIScheme) && - url.host() != chrome::kChromeUIFaviconHost && - location() != Extension::COMPONENT) - return false; - - if (host->MatchesURL(url)) - return true; - } - return false; +bool Extension::HasAPIPermission( + const std::string& function_name) const { + return permission_set()->HasAccessToFunction(function_name); } -void Extension::InitEffectiveHostPermissions() { - // Some APIs effectively grant access to every site. New ones should be - // added here. (I'm looking at you, network API) - if (HasApiPermission(api_permissions_, kProxyPermission) || - !devtools_url_.is_empty()) { - URLPattern all_urls(URLPattern::SCHEME_ALL); - all_urls.set_match_all_urls(true); - effective_host_permissions_.AddPattern(all_urls); - return; - } - - for (URLPatternList::const_iterator host = host_permissions().begin(); - host != host_permissions().end(); ++host) - effective_host_permissions_.AddPattern(*host); +const URLPatternSet& Extension::GetEffectiveHostPermissions() const { + return permission_set()->effective_hosts(); +} - for (UserScriptList::const_iterator content_script = - content_scripts().begin(); - content_script != content_scripts().end(); ++content_script) { - URLPatternList::const_iterator pattern = - content_script->url_patterns().begin(); - for (; pattern != content_script->url_patterns().end(); ++pattern) - effective_host_permissions_.AddPattern(*pattern); - } +bool Extension::HasHostPermission(const GURL& url) const { + if (url.SchemeIs(chrome::kChromeUIScheme) && + url.host() != chrome::kChromeUIFaviconHost && + location() != Extension::COMPONENT) + return false; + return permission_set()->HasExplicitAccessToOrigin(url); } -bool Extension::IsComponentOnlyPermission(const std::string& permission) const { +bool Extension::IsComponentOnlyPermission( + const ExtensionAPIPermission* api) const { if (location() == Extension::COMPONENT) return true; - // Non-component extensions are not allowed to access private apis. - for (size_t i = 0; i < Extension::kNumComponentPrivatePermissions; ++i) { - if (permission == Extension::kComponentPrivatePermissionNames[i]) - return false; - } - return true; + if (api == NULL) + return true; + + return !api->is_component_only(); } bool Extension::HasMultipleUISurfaces() const { @@ -3025,10 +2563,8 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, // Otherwise, see if this extension has permission to execute script // programmatically on pages. - for (size_t i = 0; i < host_permissions_.size(); ++i) { - if (host_permissions_[i].MatchesURL(page_url)) - return true; - } + if (permission_set()->HasExplicitAccessToOrigin(page_url)) + return true; if (error) { *error = ExtensionErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, @@ -3038,28 +2574,12 @@ bool Extension::CanExecuteScriptOnPage(const GURL& page_url, return false; } -// static -bool Extension::HasEffectiveAccessToAllHosts( - const URLPatternSet& effective_host_permissions, - const std::set<std::string>& api_permissions) { - const URLPatternList patterns = effective_host_permissions.patterns(); - for (URLPatternList::const_iterator host = patterns.begin(); - host != patterns.end(); ++host) { - if (host->match_all_urls() || - (host->match_subdomains() && host->host().empty())) - return true; - } - - return false; -} - bool Extension::HasEffectiveAccessToAllHosts() const { - return HasEffectiveAccessToAllHosts(GetEffectiveHostPermissions(), - api_permissions()); + return permission_set_->HasEffectiveAccessToAllHosts(); } bool Extension::HasFullPermissions() const { - return !plugins().empty(); + return permission_set_->HasEffectiveFullAccess(); } bool Extension::ShowConfigureContextMenus() const { @@ -3071,21 +2591,12 @@ bool Extension::ShowConfigureContextMenus() const { } bool Extension::IsDisallowedExperimentalPermission( - const std::string& permission_str) const { - return permission_str == Extension::kExperimentalPermission && + ExtensionAPIPermission::ID permission) const { + return permission == ExtensionAPIPermission::kExperimental && !CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableExperimentalExtensionApis); } -bool Extension::IsAPIPermission(const std::string& str) const { - for (size_t i = 0; i < Extension::kNumPermissions; ++i) { - if (str == Extension::kPermissions[i].name) { - return true; - } - } - return false; -} - bool Extension::CanExecuteScriptEverywhere() const { if (location() == Extension::COMPONENT #ifndef NDEBUG @@ -3161,7 +2672,8 @@ ExtensionInfo::~ExtensionInfo() {} UninstalledExtensionInfo::UninstalledExtensionInfo( const Extension& extension) : extension_id(extension.id()), - extension_api_permissions(extension.api_permissions()), + extension_api_permissions( + extension.permission_set()->GetAPIsAsStrings()), extension_type(extension.GetType()), update_url(extension.update_url()) {} diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 6624268..7e584d7 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -18,6 +18,7 @@ #include "base/memory/scoped_ptr.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_icon_set.h" +#include "chrome/common/extensions/extension_permission_set.h" #include "chrome/common/extensions/user_script.h" #include "chrome/common/extensions/url_pattern.h" #include "chrome/common/extensions/url_pattern_set.h" @@ -142,80 +143,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { std::string gender; }; - // When prompting the user to install or approve permissions, we display - // messages describing the effects of the permissions and not the permissions - // themselves. Each PermissionMessage represents one of the messages that is - // shown to the user. - class PermissionMessage { - public: - // Do not reorder or add new enumerations in this list. If you need to add a - // new enum, add it just prior to ID_ENUM_BOUNDARY and enter its l10n - // message in kMessageIds. - enum MessageId { - ID_UNKNOWN, - ID_NONE, - ID_BOOKMARKS, - ID_GEOLOCATION, - ID_BROWSING_HISTORY, - ID_TABS, - ID_MANAGEMENT, - ID_DEBUGGER, - ID_HOSTS_1, - ID_HOSTS_2, - ID_HOSTS_3, - ID_HOSTS_4_OR_MORE, - ID_HOSTS_ALL, - ID_FULL_ACCESS, - ID_CLIPBOARD, - ID_ENUM_BOUNDARY - }; - - // Creates a permission message with the given |message_id| and initializes - // its message to the appropriate value. - static PermissionMessage CreateFromMessageId(MessageId message_id); - - // Creates the corresponding permission message for a list of hosts. This - // method exists because the hosts are presented as one message that depends - // on what and how many hosts there are. - static PermissionMessage CreateFromHostList( - const std::vector<std::string>& hosts); - - // Gets the id of the permission message, which can be used in UMA - // histograms. - MessageId message_id() const { return message_id_; } - - // Gets a localized message describing this permission. Please note that - // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN. - const string16& message() const { return message_; } - - // Comparator to work with std::set. - bool operator<(const PermissionMessage& that) const { - return message_id_ < that.message_id_; - } - - private: - PermissionMessage(MessageId message_id, string16 message_); - - // The index of the id in the array is its enum value. The first two values - // are non-existent message ids to act as placeholders for "unknown" and - // "none". - // Note: Do not change the order of the items in this list since they - // are used in a histogram. The order must match the MessageId order. - static const int kMessageIds[]; - - MessageId message_id_; - string16 message_; - }; - - typedef std::vector<PermissionMessage> PermissionMessages; - - // A permission is defined by its |name| (what is used in the manifest), - // and the |message_id| that's used by install/update UI. - struct Permission { - const char* const name; - const PermissionMessage::MessageId message_id; - }; - enum InitFromValueFlags { NO_FLAGS = 0, @@ -256,38 +183,15 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // its install source should be set to GetHigherPriorityLocation(A, B). static Location GetHigherPriorityLocation(Location loc1, Location loc2); - // Get's the install message id for |permission|. Returns - // MessageId::TYPE_NONE if none exists. - static PermissionMessage::MessageId GetPermissionMessageId( - const std::string& permission); - // Returns the full list of permission messages that this extension // should display at install time. - PermissionMessages GetPermissionMessages() const; + ExtensionPermissionMessages GetPermissionMessages() const; // Returns the full list of permission messages that this extension // should display at install time. The messages are returned as strings // for convenience. std::vector<string16> GetPermissionMessageStrings() const; - // Returns the distinct hosts that should be displayed in the install UI - // for the URL patterns |list|. This discards some of the detail that is - // present in the manifest to make it as easy as possible to process by - // users. In particular we disregard the scheme and path components of - // URLPatterns and de-dupe the result, which includes filtering out common - // hosts with differing RCDs (aka Registry Controlled Domains, most of which - // are Top Level Domains but also include exceptions like co.uk). - // NOTE: when de-duping hosts the preferred RCD will be returned, given this - // order of preference: .com, .net, .org, first in list. - static std::vector<std::string> GetDistinctHostsForDisplay( - const URLPatternList& list); - - // Compares two URLPatternLists for security equality by returning whether - // the URL patterns in |new_list| contain additional distinct hosts compared - // to |old_list|. - static bool IsElevatedHostList( - const URLPatternList& old_list, const URLPatternList& new_list); - // Icon sizes used by the extension system. static const int kIconSizes[]; @@ -296,56 +200,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static const int kBrowserActionIconMaxSize; static const int kSidebarIconMaxSize; - // Each permission is a module that the extension is permitted to use. - // - // NOTE: To add a new permission, define it here, and add an entry to - // Extension::kPermissions. - static const char kBackgroundPermission[]; - static const char kBookmarkPermission[]; - static const char kClipboardReadPermission[]; - static const char kClipboardWritePermission[]; - static const char kContentSettingsPermission[]; - static const char kContextMenusPermission[]; - static const char kCookiePermission[]; - static const char kChromePrivatePermission[]; - static const char kChromeosInfoPrivatePermission[]; - static const char kDebuggerPermission[]; - static const char kExperimentalPermission[]; - static const char kFileBrowserHandlerPermission[]; - static const char kFileBrowserPrivatePermission[]; - static const char kGeolocationPermission[]; - static const char kHistoryPermission[]; - static const char kIdlePermission[]; - static const char kManagementPermission[]; - static const char kMediaPlayerPrivatePermission[]; - static const char kNotificationPermission[]; - static const char kProxyPermission[]; - static const char kTabPermission[]; - static const char kUnlimitedStoragePermission[]; - static const char kWebstorePrivatePermission[]; - static const char kWebSocketProxyPrivatePermission[]; - - static const Permission kPermissions[]; - static const size_t kNumPermissions; - static const char* const kHostedAppPermissionNames[]; - static const size_t kNumHostedAppPermissions; - static const char* const kComponentPrivatePermissionNames[]; - static const size_t kNumComponentPrivatePermissions; - - // The old name for the unlimited storage permission, which is deprecated but - // still accepted as meaning the same thing as kUnlimitedStoragePermission. - static const char kOldUnlimitedStoragePermission[]; - // Valid schemes for web extent URLPatterns. static const int kValidWebExtentSchemes; // Valid schemes for host permission URLPatterns. static const int kValidHostPermissionSchemes; - // Returns true if the string is one of the known hosted app permissions (see - // kHostedAppPermissionNames). - static bool IsHostedAppPermission(const std::string& permission); - // The name of the manifest inside an extension. static const FilePath::CharType kManifestFilename[]; @@ -455,14 +315,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { std::string* output, bool is_public); - // Determine whether |new_extension| has increased privileges compared to - // 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 URLPatternSet& granted_extent, - const Extension* new_extension); - // Given an extension and icon size, read it if present and decode it into // result. In the browser process, this will DCHECK if not called on the // file thread. To easily load extension images on the UI thread, see @@ -497,24 +349,10 @@ class Extension : public base::RefCountedThreadSafe<Extension> { static void SetScriptingWhitelist(const ScriptingWhitelist& whitelist); static const ScriptingWhitelist* GetScriptingWhitelist(); - // Returns true if the extension has the specified API permission. - 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 URLPatternSet& effective_host_permissions, - const std::set<std::string>& api_permissions); + bool HasAPIPermission(ExtensionAPIPermission::ID permission) const; + bool HasAPIPermission(const std::string& function_name) const; - bool HasApiPermission(const std::string& function_name) const { - return HasApiPermission(this->api_permissions(), function_name); - } - - const URLPatternSet& GetEffectiveHostPermissions() const { - return effective_host_permissions_; - } + const URLPatternSet& GetEffectiveHostPermissions() const; // Whether or not the extension is allowed permission for a URL pattern from // the manifest. http, https, and chrome://favicon/ is allowed for all @@ -630,10 +468,9 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const GURL& options_url() const { return options_url_; } const GURL& devtools_url() const { return devtools_url_; } const std::vector<GURL>& toolstrips() const { return toolstrips_; } - const std::set<std::string>& api_permissions() const { - return api_permissions_; + const ExtensionPermissionSet* permission_set() const { + return permission_set_.get(); } - const URLPatternList& host_permissions() const { return host_permissions_; } const GURL& update_url() const { return update_url_; } const ExtensionIconSet& icons() const { return icons_; } const DictionaryValue* manifest_value() const { @@ -766,10 +603,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { ExtensionSidebarDefaults* LoadExtensionSidebarDefaults( const DictionaryValue* sidebar, std::string* error); - // Calculates the effective host permissions from the permissions and content - // script petterns. - void InitEffectiveHostPermissions(); - // Returns true if the extension has more than one "UI surface". For example, // an extension that has a browser action and a page action. bool HasMultipleUISurfaces() const; @@ -780,21 +613,12 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Only allow the experimental API permission if the command line // flag is present. - bool IsDisallowedExperimentalPermission(const std::string& permission) const; - - // Returns true if the string is one of the known api permissions (see - // kPermissions). - bool IsAPIPermission(const std::string& permission) const; + bool IsDisallowedExperimentalPermission( + ExtensionAPIPermission::ID permission) const; // Returns true if this is a component, or we are not attempting to access a // component-private permission. - bool IsComponentOnlyPermission(const std::string& permission) const; - - // The set of unique API install messages that the extension has. - // NOTE: This only includes messages related to permissions declared in the - // "permissions" key in the manifest. Permissions implied from other features - // of the manifest, like plugins and content scripts are not included. - std::set<PermissionMessage> GetSimplePermissionMessages() const; + bool IsComponentOnlyPermission(const ExtensionAPIPermission* api) const; // Cached images for this extension. This should only be touched on the UI // thread. @@ -825,15 +649,8 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Defines the set of URLs in the extension's web content. URLPatternSet extent_; - // The set of host permissions that the extension effectively has access to, - // which is a merge of host_permissions_ and all of the match patterns in - // any content scripts the extension has. This is used to determine which - // URLs have the ability to load an extension's resources via embedded - // chrome-extension: URLs (see extension_protocols.cc). - URLPatternSet effective_host_permissions_; - - // The set of module-level APIs this extension can use. - std::set<std::string> api_permissions_; + // The set of permissions that the extension effectively has access to. + scoped_ptr<ExtensionPermissionSet> permission_set_; // The icons for the extension. ExtensionIconSet icons_; @@ -909,9 +726,6 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Whether the extension is a theme. bool is_theme_; - // The sites this extension has permission to talk to (using XHR, etc). - URLPatternList host_permissions_; - // The homepage for this extension. Useful if it is not hosted by Google and // therefore does not have a Gallery URL. GURL homepage_url_; diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index e2a2d76..7a7122a 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -296,8 +296,8 @@ TEST_F(ExtensionManifestTest, UpdateUrls) { TEST_F(ExtensionManifestTest, OldUnlimitedStoragePermission) { scoped_refptr<Extension> extension = LoadStrictAndExpectSuccess( "old_unlimited_storage.json"); - EXPECT_TRUE(extension->HasApiPermission( - Extension::kUnlimitedStoragePermission)); + EXPECT_TRUE(extension->HasAPIPermission( + ExtensionAPIPermission::kUnlimitedStorage)); } TEST_F(ExtensionManifestTest, ValidApp) { @@ -620,14 +620,18 @@ TEST_F(ExtensionManifestTest, AllowUnrecognizedPermissions) { ListValue *permissions = new ListValue(); manifest->Set(keys::kPermissions, permissions); - for (size_t i = 0; i < Extension::kNumPermissions; i++) { - const char* name = Extension::kPermissions[i].name; + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet api_perms = info->GetAll(); + for (ExtensionAPIPermissionSet::iterator i = api_perms.begin(); + i != api_perms.end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + const char* name = permission->name(); StringValue* p = new StringValue(name); permissions->Clear(); permissions->Append(p); std::string message_name = base::StringPrintf("permission-%s", name); - if (name == Extension::kExperimentalPermission) { + if (*i == ExtensionAPIPermission::kExperimental) { // Experimental permission is allowed, but requires this switch. CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc new file mode 100644 index 0000000..d87e9e3 --- /dev/null +++ b/chrome/common/extensions/extension_permission_set.cc @@ -0,0 +1,778 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/extensions/extension_permission_set.h" + +#include <algorithm> +#include <string> + +#include "base/memory/singleton.h" +#include "base/values.h" +#include "base/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_constants.h" +#include "chrome/common/extensions/extension_l10n_util.h" +#include "chrome/common/extensions/url_pattern.h" +#include "chrome/common/extensions/url_pattern_set.h" +#include "grit/generated_resources.h" +#include "net/base/registry_controlled_domain.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +// Helper for GetDistinctHosts(): com > net > org > everything else. +bool RcdBetterThan(std::string a, std::string b) { + if (a == b) + return false; + if (a == "com") + return true; + if (a == "net") + return b != "com"; + if (a == "org") + return b != "com" && b != "net"; + return false; +} + +// Names of API modules that do not require a permission. +const char kBrowserActionModuleName[] = "browserAction"; +const char kBrowserActionsModuleName[] = "browserActions"; +const char kDevToolsModuleName[] = "devtools"; +const char kExtensionModuleName[] = "extension"; +const char kI18NModuleName[] = "i18n"; +const char kOmniboxModuleName[] = "omnibox"; +const char kPageActionModuleName[] = "pageAction"; +const char kPageActionsModuleName[] = "pageActions"; +const char kTestModuleName[] = "test"; +const char kTypesModuleName[] = "types"; + +// Names of modules that can be used without listing it in the permissions +// section of the manifest. +const char* kNonPermissionModuleNames[] = { + kBrowserActionModuleName, + kBrowserActionsModuleName, + kDevToolsModuleName, + kExtensionModuleName, + kI18NModuleName, + kOmniboxModuleName, + kPageActionModuleName, + kPageActionsModuleName, + kTestModuleName, + kTypesModuleName +}; +const size_t kNumNonPermissionModuleNames = + arraysize(kNonPermissionModuleNames); + +// Names of functions (within modules requiring permissions) that can be used +// without asking for the module permission. In other words, functions you can +// use with no permissions specified. +const char* kNonPermissionFunctionNames[] = { + "tabs.create", + "tabs.onRemoved", + "tabs.remove", + "tabs.update", +}; +const size_t kNumNonPermissionFunctionNames = + arraysize(kNonPermissionFunctionNames); + +const char kOldUnlimitedStoragePermission[] = "unlimited_storage"; +const char kWindowsPermission[] = "windows"; + +void AddPatternsAndRemovePaths(const URLPatternSet& set, URLPatternSet* out) { + CHECK(out); + const URLPatternList& patterns = set.patterns(); + for (size_t i = 0; i < patterns.size(); ++i) { + URLPattern p = patterns.at(i); + p.SetPath("/*"); + out->AddPattern(p); + } +} + +} // namespace + +// +// PermissionMessage +// + +// static +ExtensionPermissionMessage ExtensionPermissionMessage::CreateFromHostList( + const std::vector<std::string>& hosts) { + CHECK(hosts.size() > 0); + ID message_id; + string16 message; + switch (hosts.size()) { + case 1: + message_id = kHosts1; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_1_HOST, + UTF8ToUTF16(hosts[0])); + break; + case 2: + message_id = kHosts2; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_2_HOSTS, + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1])); + break; + case 3: + message_id = kHosts3; + message = l10n_util::GetStringFUTF16(IDS_EXTENSION_PROMPT_WARNING_3_HOSTS, + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1]), + UTF8ToUTF16(hosts[2])); + break; + default: + message_id = kHosts4OrMore; + message = l10n_util::GetStringFUTF16( + IDS_EXTENSION_PROMPT_WARNING_4_OR_MORE_HOSTS, + UTF8ToUTF16(hosts[0]), + UTF8ToUTF16(hosts[1]), + base::IntToString16(hosts.size() - 2)); + break; + } + + return ExtensionPermissionMessage(message_id, message); +} + +ExtensionPermissionMessage::ExtensionPermissionMessage( + ExtensionPermissionMessage::ID id, const string16& message) + : id_(id), message_(message) { +} + +ExtensionPermissionMessage::~ExtensionPermissionMessage() { +} + +// +// ExtensionPermission +// + +ExtensionPermissionMessage ExtensionAPIPermission::GetMessage() const { + return ExtensionPermissionMessage( + message_id_, l10n_util::GetStringUTF16(l10n_message_id_)); +} + +ExtensionAPIPermission::ExtensionAPIPermission( + ID id, + const char* name, + bool is_hosted_app, + bool is_component_only, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id, + bool implies_full_access, + bool implies_full_url_access) + : id_(id), + name_(name), + implies_full_access_(implies_full_access), + implies_full_url_access_(implies_full_url_access), + is_hosted_app_(is_hosted_app), + is_component_only_(is_component_only), + l10n_message_id_(l10n_message_id), + message_id_(message_id) { +} + +ExtensionAPIPermission::~ExtensionAPIPermission() { +} + +// +// ExtensionPermissionsInfo +// + +// static +ExtensionPermissionsInfo* ExtensionPermissionsInfo::GetInstance() { + return Singleton<ExtensionPermissionsInfo>::get(); +} + +ExtensionAPIPermission* ExtensionPermissionsInfo::GetByID( + ExtensionAPIPermission::ID id) { + IDMap::iterator i = id_map_.find(id); + return (i == id_map_.end()) ? NULL : i->second; +} + +ExtensionAPIPermission* ExtensionPermissionsInfo::GetByName(std::string name) { + NameMap::iterator i = name_map_.find(name); + return (i == name_map_.end()) ? NULL : i->second; +} + +ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAll() { + ExtensionAPIPermissionSet permissions; + for (IDMap::const_iterator i = id_map_.begin(); i != id_map_.end(); ++i) + permissions.insert(i->second->id()); + return permissions; +} + +ExtensionAPIPermissionSet ExtensionPermissionsInfo::GetAllByName( + const std::set<std::string>& permission_names) { + ExtensionAPIPermissionSet permissions; + for (std::set<std::string>::const_iterator i = permission_names.begin(); + i != permission_names.end(); ++i) { + ExtensionAPIPermission* permission = GetByName(*i); + if (permission) + permissions.insert(permission->id()); + } + return permissions; +} + +ExtensionPermissionsInfo::~ExtensionPermissionsInfo() { + for (IDMap::iterator i = id_map_.begin(); i != id_map_.end(); ++i) + delete i->second; +} + +ExtensionPermissionsInfo::ExtensionPermissionsInfo() + : hosted_app_permission_count_(0), + permission_count_(0) { + // Hosted app permissions + RegisterHostedAppPermission( + ExtensionAPIPermission::kBackground, "background", 0, + ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kClipboardRead, "clipboardRead", + IDS_EXTENSION_PROMPT_WARNING_CLIPBOARD, + ExtensionPermissionMessage::kClipboard); + RegisterHostedAppPermission( + ExtensionAPIPermission::kClipboardWrite, "clipboardWrite", 0, + ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kChromePrivate, "chromePrivate", 0, + ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kExperimental, "experimental", 0, + ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kGeolocation, "geolocation", + IDS_EXTENSION_PROMPT_WARNING_GEOLOCATION, + ExtensionPermissionMessage::kGeolocation); + RegisterHostedAppPermission( + ExtensionAPIPermission::kNotification, "notifications", 0, + ExtensionPermissionMessage::kNone); + RegisterHostedAppPermission( + ExtensionAPIPermission::kUnlimitedStorage, "unlimitedStorage", 0, + ExtensionPermissionMessage::kNone); + + // Hosted app and private permissions. + RegisterPermission( + ExtensionAPIPermission::kWebstorePrivate, "webstorePrivate", 0, + ExtensionPermissionMessage::kNone, + true, true, false, false); + + // Extension permissions. + RegisterExtensionPermission( + ExtensionAPIPermission::kBookmark, "bookmarks", + IDS_EXTENSION_PROMPT_WARNING_BOOKMARKS, + ExtensionPermissionMessage::kBookmarks); + RegisterExtensionPermission( + ExtensionAPIPermission::kContentSettings, "contentSettings", 0, + ExtensionPermissionMessage::kNone); + RegisterExtensionPermission( + ExtensionAPIPermission::kContextMenus, "contextMenus", 0, + ExtensionPermissionMessage::kNone); + RegisterExtensionPermission( + ExtensionAPIPermission::kCookie, "cookies", 0, + ExtensionPermissionMessage::kNone); + RegisterExtensionPermission( + ExtensionAPIPermission::kDebugger, "debugger", + IDS_EXTENSION_PROMPT_WARNING_DEBUGGER, + ExtensionPermissionMessage::kDebugger); + RegisterExtensionPermission( + ExtensionAPIPermission::kFileBrowserHandler, "fileBrowserHandler", 0, + ExtensionPermissionMessage::kNone); + RegisterExtensionPermission( + ExtensionAPIPermission::kHistory, "history", + IDS_EXTENSION_PROMPT_WARNING_BROWSING_HISTORY, + ExtensionPermissionMessage::kBrowsingHistory); + RegisterExtensionPermission( + ExtensionAPIPermission::kIdle, "idle", 0, + ExtensionPermissionMessage::kNone); + RegisterExtensionPermission( + ExtensionAPIPermission::kManagement, "management", + IDS_EXTENSION_PROMPT_WARNING_MANAGEMENT, + ExtensionPermissionMessage::kManagement); + RegisterExtensionPermission( + ExtensionAPIPermission::kTab, "tabs", + IDS_EXTENSION_PROMPT_WARNING_TABS, + ExtensionPermissionMessage::kTabs); + RegisterExtensionPermission( + ExtensionAPIPermission::kWebSocketProxyPrivate, + "webSocketProxyPrivate", 0, + ExtensionPermissionMessage::kNone); + + // Private permissions + RegisterPrivatePermission( + ExtensionAPIPermission::kChromeosInfoPrivate, "chromeosInfoPrivate"); + RegisterPrivatePermission( + ExtensionAPIPermission::kFileBrowserPrivate, "fileBrowserPrivate"); + RegisterPrivatePermission( + ExtensionAPIPermission::kMediaPlayerPrivate, "mediaPlayerPrivate"); + + // Full url access permissions. + RegisterPermission( + ExtensionAPIPermission::kProxy, "proxy", 0, + ExtensionPermissionMessage::kNone, false, false, false, true); + + RegisterPermission( + ExtensionAPIPermission::kDevtools, "devtools", 0, + ExtensionPermissionMessage::kNone, false, false, false, true); + + RegisterPermission( + ExtensionAPIPermission::kPlugin, "plugin", + IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS, + ExtensionPermissionMessage::kFullAccess, false, false, true, true); + + RegisterPermission( + ExtensionAPIPermission::kDefault, "default", 0, + ExtensionPermissionMessage::kNone, false, false, false, false); + + // Register Aliases + RegisterAlias("unlimitedStorage", kOldUnlimitedStoragePermission); + RegisterAlias("tabs", kWindowsPermission); +} + +void ExtensionPermissionsInfo::RegisterAlias( + const char* name, const char* alias) { + CHECK(name_map_.find(name) != name_map_.end()); + CHECK(name_map_.find(alias) == name_map_.end()); + name_map_[alias] = name_map_[name]; +} + +void ExtensionPermissionsInfo::RegisterExtensionPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id) { + RegisterPermission(id, name, l10n_message_id, message_id, + false, false, false, false); +} + +void ExtensionPermissionsInfo::RegisterHostedAppPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id) { + RegisterPermission(id, name, l10n_message_id, message_id, + true, false, false, false); +} + +void ExtensionPermissionsInfo::RegisterPrivatePermission( + ExtensionAPIPermission::ID id, const char* name) { + RegisterPermission(id, name, 0, ExtensionPermissionMessage::kNone, + false, true, false, false); +} + +void ExtensionPermissionsInfo::RegisterPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id, + bool is_hosted_app, + bool is_component_only, + bool implies_full_access, + bool implies_full_url_access) { + CHECK(id_map_.find(id) == id_map_.end()); + CHECK(name_map_.find(name) == name_map_.end()); + + ExtensionAPIPermission* permission = + new ExtensionAPIPermission(id, + name, + is_hosted_app, + is_component_only, + l10n_message_id, + message_id, + implies_full_access, + implies_full_url_access); + id_map_[id] = permission; + name_map_[name] = permission; + + permission_count_++; + if (permission->is_hosted_app()) + hosted_app_permission_count_++; +} + +// +// ExtensionPermissionSet +// + +ExtensionPermissionSet::ExtensionPermissionSet() { +} + +ExtensionPermissionSet::ExtensionPermissionSet( + const Extension* extension, + const ExtensionAPIPermissionSet& apis, + const URLPatternSet& explicit_hosts) + : apis_(apis) { + CHECK(extension); + AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); + InitImplicitExtensionPermissions(extension); + InitEffectiveHosts(); +} + +ExtensionPermissionSet::ExtensionPermissionSet( + const ExtensionAPIPermissionSet& apis, + const URLPatternSet& explicit_hosts, + const URLPatternSet& scriptable_hosts) + : apis_(apis), + scriptable_hosts_(scriptable_hosts) { + AddPatternsAndRemovePaths(explicit_hosts, &explicit_hosts_); + InitEffectiveHosts(); +} + +ExtensionPermissionSet::~ExtensionPermissionSet() { +} + +// static +ExtensionPermissionSet* ExtensionPermissionSet::CreateUnion( + const ExtensionPermissionSet* set1, + const ExtensionPermissionSet* set2) { + ExtensionPermissionSet empty; + const ExtensionPermissionSet* set1_safe = (set1 == NULL) ? &empty : set1; + const ExtensionPermissionSet* set2_safe = (set2 == NULL) ? &empty : set2; + + ExtensionAPIPermissionSet apis; + std::set_union(set1_safe->apis().begin(), set1_safe->apis().end(), + set2_safe->apis().begin(), set2_safe->apis().end(), + std::insert_iterator<ExtensionAPIPermissionSet>( + apis, apis.begin())); + + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + URLPatternSet::CreateUnion(set1_safe->explicit_hosts(), + set2_safe->explicit_hosts(), + &explicit_hosts); + URLPatternSet::CreateUnion(set1_safe->scriptable_hosts(), + set2_safe->scriptable_hosts(), + &scriptable_hosts); + + return new ExtensionPermissionSet(apis, explicit_hosts, scriptable_hosts); +} + +std::set<std::string> ExtensionPermissionSet::GetAPIsAsStrings() const { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + std::set<std::string> apis_str; + for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); + i != apis_.end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + if (permission) + apis_str.insert(permission->name()); + } + return apis_str; +} + +std::vector<std::string> + ExtensionPermissionSet::GetDistinctHostsForDisplay() const { + return GetDistinctHosts(effective_hosts_.patterns(), true); +} + +ExtensionPermissionMessages + ExtensionPermissionSet::GetPermissionMessages() const { + ExtensionPermissionMessages messages; + + if (HasEffectiveFullAccess()) { + messages.push_back(ExtensionPermissionMessage( + ExtensionPermissionMessage::kFullAccess, + l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS))); + return messages; + } + + if (HasEffectiveAccessToAllHosts()) { + messages.push_back(ExtensionPermissionMessage( + ExtensionPermissionMessage::kHostsAll, + l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS))); + } else { + std::vector<std::string> hosts = GetDistinctHostsForDisplay(); + if (!hosts.empty()) + messages.push_back(ExtensionPermissionMessage::CreateFromHostList(hosts)); + } + + std::set<ExtensionPermissionMessage> simple_msgs = + GetSimplePermissionMessages(); + messages.insert(messages.end(), simple_msgs.begin(), simple_msgs.end()); + + return messages; +} + +std::vector<string16> ExtensionPermissionSet::GetWarningMessages() const { + std::vector<string16> messages; + ExtensionPermissionMessages permissions = GetPermissionMessages(); + for (ExtensionPermissionMessages::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) + messages.push_back(i->message()); + return messages; +} + +bool ExtensionPermissionSet::IsEmpty() const { + // Not default if any host permissions are present. + if (!(explicit_hosts().is_empty() && scriptable_hosts().is_empty())) + return false; + + // Or if it has no api permissions. + return apis().empty(); +} + +bool ExtensionPermissionSet::HasAPIPermission( + ExtensionAPIPermission::ID permission) const { + return apis().find(permission) != apis().end(); +} + +bool ExtensionPermissionSet::HasAccessToFunction( + const std::string& function_name) const { + // TODO(jstritar): Embed this information in each permission and add a method + // like GrantsAccess(function_name) to ExtensionAPIPermission. A "default" + // permission can then handle the modules and functions that everyone can + // access. + std::string permission_name = function_name; + + for (size_t i = 0; i < kNumNonPermissionFunctionNames; ++i) { + if (permission_name == kNonPermissionFunctionNames[i]) + return true; + } + + // See if this is a function or event name first and strip out the package. + // Functions will be of the form package.function + // Events will be of the form package/id or package.optional.stuff + size_t separator = function_name.find_first_of("./"); + if (separator != std::string::npos) + permission_name = function_name.substr(0, separator); + + ExtensionAPIPermission* permission = + ExtensionPermissionsInfo::GetInstance()->GetByName(permission_name); + if (permission && apis_.count(permission->id())) + return true; + + for (size_t i = 0; i < kNumNonPermissionModuleNames; ++i) { + if (permission_name == kNonPermissionModuleNames[i]) { + return true; + } + } + + return false; +} + +bool ExtensionPermissionSet::HasExplicitAccessToOrigin( + const GURL& origin) const { + return explicit_hosts().MatchesURL(origin); +} + +bool ExtensionPermissionSet::HasScriptableAccessToURL( + const GURL& origin) const { + // We only need to check our host list to verify access. The host list should + // already reflect any special rules (such as chrome://favicon, component + // all hosts access, etc.). + return scriptable_hosts().MatchesURL(origin); +} + +bool ExtensionPermissionSet::HasEffectiveAccessToAllHosts() const { + // There are two ways this set can have effective access to all hosts: + // 1) it has an <all_urls> URL pattern. + // 2) it has a named permission with implied full URL access. + const URLPatternList patterns = effective_hosts().patterns(); + for (URLPatternList::const_iterator host = patterns.begin(); + host != patterns.end(); ++host) { + if (host->match_all_urls() || + (host->match_subdomains() && host->host().empty())) + return true; + } + + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); + i != apis().end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + if (permission->implies_full_url_access()) + return true; + } + return false; +} + +bool ExtensionPermissionSet::HasEffectiveAccessToURL( + const GURL& url) const { + return effective_hosts().MatchesURL(url); +} + +bool ExtensionPermissionSet::HasEffectiveFullAccess() const { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = apis().begin(); + i != apis().end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + if (permission->implies_full_access()) + return true; + } + return false; +} + +bool ExtensionPermissionSet::HasPrivatePermissions() const { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); + i != apis_.end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + if (permission && permission->is_component_only()) + return true; + } + return false; +} + +bool ExtensionPermissionSet::HasLessPrivilegesThan( + const ExtensionPermissionSet* permissions) const { + // Things can't get worse than native code access. + if (HasEffectiveFullAccess()) + return false; + + // Otherwise, it's a privilege increase if the new one has full access. + if (permissions->HasEffectiveFullAccess()) + return true; + + if (HasLessHostPrivilegesThan(permissions)) + return true; + + if (HasLessAPIPrivilegesThan(permissions)) + return true; + + return false; +} + +// static +std::vector<std::string> ExtensionPermissionSet::GetDistinctHosts( + const URLPatternList& host_patterns, bool include_rcd) { + // Use a vector to preserve order (also faster than a map on small sets). + // Each item is a host split into two parts: host without RCDs and + // current best RCD. + typedef std::vector<std::pair<std::string, std::string> > HostVector; + HostVector hosts_best_rcd; + for (size_t i = 0; i < host_patterns.size(); ++i) { + std::string host = host_patterns[i].host(); + + // Add the subdomain wildcard back to the host, if necessary. + if (host_patterns[i].match_subdomains()) + host = "*." + host; + + // If the host has an RCD, split it off so we can detect duplicates. + std::string rcd; + size_t reg_len = net::RegistryControlledDomainService::GetRegistryLength( + host, false); + if (reg_len && reg_len != std::string::npos) { + if (include_rcd) // else leave rcd empty + rcd = host.substr(host.size() - reg_len); + host = host.substr(0, host.size() - reg_len); + } + + // Check if we've already seen this host. + HostVector::iterator it = hosts_best_rcd.begin(); + for (; it != hosts_best_rcd.end(); ++it) { + if (it->first == host) + break; + } + // If this host was found, replace the RCD if this one is better. + if (it != hosts_best_rcd.end()) { + if (include_rcd && RcdBetterThan(rcd, it->second)) + it->second = rcd; + } else { // Previously unseen host, append it. + hosts_best_rcd.push_back(std::make_pair(host, rcd)); + } + } + + // Build up the final vector by concatenating hosts and RCDs. + std::vector<std::string> distinct_hosts; + for (HostVector::iterator it = hosts_best_rcd.begin(); + it != hosts_best_rcd.end(); ++it) + distinct_hosts.push_back(it->first + it->second); + return distinct_hosts; +} + +void ExtensionPermissionSet::InitEffectiveHosts() { + effective_hosts_.ClearPatterns(); + + if (HasEffectiveAccessToAllHosts()) { + URLPattern all_urls(URLPattern::SCHEME_ALL); + all_urls.set_match_all_urls(true); + effective_hosts_.AddPattern(all_urls); + return; + } + + URLPatternSet::CreateUnion( + explicit_hosts(), scriptable_hosts(), &effective_hosts_); +} + +void ExtensionPermissionSet::InitImplicitExtensionPermissions( + const Extension* extension) { + // Add the implied permissions. + if (!extension->plugins().empty()) + apis_.insert(ExtensionAPIPermission::kPlugin); + + if (!extension->devtools_url().is_empty()) + apis_.insert(ExtensionAPIPermission::kDevtools); + + // Add the scriptable hosts. + for (UserScriptList::const_iterator content_script = + extension->content_scripts().begin(); + content_script != extension->content_scripts().end(); ++content_script) { + URLPatternList::const_iterator pattern = + content_script->url_patterns().begin(); + for (; pattern != content_script->url_patterns().end(); ++pattern) + scriptable_hosts_.AddPattern(*pattern); + } +} + +std::set<ExtensionPermissionMessage> + ExtensionPermissionSet::GetSimplePermissionMessages() const { + std::set<ExtensionPermissionMessage> messages; + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (ExtensionAPIPermissionSet::const_iterator i = apis_.begin(); + i != apis_.end(); ++i) { + DCHECK_GT(ExtensionPermissionMessage::kNone, + ExtensionPermissionMessage::kUnknown); + ExtensionAPIPermission* perm = info->GetByID(*i); + if (perm && perm->message_id() > ExtensionPermissionMessage::kNone) + messages.insert(perm->GetMessage()); + } + return messages; +} + +bool ExtensionPermissionSet::HasLessAPIPrivilegesThan( + const ExtensionPermissionSet* permissions) const { + if (permissions == NULL) + return false; + + ExtensionAPIPermissionSet new_apis = permissions->apis_; + ExtensionAPIPermissionSet new_apis_only; + std::set_difference(new_apis.begin(), new_apis.end(), + apis_.begin(), apis_.end(), + std::inserter(new_apis_only, new_apis_only.begin())); + + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + + // Ignore API permissions that don't require user approval when deciding if + // an extension has increased its privileges. + for (ExtensionAPIPermissionSet::iterator i = new_apis_only.begin(); + i != new_apis_only.end(); ++i) { + ExtensionAPIPermission* perm = info->GetByID(*i); + if (perm && perm->message_id() > ExtensionPermissionMessage::kNone) + return true; + } + return false; +} + +bool ExtensionPermissionSet::HasLessHostPrivilegesThan( + const ExtensionPermissionSet* permissions) const { + // If this permission set can access any host, then it can't be elevated. + if (HasEffectiveAccessToAllHosts()) + return false; + + // Likewise, if the other permission set has full host access, then it must be + // a privilege increase. + if (permissions->HasEffectiveAccessToAllHosts()) + return true; + + const URLPatternList old_list = effective_hosts().patterns(); + const URLPatternList new_list = permissions->effective_hosts().patterns(); + + // TODO(jstritar): This is overly conservative with respect to subdomains. + // For example, going from *.google.com to www.google.com will be + // considered an elevation, even though it is not (http://crbug.com/65337). + std::vector<std::string> new_hosts = GetDistinctHosts(new_list, false); + std::vector<std::string> old_hosts = GetDistinctHosts(old_list, false); + + 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_hosts_only; + + std::set_difference(new_hosts_set.begin(), new_hosts_set.end(), + old_hosts_set.begin(), old_hosts_set.end(), + std::inserter(new_hosts_only, new_hosts_only.begin())); + + return !new_hosts_only.empty(); +} diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h new file mode 100644 index 0000000..14f58a90 --- /dev/null +++ b/chrome/common/extensions/extension_permission_set.h @@ -0,0 +1,389 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_ +#define CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_ +#pragma once + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include "base/gtest_prod_util.h" +#include "base/memory/singleton.h" +#include "base/scoped_ptr.h" +#include "base/string16.h" +#include "chrome/common/extensions/url_pattern_set.h" + +class DictionaryValue; +class Extension; +class ExtensionPrefs; +class ListValue; + +// When prompting the user to install or approve permissions, we display +// messages describing the effects of the permissions rather than listing the +// permissions themselves. Each ExtensionPermissionMessage represents one of the +// messages shown to the user. +class ExtensionPermissionMessage { + public: + // Do not reorder this enumeration. If you need to add a new enum, add it just + // prior to kEnumBoundary. + enum ID { + kUnknown, + kNone, + kBookmarks, + kGeolocation, + kBrowsingHistory, + kTabs, + kManagement, + kDebugger, + kHosts1, + kHosts2, + kHosts3, + kHosts4OrMore, + kHostsAll, + kFullAccess, + kClipboard, + kEnumBoundary + }; + + // Creates the corresponding permission message for a list of hosts. This is + // simply a convenience method around the constructor, since the messages + // change depending on what hosts are present. + static ExtensionPermissionMessage CreateFromHostList( + const std::vector<std::string>& hosts); + + // Creates the corresponding permission message. + ExtensionPermissionMessage(ID id, const string16& message); + ~ExtensionPermissionMessage(); + + // Gets the id of the permission message, which can be used in UMA + // histograms. + ID id() const { return id_; } + + // Gets a localized message describing this permission. Please note that + // the message will be empty for message types TYPE_NONE and TYPE_UNKNOWN. + const string16& message() const { return message_; } + + // Comparator to work with std::set. + bool operator<(const ExtensionPermissionMessage& that) const { + return id_ < that.id_; + } + + private: + ID id_; + string16 message_; +}; + +typedef std::vector<ExtensionPermissionMessage> ExtensionPermissionMessages; + +// The ExtensionAPIPermission is an immutable class that describes a single +// named permission (API permission). +class ExtensionAPIPermission { + public: + enum ID { + // Error codes. + kInvalid = -2, + kUnknown = -1, + + // Default permission that every extension has implicity. + kDefault, + + // Real permissions. + kBackground, + kBookmark, + kClipboardRead, + kClipboardWrite, + kContentSettings, + kContextMenus, + kCookie, + kChromePrivate, + kChromeosInfoPrivate, + kDebugger, + kExperimental, + kFileBrowserHandler, + kFileBrowserPrivate, + kGeolocation, + kHistory, + kIdle, + kManagement, + kMediaPlayerPrivate, + kNotification, + kProxy, + kTab, + kUnlimitedStorage, + kWebSocketProxyPrivate, + kWebstorePrivate, + kDevtools, + kPlugin, + kEnumBoundary + }; + + typedef std::set<ID> IDSet; + + ~ExtensionAPIPermission(); + + // Returns the localized permission message associated with this api. + ExtensionPermissionMessage GetMessage() const; + + ID id() const { return id_; } + + // Returns the message id associated with this permission. + ExtensionPermissionMessage::ID message_id() const { + return message_id_; + } + + // Returns the name of this permission. + const char* name() const { return name_; } + + // Returns true if this permission implies full access (e.g., native code). + bool implies_full_access() const { return implies_full_access_; } + + // Returns true if this permission implies full URL access. + bool implies_full_url_access() const { return implies_full_url_access_; } + + // Returns true if this permission can be accessed by hosted apps. + bool is_hosted_app() const { return is_hosted_app_; } + + // Returns true if this permission can only be acquired by COMPONENT + // extensions. + bool is_component_only() const { return is_component_only_; } + + private: + // Instances should only be constructed from within ExtensionPermissionsInfo. + friend class ExtensionPermissionsInfo; + + explicit ExtensionAPIPermission( + ID id, + const char* name, + bool is_hosted_app, + bool is_component_only, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id, + bool implies_full_access, + bool implies_full_url_access); + + ID id_; + const char* name_; + bool implies_full_access_; + bool implies_full_url_access_; + bool is_hosted_app_; + bool is_component_only_; + int l10n_message_id_; + ExtensionPermissionMessage::ID message_id_; +}; + +typedef std::set<ExtensionAPIPermission::ID> ExtensionAPIPermissionSet; + +// Singleton that holds the extension permission instances and provides static +// methods for accessing them. +class ExtensionPermissionsInfo { + public: + // Returns a pointer to the singleton instance. + static ExtensionPermissionsInfo* GetInstance(); + + // Returns the permission with the given |id|, and NULL if it doesn't exist. + ExtensionAPIPermission* GetByID(ExtensionAPIPermission::ID id); + + // Returns the permission with the given |name|, and NULL if none + // exists. + ExtensionAPIPermission* GetByName(std::string name); + + // Returns a set containing all valid api permission ids. + ExtensionAPIPermissionSet GetAll(); + + // Converts all the permission names in |permission_names| to permission ids. + ExtensionAPIPermissionSet GetAllByName( + const std::set<std::string>& permission_names); + + // Gets the total number of API permissions available to hosted apps. + size_t get_hosted_app_permission_count() { + return hosted_app_permission_count_; + } + + // Gets the total number of API permissions. + size_t get_permission_count() { return permission_count_; } + + private: + ~ExtensionPermissionsInfo(); + ExtensionPermissionsInfo(); + + // Registers an |alias| for a given permission |name|. + void RegisterAlias(const char* name, const char* alias); + + // Registers a standard extension permission. + void RegisterExtensionPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id); + + // Registers a permission that can be accessed by hosted apps. + void RegisterHostedAppPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id); + + // Registers a permission accessible only by COMPONENT extensions. + void RegisterPrivatePermission( + ExtensionAPIPermission::ID id, + const char* name); + + // Registers a permission with a custom set of attributes not satisfied + // by the other registration functions. + void RegisterPermission( + ExtensionAPIPermission::ID id, + const char* name, + int l10n_message_id, + ExtensionPermissionMessage::ID message_id, + bool is_hosted_app, + bool is_component_only, + bool implies_full_access, + bool implies_full_url_access); + + // Maps permission ids to permissions. + typedef std::map<ExtensionAPIPermission::ID, ExtensionAPIPermission*> IDMap; + + // Maps names and aliases to permissions. + typedef std::map<std::string, ExtensionAPIPermission*> NameMap; + + IDMap id_map_; + NameMap name_map_; + + size_t hosted_app_permission_count_; + size_t permission_count_; + + friend struct DefaultSingletonTraits<ExtensionPermissionsInfo>; + DISALLOW_COPY_AND_ASSIGN(ExtensionPermissionsInfo); +}; + +// The ExtensionPermissionSet is an immutable class that encapsulates an +// extension's permissions. The class exposes set operations for combining and +// manipulating the permissions. +class ExtensionPermissionSet { + public: + // Creates an empty permission set (e.g. default permissions). + ExtensionPermissionSet(); + + // Creates a new permission set based on the |extension| manifest data, and + // the api and host permissions (|apis| and |hosts|). The effective hosts + // of the newly created permission set will be inferred from the |extension| + // manifest, |apis| and |hosts|. + ExtensionPermissionSet(const Extension* extension, + const ExtensionAPIPermissionSet& apis, + const URLPatternSet& explicit_hosts); + + // Creates a new permission set based on the specified data. + ExtensionPermissionSet(const ExtensionAPIPermissionSet& apis, + const URLPatternSet& explicit_hosts, + const URLPatternSet& scriptable_hosts); + + ~ExtensionPermissionSet(); + + // Creates a new permission set equal to the union of |set1| and |set2|. + // Passes ownership of the new set to the caller. + static ExtensionPermissionSet* CreateUnion( + const ExtensionPermissionSet* set1, const ExtensionPermissionSet* set2); + + // Gets the API permissions in this set as a set of strings. + std::set<std::string> GetAPIsAsStrings() const; + + // Gets a list of the distinct hosts for displaying to the user. + // NOTE: do not use this for comparing permissions, since this disgards some + // information. + std::vector<std::string> GetDistinctHostsForDisplay() const; + + // Gets the localized permission messages that represent this set. + ExtensionPermissionMessages GetPermissionMessages() const; + + // Gets the localized permission messages that represent this set (represented + // as strings). + std::vector<string16> GetWarningMessages() const; + + // Returns true if this is an empty set (e.g., the default permission set). + bool IsEmpty() const; + + // Returns true if the set has the specified API permission. + bool HasAPIPermission(ExtensionAPIPermission::ID permission) const; + + // Returns true if the permissions in this set grant access to the specified + // |function_name|. + bool HasAccessToFunction(const std::string& function_name) const; + + // Returns true if this includes permission to access |origin|. + bool HasExplicitAccessToOrigin(const GURL& origin) const; + + // Returns true if this permission set includes access to script |url|. + bool HasScriptableAccessToURL(const GURL& url) const; + + // Returns true if this permission set includes effective access to all + // origins. + bool HasEffectiveAccessToAllHosts() const; + + // Returns true if this permission set includes effective access to |url|. + bool HasEffectiveAccessToURL(const GURL& url) const; + + // Returns ture if this permission set effectively represents full access + // (e.g. native code). + bool HasEffectiveFullAccess() const; + + // Returns true if this permission set includes permissions that are + // restricted to internal extensions. + bool HasPrivatePermissions() const; + + // Returns true if |permissions| has a greater privilege level than this + // permission set (e.g., this permission set has less permissions). + bool HasLessPrivilegesThan(const ExtensionPermissionSet* permissions) const; + + const ExtensionAPIPermissionSet& apis() const { return apis_; } + + const URLPatternSet& effective_hosts() const { return effective_hosts_; } + + const URLPatternSet& explicit_hosts() const { return explicit_hosts_; } + + const URLPatternSet& scriptable_hosts() const { return scriptable_hosts_; } + + private: + FRIEND_TEST_ALL_PREFIXES(ExtensionPermissionSetTest, + HasLessHostPrivilegesThan); + + static std::vector<std::string> GetDistinctHosts( + const URLPatternList& host_patterns, bool include_rcd); + + // Initializes the set based on |extension|'s manifest data. + void InitImplicitExtensionPermissions(const Extension* extension); + + // Initializes the effective host permission based on the data in this set. + void InitEffectiveHosts(); + + // Gets the permission messages for the API permissions. + std::set<ExtensionPermissionMessage> GetSimplePermissionMessages() const; + + // Returns true if |permissions| has an elevated API privilege level than + // this set. + bool HasLessAPIPrivilegesThan( + const ExtensionPermissionSet* permissions) const; + + // Returns true if |permissions| has more host permissions compared to this + // set. + bool HasLessHostPrivilegesThan( + const ExtensionPermissionSet* permissions) const; + + // The api list is used when deciding if an extension can access certain + // extension APIs and features. + ExtensionAPIPermissionSet apis_; + + // The list of hosts that can be accessed directly from the extension. + URLPatternSet explicit_hosts_; + + // The list of hosts that can be scripted by content scripts. + URLPatternSet scriptable_hosts_; + + // The list of hosts this effectively grants access to. + URLPatternSet effective_hosts_; +}; + +#endif // CHROME_COMMON_EXTENSIONS_EXTENSION_PERMISSION_SET_H_ diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc new file mode 100644 index 0000000..9cfa698 --- /dev/null +++ b/chrome/common/extensions/extension_permission_set_unittest.cc @@ -0,0 +1,941 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/extensions/extension_permission_set.h" + +#include "base/logging.h" +#include "base/path_service.h" +#include "base/utf_string_conversions.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/extensions/extension.h" +#include "content/common/json_value_serializer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file, + int extra_flags) { + FilePath path; + PathService::Get(chrome::DIR_TEST_DATA, &path); + path = path.AppendASCII("extensions") + .AppendASCII(dir) + .AppendASCII(test_file); + + JSONFileValueSerializer serializer(path); + std::string error; + scoped_ptr<Value> result(serializer.Deserialize(NULL, &error)); + if (!result.get()) { + EXPECT_EQ("", error); + return NULL; + } + + scoped_refptr<Extension> extension = Extension::Create( + path.DirName(), Extension::INVALID, + *static_cast<DictionaryValue*>(result.get()), + Extension::STRICT_ERROR_CHECKS | extra_flags, &error); + EXPECT_TRUE(extension) << error; + return extension; +} + +static scoped_refptr<Extension> LoadManifest(const std::string& dir, + const std::string& test_file) { + return LoadManifest(dir, test_file, Extension::NO_FLAGS); +} + +void CompareLists(const std::vector<std::string>& expected, + const std::vector<std::string>& actual) { + ASSERT_EQ(expected.size(), actual.size()); + + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_EQ(expected[i], actual[i]); + } +} + +static void AddPattern(URLPatternSet* extent, const std::string& pattern) { + int schemes = URLPattern::SCHEME_ALL; + extent->AddPattern(URLPattern(schemes, pattern)); +} + +static void AssertEqualExtents(const URLPatternSet& extent1, + const URLPatternSet& extent2) { + URLPatternList patterns1 = extent1.patterns(); + URLPatternList 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 ExtensionAPIPermissionTest : public testing::Test { +}; + +class ExtensionPermissionSetTest : public testing::Test { +}; + + +// Tests GetByID. +TEST(ExtensionPermissionsInfoTest, GetByID) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet ids = info->GetAll(); + for (ExtensionAPIPermissionSet::iterator i = ids.begin(); + i != ids.end(); ++i) { + EXPECT_EQ(*i, info->GetByID(*i)->id()); + } +} + +// Tests that GetByName works with normal permission names and aliases. +TEST(ExtensionPermissionsInfoTest, GetByName) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("tabs")->id()); + EXPECT_EQ(ExtensionAPIPermission::kManagement, + info->GetByName("management")->id()); + EXPECT_FALSE(info->GetByName("alsdkfjasldkfj")); +} + +TEST(ExtensionPermissionsInfoTest, GetAll) { + size_t count = 0; + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet apis = info->GetAll(); + for (ExtensionAPIPermissionSet::iterator api = apis.begin(); + api != apis.end(); ++api) { + // Make sure only the valid permission IDs get returned. + EXPECT_NE(ExtensionAPIPermission::kInvalid, *api); + EXPECT_NE(ExtensionAPIPermission::kUnknown, *api); + count++; + } + EXPECT_EQ(count, info->get_permission_count()); +} + +TEST(ExtensionPermissionInfoTest, GetAllByName) { + std::set<std::string> names; + names.insert("background"); + names.insert("management"); + + // This is an alias of kTab + names.insert("windows"); + + // This unknown name should get dropped. + names.insert("sdlkfjasdlkfj"); + + ExtensionAPIPermissionSet expected; + expected.insert(ExtensionAPIPermission::kBackground); + expected.insert(ExtensionAPIPermission::kManagement); + expected.insert(ExtensionAPIPermission::kTab); + + EXPECT_EQ(expected, + ExtensionPermissionsInfo::GetInstance()->GetAllByName(names)); +} + +// Tests that the aliases are properly mapped. +TEST(ExtensionAPIPermissionTest, Aliases) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + // tabs: tabs, windows + std::string tabs_name = "tabs"; + EXPECT_EQ(tabs_name, info->GetByID(ExtensionAPIPermission::kTab)->name()); + EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("tabs")->id()); + EXPECT_EQ(ExtensionAPIPermission::kTab, info->GetByName("windows")->id()); + + // unlimitedStorage: unlimitedStorage, unlimited_storage + std::string storage_name = "unlimitedStorage"; + EXPECT_EQ(storage_name, info->GetByID( + ExtensionAPIPermission::kUnlimitedStorage)->name()); + EXPECT_EQ(ExtensionAPIPermission::kUnlimitedStorage, + info->GetByName("unlimitedStorage")->id()); + EXPECT_EQ(ExtensionAPIPermission::kUnlimitedStorage, + info->GetByName("unlimited_storage")->id()); +} + +TEST(ExtensionAPIPermissionTest, HostedAppPermissions) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet hosted_perms; + hosted_perms.insert(ExtensionAPIPermission::kBackground); + hosted_perms.insert(ExtensionAPIPermission::kClipboardRead); + hosted_perms.insert(ExtensionAPIPermission::kClipboardWrite); + hosted_perms.insert(ExtensionAPIPermission::kChromePrivate); + hosted_perms.insert(ExtensionAPIPermission::kExperimental); + hosted_perms.insert(ExtensionAPIPermission::kGeolocation); + hosted_perms.insert(ExtensionAPIPermission::kNotification); + hosted_perms.insert(ExtensionAPIPermission::kUnlimitedStorage); + hosted_perms.insert(ExtensionAPIPermission::kWebstorePrivate); + + ExtensionAPIPermissionSet perms = info->GetAll(); + size_t count = 0; + for (ExtensionAPIPermissionSet::iterator i = perms.begin(); + i != perms.end(); ++i) { + count += hosted_perms.count(*i); + EXPECT_EQ(hosted_perms.count(*i) > 0, info->GetByID(*i)->is_hosted_app()); + } + + EXPECT_EQ(9u, count); + EXPECT_EQ(9u, info->get_hosted_app_permission_count()); +} + +TEST(ExtensionAPIPermissionTest, ComponentOnlyPermissions) { + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet private_perms; + private_perms.insert(ExtensionAPIPermission::kChromeosInfoPrivate); + private_perms.insert(ExtensionAPIPermission::kFileBrowserPrivate); + private_perms.insert(ExtensionAPIPermission::kMediaPlayerPrivate); + private_perms.insert(ExtensionAPIPermission::kWebstorePrivate); + + ExtensionAPIPermissionSet perms = info->GetAll(); + int count = 0; + for (ExtensionAPIPermissionSet::iterator i = perms.begin(); + i != perms.end(); ++i) { + count += private_perms.count(*i); + EXPECT_EQ(private_perms.count(*i) > 0, + info->GetByID(*i)->is_component_only()); + } + + EXPECT_EQ(4, count); +} + +TEST(ExtensionPermissionSetTest, EffectiveHostPermissions) { + scoped_refptr<Extension> extension; + const ExtensionPermissionSet* permissions = NULL; + + extension = LoadManifest("effective_host_permissions", "empty.json"); + permissions = extension->permission_set(); + EXPECT_EQ(0u, extension->GetEffectiveHostPermissions().patterns().size()); + EXPECT_FALSE(permissions->HasEffectiveAccessToURL( + GURL("http://www.google.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", "one_host.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://www.google.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToURL( + GURL("https://www.google.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", + "one_host_wildcard.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://foo.google.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", "two_hosts.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://www.google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://www.reddit.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", + "https_not_considered.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://google.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", + "two_content_scripts.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://www.reddit.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL( + GURL("http://news.ycombinator.com"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", "all_hosts.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); + EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("https://test/"))); + EXPECT_TRUE( + permissions->HasEffectiveAccessToURL(GURL("http://www.google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", "all_hosts2.json"); + permissions = extension->permission_set(); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); + EXPECT_TRUE( + permissions->HasEffectiveAccessToURL(GURL("http://www.google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); + + extension = LoadManifest("effective_host_permissions", "all_hosts3.json"); + permissions = extension->permission_set(); + EXPECT_FALSE(permissions->HasEffectiveAccessToURL(GURL("http://test/"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToURL(GURL("https://test/"))); + EXPECT_TRUE( + permissions->HasEffectiveAccessToURL(GURL("http://www.google.com"))); + EXPECT_TRUE(permissions->HasEffectiveAccessToAllHosts()); +} + +TEST(ExtensionPermissionSetTest, ExplicitAccessToOrigin) { + ExtensionAPIPermissionSet apis; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + + AddPattern(&explicit_hosts, "http://*.google.com/*"); + // The explicit host paths should get set to /*. + AddPattern(&explicit_hosts, "http://www.example.com/a/particular/path/*"); + + ExtensionPermissionSet perm_set(apis, explicit_hosts, scriptable_hosts); + ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + GURL("http://www.google.com/"))); + ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + GURL("http://test.google.com/"))); + ASSERT_TRUE(perm_set.HasExplicitAccessToOrigin( + GURL("http://www.example.com"))); + ASSERT_TRUE(perm_set.HasEffectiveAccessToURL( + GURL("http://www.example.com"))); + ASSERT_FALSE(perm_set.HasExplicitAccessToOrigin( + GURL("http://test.example.com"))); +} + +TEST(ExtensionPermissionSetTest, CreateUnion) { + ExtensionAPIPermissionSet apis1; + ExtensionAPIPermissionSet apis2; + ExtensionAPIPermissionSet expected_apis; + + URLPatternSet explicit_hosts1; + URLPatternSet explicit_hosts2; + URLPatternSet expected_explicit_hosts; + + URLPatternSet scriptable_hosts1; + URLPatternSet scriptable_hosts2; + URLPatternSet expected_scriptable_hosts; + + URLPatternSet effective_hosts; + + scoped_ptr<ExtensionPermissionSet> set1; + scoped_ptr<ExtensionPermissionSet> set2; + scoped_ptr<ExtensionPermissionSet> union_set; + + // Union with an empty set. + apis1.insert(ExtensionAPIPermission::kTab); + apis1.insert(ExtensionAPIPermission::kBackground); + expected_apis.insert(ExtensionAPIPermission::kTab); + expected_apis.insert(ExtensionAPIPermission::kBackground); + + AddPattern(&explicit_hosts1, "http://*.google.com/*"); + AddPattern(&expected_explicit_hosts, "http://*.google.com/*"); + AddPattern(&effective_hosts, "http://*.google.com/*"); + + set1.reset(new ExtensionPermissionSet( + apis1, explicit_hosts1, scriptable_hosts1)); + set2.reset(new ExtensionPermissionSet( + apis2, explicit_hosts2, scriptable_hosts2)); + union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get())); + + EXPECT_FALSE(union_set->HasEffectiveFullAccess()); + EXPECT_EQ(expected_apis, union_set->apis()); + AssertEqualExtents(expected_explicit_hosts, union_set->explicit_hosts()); + AssertEqualExtents(expected_scriptable_hosts, union_set->scriptable_hosts()); + AssertEqualExtents(expected_explicit_hosts, union_set->effective_hosts()); + + // Now use a real second set. + apis2.insert(ExtensionAPIPermission::kTab); + apis2.insert(ExtensionAPIPermission::kProxy); + apis2.insert(ExtensionAPIPermission::kClipboardWrite); + apis2.insert(ExtensionAPIPermission::kPlugin); + expected_apis.insert(ExtensionAPIPermission::kTab); + expected_apis.insert(ExtensionAPIPermission::kProxy); + expected_apis.insert(ExtensionAPIPermission::kClipboardWrite); + expected_apis.insert(ExtensionAPIPermission::kPlugin); + + AddPattern(&explicit_hosts2, "http://*.example.com/*"); + AddPattern(&scriptable_hosts2, "http://*.google.com/*"); + AddPattern(&expected_explicit_hosts, "http://*.example.com/*"); + AddPattern(&expected_scriptable_hosts, "http://*.google.com/*"); + + effective_hosts.ClearPatterns(); + AddPattern(&effective_hosts, "<all_urls>"); + + set2.reset(new ExtensionPermissionSet( + apis2, explicit_hosts2, scriptable_hosts2)); + union_set.reset(ExtensionPermissionSet::CreateUnion(set1.get(), set2.get())); + EXPECT_TRUE(union_set->HasEffectiveFullAccess()); + EXPECT_TRUE(union_set->HasEffectiveAccessToAllHosts()); + EXPECT_EQ(expected_apis, union_set->apis()); + AssertEqualExtents(expected_explicit_hosts, union_set->explicit_hosts()); + AssertEqualExtents(expected_scriptable_hosts, union_set->scriptable_hosts()); + AssertEqualExtents(effective_hosts, union_set->effective_hosts()); +} + +TEST(ExtensionPermissionSetTest, HasLessPrivilegesThan) { + 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_increase; + } kTests[] = { + { "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, + true }, // 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", {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", {NULL}, + {NULL}, false, false }, // none -> storage + { "notifications", {NULL}, + {NULL}, false, false } // none -> notifications + }; + + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { + scoped_refptr<Extension> old_extension( + LoadManifest("allow_silent_upgrade", + std::string(kTests[i].base_name) + "_old.json")); + scoped_refptr<Extension> new_extension( + LoadManifest("allow_silent_upgrade", + std::string(kTests[i].base_name) + "_new.json")); + + ExtensionAPIPermissionSet granted_apis; + for (size_t j = 0; kTests[i].granted_apis[j] != NULL; ++j) { + granted_apis.insert(info->GetByName(kTests[i].granted_apis[j])->id()); + } + + URLPatternSet 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 (!new_extension.get()) + continue; + + const ExtensionPermissionSet* old_p = old_extension->permission_set(); + const ExtensionPermissionSet* new_p = new_extension->permission_set(); + + EXPECT_EQ(kTests[i].expect_increase, old_p->HasLessPrivilegesThan(new_p)) + << kTests[i].base_name; + } +} + +TEST(ExtensionPermissionSetTest, PermissionMessages) { + // Ensure that all permissions that needs to show install UI actually have + // strings associated with them. + ExtensionAPIPermissionSet skip; + + skip.insert(ExtensionAPIPermission::kDefault); + + // These are considered "nuisance" or "trivial" permissions that don't need + // a prompt. + skip.insert(ExtensionAPIPermission::kContextMenus); + skip.insert(ExtensionAPIPermission::kIdle); + skip.insert(ExtensionAPIPermission::kNotification); + skip.insert(ExtensionAPIPermission::kUnlimitedStorage); + skip.insert(ExtensionAPIPermission::kContentSettings); + + // TODO(erikkay) add a string for this permission. + skip.insert(ExtensionAPIPermission::kBackground); + + skip.insert(ExtensionAPIPermission::kClipboardWrite); + + // The cookie permission does nothing unless you have associated host + // permissions. + skip.insert(ExtensionAPIPermission::kCookie); + + // The proxy permission is warned as part of host permission checks. + skip.insert(ExtensionAPIPermission::kProxy); + + // This permission requires explicit user action (context menu handler) + // so we won't prompt for it for now. + skip.insert(ExtensionAPIPermission::kFileBrowserHandler); + + // If you've turned on the experimental command-line flag, we don't need + // to warn you further. + skip.insert(ExtensionAPIPermission::kExperimental); + + // These are private. + skip.insert(ExtensionAPIPermission::kWebstorePrivate); + skip.insert(ExtensionAPIPermission::kFileBrowserPrivate); + skip.insert(ExtensionAPIPermission::kMediaPlayerPrivate); + skip.insert(ExtensionAPIPermission::kChromePrivate); + skip.insert(ExtensionAPIPermission::kChromeosInfoPrivate); + skip.insert(ExtensionAPIPermission::kWebSocketProxyPrivate); + + // Warned as part of host permissions. + skip.insert(ExtensionAPIPermission::kDevtools); + ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); + ExtensionAPIPermissionSet permissions = info->GetAll(); + for (ExtensionAPIPermissionSet::const_iterator i = permissions.begin(); + i != permissions.end(); ++i) { + ExtensionAPIPermission* permission = info->GetByID(*i); + EXPECT_TRUE(permission); + if (skip.count(*i)) { + EXPECT_EQ(ExtensionPermissionMessage::kNone, permission->message_id()) + << "unexpected message_id for " << permission->name(); + } else { + EXPECT_NE(ExtensionPermissionMessage::kNone, permission->message_id()) + << "missing message_id for " << permission->name(); + } + } +} + +// Tests the default permissions (empty API permission set). +TEST(ExtensionPermissionSetTest, DefaultFunctionAccess) { + const struct { + const char* permission_name; + bool expect_success; + } kTests[] = { + // Negative test. + { "non_existing_permission", false }, + // Test default module/package permission. + { "browserAction", true }, + { "browserActions", true }, + { "devtools", true }, + { "extension", true }, + { "i18n", true }, + { "pageAction", true }, + { "pageActions", true }, + { "test", true }, + // Some negative tests. + { "bookmarks", false }, + { "cookies", false }, + { "history", false }, + { "tabs.onUpdated", false }, + // Make sure we find the module name after stripping '.' and '/'. + { "browserAction/abcd/onClick", true }, + { "browserAction.abcd.onClick", true }, + // Test Tabs functions. + { "tabs.create", true}, + { "tabs.update", true}, + { "tabs.getSelected", false}, + }; + + ExtensionPermissionSet permissions; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { + EXPECT_EQ(kTests[i].expect_success, + permissions.HasAccessToFunction(kTests[i].permission_name)); + } +} + +TEST(ExtensionPermissionSetTest, GetWarningMessages_ManyHosts) { + scoped_refptr<Extension> extension; + + extension = LoadManifest("permissions", "many-hosts.json"); + std::vector<string16> warnings = + extension->permission_set()->GetWarningMessages(); + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("Your data on www.google.com and encrypted.google.com", + UTF16ToUTF8(warnings[0])); +} + +TEST(ExtensionPermissionSetTest, GetWarningMessages_Plugins) { + scoped_refptr<Extension> extension; + scoped_ptr<ExtensionPermissionSet> permissions; + + extension = LoadManifest("permissions", "plugins.json"); + std::vector<string16> warnings = + extension->permission_set()->GetWarningMessages(); + // We don't parse the plugins key on Chrome OS, so it should not ask for any + // permissions. +#if defined(OS_CHROMEOS) + ASSERT_EQ(0u, warnings.size()); +#else + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("All data on your computer and the websites you visit", + UTF16ToUTF8(warnings[0])); +#endif +} + +TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay) { + scoped_ptr<ExtensionPermissionSet> perm_set; + ExtensionAPIPermissionSet empty_perms; + std::vector<std::string> expected; + expected.push_back("www.foo.com"); + expected.push_back("www.bar.com"); + expected.push_back("www.baz.com"); + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + + { + SCOPED_TRACE("no dupes"); + + // Simple list with no dupes. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("two dupes"); + + // Add some dupes. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("schemes differ"); + + // Add a pattern that differs only by scheme. This should be filtered out. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path")); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("paths differ"); + + // Add some dupes by path. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath")); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("subdomains differ"); + + // We don't do anything special for subdomains. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path")); + + expected.push_back("monkey.www.bar.com"); + expected.push_back("bar.com"); + + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("RCDs differ"); + + // Now test for RCD uniquing. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path")); + + // This is an unknown RCD, which shouldn't be uniqued out. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); + // But it should only occur once. + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); + + expected.push_back("www.foo.xyzzy"); + + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("wildcards"); + + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*")); + + expected.push_back("*.google.com"); + + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } + + { + SCOPED_TRACE("scriptable hosts"); + explicit_hosts.ClearPatterns(); + scriptable_hosts.ClearPatterns(); + expected.clear(); + + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*")); + scriptable_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://*.example.com/*")); + + expected.push_back("*.google.com"); + expected.push_back("*.example.com"); + + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); + } +} + +TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_ComIsBestRcd) { + scoped_ptr<ExtensionPermissionSet> perm_set; + ExtensionAPIPermissionSet empty_perms; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); + + std::vector<std::string> expected; + expected.push_back("www.foo.com"); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); +} + +TEST(ExtensionPermissionSetTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) { + scoped_ptr<ExtensionPermissionSet> perm_set; + ExtensionAPIPermissionSet empty_perms; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.net"); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); +} + +TEST(ExtensionPermissionSetTest, + GetDistinctHostsForDisplay_OrgIs3rdBestRcd) { + scoped_ptr<ExtensionPermissionSet> perm_set; + ExtensionAPIPermissionSet empty_perms; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + // No http://www.foo.net/path + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.org"); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); +} + +TEST(ExtensionPermissionSetTest, + GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) { + scoped_ptr<ExtensionPermissionSet> perm_set; + ExtensionAPIPermissionSet empty_perms; + URLPatternSet explicit_hosts; + URLPatternSet scriptable_hosts; + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); + // No http://www.foo.org/path + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); + // No http://www.foo.net/path + explicit_hosts.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); + // No http://www.foo.com/path + + std::vector<std::string> expected; + expected.push_back("www.foo.ca"); + perm_set.reset(new ExtensionPermissionSet( + empty_perms, explicit_hosts, scriptable_hosts)); + CompareLists(expected, perm_set->GetDistinctHostsForDisplay()); +} + +TEST(ExtensionPermissionSetTest, HasLessHostPrivilegesThan) { + URLPatternSet elist1; + URLPatternSet elist2; + URLPatternSet slist1; + URLPatternSet slist2; + scoped_ptr<ExtensionPermissionSet> set1; + scoped_ptr<ExtensionPermissionSet> set2; + ExtensionAPIPermissionSet empty_perms; + elist1.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); + elist1.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); + + // Test that the host order does not matter. + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); + + set1.reset(new ExtensionPermissionSet(empty_perms, elist1, slist1)); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + + EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); + EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); + + // Test that paths are ignored. + elist2.ClearPatterns(); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*")); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); + EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); + + // Test that RCDs are ignored. + elist2.ClearPatterns(); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*")); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + EXPECT_FALSE(set1->HasLessHostPrivilegesThan(set2.get())); + EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); + + // Test that subdomain wildcards are handled properly. + elist2.ClearPatterns(); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*")); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); + //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337 + //EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); + + // Test that different domains count as different hosts. + elist2.ClearPatterns(); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path")); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); + EXPECT_FALSE(set2->HasLessHostPrivilegesThan(set1.get())); + + // Test that different subdomains count as different hosts. + elist2.ClearPatterns(); + elist2.AddPattern( + URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*")); + set2.reset(new ExtensionPermissionSet(empty_perms, elist2, slist2)); + EXPECT_TRUE(set1->HasLessHostPrivilegesThan(set2.get())); + EXPECT_TRUE(set2->HasLessHostPrivilegesThan(set1.get())); +} + +TEST(ExtensionPermissionSetTest, GetAPIsAsStrings) { + ExtensionAPIPermissionSet apis; + URLPatternSet empty_set; + + apis.insert(ExtensionAPIPermission::kProxy); + apis.insert(ExtensionAPIPermission::kBackground); + apis.insert(ExtensionAPIPermission::kNotification); + apis.insert(ExtensionAPIPermission::kTab); + + ExtensionPermissionSet perm_set(apis, empty_set, empty_set); + std::set<std::string> api_names = perm_set.GetAPIsAsStrings(); + + // The result is correct if it has the same number of elements + // and we can convert it back to the id set. + EXPECT_EQ(4u, api_names.size()); + EXPECT_EQ(apis, + ExtensionPermissionsInfo::GetInstance()->GetAllByName(api_names)); +} + +TEST(ExtensionPermissionSetTest, IsEmpty) { + ExtensionAPIPermissionSet empty_apis; + URLPatternSet empty_extent; + + ExtensionPermissionSet perm_set; + EXPECT_TRUE(perm_set.IsEmpty()); + + perm_set = ExtensionPermissionSet(empty_apis, empty_extent, empty_extent); + EXPECT_TRUE(perm_set.IsEmpty()); + + ExtensionAPIPermissionSet non_empty_apis; + non_empty_apis.insert(ExtensionAPIPermission::kBackground); + perm_set = ExtensionPermissionSet( + non_empty_apis, empty_extent, empty_extent); + EXPECT_FALSE(perm_set.IsEmpty()); + + // Try non standard host + URLPatternSet non_empty_extent; + AddPattern(&non_empty_extent, "http://www.google.com/*"); + + perm_set = ExtensionPermissionSet( + empty_apis, non_empty_extent, empty_extent); + EXPECT_FALSE(perm_set.IsEmpty()); + + perm_set = ExtensionPermissionSet( + empty_apis, empty_extent, non_empty_extent); + EXPECT_FALSE(perm_set.IsEmpty()); +} diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index c74f6f8..aa8555c 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -42,11 +42,6 @@ void CompareLists(const std::vector<std::string>& expected, } } -static void AddPattern(URLPatternSet* extent, const std::string& pattern) { - int schemes = URLPattern::SCHEME_ALL; - extent->AddPattern(URLPattern(schemes, pattern)); -} - static scoped_refptr<Extension> LoadManifestUnchecked( const std::string& dir, const std::string& test_file, @@ -394,7 +389,11 @@ TEST(ExtensionTest, EffectiveHostPermissions) { hosts = extension->GetEffectiveHostPermissions(); EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com"))); EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com"))); + EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL( + GURL("http://www.reddit.com"))); EXPECT_TRUE(hosts.MatchesURL(GURL("http://news.ycombinator.com"))); + EXPECT_TRUE(extension->permission_set()->HasEffectiveAccessToURL( + GURL("http://news.ycombinator.com"))); EXPECT_FALSE(extension->HasEffectiveAccessToAllHosts()); extension = LoadManifest("effective_host_permissions", "all_hosts.json"); @@ -418,151 +417,6 @@ TEST(ExtensionTest, EffectiveHostPermissions) { EXPECT_TRUE(extension->HasEffectiveAccessToAllHosts()); } -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_increase; - } kTests[] = { - { "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, - true }, // 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", {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", {NULL}, - {NULL}, false, false }, // none -> storage - { "notifications", {NULL}, - {NULL}, false, false } // none -> notifications - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { - scoped_refptr<Extension> old_extension( - LoadManifest("allow_silent_upgrade", - std::string(kTests[i].base_name) + "_old.json")); - scoped_refptr<Extension> new_extension( - LoadManifest("allow_silent_upgrade", - std::string(kTests[i].base_name) + "_new.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]); - - URLPatternSet 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 (!new_extension.get()) - continue; - - EXPECT_EQ(kTests[i].expect_increase, - Extension::IsPrivilegeIncrease(kTests[i].full_access, - granted_apis, - granted_hosts, - new_extension.get())) - << kTests[i].base_name; - } -} - -TEST(ExtensionTest, PermissionMessages) { - // Ensure that all permissions that needs to show install UI actually have - // strings associated with them. - - std::set<std::string> skip; - - // These are considered "nuisance" or "trivial" permissions that don't need - // a prompt. - skip.insert(Extension::kContextMenusPermission); - skip.insert(Extension::kIdlePermission); - skip.insert(Extension::kNotificationPermission); - skip.insert(Extension::kUnlimitedStoragePermission); - skip.insert(Extension::kContentSettingsPermission); - - // TODO(erikkay) add a string for this permission. - skip.insert(Extension::kBackgroundPermission); - - skip.insert(Extension::kClipboardWritePermission); - - // The cookie permission does nothing unless you have associated host - // permissions. - skip.insert(Extension::kCookiePermission); - - // The proxy permission is warned as part of host permission checks. - skip.insert(Extension::kProxyPermission); - - // This permission requires explicit user action (context menu handler) - // so we won't prompt for it for now. - skip.insert(Extension::kFileBrowserHandlerPermission); - - // If you've turned on the experimental command-line flag, we don't need - // to warn you further. - skip.insert(Extension::kExperimentalPermission); - - // These are private. - skip.insert(Extension::kWebstorePrivatePermission); - skip.insert(Extension::kFileBrowserPrivatePermission); - skip.insert(Extension::kMediaPlayerPrivatePermission); - skip.insert(Extension::kChromePrivatePermission); - skip.insert(Extension::kChromeosInfoPrivatePermission); - skip.insert(Extension::kWebSocketProxyPrivatePermission); - - const Extension::PermissionMessage::MessageId ID_NONE = - Extension::PermissionMessage::ID_NONE; - - for (size_t i = 0; i < Extension::kNumPermissions; ++i) { - Extension::Permission permission = Extension::kPermissions[i]; - if (skip.count(permission.name)) { - EXPECT_EQ(ID_NONE, permission.message_id) - << "unexpected message_id for " << permission.name; - } else { - EXPECT_NE(ID_NONE, permission.message_id) - << "missing message_id for " << permission.name; - } - } -} - // Returns a copy of |source| resized to |size| x |size|. static SkBitmap ResizedCopy(const SkBitmap& source, int size) { return skia::ImageOperations::Resize(source, @@ -687,7 +541,7 @@ TEST(ExtensionTest, ApiPermissions) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTests); ++i) { EXPECT_EQ(kTests[i].expect_success, - extension->HasApiPermission(kTests[i].permission_name)) + extension->HasAPIPermission(kTests[i].permission_name)) << "Permission being tested: " << kTests[i].permission_name; } } @@ -904,257 +758,6 @@ TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { EXPECT_FALSE(extension->HasHostPermission(settings_url)); } -TEST(ExtensionTest, GetDistinctHostsForDisplay) { - std::vector<std::string> expected; - expected.push_back("www.foo.com"); - expected.push_back("www.bar.com"); - expected.push_back("www.baz.com"); - URLPatternList actual; - - { - SCOPED_TRACE("no dupes"); - - // Simple list with no dupes. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("two dupes"); - - // Add some dupes. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.baz.com/path")); - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("schemes differ"); - - // Add a pattern that differs only by scheme. This should be filtered out. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTPS, "https://www.bar.com/path")); - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("paths differ"); - - // Add some dupes by path. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.bar.com/pathypath")); - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("subdomains differ"); - - // We don't do anything special for subdomains. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://monkey.www.bar.com/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://bar.com/path")); - - expected.push_back("monkey.www.bar.com"); - expected.push_back("bar.com"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("RCDs differ"); - - // Now test for RCD uniquing. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.de/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca.us/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com.my/path")); - - // This is an unknown RCD, which shouldn't be uniqued out. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); - // But it should only occur once. - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.xyzzy/path")); - - expected.push_back("www.foo.xyzzy"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } - - { - SCOPED_TRACE("wildcards"); - - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com/*")); - - expected.push_back("*.google.com"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); - } -} - -TEST(ExtensionTest, GetDistinctHostsForDisplay_ComIsBestRcd) { - URLPatternList actual; - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.com/path")); - - std::vector<std::string> expected; - expected.push_back("www.foo.com"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); -} - -TEST(ExtensionTest, GetDistinctHostsForDisplay_NetIs2ndBestRcd) { - URLPatternList actual; - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.net/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); - // No http://www.foo.com/path - - std::vector<std::string> expected; - expected.push_back("www.foo.net"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); -} - -TEST(ExtensionTest, GetDistinctHostsForDisplay_OrgIs3rdBestRcd) { - URLPatternList actual; - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.org/path")); - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); - // No http://www.foo.net/path - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); - // No http://www.foo.com/path - - std::vector<std::string> expected; - expected.push_back("www.foo.org"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); -} - -TEST(ExtensionTest, GetDistinctHostsForDisplay_FirstInListIs4thBestRcd) { - URLPatternList actual; - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.ca/path")); - // No http://www.foo.org/path - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.co.uk/path")); - // No http://www.foo.net/path - actual.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.foo.jp/path")); - // No http://www.foo.com/path - - std::vector<std::string> expected; - expected.push_back("www.foo.ca"); - - CompareLists(expected, - Extension::GetDistinctHostsForDisplay(actual)); -} - -TEST(ExtensionTest, IsElevatedHostList) { - URLPatternList list1; - URLPatternList list2; - - list1.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); - list1.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); - - // Test that the host order does not matter. - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/path")); - - EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2)); - EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1)); - - // Test that paths are ignored. - list2.clear(); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/*")); - EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2)); - EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1)); - - // Test that RCDs are ignored. - list2.clear(); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com.hk/*")); - EXPECT_FALSE(Extension::IsElevatedHostList(list1, list2)); - EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1)); - - // Test that subdomain wildcards are handled properly. - list2.clear(); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://*.google.com.hk/*")); - EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2)); - //TODO(jstritar): Does not match subdomains properly. http://crbug.com/65337 - //EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1)); - - // Test that different domains count as different hosts. - list2.clear(); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/path")); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://www.example.org/path")); - EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2)); - EXPECT_FALSE(Extension::IsElevatedHostList(list2, list1)); - - // Test that different subdomains count as different hosts. - list2.clear(); - list2.push_back( - URLPattern(URLPattern::SCHEME_HTTP, "http://mail.google.com/*")); - EXPECT_TRUE(Extension::IsElevatedHostList(list1, list2)); - EXPECT_TRUE(Extension::IsElevatedHostList(list2, list1)); -} - TEST(ExtensionTest, GenerateId) { std::string result; EXPECT_TRUE(Extension::GenerateId("", &result)); diff --git a/chrome/common/extensions/url_pattern_set.cc b/chrome/common/extensions/url_pattern_set.cc index c8374ef..65f8635 100644 --- a/chrome/common/extensions/url_pattern_set.cc +++ b/chrome/common/extensions/url_pattern_set.cc @@ -7,6 +7,22 @@ #include "chrome/common/extensions/url_pattern.h" #include "googleurl/src/gurl.h" +// static +void URLPatternSet::CreateUnion(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out) { + const URLPatternList list1 = set1.patterns(); + const URLPatternList list2 = set2.patterns(); + + out->ClearPatterns(); + + for (size_t i = 0; i < list1.size(); ++i) + out->AddPattern(list1.at(i)); + + for (size_t i = 0; i < list2.size(); ++i) + out->AddPattern(list2.at(i)); +} + URLPatternSet::URLPatternSet() { } diff --git a/chrome/common/extensions/url_pattern_set.h b/chrome/common/extensions/url_pattern_set.h index ac50afc..2120e547 100644 --- a/chrome/common/extensions/url_pattern_set.h +++ b/chrome/common/extensions/url_pattern_set.h @@ -15,6 +15,12 @@ class GURL; // Represents the set of URLs an extension uses for web content. class URLPatternSet { public: + // Clears |out| and populates the set with the union of |set1| and |set2|. + // NOTE: this does not discard duplicates. + static void CreateUnion(const URLPatternSet& set1, + const URLPatternSet& set2, + URLPatternSet* out); + URLPatternSet(); URLPatternSet(const URLPatternSet& rhs); ~URLPatternSet(); diff --git a/chrome/common/extensions/url_pattern_set_unittest.cc b/chrome/common/extensions/url_pattern_set_unittest.cc index 5336410..8c05f2c 100644 --- a/chrome/common/extensions/url_pattern_set_unittest.cc +++ b/chrome/common/extensions/url_pattern_set_unittest.cc @@ -7,6 +7,27 @@ #include "googleurl/src/gurl.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { + +static void AssertEqualExtents(const URLPatternSet& extent1, + const URLPatternSet& extent2) { + URLPatternList patterns1 = extent1.patterns(); + URLPatternList 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 + static const int kAllSchemes = URLPattern::SCHEME_HTTP | URLPattern::SCHEME_HTTPS | @@ -60,3 +81,35 @@ TEST(URLPatternSetTest, OverlapsWith) { EXPECT_TRUE(extent1.OverlapsWith(extent3)); EXPECT_TRUE(extent3.OverlapsWith(extent1)); } + +TEST(URLPatternSetTest, CreateUnion) { + URLPatternSet empty_extent; + + URLPatternSet extent1; + extent1.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + extent1.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*")); + + URLPatternSet expected; + expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/b*")); + + // Union with an empty set. + URLPatternSet result; + URLPatternSet::CreateUnion(extent1, empty_extent, &result); + AssertEqualExtents(expected, result); + + // Union with a real set (including a duplicate). + URLPatternSet extent2; + extent2.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*")); + extent2.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*")); + extent2.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + + expected.AddPattern(URLPattern(kAllSchemes, "http://www.reddit.com/f*")); + expected.AddPattern(URLPattern(kAllSchemes, "http://www.yahoo.com/z*")); + // CreateUnion does not filter out duplicates right now. + expected.AddPattern(URLPattern(kAllSchemes, "http://www.google.com/f*")); + + result.ClearPatterns(); + URLPatternSet::CreateUnion(extent1, extent2, &result); + AssertEqualExtents(expected, result); +} diff --git a/chrome/renderer/extensions/bindings_utils.cc b/chrome/renderer/extensions/bindings_utils.cc index 89fa70b..0d786e7 100644 --- a/chrome/renderer/extensions/bindings_utils.cc +++ b/chrome/renderer/extensions/bindings_utils.cc @@ -65,7 +65,7 @@ bool ExtensionBase::CheckPermissionForCurrentContext( const ::Extension* extension = GetExtensionForCurrentContext(); if (extension && extension_dispatcher_->IsExtensionActive(extension->id()) && - extension->HasApiPermission(function_name)) + extension->HasAPIPermission(function_name)) return true; static const char kMessage[] = diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc index 370f36d..e654623 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -235,7 +235,7 @@ void ExtensionDispatcher::OnActivateExtension( } void ExtensionDispatcher::InitHostPermissions(const Extension* extension) { - if (extension->HasApiPermission(Extension::kManagementPermission)) { + if (extension->HasAPIPermission(ExtensionAPIPermission::kManagement)) { WebSecurityPolicy::addOriginAccessWhitelistEntry( extension->url(), WebString::fromUTF8(chrome::kChromeUIScheme), @@ -243,7 +243,8 @@ void ExtensionDispatcher::InitHostPermissions(const Extension* extension) { false); } - const URLPatternList& permissions = extension->host_permissions(); + const URLPatternList& permissions = + extension->permission_set()->explicit_hosts().patterns(); for (size_t i = 0; i < permissions.size(); ++i) { const char* schemes[] = { chrome::kHttpScheme, |