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