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 api_permissions
->AddImpliedPermissions();
154 // Remove permissions that are not available to this extension.
155 for (std::vector
<APIPermission::ID
>::const_iterator iter
= to_remove
.begin();
156 iter
!= to_remove
.end();
158 api_permissions
->erase(*iter
);
161 // Parse host pattern permissions.
162 const int kAllowedSchemes
=
163 PermissionsData::CanExecuteScriptEverywhere(extension
)
164 ? URLPattern::SCHEME_ALL
165 : Extension::kValidHostPermissionSchemes
;
167 for (std::vector
<std::string
>::const_iterator iter
= host_data
.begin();
168 iter
!= host_data
.end();
170 const std::string
& permission_str
= *iter
;
172 // Check if it's a host pattern permission.
173 URLPattern pattern
= URLPattern(kAllowedSchemes
);
174 URLPattern::ParseResult parse_result
= pattern
.Parse(permission_str
);
175 if (parse_result
== URLPattern::PARSE_SUCCESS
) {
176 // The path component is not used for host permissions, so we force it
177 // to match all paths.
178 pattern
.SetPath("/*");
179 int valid_schemes
= pattern
.valid_schemes();
180 if (pattern
.MatchesScheme(url::kFileScheme
) &&
181 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
182 extension
->set_wants_file_access(true);
183 if (!(extension
->creation_flags() & Extension::ALLOW_FILE_ACCESS
))
184 valid_schemes
&= ~URLPattern::SCHEME_FILE
;
187 if (pattern
.scheme() != content::kChromeUIScheme
&&
188 !PermissionsData::CanExecuteScriptEverywhere(extension
)) {
189 // Keep chrome:// in allowed schemes only if it's explicitly requested
190 // or CanExecuteScriptEverywhere is true. If the
191 // extensions_on_chrome_urls flag is not set, CanSpecifyHostPermission
192 // will fail, so don't check the flag here.
193 valid_schemes
&= ~URLPattern::SCHEME_CHROMEUI
;
195 pattern
.SetValidSchemes(valid_schemes
);
197 if (!CanSpecifyHostPermission(extension
, pattern
, *api_permissions
)) {
198 // TODO(aboxhall): make a warning (see pattern.match_all_urls() block
200 extension
->AddInstallWarning(InstallWarning(
201 ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme
,
208 host_permissions
->AddPattern(pattern
);
209 // We need to make sure all_urls matches chrome://favicon and (maybe)
210 // chrome://thumbnail, so add them back in to host_permissions separately.
211 if (pattern
.match_all_urls()) {
212 host_permissions
->AddPatterns(
213 ExtensionsClient::Get()->GetPermittedChromeSchemeHosts(
214 extension
, *api_permissions
));
219 // It's probably an unknown API permission. Do not throw an error so
220 // extensions can retain backwards compatability (http://crbug.com/42742).
221 extension
->AddInstallWarning(InstallWarning(
222 ErrorUtils::FormatErrorMessage(
223 manifest_errors::kPermissionUnknownOrMalformed
, permission_str
),
233 struct PermissionsParser::InitialPermissions
{
234 APIPermissionSet api_permissions
;
235 ManifestPermissionSet manifest_permissions
;
236 URLPatternSet host_permissions
;
237 URLPatternSet scriptable_hosts
;
240 PermissionsParser::PermissionsParser() {
243 PermissionsParser::~PermissionsParser() {
246 bool PermissionsParser::Parse(Extension
* extension
, base::string16
* error
) {
247 initial_required_permissions_
.reset(new InitialPermissions
);
248 if (!ParseHelper(extension
,
250 &initial_required_permissions_
->api_permissions
,
251 &initial_required_permissions_
->host_permissions
,
256 initial_optional_permissions_
.reset(new InitialPermissions
);
257 if (!ParseHelper(extension
,
258 keys::kOptionalPermissions
,
259 &initial_optional_permissions_
->api_permissions
,
260 &initial_optional_permissions_
->host_permissions
,
268 void PermissionsParser::Finalize(Extension
* extension
) {
269 ManifestHandler::AddExtensionInitialRequiredPermissions(
270 extension
, &initial_required_permissions_
->manifest_permissions
);
272 scoped_refptr
<const PermissionSet
> required_permissions(
273 new PermissionSet(initial_required_permissions_
->api_permissions
,
274 initial_required_permissions_
->manifest_permissions
,
275 initial_required_permissions_
->host_permissions
,
276 initial_required_permissions_
->scriptable_hosts
));
277 extension
->SetManifestData(keys::kPermissions
,
278 new ManifestPermissions(required_permissions
));
280 scoped_refptr
<const PermissionSet
> optional_permissions(
281 new PermissionSet(initial_optional_permissions_
->api_permissions
,
282 initial_optional_permissions_
->manifest_permissions
,
283 initial_optional_permissions_
->host_permissions
,
285 extension
->SetManifestData(keys::kOptionalPermissions
,
286 new ManifestPermissions(optional_permissions
));
290 void PermissionsParser::AddAPIPermission(Extension
* extension
,
291 APIPermission::ID permission
) {
292 DCHECK(extension
->permissions_parser());
293 extension
->permissions_parser()
294 ->initial_required_permissions_
->api_permissions
.insert(permission
);
298 void PermissionsParser::AddAPIPermission(Extension
* extension
,
299 APIPermission
* permission
) {
300 DCHECK(extension
->permissions_parser());
301 extension
->permissions_parser()
302 ->initial_required_permissions_
->api_permissions
.insert(permission
);
306 bool PermissionsParser::HasAPIPermission(const Extension
* extension
,
307 APIPermission::ID permission
) {
308 DCHECK(extension
->permissions_parser());
309 return extension
->permissions_parser()
310 ->initial_required_permissions_
->api_permissions
.count(
315 void PermissionsParser::SetScriptableHosts(
316 Extension
* extension
,
317 const URLPatternSet
& scriptable_hosts
) {
318 DCHECK(extension
->permissions_parser());
319 extension
->permissions_parser()
320 ->initial_required_permissions_
->scriptable_hosts
= scriptable_hosts
;
324 scoped_refptr
<const PermissionSet
> PermissionsParser::GetRequiredPermissions(
325 const Extension
* extension
) {
326 DCHECK(extension
->GetManifestData(keys::kPermissions
));
327 return static_cast<const ManifestPermissions
*>(
328 extension
->GetManifestData(keys::kPermissions
))->permissions
;
332 scoped_refptr
<const PermissionSet
> PermissionsParser::GetOptionalPermissions(
333 const Extension
* extension
) {
334 DCHECK(extension
->GetManifestData(keys::kOptionalPermissions
));
335 return static_cast<const ManifestPermissions
*>(
336 extension
->GetManifestData(keys::kOptionalPermissions
))
340 } // namespace extensions