diff options
author | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 23:07:41 +0000 |
---|---|---|
committer | yoz@chromium.org <yoz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-15 23:07:41 +0000 |
commit | e4452d31c6aadb6e9c86836e8b45c0c151223826 (patch) | |
tree | 465eb8100a1d86ab889cd9f3f709df1703597a81 /extensions/common/permissions | |
parent | 741c3917b61418986e76534c2bc68394f6f23d29 (diff) | |
download | chromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.zip chromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.tar.gz chromium_src-e4452d31c6aadb6e9c86836e8b45c0c151223826.tar.bz2 |
Move Extension and PermissionsData to extensions/common.
BUG=298586
R=cdn@chromium.org, miket@chromium.org, sky@chromium.org
TBR=cdn@chromium.org
Review URL: https://codereview.chromium.org/15239002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235462 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common/permissions')
-rw-r--r-- | extensions/common/permissions/permissions_data.cc | 626 | ||||
-rw-r--r-- | extensions/common/permissions/permissions_data.h | 218 | ||||
-rw-r--r-- | extensions/common/permissions/permissions_data_unittest.cc | 620 |
3 files changed, 1464 insertions, 0 deletions
diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc new file mode 100644 index 0000000..f43d1d7 --- /dev/null +++ b/extensions/common/permissions/permissions_data.cc @@ -0,0 +1,626 @@ +// Copyright (c) 2013 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 "extensions/common/permissions/permissions_data.h" + +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "content/public/common/url_constants.h" +#include "extensions/common/constants.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/extensions_client.h" +#include "extensions/common/features/feature.h" +#include "extensions/common/features/feature_provider.h" +#include "extensions/common/manifest.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handler.h" +#include "extensions/common/permissions/api_permission_set.h" +#include "extensions/common/permissions/permission_message_provider.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/permissions_info.h" +#include "extensions/common/switches.h" +#include "extensions/common/url_pattern_set.h" +#include "extensions/common/user_script.h" +#include "url/gurl.h" + +namespace extensions { + +namespace keys = manifest_keys; +namespace errors = manifest_errors; + +namespace { + +PermissionsData::PolicyDelegate* g_policy_delegate = NULL; + +// Custom checks for the experimental permission that can't be expressed in +// _permission_features.json. +bool CanSpecifyExperimentalPermission(const Extension* extension) { + if (extension->location() == Manifest::COMPONENT) + return true; + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalExtensionApis)) { + return true; + } + + // We rely on the webstore to check access to experimental. This way we can + // whitelist extensions to have access to experimental in just the store, and + // not have to push a new version of the client. + if (extension->from_webstore()) + return true; + + return false; +} + +// Checks whether the host |pattern| is allowed for the given |extension|, +// given API permissions |permissions|. +bool CanSpecifyHostPermission(const Extension* extension, + const URLPattern& pattern, + const APIPermissionSet& permissions) { + if (!pattern.match_all_urls() && + pattern.MatchesScheme(chrome::kChromeUIScheme)) { + URLPatternSet chrome_scheme_hosts = ExtensionsClient::Get()-> + GetPermittedChromeSchemeHosts(extension, permissions); + if (chrome_scheme_hosts.ContainsPattern(pattern)) + return true; + + // Component extensions can have access to all of chrome://*. + if (PermissionsData::CanExecuteScriptEverywhere(extension)) + return true; + + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExtensionsOnChromeURLs)) { + return true; + } + + // TODO(aboxhall): return from_webstore() when webstore handles blocking + // extensions which request chrome:// urls + return false; + } + + // Otherwise, the valid schemes were handled by URLPattern. + return true; +} + +// Parses the host and api permissions from the specified permission |key| +// from |extension|'s manifest. +bool ParseHelper(Extension* extension, + const char* key, + APIPermissionSet* api_permissions, + URLPatternSet* host_permissions, + string16* error) { + if (!extension->manifest()->HasKey(key)) + return true; + + const base::ListValue* permissions = NULL; + if (!extension->manifest()->GetList(key, &permissions)) { + *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions, + std::string()); + return false; + } + + // NOTE: We need to get the APIPermission before we check if features + // associated with them are available because the feature system does not + // know about aliases. + + std::vector<std::string> host_data; + if (!APIPermissionSet::ParseFromJSON( + permissions, APIPermissionSet::kDisallowInternalPermissions, + api_permissions, error, &host_data)) { + return false; + } + + // Verify feature availability of permissions. + std::vector<APIPermission::ID> to_remove; + FeatureProvider* permission_features = + FeatureProvider::GetPermissionFeatures(); + for (APIPermissionSet::const_iterator iter = api_permissions->begin(); + iter != api_permissions->end(); ++iter) { + Feature* feature = permission_features->GetFeature(iter->name()); + + // The feature should exist since we just got an APIPermission for it. The + // two systems should be updated together whenever a permission is added. + DCHECK(feature); + // http://crbug.com/176381 + if (!feature) { + to_remove.push_back(iter->id()); + continue; + } + + Feature::Availability availability = feature->IsAvailableToManifest( + extension->id(), + extension->GetType(), + Feature::ConvertLocation(extension->location()), + extension->manifest_version()); + + if (!availability.is_available()) { + // Don't fail, but warn the developer that the manifest contains + // unrecognized permissions. This may happen legitimately if the + // extensions requests platform- or channel-specific permissions. + extension->AddInstallWarning(InstallWarning(availability.message(), + feature->name())); + to_remove.push_back(iter->id()); + continue; + } + + if (iter->id() == APIPermission::kExperimental) { + if (!CanSpecifyExperimentalPermission(extension)) { + *error = ASCIIToUTF16(errors::kExperimentalFlagRequired); + return false; + } + } + } + + api_permissions->AddImpliedPermissions(); + + // Remove permissions that are not available to this extension. + for (std::vector<APIPermission::ID>::const_iterator iter = to_remove.begin(); + iter != to_remove.end(); ++iter) { + api_permissions->erase(*iter); + } + + // Parse host pattern permissions. + const int kAllowedSchemes = + PermissionsData::CanExecuteScriptEverywhere(extension) ? + URLPattern::SCHEME_ALL : Extension::kValidHostPermissionSchemes; + + for (std::vector<std::string>::const_iterator iter = host_data.begin(); + iter != host_data.end(); ++iter) { + const std::string& permission_str = *iter; + + // Check if it's a host pattern permission. + URLPattern pattern = URLPattern(kAllowedSchemes); + URLPattern::ParseResult parse_result = pattern.Parse(permission_str); + if (parse_result == URLPattern::PARSE_SUCCESS) { + // The path component is not used for host permissions, so we force it + // to match all paths. + pattern.SetPath("/*"); + int valid_schemes = pattern.valid_schemes(); + if (pattern.MatchesScheme(chrome::kFileScheme) && + !PermissionsData::CanExecuteScriptEverywhere(extension)) { + extension->set_wants_file_access(true); + if (!(extension->creation_flags() & Extension::ALLOW_FILE_ACCESS)) + valid_schemes &= ~URLPattern::SCHEME_FILE; + } + + if (pattern.scheme() != chrome::kChromeUIScheme && + !PermissionsData::CanExecuteScriptEverywhere(extension)) { + // Keep chrome:// in allowed schemes only if it's explicitly requested + // or CanExecuteScriptEverywhere is true. If the + // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission + // will fail, so don't check the flag here. + valid_schemes &= ~URLPattern::SCHEME_CHROMEUI; + } + pattern.SetValidSchemes(valid_schemes); + + if (!CanSpecifyHostPermission(extension, pattern, *api_permissions)) { + // TODO(aboxhall): make a warning (see pattern.match_all_urls() block + // below). + extension->AddInstallWarning(InstallWarning( + ErrorUtils::FormatErrorMessage( + errors::kInvalidPermissionScheme, permission_str), + key, + permission_str)); + continue; + } + + host_permissions->AddPattern(pattern); + // We need to make sure all_urls matches chrome://favicon and (maybe) + // chrome://thumbnail, so add them back in to host_permissions separately. + if (pattern.match_all_urls()) { + host_permissions->AddPatterns( + ExtensionsClient::Get()->GetPermittedChromeSchemeHosts( + extension, *api_permissions)); + } + continue; + } + + // It's probably an unknown API permission. Do not throw an error so + // extensions can retain backwards compatability (http://crbug.com/42742). + extension->AddInstallWarning(InstallWarning( + ErrorUtils::FormatErrorMessage( + manifest_errors::kPermissionUnknownOrMalformed, + permission_str), + key, + permission_str)); + } + + return true; +} + +// Returns true if this extension id is from a trusted provider. +bool IsTrustedId(const std::string& extension_id) { + // See http://b/4946060 for more details. + return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); +} + +} // namespace + +struct PermissionsData::InitialPermissions { + APIPermissionSet api_permissions; + ManifestPermissionSet manifest_permissions; + URLPatternSet host_permissions; + URLPatternSet scriptable_hosts; +}; + +PermissionsData::PermissionsData() { +} + +PermissionsData::~PermissionsData() { +} + +// static +void PermissionsData::SetPolicyDelegate(PolicyDelegate* delegate) { + g_policy_delegate = delegate; +} + +// static +const PermissionSet* PermissionsData::GetOptionalPermissions( + const Extension* extension) { + return extension->permissions_data()->optional_permission_set_.get(); +} + +// static +const PermissionSet* PermissionsData::GetRequiredPermissions( + const Extension* extension) { + return extension->permissions_data()->required_permission_set_.get(); +} + +// static +const APIPermissionSet* PermissionsData::GetInitialAPIPermissions( + const Extension* extension) { + return &extension->permissions_data()-> + initial_required_permissions_->api_permissions; +} + +// static +APIPermissionSet* PermissionsData::GetInitialAPIPermissions( + Extension* extension) { + return &extension->permissions_data()-> + initial_required_permissions_->api_permissions; +} + +// static +void PermissionsData::SetInitialScriptableHosts( + Extension* extension, const URLPatternSet& scriptable_hosts) { + extension->permissions_data()-> + initial_required_permissions_->scriptable_hosts = scriptable_hosts; +} + +// static +void PermissionsData::SetActivePermissions(const Extension* extension, + const PermissionSet* permissions) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + extension->permissions_data()->active_permissions_ = permissions; +} + +// static +scoped_refptr<const PermissionSet> PermissionsData::GetActivePermissions( + const Extension* extension) { + return extension->permissions_data()->active_permissions_; +} + +// static +scoped_refptr<const PermissionSet> PermissionsData::GetTabSpecificPermissions( + const Extension* extension, + int tab_id) { + CHECK_GE(tab_id, 0); + TabPermissionsMap::const_iterator iter = + extension->permissions_data()->tab_specific_permissions_.find(tab_id); + return + (iter != extension->permissions_data()->tab_specific_permissions_.end()) + ? iter->second + : NULL; +} + +// static +void PermissionsData::UpdateTabSpecificPermissions( + const Extension* extension, + int tab_id, + scoped_refptr<const PermissionSet> permissions) { + CHECK_GE(tab_id, 0); + TabPermissionsMap* tab_permissions = + &extension->permissions_data()->tab_specific_permissions_; + if (tab_permissions->count(tab_id)) { + (*tab_permissions)[tab_id] = PermissionSet::CreateUnion( + (*tab_permissions)[tab_id].get(), permissions.get()); + } else { + (*tab_permissions)[tab_id] = permissions; + } +} + +// static +void PermissionsData::ClearTabSpecificPermissions( + const Extension* extension, + int tab_id) { + CHECK_GE(tab_id, 0); + extension->permissions_data()->tab_specific_permissions_.erase(tab_id); +} + +// static +bool PermissionsData::HasAPIPermission(const Extension* extension, + APIPermission::ID permission) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->HasAPIPermission(permission); +} + +// static +bool PermissionsData::HasAPIPermission( + const Extension* extension, + const std::string& permission_name) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->HasAPIPermission(permission_name); +} + +// static +bool PermissionsData::HasAPIPermissionForTab( + const Extension* extension, + int tab_id, + APIPermission::ID permission) { + if (HasAPIPermission(extension, permission)) + return true; + + // Place autolock below the HasAPIPermission() check, since HasAPIPermission + // also acquires the lock. + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + scoped_refptr<const PermissionSet> tab_permissions = + GetTabSpecificPermissions(extension, tab_id); + return tab_permissions.get() && tab_permissions->HasAPIPermission(permission); +} + +// static +bool PermissionsData::CheckAPIPermissionWithParam( + const Extension* extension, + APIPermission::ID permission, + const APIPermission::CheckParam* param) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->CheckAPIPermissionWithParam( + permission, param); +} + +// static +const URLPatternSet& PermissionsData::GetEffectiveHostPermissions( + const Extension* extension) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->effective_hosts(); +} + +// static +bool PermissionsData::CanSilentlyIncreasePermissions( + const Extension* extension) { + return extension->location() != Manifest::INTERNAL; +} + +// static +bool PermissionsData::ShouldSkipPermissionWarnings(const Extension* extension) { + return IsTrustedId(extension->id()); +} + +// static +bool PermissionsData::HasHostPermission(const Extension* extension, + const GURL& url) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->HasExplicitAccessToOrigin(url); +} + +// static +bool PermissionsData::HasEffectiveAccessToAllHosts(const Extension* extension) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + return GetActivePermissions(extension)->HasEffectiveAccessToAllHosts(); +} + +// static +PermissionMessages PermissionsData::GetPermissionMessages( + const Extension* extension) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + if (ShouldSkipPermissionWarnings(extension)) { + return PermissionMessages(); + } else { + return PermissionMessageProvider::Get()->GetPermissionMessages( + GetActivePermissions(extension), extension->GetType()); + } +} + +// static +std::vector<string16> PermissionsData::GetPermissionMessageStrings( + const Extension* extension) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + if (ShouldSkipPermissionWarnings(extension)) { + return std::vector<string16>(); + } else { + return PermissionMessageProvider::Get()->GetWarningMessages( + GetActivePermissions(extension), extension->GetType()); + } +} + +// static +std::vector<string16> PermissionsData::GetPermissionMessageDetailsStrings( + const Extension* extension) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + if (ShouldSkipPermissionWarnings(extension)) { + return std::vector<string16>(); + } else { + return PermissionMessageProvider::Get()->GetWarningMessagesDetails( + GetActivePermissions(extension), extension->GetType()); + } +} + +// static +bool PermissionsData::CanExecuteScriptOnPage(const Extension* extension, + const GURL& document_url, + const GURL& top_frame_url, + int tab_id, + const UserScript* script, + int process_id, + std::string* error) { + base::AutoLock auto_lock(extension->permissions_data()->runtime_lock_); + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + bool can_execute_everywhere = CanExecuteScriptEverywhere(extension); + + if (g_policy_delegate && + !g_policy_delegate->CanExecuteScriptOnPage( + extension, document_url, top_frame_url, tab_id, + script, process_id, error)) + return false; + + if (!can_execute_everywhere && + !ExtensionsClient::Get()->IsScriptableURL(document_url, error)) { + return false; + } + + if (!command_line->HasSwitch(switches::kExtensionsOnChromeURLs)) { + if (document_url.SchemeIs(chrome::kChromeUIScheme) && + !can_execute_everywhere) { + if (error) + *error = errors::kCannotAccessChromeUrl; + return false; + } + } + + if (top_frame_url.SchemeIs(extensions::kExtensionScheme) && + top_frame_url.GetOrigin() != + Extension::GetBaseURLFromExtensionId(extension->id()).GetOrigin() && + !can_execute_everywhere) { + if (error) + *error = errors::kCannotAccessExtensionUrl; + return false; + } + + // If a tab ID is specified, try the tab-specific permissions. + if (tab_id >= 0) { + scoped_refptr<const PermissionSet> tab_permissions = + GetTabSpecificPermissions(extension, tab_id); + if (tab_permissions.get() && + tab_permissions->explicit_hosts().MatchesSecurityOrigin(document_url)) { + return true; + } + } + + bool can_access = false; + + if (script) { + // If a script is specified, use its matches. + can_access = script->MatchesURL(document_url); + } else { + // Otherwise, see if this extension has permission to execute script + // programmatically on pages. + can_access = GetActivePermissions(extension)-> + HasExplicitAccessToOrigin(document_url); + } + + if (!can_access && error) { + *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, + document_url.spec()); + } + + return can_access; +} + +// static +bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) { + if (extension->location() == Manifest::COMPONENT) + return true; + + const ExtensionsClient::ScriptingWhitelist& whitelist = + ExtensionsClient::Get()->GetScriptingWhitelist(); + + return std::find(whitelist.begin(), whitelist.end(), extension->id()) != + whitelist.end(); +} + +// static +bool PermissionsData::CanCaptureVisiblePage(const Extension* extension, + const GURL& page_url, + int tab_id, + std::string* error) { + if (tab_id >= 0) { + scoped_refptr<const PermissionSet> tab_permissions = + GetTabSpecificPermissions(extension, tab_id); + if (tab_permissions.get() && + tab_permissions->explicit_hosts().MatchesSecurityOrigin(page_url)) { + return true; + } + } + + if (HasHostPermission(extension, page_url) || + page_url.GetOrigin() == extension->url()) { + return true; + } + + if (error) { + *error = ErrorUtils::FormatErrorMessage(errors::kCannotAccessPage, + page_url.spec()); + } + return false; +} + +bool PermissionsData::ParsePermissions(Extension* extension, string16* error) { + initial_required_permissions_.reset(new InitialPermissions); + if (!ParseHelper(extension, + keys::kPermissions, + &initial_required_permissions_->api_permissions, + &initial_required_permissions_->host_permissions, + error)) { + return false; + } + + // TODO(jeremya/kalman) do this via the features system by exposing the + // app.window API to platform apps, with no dependency on any permissions. + // See http://crbug.com/120069. + if (extension->is_platform_app()) { + initial_required_permissions_->api_permissions.insert( + APIPermission::kAppCurrentWindowInternal); + initial_required_permissions_->api_permissions.insert( + APIPermission::kAppRuntime); + initial_required_permissions_->api_permissions.insert( + APIPermission::kAppWindow); + } + + initial_optional_permissions_.reset(new InitialPermissions); + if (!ParseHelper(extension, + keys::kOptionalPermissions, + &initial_optional_permissions_->api_permissions, + &initial_optional_permissions_->host_permissions, + error)) { + return false; + } + + return true; +} + +void PermissionsData::InitializeManifestPermissions(Extension* extension) { + ManifestHandler::AddExtensionInitialRequiredPermissions( + extension, &initial_required_permissions_->manifest_permissions); +} + +void PermissionsData::FinalizePermissions(Extension* extension) { + active_permissions_ = new PermissionSet( + initial_required_permissions_->api_permissions, + initial_required_permissions_->manifest_permissions, + initial_required_permissions_->host_permissions, + initial_required_permissions_->scriptable_hosts); + + required_permission_set_ = new PermissionSet( + initial_required_permissions_->api_permissions, + initial_required_permissions_->manifest_permissions, + initial_required_permissions_->host_permissions, + initial_required_permissions_->scriptable_hosts); + + optional_permission_set_ = new PermissionSet( + initial_optional_permissions_->api_permissions, + initial_optional_permissions_->manifest_permissions, + initial_optional_permissions_->host_permissions, + URLPatternSet()); + + initial_required_permissions_.reset(); + initial_optional_permissions_.reset(); +} + +} // namespace extensions diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h new file mode 100644 index 0000000..72b9d24 --- /dev/null +++ b/extensions/common/permissions/permissions_data.h @@ -0,0 +1,218 @@ +// Copyright (c) 2013 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 EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_DATA_H_ +#define EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_DATA_H_ + +#include <map> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" +#include "base/synchronization/lock.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/permission_message.h" + +class GURL; + +namespace extensions { + +class PermissionSet; +class APIPermissionSet; +class Extension; +class ManifestPermissionSet; +class URLPatternSet; +class UserScript; + +// A container for the permissions data of the extension; also responsible for +// parsing the "permissions" and "optional_permissions" manifest keys. This +// class also contains the active (runtime) permissions for the extension. +class PermissionsData { + public: + PermissionsData(); + ~PermissionsData(); + + // Delegate class to allow different contexts (e.g. browser vs renderer) to + // have control over policy decisions. + class PolicyDelegate { + public: + virtual ~PolicyDelegate() {} + + // Returns false if script access should be blocked on this page. + // Otherwise, default policy should decide. + virtual bool CanExecuteScriptOnPage(const Extension* extension, + const GURL& document_url, + const GURL& top_document_url, + int tab_id, + const UserScript* script, + int process_id, + std::string* error) = 0; + }; + + static void SetPolicyDelegate(PolicyDelegate* delegate); + + // Return the optional or required permission set for the given |extension|. + static const PermissionSet* GetOptionalPermissions( + const Extension* extension); + static const PermissionSet* GetRequiredPermissions( + const Extension* extension); + + // Return the temporary API permission set which is used during extension + // initialization. Once initialization completes, this is NULL. + static const APIPermissionSet* GetInitialAPIPermissions( + const Extension* extension); + static APIPermissionSet* GetInitialAPIPermissions(Extension* extension); + + // Set the scriptable hosts for the given |extension| during initialization. + static void SetInitialScriptableHosts(Extension* extension, + const URLPatternSet& scriptable_hosts); + + // Return the active (runtime) permissions for the given |extension|. + static scoped_refptr<const PermissionSet> GetActivePermissions( + const Extension* extension); + // Sets the runtime permissions of the given |extension| to |permissions|. + static void SetActivePermissions(const Extension* extension, + const PermissionSet* active); + + // Gets the tab-specific host permissions of |tab_id|, or NULL if there + // aren't any. + static scoped_refptr<const PermissionSet> GetTabSpecificPermissions( + const Extension* extension, + int tab_id); + // Updates the tab-specific permissions of |tab_id| to include those from + // |permissions|. + static void UpdateTabSpecificPermissions( + const Extension* extension, + int tab_id, + scoped_refptr<const PermissionSet> permissions); + // Clears the tab-specific permissions of |tab_id|. + static void ClearTabSpecificPermissions(const Extension* extension, + int tab_id); + + // Returns true if the |extension| has the given |permission|. Prefer + // IsExtensionWithPermissionOrSuggestInConsole when developers may be using an + // api that requires a permission they didn't know about, e.g. open web apis. + // Note this does not include APIs with no corresponding permission, like + // "runtime" or "browserAction". + // TODO(mpcomplete): drop the "API" from these names, it's confusing. + static bool HasAPIPermission(const Extension* extension, + APIPermission::ID permission); + static bool HasAPIPermission(const Extension* extension, + const std::string& permission_name); + static bool HasAPIPermissionForTab(const Extension* extension, + int tab_id, + APIPermission::ID permission); + + static bool CheckAPIPermissionWithParam( + const Extension* extension, + APIPermission::ID permission, + const APIPermission::CheckParam* param); + + static const URLPatternSet& GetEffectiveHostPermissions( + const Extension* extension); + + // Returns true if the |extension| can silently increase its permission level. + // Users must approve permissions for unpacked and packed extensions in the + // following situations: + // - when installing or upgrading packed extensions + // - when installing unpacked extensions that have NPAPI plugins + // - when either type of extension requests optional permissions + static bool CanSilentlyIncreasePermissions(const Extension* extension); + + // Returns true if the extension does not require permission warnings + // to be displayed at install time. + static bool ShouldSkipPermissionWarnings(const Extension* extension); + + // Whether the |extension| has access to the given |url|. + static bool HasHostPermission(const Extension* extension, const GURL& url); + + // Whether the |extension| has effective access to all hosts. This is true if + // there is a content script that matches all hosts, if there is a host + // permission grants access to all hosts (like <all_urls>) or an api + // permission that effectively grants access to all hosts (e.g. proxy, + // network, etc.) + static bool HasEffectiveAccessToAllHosts(const Extension* extension); + + // Returns the full list of permission messages that the given |extension| + // should display at install time. + static PermissionMessages GetPermissionMessages(const Extension* extension); + // Returns the full list of permission messages that the given |extension| + // should display at install time. The messages are returned as strings + // for convenience. + static std::vector<string16> GetPermissionMessageStrings( + const Extension* extension); + + // Returns the full list of permission details for messages that the given + // |extension| should display at install time. The messages are returned as + // strings for convenience. + static std::vector<string16> GetPermissionMessageDetailsStrings( + const Extension* extension); + + // Returns true if the given |extension| can execute script on a page. If a + // UserScript object is passed, permission to run that specific script is + // checked (using its matches list). Otherwise, permission to execute script + // programmatically is checked (using the extension's host permission). + // + // This method is also aware of certain special pages that extensions are + // usually not allowed to run script on. + static bool CanExecuteScriptOnPage(const Extension* extension, + const GURL& document_url, + const GURL& top_document_url, + int tab_id, + const UserScript* script, + int process_id, + std::string* error); + + // Returns true if the given |extension| is a COMPONENT extension, or if it is + // on the whitelist of extensions that can script all pages. + static bool CanExecuteScriptEverywhere(const Extension* extension); + + // Returns true if the |extension| is allowed to obtain the contents of a + // page as an image. Since a page may contain sensitive information, this + // is restricted to the extension's host permissions as well as the + // extension page itself. + static bool CanCaptureVisiblePage(const Extension* extension, + const GURL& page_url, + int tab_id, + std::string* error); + + // Parse the permissions of a given extension in the initialization process. + bool ParsePermissions(Extension* extension, string16* error); + + // Ensure manifest handlers provide their custom manifest permissions. + void InitializeManifestPermissions(Extension* extension); + + // Finalize permissions after the initialization process completes. + void FinalizePermissions(Extension* extension); + + private: + struct InitialPermissions; + typedef std::map<int, scoped_refptr<const PermissionSet> > TabPermissionsMap; + + // Temporary permissions during the initialization process; NULL after + // initialization completes. + scoped_ptr<InitialPermissions> initial_required_permissions_; + scoped_ptr<InitialPermissions> initial_optional_permissions_; + + // The set of permissions the extension can request at runtime. + scoped_refptr<const PermissionSet> optional_permission_set_; + + // The extension's required / default set of permissions. + scoped_refptr<const PermissionSet> required_permission_set_; + + mutable base::Lock runtime_lock_; + + // The permission's which are currently active on the extension during + // runtime. + mutable scoped_refptr<const PermissionSet> active_permissions_; + + mutable TabPermissionsMap tab_specific_permissions_; + + DISALLOW_COPY_AND_ASSIGN(PermissionsData); +}; + +} // namespace extensions + +#endif // EXTENSIONS_COMMON_PERMISSIONS_PERMISSIONS_DATA_H_ diff --git a/extensions/common/permissions/permissions_data_unittest.cc b/extensions/common/permissions/permissions_data_unittest.cc new file mode 100644 index 0000000..3ea4856 --- /dev/null +++ b/extensions/common/permissions/permissions_data_unittest.cc @@ -0,0 +1,620 @@ +// Copyright (c) 2013 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 <vector> + +#include "base/command_line.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/common/chrome_version_info.h" +#include "chrome/common/extensions/extension_test_util.h" +#include "chrome/common/extensions/features/feature_channel.h" +#include "chrome/common/extensions/permissions/socket_permission.h" +#include "content/public/common/socket_permission_request.h" +#include "extensions/common/error_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/id_util.h" +#include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/api_permission.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/switches.h" +#include "extensions/common/url_pattern_set.h" +#include "testing/gtest/include/gtest/gtest.h" + +using content::SocketPermissionRequest; +using extension_test_util::LoadManifest; +using extension_test_util::LoadManifestUnchecked; +using extension_test_util::LoadManifestStrict; + +namespace extensions { + +namespace { + +bool CheckSocketPermission( + scoped_refptr<Extension> extension, + SocketPermissionRequest::OperationType type, + const char* host, + int port) { + SocketPermission::CheckParam param(type, host, port); + return PermissionsData::CheckAPIPermissionWithParam( + extension.get(), APIPermission::kSocket, ¶m); +} + +} // namespace + +TEST(ExtensionPermissionsTest, EffectiveHostPermissions) { + scoped_refptr<Extension> extension; + URLPatternSet hosts; + + extension = LoadManifest("effective_host_permissions", "empty.json"); + EXPECT_EQ(0u, + PermissionsData::GetEffectiveHostPermissions(extension.get()) + .patterns().size()); + EXPECT_FALSE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", "one_host.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_FALSE(hosts.MatchesURL(GURL("https://www.google.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", + "one_host_wildcard.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://foo.google.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", "two_hosts.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", + "https_not_considered.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("https://google.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", + "two_content_scripts.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://google.com"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.reddit.com"))); + EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL( + GURL("http://www.reddit.com"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://news.ycombinator.com"))); + EXPECT_TRUE(extension->GetActivePermissions()->HasEffectiveAccessToURL( + GURL("http://news.ycombinator.com"))); + EXPECT_FALSE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", "all_hosts.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://test/"))); + EXPECT_FALSE(hosts.MatchesURL(GURL("https://test/"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_TRUE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", "all_hosts2.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://test/"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_TRUE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); + + extension = LoadManifest("effective_host_permissions", "all_hosts3.json"); + hosts = PermissionsData::GetEffectiveHostPermissions(extension.get()); + EXPECT_FALSE(hosts.MatchesURL(GURL("http://test/"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("https://test/"))); + EXPECT_TRUE(hosts.MatchesURL(GURL("http://www.google.com"))); + EXPECT_TRUE(PermissionsData::HasEffectiveAccessToAllHosts(extension.get())); +} + +TEST(ExtensionPermissionsTest, SocketPermissions) { + // Set feature current channel to appropriate value. + ScopedCurrentChannel scoped_channel(chrome::VersionInfo::CHANNEL_DEV); + scoped_refptr<Extension> extension; + std::string error; + + extension = LoadManifest("socket_permissions", "empty.json"); + EXPECT_FALSE(CheckSocketPermission(extension, + SocketPermissionRequest::TCP_CONNECT, "www.example.com", 80)); + + extension = LoadManifestUnchecked("socket_permissions", + "socket1.json", + Manifest::INTERNAL, Extension::NO_FLAGS, + &error); + EXPECT_TRUE(extension.get() == NULL); + ASSERT_EQ(ErrorUtils::FormatErrorMessage( + manifest_errors::kInvalidPermission, "socket"), + error); + + extension = LoadManifest("socket_permissions", "socket2.json"); + EXPECT_TRUE(CheckSocketPermission(extension, + SocketPermissionRequest::TCP_CONNECT, "www.example.com", 80)); + EXPECT_FALSE(CheckSocketPermission( + extension, SocketPermissionRequest::UDP_BIND, "", 80)); + EXPECT_TRUE(CheckSocketPermission( + extension, SocketPermissionRequest::UDP_BIND, "", 8888)); + + EXPECT_FALSE(CheckSocketPermission( + extension, SocketPermissionRequest::UDP_SEND_TO, "example.com", 1900)); + EXPECT_TRUE(CheckSocketPermission( + extension, + SocketPermissionRequest::UDP_SEND_TO, + "239.255.255.250", 1900)); +} + +TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyAPIPermissions) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "many-apis.json"); + std::vector<string16> warnings = + PermissionsData::GetPermissionMessageStrings(extension.get()); + ASSERT_EQ(6u, warnings.size()); + EXPECT_EQ("Access your data on api.flickr.com", + UTF16ToUTF8(warnings[0])); + EXPECT_EQ("Read and modify your bookmarks", UTF16ToUTF8(warnings[1])); + EXPECT_EQ("Detect your physical location", UTF16ToUTF8(warnings[2])); + EXPECT_EQ("Read and modify your browsing history", UTF16ToUTF8(warnings[3])); + EXPECT_EQ("Access your tabs and browsing activity", UTF16ToUTF8(warnings[4])); + EXPECT_EQ("Manage your apps, extensions, and themes", + UTF16ToUTF8(warnings[5])); +} + +TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyHostsPermissions) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "more-than-3-hosts.json"); + std::vector<string16> warnings = + PermissionsData::GetPermissionMessageStrings(extension.get()); + std::vector<string16> warnings_details = + PermissionsData::GetPermissionMessageDetailsStrings(extension.get()); + ASSERT_EQ(1u, warnings.size()); + ASSERT_EQ(1u, warnings_details.size()); + EXPECT_EQ("Access your data on 5 websites", UTF16ToUTF8(warnings[0])); + EXPECT_EQ("- www.a.com\n- www.b.com\n- www.c.com\n- www.d.com\n- www.e.com", + UTF16ToUTF8(warnings_details[0])); +} + +TEST(ExtensionPermissionsTest, GetPermissionMessages_LocationApiPermission) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", + "location-api.json", + Manifest::COMPONENT, + Extension::NO_FLAGS); + std::vector<string16> warnings = + PermissionsData::GetPermissionMessageStrings(extension.get()); + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("Detect your physical location", UTF16ToUTF8(warnings[0])); +} + +TEST(ExtensionPermissionsTest, GetPermissionMessages_ManyHosts) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "many-hosts.json"); + std::vector<string16> warnings = + PermissionsData::GetPermissionMessageStrings(extension.get()); + ASSERT_EQ(1u, warnings.size()); + EXPECT_EQ("Access your data on encrypted.google.com and www.google.com", + UTF16ToUTF8(warnings[0])); +} + +TEST(ExtensionPermissionsTest, GetPermissionMessages_Plugins) { + scoped_refptr<Extension> extension; + extension = LoadManifest("permissions", "plugins.json"); + std::vector<string16> warnings = + PermissionsData::GetPermissionMessageStrings(extension.get()); +// 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("Access all data on your computer and the websites you visit", + UTF16ToUTF8(warnings[0])); +#endif +} + +// Base class for testing the CanExecuteScriptOnPage and CanCaptureVisiblePage +// methods of Extension for extensions with various permissions. +class ExtensionScriptAndCaptureVisibleTest : public testing::Test { + protected: + ExtensionScriptAndCaptureVisibleTest() + : http_url("http://www.google.com"), + http_url_with_path("http://www.google.com/index.html"), + https_url("https://www.google.com"), + file_url("file:///foo/bar"), + favicon_url("chrome://favicon/http://www.google.com"), + extension_url("chrome-extension://" + + id_util::GenerateIdForPath( + base::FilePath(FILE_PATH_LITERAL("foo")))), + settings_url("chrome://settings"), + about_url("about:flags") { + urls_.insert(http_url); + urls_.insert(http_url_with_path); + urls_.insert(https_url); + urls_.insert(file_url); + urls_.insert(favicon_url); + urls_.insert(extension_url); + urls_.insert(settings_url); + urls_.insert(about_url); + // Ignore the policy delegate for this test. + PermissionsData::SetPolicyDelegate(NULL); + } + + bool AllowedScript(const Extension* extension, const GURL& url, + const GURL& top_url) { + return PermissionsData::CanExecuteScriptOnPage( + extension, url, top_url, -1, NULL, -1, NULL); + } + + bool BlockedScript(const Extension* extension, const GURL& url, + const GURL& top_url) { + return !PermissionsData::CanExecuteScriptOnPage( + extension, url, top_url, -1, NULL, -1, NULL); + } + + bool Allowed(const Extension* extension, const GURL& url) { + return Allowed(extension, url, -1); + } + + bool Allowed(const Extension* extension, const GURL& url, int tab_id) { + return (PermissionsData::CanExecuteScriptOnPage( + extension, url, url, tab_id, NULL, -1, NULL) && + PermissionsData::CanCaptureVisiblePage( + extension, url, tab_id, NULL)); + } + + bool CaptureOnly(const Extension* extension, const GURL& url) { + return CaptureOnly(extension, url, -1); + } + + bool CaptureOnly(const Extension* extension, const GURL& url, int tab_id) { + return !PermissionsData::CanExecuteScriptOnPage( + extension, url, url, tab_id, NULL, -1, NULL) && + PermissionsData::CanCaptureVisiblePage(extension, url, tab_id, NULL); + } + + bool Blocked(const Extension* extension, const GURL& url) { + return Blocked(extension, url, -1); + } + + bool Blocked(const Extension* extension, const GURL& url, int tab_id) { + return !(PermissionsData::CanExecuteScriptOnPage( + extension, url, url, tab_id, NULL, -1, NULL) || + PermissionsData::CanCaptureVisiblePage( + extension, url, tab_id, NULL)); + } + + bool AllowedExclusivelyOnTab( + const Extension* extension, + const std::set<GURL>& allowed_urls, + int tab_id) { + bool result = true; + for (std::set<GURL>::iterator it = urls_.begin(); it != urls_.end(); ++it) { + const GURL& url = *it; + if (allowed_urls.count(url)) + result &= Allowed(extension, url, tab_id); + else + result &= Blocked(extension, url, tab_id); + } + return result; + } + + // URLs that are "safe" to provide scripting and capture visible tab access + // to if the permissions allow it. + const GURL http_url; + const GURL http_url_with_path; + const GURL https_url; + const GURL file_url; + + // We should allow host permission but not scripting permission for favicon + // urls. + const GURL favicon_url; + + // URLs that regular extensions should never get access to. + const GURL extension_url; + const GURL settings_url; + const GURL about_url; + + private: + // The set of all URLs above. + std::set<GURL> urls_; +}; + +TEST_F(ExtensionScriptAndCaptureVisibleTest, Permissions) { + // Test <all_urls> for regular extensions. + scoped_refptr<Extension> extension = LoadManifestStrict("script_and_capture", + "extension_regular_all.json"); + + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), extension_url)); + + // Test access to iframed content. + GURL within_extension_url = extension->GetResourceURL("page.html"); + EXPECT_TRUE(AllowedScript(extension.get(), http_url, http_url_with_path)); + EXPECT_TRUE(AllowedScript(extension.get(), https_url, http_url_with_path)); + EXPECT_TRUE(AllowedScript(extension.get(), http_url, within_extension_url)); + EXPECT_TRUE(AllowedScript(extension.get(), https_url, within_extension_url)); + EXPECT_TRUE(BlockedScript(extension.get(), http_url, extension_url)); + EXPECT_TRUE(BlockedScript(extension.get(), https_url, extension_url)); + + EXPECT_FALSE( + PermissionsData::HasHostPermission(extension.get(), settings_url)); + EXPECT_FALSE(PermissionsData::HasHostPermission(extension.get(), about_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Test * for scheme, which implies just the http/https schemes. + extension = LoadManifestStrict("script_and_capture", + "extension_wildcard.json"); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + extension = + LoadManifest("script_and_capture", "extension_wildcard_settings.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + + // Having chrome://*/ should not work for regular extensions. Note that + // for favicon access, we require the explicit pattern chrome://favicon/*. + std::string error; + extension = LoadManifestUnchecked("script_and_capture", + "extension_wildcard_chrome.json", + Manifest::INTERNAL, Extension::NO_FLAGS, + &error); + std::vector<InstallWarning> warnings = extension->install_warnings(); + EXPECT_FALSE(warnings.empty()); + EXPECT_EQ(ErrorUtils::FormatErrorMessage( + manifest_errors::kInvalidPermissionScheme, + "chrome://*/"), + warnings[0].message); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + + // Having chrome://favicon/* should not give you chrome://* + extension = LoadManifestStrict("script_and_capture", + "extension_chrome_favicon_wildcard.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(CaptureOnly(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Having http://favicon should not give you chrome://favicon + extension = LoadManifestStrict("script_and_capture", + "extension_http_favicon.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + + // Component extensions with <all_urls> should get everything. + extension = LoadManifest("script_and_capture", "extension_component_all.json", + Manifest::COMPONENT, Extension::NO_FLAGS); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Allowed(extension.get(), settings_url)); + EXPECT_TRUE(Allowed(extension.get(), about_url)); + EXPECT_TRUE(Allowed(extension.get(), favicon_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Component extensions should only get access to what they ask for. + extension = LoadManifest("script_and_capture", + "extension_component_google.json", Manifest::COMPONENT, + Extension::NO_FLAGS); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Blocked(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), extension_url)); + EXPECT_FALSE( + PermissionsData::HasHostPermission(extension.get(), settings_url)); +} + +TEST_F(ExtensionScriptAndCaptureVisibleTest, PermissionsWithChromeURLsEnabled) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kExtensionsOnChromeURLs); + + scoped_refptr<Extension> extension; + + // Test <all_urls> for regular extensions. + extension = LoadManifestStrict("script_and_capture", + "extension_regular_all.json"); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), extension_url)); + + // Test access to iframed content. + GURL within_extension_url = extension->GetResourceURL("page.html"); + EXPECT_TRUE(AllowedScript(extension.get(), http_url, http_url_with_path)); + EXPECT_TRUE(AllowedScript(extension.get(), https_url, http_url_with_path)); + EXPECT_TRUE(AllowedScript(extension.get(), http_url, within_extension_url)); + EXPECT_TRUE(AllowedScript(extension.get(), https_url, within_extension_url)); + EXPECT_TRUE(BlockedScript(extension.get(), http_url, extension_url)); + EXPECT_TRUE(BlockedScript(extension.get(), https_url, extension_url)); + + EXPECT_FALSE( + PermissionsData::HasHostPermission(extension.get(), settings_url)); + EXPECT_FALSE(PermissionsData::HasHostPermission(extension.get(), about_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Test * for scheme, which implies just the http/https schemes. + extension = LoadManifestStrict("script_and_capture", + "extension_wildcard.json"); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + extension = + LoadManifest("script_and_capture", "extension_wildcard_settings.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + + // Having chrome://*/ should work for regular extensions with the flag + // enabled. + std::string error; + extension = LoadManifestUnchecked("script_and_capture", + "extension_wildcard_chrome.json", + Manifest::INTERNAL, Extension::NO_FLAGS, + &error); + EXPECT_FALSE(extension.get() == NULL); + EXPECT_TRUE(Blocked(extension.get(), http_url)); + EXPECT_TRUE(Blocked(extension.get(), https_url)); + EXPECT_TRUE(Allowed(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested + + // Having chrome://favicon/* should not give you chrome://* + extension = LoadManifestStrict("script_and_capture", + "extension_chrome_favicon_wildcard.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Allowed(extension.get(), favicon_url)); // chrome:// requested + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Having http://favicon should not give you chrome://favicon + extension = LoadManifestStrict("script_and_capture", + "extension_http_favicon.json"); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + + // Component extensions with <all_urls> should get everything. + extension = LoadManifest("script_and_capture", "extension_component_all.json", + Manifest::COMPONENT, Extension::NO_FLAGS); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Allowed(extension.get(), https_url)); + EXPECT_TRUE(Allowed(extension.get(), settings_url)); + EXPECT_TRUE(Allowed(extension.get(), about_url)); + EXPECT_TRUE(Allowed(extension.get(), favicon_url)); + EXPECT_TRUE(PermissionsData::HasHostPermission(extension.get(), favicon_url)); + + // Component extensions should only get access to what they ask for. + extension = LoadManifest("script_and_capture", + "extension_component_google.json", Manifest::COMPONENT, + Extension::NO_FLAGS); + EXPECT_TRUE(Allowed(extension.get(), http_url)); + EXPECT_TRUE(Blocked(extension.get(), https_url)); + EXPECT_TRUE(Blocked(extension.get(), file_url)); + EXPECT_TRUE(Blocked(extension.get(), settings_url)); + EXPECT_TRUE(Blocked(extension.get(), favicon_url)); + EXPECT_TRUE(Blocked(extension.get(), about_url)); + EXPECT_TRUE(Blocked(extension.get(), extension_url)); + EXPECT_FALSE( + PermissionsData::HasHostPermission(extension.get(), settings_url)); +} + +TEST_F(ExtensionScriptAndCaptureVisibleTest, TabSpecific) { + scoped_refptr<Extension> extension = + LoadManifestStrict("script_and_capture", "tab_specific.json"); + + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 0) + .get()); + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 1) + .get()); + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 2) + .get()); + + std::set<GURL> no_urls; + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + + URLPatternSet allowed_hosts; + allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, + http_url.spec())); + std::set<GURL> allowed_urls; + allowed_urls.insert(http_url); + // http_url_with_path() will also be allowed, because Extension should be + // considering the security origin of the URL not the URL itself, and + // http_url is in allowed_hosts. + allowed_urls.insert(http_url_with_path); + + { + scoped_refptr<PermissionSet> permissions( + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + allowed_hosts, URLPatternSet())); + PermissionsData::UpdateTabSpecificPermissions( + extension.get(), 0, permissions); + EXPECT_EQ(permissions->explicit_hosts(), + PermissionsData::GetTabSpecificPermissions(extension.get(), 0) + ->explicit_hosts()); + } + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + + PermissionsData::ClearTabSpecificPermissions(extension.get(), 0); + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 0) + .get()); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + + std::set<GURL> more_allowed_urls = allowed_urls; + more_allowed_urls.insert(https_url); + URLPatternSet more_allowed_hosts = allowed_hosts; + more_allowed_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, + https_url.spec())); + + { + scoped_refptr<PermissionSet> permissions( + new PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + allowed_hosts, URLPatternSet())); + PermissionsData::UpdateTabSpecificPermissions( + extension.get(), 0, permissions); + EXPECT_EQ(permissions->explicit_hosts(), + PermissionsData::GetTabSpecificPermissions(extension.get(), 0) + ->explicit_hosts()); + + permissions = new PermissionSet(APIPermissionSet(), + ManifestPermissionSet(), + more_allowed_hosts, + URLPatternSet()); + PermissionsData::UpdateTabSpecificPermissions( + extension.get(), 1, permissions); + EXPECT_EQ(permissions->explicit_hosts(), + PermissionsData::GetTabSpecificPermissions(extension.get(), 1) + ->explicit_hosts()); + } + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), allowed_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + + PermissionsData::ClearTabSpecificPermissions(extension.get(), 0); + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 0) + .get()); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), more_allowed_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); + + PermissionsData::ClearTabSpecificPermissions(extension.get(), 1); + EXPECT_FALSE(PermissionsData::GetTabSpecificPermissions(extension.get(), 1) + .get()); + + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 0)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 1)); + EXPECT_TRUE(AllowedExclusivelyOnTab(extension.get(), no_urls, 2)); +} + +} // namespace extensions |