Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / common / manifest_handlers / permissions_parser.cc
blobe221ed36e78ff81af6dfacb84e06f2b45b1ec2be
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 {
29 namespace {
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,
58 permissions);
59 if (chrome_scheme_hosts.ContainsPattern(pattern))
60 return true;
62 // Component extensions can have access to all of chrome://*.
63 if (PermissionsData::CanExecuteScriptEverywhere(extension))
64 return true;
66 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
67 switches::kExtensionsOnChromeURLs)) {
68 return true;
71 // TODO(aboxhall): return from_webstore() when webstore handles blocking
72 // extensions which request chrome:// urls
73 return false;
76 // Otherwise, the valid schemes were handled by URLPattern.
77 return true;
80 // Parses the host and api permissions from the specified permission |key|
81 // from |extension|'s manifest.
82 bool ParseHelper(Extension* extension,
83 const char* key,
84 APIPermissionSet* api_permissions,
85 URLPatternSet* host_permissions,
86 base::string16* error) {
87 if (!extension->manifest()->HasKey(key))
88 return true;
90 const base::ListValue* permissions = NULL;
91 if (!extension->manifest()->GetList(key, &permissions)) {
92 *error = ErrorUtils::FormatErrorMessageUTF16(errors::kInvalidPermissions,
93 std::string());
94 return false;
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(
103 permissions,
104 APIPermissionSet::kDisallowInternalPermissions,
105 api_permissions,
106 error,
107 &host_data)) {
108 return false;
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();
117 ++iter) {
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
124 if (!feature) {
125 to_remove.push_back(iter->id());
126 continue;
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()) {
136 continue;
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());
148 continue;
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();
155 ++iter) {
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();
167 ++iter) {
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
197 // below).
198 extension->AddInstallWarning(InstallWarning(
199 ErrorUtils::FormatErrorMessage(errors::kInvalidPermissionScheme,
200 permission_str),
201 key,
202 permission_str));
203 continue;
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));
214 continue;
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),
222 key,
223 permission_str));
226 return true;
229 } // namespace
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,
247 keys::kPermissions,
248 &initial_required_permissions_->api_permissions,
249 &initial_required_permissions_->host_permissions,
250 error)) {
251 return false;
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,
259 error)) {
260 return false;
263 return true;
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,
282 URLPatternSet()));
283 extension->SetManifestData(keys::kOptionalPermissions,
284 new ManifestPermissions(optional_permissions));
287 // static
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);
295 // static
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);
303 // static
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(
309 permission) > 0;
312 // static
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;
321 // static
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;
329 // static
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))
335 ->permissions;
338 } // namespace extensions