1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/common/manifest_handlers/permissions_parser.h"
7 #include "base/command_line.h"
8 #include "base/memory/ref_counted.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/values.h"
11 #include "content/public/common/url_constants.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/extensions_client.h"
15 #include "extensions/common/features/feature.h"
16 #include "extensions/common/features/feature_provider.h"
17 #include "extensions/common/manifest.h"
18 #include "extensions/common/manifest_constants.h"
19 #include "extensions/common/manifest_handler.h"
20 #include "extensions/common/permissions/api_permission_set.h"
21 #include "extensions/common/permissions/permission_set.h"
22 #include "extensions/common/permissions/permissions_data.h"
23 #include "extensions/common/switches.h"
24 #include "extensions/common/url_pattern_set.h"
25 #include "url/url_constants.h"
27 namespace extensions
{
31 namespace keys
= manifest_keys
;
32 namespace errors
= manifest_errors
;
34 struct ManifestPermissions
: public Extension::ManifestData
{
35 ManifestPermissions(scoped_refptr
<const PermissionSet
> permissions
);
36 virtual ~ManifestPermissions();
38 scoped_refptr
<const PermissionSet
> permissions
;
41 ManifestPermissions::ManifestPermissions(
42 scoped_refptr
<const PermissionSet
> permissions
)
43 : permissions(permissions
) {
46 ManifestPermissions::~ManifestPermissions() {
49 // Custom checks for the experimental permission that can't be expressed in
50 // _permission_features.json.
51 bool CanSpecifyExperimentalPermission(const Extension
* extension
) {
52 if (extension
->location() == Manifest::COMPONENT
)
55 if (CommandLine::ForCurrentProcess()->HasSwitch(
56 switches::kEnableExperimentalExtensionApis
)) {
60 // We rely on the webstore to check access to experimental. This way we can
61 // whitelist extensions to have access to experimental in just the store, and
62 // not have to push a new version of the client.
63 if (extension
->from_webstore())
69 // Checks whether the host |pattern| is allowed for the given |extension|,
70 // given API permissions |permissions|.
71 bool CanSpecifyHostPermission(const Extension
* extension
,
72 const URLPattern
& pattern
,
73 const APIPermissionSet
& permissions
) {
74 if (!pattern
.match_all_urls() &&
75 pattern
.MatchesScheme(content::kChromeUIScheme
)) {
76 URLPatternSet chrome_scheme_hosts
=
77 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(extension
,
79 if (chrome_scheme_hosts
.ContainsPattern(pattern
))
82 // Component extensions can have access to all of chrome://*.
83 if (PermissionsData::CanExecuteScriptEverywhere(extension
))
86 if (CommandLine::ForCurrentProcess()->HasSwitch(
87 switches::kExtensionsOnChromeURLs
)) {
91 // TODO(aboxhall): return from_webstore() when webstore handles blocking
92 // extensions which request chrome:// urls
96 // Otherwise, the valid schemes were handled by URLPattern.
100 // Parses the host and api permissions from the specified permission |key|
101 // from |extension|'s manifest.
102 bool ParseHelper(Extension
* extension
,
104 APIPermissionSet
* api_permissions
,
105 URLPatternSet
* host_permissions
,
106 base::string16
* error
) {
107 if (!extension
->manifest()->HasKey(key
))
110 const base::ListValue
* permissions
= NULL
;
111 if (!extension
->manifest()->GetList(key
, &permissions
)) {
112 *error
= ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions
,
117 // NOTE: We need to get the APIPermission before we check if features
118 // associated with them are available because the feature system does not
119 // know about aliases.
121 std::vector
<std::string
> host_data
;
122 if (!APIPermissionSet::ParseFromJSON(
124 APIPermissionSet::kDisallowInternalPermissions
,
131 // Verify feature availability of permissions.
132 std::vector
<APIPermission::ID
> to_remove
;
133 const FeatureProvider
* permission_features
=
134 FeatureProvider::GetPermissionFeatures();
135 for (APIPermissionSet::const_iterator iter
= api_permissions
->begin();
136 iter
!= api_permissions
->end();
138 Feature
* feature
= permission_features
->GetFeature(iter
->name());
140 // The feature should exist since we just got an APIPermission for it. The
141 // two systems should be updated together whenever a permission is added.
142 DCHECK(feature
) << "Could not find feature for " << iter
->name();
143 // http://crbug.com/176381
145 to_remove
.push_back(iter
->id());
149 Feature::Availability availability
=
150 feature
->IsAvailableToExtension(extension
);
151 if (!availability
.is_available()) {
152 // Don't fail, but warn the developer that the manifest contains
153 // unrecognized permissions. This may happen legitimately if the
154 // extensions requests platform- or channel-specific permissions.
155 extension
->AddInstallWarning(
156 InstallWarning(availability
.message(), feature
->name()));
157 to_remove
.push_back(iter
->id());
161 if (iter
->id() == APIPermission::kExperimental
) {
162 if (!CanSpecifyExperimentalPermission(extension
)) {
163 *error
= base::ASCIIToUTF16(errors::kExperimentalFlagRequired
);
169 api_permissions
->AddImpliedPermissions();
171 // Remove permissions that are not available to this extension.
172 for (std::vector
<APIPermission::ID
>::const_iterator iter
= to_remove
.begin();
173 iter
!= to_remove
.end();
175 api_permissions
->erase(*iter
);
178 // Parse host pattern permissions.
179 const int kAllowedSchemes
=
180 PermissionsData::CanExecuteScriptEverywhere(extension
)
181 ? URLPattern::SCHEME_ALL
182 : Extension::kValidHostPermissionSchemes
;
184 for (std::vector
<std::string
>::const_iterator iter
= host_data
.begin();
185 iter
!= host_data
.end();
187 const std::string
& permission_str
= *iter
;
189 // Check if it's a host pattern permission.
190 URLPattern pattern
= URLPattern(kAllowedSchemes
);
191 URLPattern::ParseResult parse_result
= pattern
.Parse(permission_str
);
192 if (parse_result
== URLPattern::PARSE_SUCCESS
) {
193 // The path component is not used for host permissions, so we force it
194 // to match all paths.
195 pattern
.SetPath("/*");
196 int valid_schemes
= pattern
.valid_schemes();
197 if (pattern
.MatchesScheme(url::kFileScheme
) &&
198 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
199 extension
->set_wants_file_access(true);
200 if (!(extension
->creation_flags() & Extension::ALLOW_FILE_ACCESS
))
201 valid_schemes
&= ~URLPattern::SCHEME_FILE
;
204 if (pattern
.scheme() != content::kChromeUIScheme
&&
205 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
206 // Keep chrome:// in allowed schemes only if it's explicitly requested
207 // or CanExecuteScriptEverywhere is true. If the
208 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
209 // will fail, so don't check the flag here.
210 valid_schemes
&= ~URLPattern::SCHEME_CHROMEUI
;
212 pattern
.SetValidSchemes(valid_schemes
);
214 if (!CanSpecifyHostPermission(extension
, pattern
, *api_permissions
)) {
215 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
217 extension
->AddInstallWarning(InstallWarning(
218 ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme
,
225 host_permissions
->AddPattern(pattern
);
226 // We need to make sure all_urls matches chrome://favicon and (maybe)
227 // chrome://thumbnail, so add them back in to host_permissions separately.
228 if (pattern
.match_all_urls()) {
229 host_permissions
->AddPatterns(
230 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
231 extension
, *api_permissions
));
236 // It's probably an unknown API permission. Do not throw an error so
237 // extensions can retain backwards compatability (http://crbug.com/42742).
238 extension
->AddInstallWarning(InstallWarning(
239 ErrorUtils::FormatErrorMessage(
240 manifest_errors::kPermissionUnknownOrMalformed
, permission_str
),
250 struct PermissionsParser::InitialPermissions
{
251 APIPermissionSet api_permissions
;
252 ManifestPermissionSet manifest_permissions
;
253 URLPatternSet host_permissions
;
254 URLPatternSet scriptable_hosts
;
257 PermissionsParser::PermissionsParser() {
260 PermissionsParser::~PermissionsParser() {
263 bool PermissionsParser::Parse(Extension
* extension
, base::string16
* error
) {
264 initial_required_permissions_
.reset(new InitialPermissions
);
265 if (!ParseHelper(extension
,
267 &initial_required_permissions_
->api_permissions
,
268 &initial_required_permissions_
->host_permissions
,
273 initial_optional_permissions_
.reset(new InitialPermissions
);
274 if (!ParseHelper(extension
,
275 keys::kOptionalPermissions
,
276 &initial_optional_permissions_
->api_permissions
,
277 &initial_optional_permissions_
->host_permissions
,
285 void PermissionsParser::Finalize(Extension
* extension
) {
286 ManifestHandler::AddExtensionInitialRequiredPermissions(
287 extension
, &initial_required_permissions_
->manifest_permissions
);
289 scoped_refptr
<const PermissionSet
> required_permissions(
290 new PermissionSet(initial_required_permissions_
->api_permissions
,
291 initial_required_permissions_
->manifest_permissions
,
292 initial_required_permissions_
->host_permissions
,
293 initial_required_permissions_
->scriptable_hosts
));
294 extension
->SetManifestData(keys::kPermissions
,
295 new ManifestPermissions(required_permissions
));
297 scoped_refptr
<const PermissionSet
> optional_permissions(
298 new PermissionSet(initial_optional_permissions_
->api_permissions
,
299 initial_optional_permissions_
->manifest_permissions
,
300 initial_optional_permissions_
->host_permissions
,
302 extension
->SetManifestData(keys::kOptionalPermissions
,
303 new ManifestPermissions(optional_permissions
));
307 void PermissionsParser::AddAPIPermission(Extension
* extension
,
308 APIPermission::ID permission
) {
309 DCHECK(extension
->permissions_parser());
310 extension
->permissions_parser()
311 ->initial_required_permissions_
->api_permissions
.insert(permission
);
315 void PermissionsParser::AddAPIPermission(Extension
* extension
,
316 APIPermission
* permission
) {
317 DCHECK(extension
->permissions_parser());
318 extension
->permissions_parser()
319 ->initial_required_permissions_
->api_permissions
.insert(permission
);
323 bool PermissionsParser::HasAPIPermission(const Extension
* extension
,
324 APIPermission::ID permission
) {
325 DCHECK(extension
->permissions_parser());
326 return extension
->permissions_parser()
327 ->initial_required_permissions_
->api_permissions
.count(
332 void PermissionsParser::SetScriptableHosts(
333 Extension
* extension
,
334 const URLPatternSet
& scriptable_hosts
) {
335 DCHECK(extension
->permissions_parser());
336 extension
->permissions_parser()
337 ->initial_required_permissions_
->scriptable_hosts
= scriptable_hosts
;
341 scoped_refptr
<const PermissionSet
> PermissionsParser::GetRequiredPermissions(
342 const Extension
* extension
) {
343 DCHECK(extension
->GetManifestData(keys::kPermissions
));
344 return static_cast<const ManifestPermissions
*>(
345 extension
->GetManifestData(keys::kPermissions
))->permissions
;
349 scoped_refptr
<const PermissionSet
> PermissionsParser::GetOptionalPermissions(
350 const Extension
* extension
) {
351 DCHECK(extension
->GetManifestData(keys::kOptionalPermissions
));
352 return static_cast<const ManifestPermissions
*>(
353 extension
->GetManifestData(keys::kOptionalPermissions
))
357 } // namespace extensions