1 // Copyright 2013 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.
4
5 #include "extensions/common/manifest_handlers/background_info.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/lazy_instance.h"
14 #include "base/macros.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "extensions/common/constants.h"
18 #include "extensions/common/error_utils.h"
19 #include "extensions/common/file_util.h"
20 #include "extensions/common/manifest_constants.h"
21 #include "extensions/common/manifest_handlers/permissions_parser.h"
22 #include "extensions/common/permissions/api_permission_set.h"
23 #include "extensions/common/switches.h"
24 #include "extensions/strings/grit/extensions_strings.h"
25 #include "ui/base/l10n/l10n_util.h"
26
27 using base::ASCIIToUTF16;
28
29 namespace extensions {
30
31 namespace keys = manifest_keys;
32 namespace values = manifest_values;
33 namespace errors = manifest_errors;
34
35 namespace {
36
37 const char kBackground[] = "background";
38
39 static base::LazyInstance<BackgroundInfo>::DestructorAtExit
40 g_empty_background_info = LAZY_INSTANCE_INITIALIZER;
41
GetBackgroundInfo(const Extension * extension)42 const BackgroundInfo& GetBackgroundInfo(const Extension* extension) {
43 BackgroundInfo* info = static_cast<BackgroundInfo*>(
44 extension->GetManifestData(kBackground));
45 if (!info)
46 return g_empty_background_info.Get();
47 return *info;
48 }
49
50 // Checks features that are restricted to manifest v2, populating |error| if
51 // the |extension|'s manifest version is too high.
52 // TODO(devlin): It's unfortunate that the features system doesn't handle this
53 // automatically, but it only adds a warning (rather than an error). Depending
54 // on how many keys we want to error on, it may make sense to change that.
CheckManifestV2RestrictedFeatures(const Extension * extension,base::string16 * error)55 bool CheckManifestV2RestrictedFeatures(const Extension* extension,
56 base::string16* error) {
57 if (extension->manifest_version() < 3) {
58 // No special restrictions for manifest v2 extensions (or v1, if the legacy
59 // commandline flag is being used).
60 return true;
61 }
62
63 auto check_path = [error, extension](const char* path) {
64 if (extension->manifest()->HasPath(path)) {
65 *error = base::UTF8ToUTF16(ErrorUtils::FormatErrorMessage(
66 errors::kBackgroundSpecificationInvalidForManifestV3, path));
67 return false;
68 }
69 return true;
70 };
71
72 if (!check_path(keys::kBackgroundPage) ||
73 !check_path(keys::kBackgroundScripts) ||
74 !check_path(keys::kBackgroundPersistent)) {
75 return false;
76 }
77
78 return true;
79 }
80
81 } // namespace
82
BackgroundInfo()83 BackgroundInfo::BackgroundInfo()
84 : is_persistent_(true),
85 allow_js_access_(true) {
86 }
87
~BackgroundInfo()88 BackgroundInfo::~BackgroundInfo() {
89 }
90
91 // static
GetBackgroundURL(const Extension * extension)92 GURL BackgroundInfo::GetBackgroundURL(const Extension* extension) {
93 const BackgroundInfo& info = GetBackgroundInfo(extension);
94 if (info.background_scripts_.empty())
95 return info.background_url_;
96 return extension->GetResourceURL(kGeneratedBackgroundPageFilename);
97 }
98
99 // static
GetBackgroundServiceWorkerScript(const Extension * extension)100 const std::string& BackgroundInfo::GetBackgroundServiceWorkerScript(
101 const Extension* extension) {
102 const BackgroundInfo& info = GetBackgroundInfo(extension);
103 DCHECK(info.background_service_worker_script_.has_value());
104 return *info.background_service_worker_script_;
105 }
106
107 // static
GetBackgroundScripts(const Extension * extension)108 const std::vector<std::string>& BackgroundInfo::GetBackgroundScripts(
109 const Extension* extension) {
110 return GetBackgroundInfo(extension).background_scripts_;
111 }
112
113 // static
HasBackgroundPage(const Extension * extension)114 bool BackgroundInfo::HasBackgroundPage(const Extension* extension) {
115 return GetBackgroundInfo(extension).has_background_page();
116 }
117
118 // static
HasPersistentBackgroundPage(const Extension * extension)119 bool BackgroundInfo::HasPersistentBackgroundPage(const Extension* extension) {
120 return GetBackgroundInfo(extension).has_persistent_background_page();
121 }
122
123 // static
HasLazyBackgroundPage(const Extension * extension)124 bool BackgroundInfo::HasLazyBackgroundPage(const Extension* extension) {
125 return GetBackgroundInfo(extension).has_lazy_background_page();
126 }
127
128 // static
HasGeneratedBackgroundPage(const Extension * extension)129 bool BackgroundInfo::HasGeneratedBackgroundPage(const Extension* extension) {
130 const BackgroundInfo& info = GetBackgroundInfo(extension);
131 return !info.background_scripts_.empty();
132 }
133
134 // static
AllowJSAccess(const Extension * extension)135 bool BackgroundInfo::AllowJSAccess(const Extension* extension) {
136 return GetBackgroundInfo(extension).allow_js_access_;
137 }
138
139 // static
IsServiceWorkerBased(const Extension * extension)140 bool BackgroundInfo::IsServiceWorkerBased(const Extension* extension) {
141 return GetBackgroundInfo(extension)
142 .background_service_worker_script_.has_value();
143 }
144
Parse(const Extension * extension,base::string16 * error)145 bool BackgroundInfo::Parse(const Extension* extension, base::string16* error) {
146 const std::string& bg_scripts_key = extension->is_platform_app() ?
147 keys::kPlatformAppBackgroundScripts : keys::kBackgroundScripts;
148 if (!CheckManifestV2RestrictedFeatures(extension, error) ||
149 !LoadBackgroundScripts(extension, bg_scripts_key, error) ||
150 !LoadBackgroundPage(extension, error) ||
151 !LoadBackgroundServiceWorkerScript(extension, error) ||
152 !LoadBackgroundPersistent(extension, error) ||
153 !LoadAllowJSAccess(extension, error)) {
154 return false;
155 }
156
157 int background_solution_sum =
158 (background_url_.is_valid() ? 1 : 0) +
159 (!background_scripts_.empty() ? 1 : 0) +
160 (background_service_worker_script_.has_value() ? 1 : 0);
161 if (background_solution_sum > 1) {
162 *error = ASCIIToUTF16(errors::kInvalidBackgroundCombination);
163 return false;
164 }
165
166 return true;
167 }
168
LoadBackgroundScripts(const Extension * extension,const std::string & key,base::string16 * error)169 bool BackgroundInfo::LoadBackgroundScripts(const Extension* extension,
170 const std::string& key,
171 base::string16* error) {
172 const base::Value* background_scripts_value = nullptr;
173 if (!extension->manifest()->Get(key, &background_scripts_value))
174 return true;
175
176 CHECK(background_scripts_value);
177 if (!background_scripts_value->is_list()) {
178 *error = ASCIIToUTF16(errors::kInvalidBackgroundScripts);
179 return false;
180 }
181
182 base::Value::ConstListView background_scripts =
183 background_scripts_value->GetList();
184 for (size_t i = 0; i < background_scripts.size(); ++i) {
185 if (!background_scripts[i].is_string()) {
186 *error = ErrorUtils::FormatErrorMessageUTF16(
187 errors::kInvalidBackgroundScript, base::NumberToString(i));
188 return false;
189 }
190 background_scripts_.push_back(background_scripts[i].GetString());
191 }
192
193 return true;
194 }
195
LoadBackgroundPage(const Extension * extension,const std::string & key,base::string16 * error)196 bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
197 const std::string& key,
198 base::string16* error) {
199 const base::Value* background_page_value = nullptr;
200 if (!extension->manifest()->Get(key, &background_page_value))
201 return true;
202
203 if (!background_page_value->is_string()) {
204 *error = ASCIIToUTF16(errors::kInvalidBackground);
205 return false;
206 }
207 const std::string& background_str = background_page_value->GetString();
208
209 if (extension->is_hosted_app()) {
210 background_url_ = GURL(background_str);
211
212 if (!PermissionsParser::HasAPIPermission(extension,
213 APIPermission::kBackground)) {
214 *error = ASCIIToUTF16(errors::kBackgroundPermissionNeeded);
215 return false;
216 }
217 // Hosted apps require an absolute URL.
218 if (!background_url_.is_valid()) {
219 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
220 return false;
221 }
222
223 if (!(background_url_.SchemeIs("https") ||
224 (base::CommandLine::ForCurrentProcess()->HasSwitch(
225 switches::kAllowHTTPBackgroundPage) &&
226 background_url_.SchemeIs("http")))) {
227 *error = ASCIIToUTF16(errors::kInvalidBackgroundInHostedApp);
228 return false;
229 }
230 } else {
231 background_url_ = extension->GetResourceURL(background_str);
232 }
233
234 return true;
235 }
236
LoadBackgroundServiceWorkerScript(const Extension * extension,base::string16 * error)237 bool BackgroundInfo::LoadBackgroundServiceWorkerScript(
238 const Extension* extension,
239 base::string16* error) {
240 const base::Value* scripts_value = nullptr;
241 if (!extension->manifest()->Get(keys::kBackgroundServiceWorkerScript,
242 &scripts_value)) {
243 return true;
244 }
245
246 DCHECK(scripts_value);
247 if (!scripts_value->is_string()) {
248 *error = ASCIIToUTF16(errors::kInvalidBackgroundServiceWorkerScript);
249 return false;
250 }
251
252 background_service_worker_script_ = scripts_value->GetString();
253
254 return true;
255 }
256
LoadBackgroundPage(const Extension * extension,base::string16 * error)257 bool BackgroundInfo::LoadBackgroundPage(const Extension* extension,
258 base::string16* error) {
259 const char* key = extension->is_platform_app()
260 ? keys::kPlatformAppBackgroundPage
261 : keys::kBackgroundPage;
262 return LoadBackgroundPage(extension, key, error);
263 }
264
LoadBackgroundPersistent(const Extension * extension,base::string16 * error)265 bool BackgroundInfo::LoadBackgroundPersistent(const Extension* extension,
266 base::string16* error) {
267 if (extension->is_platform_app()) {
268 is_persistent_ = false;
269 return true;
270 }
271
272 const base::Value* background_persistent = NULL;
273 if (!extension->manifest()->Get(keys::kBackgroundPersistent,
274 &background_persistent)) {
275 return true;
276 }
277
278 if (!background_persistent->GetAsBoolean(&is_persistent_)) {
279 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistent);
280 return false;
281 }
282
283 if (!has_background_page()) {
284 *error = ASCIIToUTF16(errors::kInvalidBackgroundPersistentNoPage);
285 return false;
286 }
287
288 return true;
289 }
290
LoadAllowJSAccess(const Extension * extension,base::string16 * error)291 bool BackgroundInfo::LoadAllowJSAccess(const Extension* extension,
292 base::string16* error) {
293 const base::Value* allow_js_access = NULL;
294 if (!extension->manifest()->Get(keys::kBackgroundAllowJsAccess,
295 &allow_js_access))
296 return true;
297
298 if (!allow_js_access->is_bool() ||
299 !allow_js_access->GetAsBoolean(&allow_js_access_)) {
300 *error = ASCIIToUTF16(errors::kInvalidBackgroundAllowJsAccess);
301 return false;
302 }
303
304 return true;
305 }
306
BackgroundManifestHandler()307 BackgroundManifestHandler::BackgroundManifestHandler() {
308 }
309
~BackgroundManifestHandler()310 BackgroundManifestHandler::~BackgroundManifestHandler() {
311 }
312
Parse(Extension * extension,base::string16 * error)313 bool BackgroundManifestHandler::Parse(Extension* extension,
314 base::string16* error) {
315 std::unique_ptr<BackgroundInfo> info(new BackgroundInfo);
316 if (!info->Parse(extension, error))
317 return false;
318
319 // Platform apps must have background pages.
320 if (extension->is_platform_app() && !info->has_background_page()) {
321 *error = ASCIIToUTF16(errors::kBackgroundRequiredForPlatformApps);
322 return false;
323 }
324 // Lazy background pages are incompatible with the webRequest API.
325 if (info->has_lazy_background_page() &&
326 PermissionsParser::HasAPIPermission(extension,
327 APIPermission::kWebRequest)) {
328 *error = ASCIIToUTF16(errors::kWebRequestConflictsWithLazyBackground);
329 return false;
330 }
331
332 if (!info->has_lazy_background_page() &&
333 PermissionsParser::HasAPIPermission(
334 extension, APIPermission::kTransientBackground)) {
335 *error = ASCIIToUTF16(
336 errors::kTransientBackgroundConflictsWithPersistentBackground);
337 return false;
338 }
339
340 extension->SetManifestData(kBackground, std::move(info));
341 return true;
342 }
343
Validate(const Extension * extension,std::string * error,std::vector<InstallWarning> * warnings) const344 bool BackgroundManifestHandler::Validate(
345 const Extension* extension,
346 std::string* error,
347 std::vector<InstallWarning>* warnings) const {
348 // Validate that background scripts exist.
349 const std::vector<std::string>& background_scripts =
350 BackgroundInfo::GetBackgroundScripts(extension);
351 for (size_t i = 0; i < background_scripts.size(); ++i) {
352 if (!base::PathExists(
353 extension->GetResource(background_scripts[i]).GetFilePath())) {
354 *error = l10n_util::GetStringFUTF8(
355 IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED,
356 base::UTF8ToUTF16(background_scripts[i]));
357 return false;
358 }
359 }
360
361 if (BackgroundInfo::IsServiceWorkerBased(extension)) {
362 DCHECK(extension->is_extension());
363 const std::string& background_service_worker_script =
364 BackgroundInfo::GetBackgroundServiceWorkerScript(extension);
365 if (!base::PathExists(
366 extension->GetResource(background_service_worker_script)
367 .GetFilePath())) {
368 *error = l10n_util::GetStringFUTF8(
369 IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED,
370 base::UTF8ToUTF16(background_service_worker_script));
371 return false;
372 }
373 }
374
375 // Validate background page location, except for hosted apps, which should use
376 // an external URL. Background page for hosted apps are verified when the
377 // extension is created (in Extension::InitFromValue)
378 if (BackgroundInfo::HasBackgroundPage(extension) &&
379 !extension->is_hosted_app() && background_scripts.empty()) {
380 base::FilePath page_path = file_util::ExtensionURLToRelativeFilePath(
381 BackgroundInfo::GetBackgroundURL(extension));
382 const base::FilePath path = extension->GetResource(page_path).GetFilePath();
383 if (path.empty() || !base::PathExists(path)) {
384 *error =
385 l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_BACKGROUND_PAGE_FAILED,
386 page_path.LossyDisplayName());
387 return false;
388 }
389 }
390
391 if (extension->is_platform_app()) {
392 const std::string manifest_key =
393 std::string(keys::kPlatformAppBackground) + ".persistent";
394 bool is_persistent = false;
395 // Validate that packaged apps do not use a persistent background page.
396 if (extension->manifest()->GetBoolean(manifest_key, &is_persistent) &&
397 is_persistent) {
398 warnings->push_back(
399 InstallWarning(errors::kInvalidBackgroundPersistentInPlatformApp));
400 }
401 // Validate that packaged apps do not use the key 'background.persistent'.
402 // Use the dictionary directly to prevent an access check as
403 // 'background.persistent' is not available for packaged apps.
404 if (extension->manifest()->value()->Get(keys::kBackgroundPersistent,
405 NULL)) {
406 warnings->push_back(
407 InstallWarning(errors::kBackgroundPersistentInvalidForPlatformApps));
408 }
409 }
410
411 return true;
412 }
413
AlwaysParseForType(Manifest::Type type) const414 bool BackgroundManifestHandler::AlwaysParseForType(Manifest::Type type) const {
415 return type == Manifest::TYPE_PLATFORM_APP;
416 }
417
Keys() const418 base::span<const char* const> BackgroundManifestHandler::Keys() const {
419 static constexpr const char* kKeys[] = {
420 keys::kBackgroundAllowJsAccess, keys::kBackgroundPage,
421 keys::kBackgroundPersistent, keys::kBackgroundScripts,
422 keys::kBackgroundServiceWorkerScript, keys::kPlatformAppBackgroundPage,
423 keys::kPlatformAppBackgroundScripts};
424 #if !defined(__GNUC__) || __GNUC__ > 5
425 return kKeys;
426 #else
427 return base::make_span(kKeys, 7);
428 #endif
429
430 }
431
432 } // namespace extensions
433