blob: 1ba2500edca4323a6ce776a1e7e22d0ae29cb787 [file] [log] [blame]
[email protected]a999d672012-01-23 22:31:401// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1fca1492009-05-15 22:23:432// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
asargent9156f0292015-01-15 01:07:025#include "extensions/utility/unpacker.h"
[email protected]1fca1492009-05-15 22:23:436
avi2d124c02015-12-23 06:36:427#include <stddef.h>
8
meacerbd735062016-09-28 19:58:379#include <algorithm>
[email protected]ccea03c2010-12-17 03:31:5010#include <set>
tzikb2f765b2016-03-09 00:51:4511#include <tuple>
dchenge59eca1602015-12-18 17:48:0012#include <utility>
[email protected]ccea03c2010-12-17 03:31:5013
[email protected]25a4c1c2013-06-08 04:53:3614#include "base/files/file_enumerator.h"
thestig18dfb7a52014-08-26 10:44:0415#include "base/files/file_util.h"
[email protected]ea1a3f62012-11-16 20:34:2316#include "base/files/scoped_temp_dir.h"
[email protected]3367f3a2012-09-01 02:40:0617#include "base/i18n/rtl.h"
[email protected]ffbec692012-02-26 20:26:4218#include "base/json/json_file_value_serializer.h"
[email protected]cb154062014-01-17 03:32:4019#include "base/numerics/safe_conversions.h"
[email protected]3c8a6b02013-06-11 00:49:4920#include "base/strings/string_util.h"
[email protected]12bfb612013-06-07 19:54:0221#include "base/strings/utf_string_conversions.h"
[email protected]34b99632011-01-01 01:01:0622#include "base/threading/thread.h"
[email protected]1fca1492009-05-15 22:23:4323#include "base/values.h"
[email protected]e689cf252013-06-26 18:21:1424#include "content/public/child/image_decoder_utils.h"
Karan Bhatia71f6a622017-10-02 19:39:1025#include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
[email protected]993da5e2013-03-23 21:25:1626#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4127#include "extensions/common/extension.h"
[email protected]6668e5d2014-04-08 23:32:5228#include "extensions/common/extension_l10n_util.h"
noelc8702c42017-03-16 08:51:1929#include "extensions/common/extension_utility_types.h"
rockotec1e64b2014-11-13 22:06:5130#include "extensions/common/extensions_client.h"
[email protected]85df9d12014-04-15 17:02:1431#include "extensions/common/file_util.h"
[email protected]d42c1112013-08-22 19:36:3232#include "extensions/common/manifest.h"
[email protected]0c3c9732013-09-16 08:53:4133#include "extensions/common/manifest_constants.h"
rockote5fd3d92014-11-13 00:21:2234#include "extensions/common/manifest_handlers/default_locale_handler.h"
asargent9156f0292015-01-15 01:07:0235#include "extensions/strings/grit/extensions_strings.h"
[email protected]946d1b22009-07-22 23:57:2136#include "ipc/ipc_message_utils.h"
[email protected]e0785902011-05-19 23:34:1737#include "net/base/file_stream.h"
[email protected]902f7cd2009-05-22 19:02:1938#include "third_party/skia/include/core/SkBitmap.h"
[email protected]3367f3a2012-09-01 02:40:0639#include "ui/base/l10n/l10n_util.h"
tfarinaebe974f02015-01-03 04:25:3240#include "ui/gfx/geometry/size.h"
[email protected]1fca1492009-05-15 22:23:4341
[email protected]0c3c9732013-09-16 08:53:4142namespace extensions {
[email protected]9428edc2009-11-18 18:02:4743
[email protected]1fca1492009-05-15 22:23:4344namespace {
[email protected]6d377142010-03-17 20:36:0545
[email protected]0c3c9732013-09-16 08:53:4146namespace errors = manifest_errors;
47namespace keys = manifest_keys;
48
[email protected]fcfd12f2009-08-14 22:20:4649// A limit to stop us passing dangerously large canvases to the browser.
50const int kMaxImageCanvas = 4096 * 4096;
51
Lei Zhangeb9b3412017-09-25 20:57:3852constexpr const base::FilePath::CharType* kAllowedThemeFiletypes[] = {
meacerbd735062016-09-28 19:58:3753 FILE_PATH_LITERAL(".bmp"), FILE_PATH_LITERAL(".gif"),
54 FILE_PATH_LITERAL(".jpeg"), FILE_PATH_LITERAL(".jpg"),
55 FILE_PATH_LITERAL(".json"), FILE_PATH_LITERAL(".png"),
56 FILE_PATH_LITERAL(".webp")};
57
[email protected]a7329162013-02-07 19:21:4858SkBitmap DecodeImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:1959 // Read the file from disk.
60 std::string file_contents;
[email protected]7567484142013-07-11 17:36:0761 if (!base::PathExists(path) ||
[email protected]82f84b92013-08-30 18:23:5062 !base::ReadFileToString(path, &file_contents)) {
[email protected]902f7cd2009-05-22 19:02:1963 return SkBitmap();
64 }
65
66 // Decode the image using WebKit's image decoder.
67 const unsigned char* data =
68 reinterpret_cast<const unsigned char*>(file_contents.data());
asargent9156f0292015-01-15 01:07:0269 SkBitmap bitmap =
70 content::DecodeImage(data, gfx::Size(), file_contents.length());
Mike Reed8eac88c42017-10-12 21:24:3871 if (bitmap.computeByteSize() > kMaxImageCanvas)
[email protected]fcfd12f2009-08-14 22:20:4672 return SkBitmap();
73 return bitmap;
[email protected]902f7cd2009-05-22 19:02:1974}
75
[email protected]a7329162013-02-07 19:21:4876bool PathContainsParentDirectory(const base::FilePath& path) {
77 const base::FilePath::StringType kSeparators(base::FilePath::kSeparators);
78 const base::FilePath::StringType kParentDirectory(
79 base::FilePath::kParentDirectory);
80 const size_t npos = base::FilePath::StringType::npos;
81 const base::FilePath::StringType& value = path.value();
[email protected]902f7cd2009-05-22 19:02:1982
asargent9156f0292015-01-15 01:07:0283 for (size_t i = 0; i < value.length();) {
[email protected]902f7cd2009-05-22 19:02:1984 i = value.find(kParentDirectory, i);
85 if (i != npos) {
asargent9156f0292015-01-15 01:07:0286 if ((i == 0 || kSeparators.find(value[i - 1]) == npos) &&
87 (i + 1 < value.length() || kSeparators.find(value[i + 1]) == npos)) {
[email protected]902f7cd2009-05-22 19:02:1988 return true;
89 }
90 ++i;
91 }
92 }
93
94 return false;
[email protected]1fca1492009-05-15 22:23:4395}
96
[email protected]fc006cac2013-09-17 22:43:3197bool WritePickle(const IPC::Message& pickle, const base::FilePath& dest_path) {
[email protected]cb154062014-01-17 03:32:4098 int size = base::checked_cast<int>(pickle.size());
[email protected]fc006cac2013-09-17 22:43:3199 const char* data = static_cast<const char*>(pickle.data());
[email protected]e5c2a22e2014-03-06 20:42:30100 int bytes_written = base::WriteFile(dest_path, data, size);
[email protected]fc006cac2013-09-17 22:43:31101 return (bytes_written == size);
102}
103
[email protected]3bb84992010-08-26 17:23:46104} // namespace
105
[email protected]43c05d902013-07-10 21:27:00106struct Unpacker::InternalData {
107 DecodedImages decoded_images;
108};
109
asargentcac815112015-06-08 18:52:50110Unpacker::Unpacker(const base::FilePath& working_dir,
111 const base::FilePath& extension_dir,
[email protected]b3fe68d2012-07-16 19:14:39112 const std::string& extension_id,
[email protected]1d5e58b2013-01-31 08:41:40113 Manifest::Location location,
[email protected]b3fe68d2012-07-16 19:14:39114 int creation_flags)
asargentcac815112015-06-08 18:52:50115 : working_dir_(working_dir),
116 extension_dir_(extension_dir),
[email protected]f5bf1842012-02-15 02:52:26117 extension_id_(extension_id),
118 location_(location),
[email protected]fc38935a2011-10-31 23:53:28119 creation_flags_(creation_flags) {
[email protected]43c05d902013-07-10 21:27:00120 internal_data_.reset(new InternalData());
[email protected]3bb84992010-08-26 17:23:46121}
122
[email protected]b3fe68d2012-07-16 19:14:39123Unpacker::~Unpacker() {
[email protected]3bb84992010-08-26 17:23:46124}
125
meacerb040d3a2016-09-09 03:20:20126// static
meacerbd735062016-09-28 19:58:37127bool Unpacker::ShouldExtractFile(bool is_theme,
128 const base::FilePath& file_path) {
129 if (is_theme) {
130 const base::FilePath::StringType extension =
131 base::ToLowerASCII(file_path.FinalExtension());
132 // Allow filenames with no extension.
133 if (extension.empty())
134 return true;
135 return std::find(kAllowedThemeFiletypes,
136 kAllowedThemeFiletypes + arraysize(kAllowedThemeFiletypes),
137 extension) !=
138 (kAllowedThemeFiletypes + arraysize(kAllowedThemeFiletypes));
139 }
meacerb040d3a2016-09-09 03:20:20140 return !base::FilePath::CompareEqualIgnoreCase(file_path.FinalExtension(),
141 FILE_PATH_LITERAL(".exe"));
142}
143
meacerbd735062016-09-28 19:58:37144// static
145bool Unpacker::IsManifestFile(const base::FilePath& file_path) {
146 CHECK(!file_path.IsAbsolute());
147 return base::FilePath::CompareEqualIgnoreCase(file_path.value(),
148 kManifestFilename);
149}
150
151// static
152std::unique_ptr<base::DictionaryValue> Unpacker::ReadManifest(
153 const base::FilePath& extension_dir,
154 std::string* error) {
155 DCHECK(error);
156 base::FilePath manifest_path = extension_dir.Append(kManifestFilename);
[email protected]7567484142013-07-11 17:36:07157 if (!base::PathExists(manifest_path)) {
meacerbd735062016-09-28 19:58:37158 *error = errors::kInvalidManifest;
159 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19160 }
161
prashhir54a994502015-03-05 09:30:57162 JSONFileValueDeserializer deserializer(manifest_path);
meacerbd735062016-09-28 19:58:37163 std::unique_ptr<base::Value> root = deserializer.Deserialize(NULL, error);
[email protected]902f7cd2009-05-22 19:02:19164 if (!root.get()) {
meacerbd735062016-09-28 19:58:37165 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19166 }
167
jdoerriedc72ee942016-12-07 15:43:28168 if (!root->IsType(base::Value::Type::DICTIONARY)) {
meacerbd735062016-09-28 19:58:37169 *error = errors::kInvalidManifest;
170 return nullptr;
[email protected]902f7cd2009-05-22 19:02:19171 }
172
dchenge59eca1602015-12-18 17:48:00173 return base::DictionaryValue::From(std::move(root));
[email protected]902f7cd2009-05-22 19:02:19174}
175
lazyboy4c9991d2016-12-16 23:54:03176bool Unpacker::ReadAllMessageCatalogs() {
asargentcac815112015-06-08 18:52:50177 base::FilePath locales_path = extension_dir_.Append(kLocaleFolder);
[email protected]9428edc2009-11-18 18:02:47178
[email protected]1acbb4b62010-03-09 17:52:29179 // Not all folders under _locales have to be valid locales.
asargent9156f0292015-01-15 01:07:02180 base::FileEnumerator locales(locales_path, false,
[email protected]25a4c1c2013-06-08 04:53:36181 base::FileEnumerator::DIRECTORIES);
[email protected]9428edc2009-11-18 18:02:47182
[email protected]1acbb4b62010-03-09 17:52:29183 std::set<std::string> all_locales;
184 extension_l10n_util::GetAllLocales(&all_locales);
[email protected]a7329162013-02-07 19:21:48185 base::FilePath locale_path;
[email protected]1acbb4b62010-03-09 17:52:29186 while (!(locale_path = locales.Next()).empty()) {
187 if (extension_l10n_util::ShouldSkipValidation(locales_path, locale_path,
188 all_locales))
[email protected]9428edc2009-11-18 18:02:47189 continue;
190
[email protected]993da5e2013-03-23 21:25:16191 base::FilePath messages_path = locale_path.Append(kMessagesFilename);
[email protected]9428edc2009-11-18 18:02:47192
193 if (!ReadMessageCatalog(messages_path))
194 return false;
[email protected]1acbb4b62010-03-09 17:52:29195 }
[email protected]9428edc2009-11-18 18:02:47196
197 return true;
198}
199
[email protected]b3fe68d2012-07-16 19:14:39200bool Unpacker::Run() {
[email protected]902f7cd2009-05-22 19:02:19201 // Parse the manifest.
dschuyler3fe4bfc2016-09-28 01:11:29202 std::string error;
meacerbd735062016-09-28 19:58:37203 parsed_manifest_ = ReadManifest(extension_dir_, &error);
204 if (!parsed_manifest_.get()) {
205 SetError(error);
206 return false;
207 }
208
asargent9156f0292015-01-15 01:07:02209 scoped_refptr<Extension> extension(
asargentcac815112015-06-08 18:52:50210 Extension::Create(extension_dir_, location_, *parsed_manifest_,
asargent9156f0292015-01-15 01:07:02211 creation_flags_, extension_id_, &error));
[email protected]66e4eb32010-10-27 20:37:41212 if (!extension.get()) {
[email protected]902f7cd2009-05-22 19:02:19213 SetError(error);
214 return false;
215 }
[email protected]99872e32009-09-25 22:02:49216
[email protected]1d5e58b2013-01-31 08:41:40217 std::vector<InstallWarning> warnings;
[email protected]85df9d12014-04-15 17:02:14218 if (!file_util::ValidateExtension(extension.get(), &error, &warnings)) {
[email protected]99872e32009-09-25 22:02:49219 SetError(error);
220 return false;
221 }
[email protected]ab55c2b2012-06-01 23:55:03222 extension->AddInstallWarnings(warnings);
[email protected]99872e32009-09-25 22:02:49223
[email protected]902f7cd2009-05-22 19:02:19224 // Decode any images that the browser needs to display.
[email protected]72b49d42013-04-19 12:47:31225 std::set<base::FilePath> image_paths =
rockotec1e64b2014-11-13 22:06:51226 ExtensionsClient::Get()->GetBrowserImagePaths(extension.get());
estade82eaf582015-12-29 17:51:31227 for (const base::FilePath& path : image_paths) {
228 if (!AddDecodedImage(path))
[email protected]902f7cd2009-05-22 19:02:19229 return false; // Error was already reported.
230 }
231
[email protected]9428edc2009-11-18 18:02:47232 // Parse all message catalogs (if any).
[email protected]a371b4b2013-06-18 20:29:27233 parsed_catalogs_.reset(new base::DictionaryValue);
[email protected]5c6ac842013-06-02 23:37:03234 if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) {
lazyboy4c9991d2016-12-16 23:54:03235 if (!ReadAllMessageCatalogs())
[email protected]9428edc2009-11-18 18:02:47236 return false; // Error was already reported.
237 }
238
Karan Bhatia71f6a622017-10-02 19:39:10239 return ReadJSONRulesetIfNeeded(extension.get()) && DumpImagesToFile() &&
240 DumpMessageCatalogsToFile();
241}
242
243bool Unpacker::ReadJSONRulesetIfNeeded(const Extension* extension) {
244 const ExtensionResource* resource =
245 declarative_net_request::DNRManifestData::GetRulesetResource(extension);
246 // The extension did not provide a ruleset.
247 if (!resource)
248 return true;
249
250 std::string error;
251 JSONFileValueDeserializer deserializer(resource->GetFilePath());
252 std::unique_ptr<base::Value> root = deserializer.Deserialize(nullptr, &error);
253 if (!root) {
254 SetError(error);
255 return false;
256 }
257
258 if (!root->is_list()) {
259 SetError(errors::kDeclarativeNetRequestListNotPassed);
260 return false;
261 }
262
263 parsed_json_ruleset_ = base::ListValue::From(std::move(root));
264 return true;
[email protected]902f7cd2009-05-22 19:02:19265}
266
[email protected]b3fe68d2012-07-16 19:14:39267bool Unpacker::DumpImagesToFile() {
[email protected]facd7a7652009-06-05 23:15:02268 IPC::Message pickle; // We use a Message so we can use WriteParam.
[email protected]43c05d902013-07-10 21:27:00269 IPC::WriteParam(&pickle, internal_data_->decoded_images);
[email protected]facd7a7652009-06-05 23:15:02270
asargentcac815112015-06-08 18:52:50271 base::FilePath path = working_dir_.AppendASCII(kDecodedImagesFilename);
[email protected]fc006cac2013-09-17 22:43:31272 if (!WritePickle(pickle, path)) {
[email protected]facd7a7652009-06-05 23:15:02273 SetError("Could not write image data to disk.");
274 return false;
275 }
276
277 return true;
278}
279
[email protected]b3fe68d2012-07-16 19:14:39280bool Unpacker::DumpMessageCatalogsToFile() {
[email protected]6d377142010-03-17 20:36:05281 IPC::Message pickle;
282 IPC::WriteParam(&pickle, *parsed_catalogs_.get());
283
asargent9156f0292015-01-15 01:07:02284 base::FilePath path =
asargentcac815112015-06-08 18:52:50285 working_dir_.AppendASCII(kDecodedMessageCatalogsFilename);
[email protected]fc006cac2013-09-17 22:43:31286 if (!WritePickle(pickle, path)) {
[email protected]6d377142010-03-17 20:36:05287 SetError("Could not write message catalogs to disk.");
288 return false;
289 }
290
291 return true;
292}
293
[email protected]a7329162013-02-07 19:21:48294bool Unpacker::AddDecodedImage(const base::FilePath& path) {
[email protected]902f7cd2009-05-22 19:02:19295 // Make sure it's not referencing a file outside the extension's subdir.
296 if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
asargent9156f0292015-01-15 01:07:02297 SetUTF16Error(l10n_util::GetStringFUTF16(
298 IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR,
299 base::i18n::GetDisplayStringInLTRDirectionality(
300 path.LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19301 return false;
302 }
303
asargentcac815112015-06-08 18:52:50304 SkBitmap image_bitmap = DecodeImage(extension_dir_.Append(path));
[email protected]902f7cd2009-05-22 19:02:19305 if (image_bitmap.isNull()) {
asargent9156f0292015-01-15 01:07:02306 SetUTF16Error(l10n_util::GetStringFUTF16(
307 IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
308 base::i18n::GetDisplayStringInLTRDirectionality(
309 path.BaseName().LossyDisplayName())));
[email protected]902f7cd2009-05-22 19:02:19310 return false;
311 }
312
tzikb2f765b2016-03-09 00:51:45313 internal_data_->decoded_images.push_back(std::make_tuple(image_bitmap, path));
[email protected]1fca1492009-05-15 22:23:43314 return true;
315}
316
[email protected]a7329162013-02-07 19:21:48317bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) {
[email protected]9428edc2009-11-18 18:02:47318 std::string error;
prashhir54a994502015-03-05 09:30:57319 JSONFileValueDeserializer deserializer(message_path);
dcheng4a7e93b2016-04-23 00:27:16320 std::unique_ptr<base::DictionaryValue> root =
olli.raulaba045252015-10-16 06:16:40321 base::DictionaryValue::From(deserializer.Deserialize(NULL, &error));
[email protected]6d377142010-03-17 20:36:05322 if (!root.get()) {
[email protected]05aab99c2013-12-20 09:03:15323 base::string16 messages_file = message_path.LossyDisplayName();
[email protected]9428edc2009-11-18 18:02:47324 if (error.empty()) {
325 // If file is missing, Deserialize will fail with empty error.
[email protected]93f10522010-10-31 16:27:48326 SetError(base::StringPrintf("%s %s", errors::kLocalesMessagesFileMissing,
[email protected]c91b9d42013-12-25 00:53:42327 base::UTF16ToUTF8(messages_file).c_str()));
[email protected]9428edc2009-11-18 18:02:47328 } else {
asargent9156f0292015-01-15 01:07:02329 SetError(base::StringPrintf(
330 "%s: %s", base::UTF16ToUTF8(messages_file).c_str(), error.c_str()));
[email protected]9428edc2009-11-18 18:02:47331 }
332 return false;
333 }
334
[email protected]a7329162013-02-07 19:21:48335 base::FilePath relative_path;
[email protected]9428edc2009-11-18 18:02:47336 // message_path was created from temp_install_dir. This should never fail.
asargentcac815112015-06-08 18:52:50337 if (!extension_dir_.AppendRelativePath(message_path, &relative_path)) {
[email protected]9428edc2009-11-18 18:02:47338 NOTREACHED();
[email protected]967d18b2011-03-02 22:22:07339 return false;
340 }
[email protected]9428edc2009-11-18 18:02:47341
[email protected]967d18b2011-03-02 22:22:07342 std::string dir_name = relative_path.DirName().MaybeAsASCII();
343 if (dir_name.empty()) {
344 NOTREACHED();
345 return false;
346 }
jdoerrie6ff270ca2017-06-07 10:31:45347 parsed_catalogs_->Set(dir_name, std::move(root));
[email protected]9428edc2009-11-18 18:02:47348
349 return true;
350}
351
asargent9156f0292015-01-15 01:07:02352void Unpacker::SetError(const std::string& error) {
[email protected]c91b9d42013-12-25 00:53:42353 SetUTF16Error(base::UTF8ToUTF16(error));
[email protected]3367f3a2012-09-01 02:40:06354}
355
[email protected]05aab99c2013-12-20 09:03:15356void Unpacker::SetUTF16Error(const base::string16& error) {
[email protected]3367f3a2012-09-01 02:40:06357 error_message_ = error;
[email protected]1fca1492009-05-15 22:23:43358}
[email protected]b3fe68d2012-07-16 19:14:39359
360} // namespace extensions