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 ~ManifestPermissions() override
;
38 scoped_refptr
<const PermissionSet
> permissions
;
41 ManifestPermissions::ManifestPermissions(
42 scoped_refptr
<const PermissionSet
> permissions
)
43 : permissions(permissions
) {
46 ManifestPermissions::~ManifestPermissions() {
49 // Checks whether the host |pattern| is allowed for the given |extension|,
50 // given API permissions |permissions|.
51 bool CanSpecifyHostPermission(const Extension
* extension
,
52 const URLPattern
& pattern
,
53 const APIPermissionSet
& permissions
) {
54 if (!pattern
.match_all_urls() &&
55 pattern
.MatchesScheme(content::kChromeUIScheme
)) {
56 URLPatternSet chrome_scheme_hosts
=
57 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(extension
,
59 if (chrome_scheme_hosts
.ContainsPattern(pattern
))
62 // Component extensions can have access to all of chrome://*.
63 if (PermissionsData::CanExecuteScriptEverywhere(extension
))
66 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
67 switches::kExtensionsOnChromeURLs
)) {
71 // TODO(aboxhall): return from_webstore() when webstore handles blocking
72 // extensions which request chrome:// urls
76 // Otherwise, the valid schemes were handled by URLPattern.
80 // Parses the host and api permissions from the specified permission |key|
81 // from |extension|'s manifest.
82 bool ParseHelper(Extension
* extension
,
84 APIPermissionSet
* api_permissions
,
85 URLPatternSet
* host_permissions
,
86 base::string16
* error
) {
87 if (!extension
->manifest()->HasKey(key
))
90 const base::ListValue
* permissions
= NULL
;
91 if (!extension
->manifest()->GetList(key
, &permissions
)) {
92 *error
= ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions
,
97 // NOTE: We need to get the APIPermission before we check if features
98 // associated with them are available because the feature system does not
99 // know about aliases.
101 std::vector
<std::string
> host_data
;
102 if (!APIPermissionSet::ParseFromJSON(
104 APIPermissionSet::kDisallowInternalPermissions
,
111 // Verify feature availability of permissions.
112 std::vector
<APIPermission::ID
> to_remove
;
113 const FeatureProvider
* permission_features
=
114 FeatureProvider::GetPermissionFeatures();
115 for (APIPermissionSet::const_iterator iter
= api_permissions
->begin();
116 iter
!= api_permissions
->end();
118 Feature
* feature
= permission_features
->GetFeature(iter
->name());
120 // The feature should exist since we just got an APIPermission for it. The
121 // two systems should be updated together whenever a permission is added.
122 DCHECK(feature
) << "Could not find feature for " << iter
->name();
123 // http://crbug.com/176381
125 to_remove
.push_back(iter
->id());
129 // Sneaky check for "experimental", which we always allow for extensions
130 // installed from the Webstore. This way we can whitelist extensions to
131 // have access to experimental in just the store, and not have to push a
132 // new version of the client. Otherwise, experimental goes through the
133 // usual features check.
134 if (iter
->id() == APIPermission::kExperimental
&&
135 extension
->from_webstore()) {
139 Feature::Availability availability
=
140 feature
->IsAvailableToExtension(extension
);
141 if (!availability
.is_available()) {
142 // Don't fail, but warn the developer that the manifest contains
143 // unrecognized permissions. This may happen legitimately if the
144 // extensions requests platform- or channel-specific permissions.
145 extension
->AddInstallWarning(
146 InstallWarning(availability
.message(), feature
->name()));
147 to_remove
.push_back(iter
->id());
152 // Remove permissions that are not available to this extension.
153 for (std::vector
<APIPermission::ID
>::const_iterator iter
= to_remove
.begin();
154 iter
!= to_remove
.end();
156 api_permissions
->erase(*iter
);
159 // Parse host pattern permissions.
160 const int kAllowedSchemes
=
161 PermissionsData::CanExecuteScriptEverywhere(extension
)
162 ? URLPattern::SCHEME_ALL
163 : Extension::kValidHostPermissionSchemes
;
165 for (std::vector
<std::string
>::const_iterator iter
= host_data
.begin();
166 iter
!= host_data
.end();
168 const std::string
& permission_str
= *iter
;
170 // Check if it's a host pattern permission.
171 URLPattern pattern
= URLPattern(kAllowedSchemes
);
172 URLPattern::ParseResult parse_result
= pattern
.Parse(permission_str
);
173 if (parse_result
== URLPattern::PARSE_SUCCESS
) {
174 // The path component is not used for host permissions, so we force it
175 // to match all paths.
176 pattern
.SetPath("/*");
177 int valid_schemes
= pattern
.valid_schemes();
178 if (pattern
.MatchesScheme(url::kFileScheme
) &&
179 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
180 extension
->set_wants_file_access(true);
181 if (!(extension
->creation_flags() & Extension::ALLOW_FILE_ACCESS
))
182 valid_schemes
&= ~URLPattern::SCHEME_FILE
;
185 if (pattern
.scheme() != content::kChromeUIScheme
&&
186 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
187 // Keep chrome:// in allowed schemes only if it's explicitly requested
188 // or CanExecuteScriptEverywhere is true. If the
189 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
190 // will fail, so don't check the flag here.
191 valid_schemes
&= ~URLPattern::SCHEME_CHROMEUI
;
193 pattern
.SetValidSchemes(valid_schemes
);
195 if (!CanSpecifyHostPermission(extension
, pattern
, *api_permissions
)) {
196 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
198 extension
->AddInstallWarning(InstallWarning(
199 ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme
,
206 host_permissions
->AddPattern(pattern
);
207 // We need to make sure all_urls matches chrome://favicon and (maybe)
208 // chrome://thumbnail, so add them back in to host_permissions separately.
209 if (pattern
.match_all_urls()) {
210 host_permissions
->AddPatterns(
211 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
212 extension
, *api_permissions
));
217 // It's probably an unknown API permission. Do not throw an error so
218 // extensions can retain backwards compatability (http://crbug.com/42742).
219 extension
->AddInstallWarning(InstallWarning(
220 ErrorUtils::FormatErrorMessage(
221 manifest_errors::kPermissionUnknownOrMalformed
, permission_str
),
231 struct PermissionsParser::InitialPermissions
{
232 APIPermissionSet api_permissions
;
233 ManifestPermissionSet manifest_permissions
;
234 URLPatternSet host_permissions
;
235 URLPatternSet scriptable_hosts
;
238 PermissionsParser::PermissionsParser() {
241 PermissionsParser::~PermissionsParser() {
244 bool PermissionsParser::Parse(Extension
* extension
, base::string16
* error
) {
245 initial_required_permissions_
.reset(new InitialPermissions
);
246 if (!ParseHelper(extension
,
248 &initial_required_permissions_
->api_permissions
,
249 &initial_required_permissions_
->host_permissions
,
254 initial_optional_permissions_
.reset(new InitialPermissions
);
255 if (!ParseHelper(extension
,
256 keys::kOptionalPermissions
,
257 &initial_optional_permissions_
->api_permissions
,
258 &initial_optional_permissions_
->host_permissions
,
266 void PermissionsParser::Finalize(Extension
* extension
) {
267 ManifestHandler::AddExtensionInitialRequiredPermissions(
268 extension
, &initial_required_permissions_
->manifest_permissions
);
270 scoped_refptr
<const PermissionSet
> required_permissions(
271 new PermissionSet(initial_required_permissions_
->api_permissions
,
272 initial_required_permissions_
->manifest_permissions
,
273 initial_required_permissions_
->host_permissions
,
274 initial_required_permissions_
->scriptable_hosts
));
275 extension
->SetManifestData(keys::kPermissions
,
276 new ManifestPermissions(required_permissions
));
278 scoped_refptr
<const PermissionSet
> optional_permissions(
279 new PermissionSet(initial_optional_permissions_
->api_permissions
,
280 initial_optional_permissions_
->manifest_permissions
,
281 initial_optional_permissions_
->host_permissions
,
283 extension
->SetManifestData(keys::kOptionalPermissions
,
284 new ManifestPermissions(optional_permissions
));
288 void PermissionsParser::AddAPIPermission(Extension
* extension
,
289 APIPermission::ID permission
) {
290 DCHECK(extension
->permissions_parser());
291 extension
->permissions_parser()
292 ->initial_required_permissions_
->api_permissions
.insert(permission
);
296 void PermissionsParser::AddAPIPermission(Extension
* extension
,
297 APIPermission
* permission
) {
298 DCHECK(extension
->permissions_parser());
299 extension
->permissions_parser()
300 ->initial_required_permissions_
->api_permissions
.insert(permission
);
304 bool PermissionsParser::HasAPIPermission(const Extension
* extension
,
305 APIPermission::ID permission
) {
306 DCHECK(extension
->permissions_parser());
307 return extension
->permissions_parser()
308 ->initial_required_permissions_
->api_permissions
.count(
313 void PermissionsParser::SetScriptableHosts(
314 Extension
* extension
,
315 const URLPatternSet
& scriptable_hosts
) {
316 DCHECK(extension
->permissions_parser());
317 extension
->permissions_parser()
318 ->initial_required_permissions_
->scriptable_hosts
= scriptable_hosts
;
322 scoped_refptr
<const PermissionSet
> PermissionsParser::GetRequiredPermissions(
323 const Extension
* extension
) {
324 DCHECK(extension
->GetManifestData(keys::kPermissions
));
325 return static_cast<const ManifestPermissions
*>(
326 extension
->GetManifestData(keys::kPermissions
))->permissions
;
330 scoped_refptr
<const PermissionSet
> PermissionsParser::GetOptionalPermissions(
331 const Extension
* extension
) {
332 DCHECK(extension
->GetManifestData(keys::kOptionalPermissions
));
333 return static_cast<const ManifestPermissions
*>(
334 extension
->GetManifestData(keys::kOptionalPermissions
))
338 } // namespace extensions