Changing SandboxedUnpacker image sanitization.
In preparation for moving extension unpacking to the browser process,
making the image sanitization (decoding/reencoding) be triggered by
SandboxedUnpacker in the browser process, using the new ImageSanitizer
class. ImageSanitizer decodes images safely using the data decoder
service and then reencodes them.
Bug: 800540
Change-Id: I40edae3cbd4ab8f03a2494a8d615107bc7feff6d
Tbr: [email protected], [email protected]
Reviewed-on: https://chromium-review.googlesource.com/870744
Reviewed-by: Ken Rockot <[email protected]>
Reviewed-by: Tom Sepez <[email protected]>
Reviewed-by: James Cook <[email protected]>
Reviewed-by: Jay Civelli <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Reilly Grant <[email protected]>
Commit-Queue: Jay Civelli <[email protected]>
Cr-Commit-Position: refs/heads/master@{#531440}
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 39a69ca..029d661 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4792,6 +4792,7 @@
"//components/drive:test_support",
"//components/storage_monitor:test_support",
"//extensions:test_support",
+ "//services/data_decoder/public/cpp:test_support",
]
}
diff --git a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
index 875188f5..65b5ee1 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_app_data.cc
@@ -25,6 +25,7 @@
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/image_loader.h"
#include "extensions/browser/sandboxed_unpacker.h"
@@ -35,6 +36,7 @@
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/manifest_handlers/kiosk_mode_info.h"
#include "services/network/public/interfaces/url_loader_factory.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image.h"
@@ -85,8 +87,12 @@
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})) {}
void Start() {
+ auto connector = content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone();
task_runner_->PostTask(FROM_HERE,
- base::BindOnce(&CrxLoader::StartInThreadPool, this));
+ base::BindOnce(&CrxLoader::StartInThreadPool, this,
+ std::move(connector)));
}
bool success() const { return success_; }
@@ -130,7 +136,8 @@
NotifyFinishedInThreadPool();
}
- void StartInThreadPool() {
+ void StartInThreadPool(
+ std::unique_ptr<service_manager::Connector> connector) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (!temp_dir_.CreateUniqueTempDir()) {
@@ -139,10 +146,10 @@
return;
}
- scoped_refptr<extensions::SandboxedUnpacker> unpacker(
- new extensions::SandboxedUnpacker(
- extensions::Manifest::INTERNAL, extensions::Extension::NO_FLAGS,
- temp_dir_.GetPath(), task_runner_.get(), this));
+ auto unpacker = base::MakeRefCounted<extensions::SandboxedUnpacker>(
+ std::move(connector), extensions::Manifest::INTERNAL,
+ extensions::Extension::NO_FLAGS, temp_dir_.GetPath(),
+ task_runner_.get(), this);
unpacker->StartWithCrx(extensions::CRXFileInfo(crx_file_));
}
diff --git a/chrome/browser/chromeos/app_mode/kiosk_external_update_validator.cc b/chrome/browser/chromeos/app_mode/kiosk_external_update_validator.cc
index 6a7d64e5..bac4700 100644
--- a/chrome/browser/chromeos/app_mode/kiosk_external_update_validator.cc
+++ b/chrome/browser/chromeos/app_mode/kiosk_external_update_validator.cc
@@ -7,8 +7,10 @@
#include "base/bind.h"
#include "base/location.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/common/extension.h"
#include "extensions/common/manifest_constants.h"
+#include "services/service_manager/public/cpp/connector.h"
namespace chromeos {
@@ -27,10 +29,12 @@
}
void KioskExternalUpdateValidator::Start() {
- scoped_refptr<extensions::SandboxedUnpacker> unpacker(
- new extensions::SandboxedUnpacker(
- extensions::Manifest::EXTERNAL_PREF, extensions::Extension::NO_FLAGS,
- crx_unpack_dir_, backend_task_runner_.get(), this));
+ auto unpacker = base::MakeRefCounted<extensions::SandboxedUnpacker>(
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone(),
+ extensions::Manifest::EXTERNAL_PREF, extensions::Extension::NO_FLAGS,
+ crx_unpack_dir_, backend_task_runner_.get(), this);
if (!backend_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&extensions::SandboxedUnpacker::StartWithCrx,
diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc
index 666b488..65713040 100644
--- a/chrome/browser/extensions/crx_installer.cc
+++ b/chrome/browser/extensions/crx_installer.cc
@@ -40,6 +40,7 @@
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/resource_dispatcher_host.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
@@ -62,6 +63,7 @@
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/user_script.h"
#include "extensions/strings/grit/extensions_strings.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
@@ -100,6 +102,9 @@
}
// static
+service_manager::Connector* CrxInstaller::connector_for_test_ = nullptr;
+
+// static
scoped_refptr<CrxInstaller> CrxInstaller::Create(
ExtensionService* service,
std::unique_ptr<ExtensionInstallPrompt> client,
@@ -185,8 +190,8 @@
source_file_ = source_file.path;
auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
- install_source_, creation_flags_, install_directory_,
- installer_task_runner_.get(), this);
+ GetConnector()->Clone(), install_source_, creation_flags_,
+ install_directory_, installer_task_runner_.get(), this);
if (!installer_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&SandboxedUnpacker::StartWithCrx, unpacker,
@@ -207,8 +212,8 @@
source_file_ = unpacked_dir;
auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
- install_source_, creation_flags_, install_directory_,
- installer_task_runner_.get(), this);
+ GetConnector()->Clone(), install_source_, creation_flags_,
+ install_directory_, installer_task_runner_.get(), this);
if (!installer_task_runner_->PostTask(
FROM_HERE,
@@ -1026,4 +1031,11 @@
}
}
+service_manager::Connector* CrxInstaller::GetConnector() const {
+ return connector_for_test_
+ ? connector_for_test_
+ : content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector();
+}
+
} // namespace extensions
diff --git a/chrome/browser/extensions/crx_installer.h b/chrome/browser/extensions/crx_installer.h
index d14ea61..17205c97 100644
--- a/chrome/browser/extensions/crx_installer.h
+++ b/chrome/browser/extensions/crx_installer.h
@@ -37,6 +37,10 @@
class SequencedTaskRunner;
}
+namespace service_manager {
+class Connector;
+}
+
namespace extensions {
class CrxInstallError;
class ExtensionUpdaterTest;
@@ -237,6 +241,10 @@
// invalid if this isn't an update.
const base::Version& current_version() const { return current_version_; }
+ static void set_connector_for_test(service_manager::Connector* connector) {
+ connector_for_test_ = connector;
+ }
+
private:
friend class ::ExtensionServiceTest;
friend class ExtensionUpdaterTest;
@@ -307,6 +315,9 @@
// and needs additional permissions.
void ConfirmReEnable();
+ // Returns the connector to the ServiceManager.
+ service_manager::Connector* GetConnector() const;
+
void set_install_flag(int flag, bool val) {
if (val)
install_flags_ |= flag;
@@ -484,6 +495,8 @@
// Invoked when the install is completed.
InstallerResultCallback installer_callback_;
+ static service_manager::Connector* connector_for_test_;
+
DISALLOW_COPY_AND_ASSIGN(CrxInstaller);
};
diff --git a/chrome/browser/extensions/extension_service_test_base.cc b/chrome/browser/extensions/extension_service_test_base.cc
index 09b97fd9..69426a8 100644
--- a/chrome/browser/extensions/extension_service_test_base.cc
+++ b/chrome/browser/extensions/extension_service_test_base.cc
@@ -15,6 +15,7 @@
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/component_loader.h"
+#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_garbage_collector_factory.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/load_error_reporter.h"
@@ -30,6 +31,7 @@
#include "components/sync_preferences/pref_service_mock_factory.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/pref_names.h"
@@ -91,6 +93,7 @@
return;
}
data_dir_ = test_data_dir.AppendASCII("extensions");
+ CrxInstaller::set_connector_for_test(test_data_decoder_service_.connector());
}
ExtensionServiceTestBase::~ExtensionServiceTestBase() {
diff --git a/chrome/browser/extensions/extension_service_test_base.h b/chrome/browser/extensions/extension_service_test_base.h
index e90e534..0d210e6 100644
--- a/chrome/browser/extensions/extension_service_test_base.h
+++ b/chrome/browser/extensions/extension_service_test_base.h
@@ -19,6 +19,7 @@
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_renderer_host.h"
#include "content/public/test/test_utils.h"
+#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
#include "testing/gtest/include/gtest/gtest.h"
#if defined(OS_CHROMEOS)
@@ -181,6 +182,10 @@
chromeos::ScopedTestUserManager test_user_manager_;
#endif
+ // An instance of the data decoder service that does not require the
+ // ServiceManager.
+ data_decoder::TestDataDecoderService test_data_decoder_service_;
+
DISALLOW_COPY_AND_ASSIGN(ExtensionServiceTestBase);
};
diff --git a/chrome/browser/extensions/startup_helper.cc b/chrome/browser/extensions/startup_helper.cc
index 8e7f342..bb99318 100644
--- a/chrome/browser/extensions/startup_helper.cc
+++ b/chrome/browser/extensions/startup_helper.cc
@@ -17,9 +17,11 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/chrome_extensions_client.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/browser/sandboxed_unpacker.h"
#include "extensions/common/extension.h"
+#include "services/service_manager/public/cpp/connector.h"
using content::BrowserThread;
@@ -86,13 +88,17 @@
quit_closure_(std::move(quit_closure)),
success_(false) {}
- bool success() { return success_; }
- const base::string16& error() { return error_; }
+ bool success() const { return success_; }
+ const base::string16& error() const { return error_; }
void Start() {
+ std::unique_ptr<::service_manager::Connector> connector =
+ content::ServiceManagerConnection::GetForProcess()
+ ->GetConnector()
+ ->Clone();
GetExtensionFileTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ValidateCrxHelper::StartOnBlockingThread, this));
+ FROM_HERE, base::BindOnce(&ValidateCrxHelper::StartOnBlockingThread,
+ this, std::move(connector)));
}
protected:
@@ -126,11 +132,13 @@
std::move(quit_closure_).Run();
}
- void StartOnBlockingThread() {
+ void StartOnBlockingThread(
+ std::unique_ptr<service_manager::Connector> connector) {
DCHECK(GetExtensionFileTaskRunner()->RunsTasksInCurrentSequence());
- scoped_refptr<SandboxedUnpacker> unpacker(new SandboxedUnpacker(
- Manifest::INTERNAL, 0, /* no special creation flags */
- temp_dir_, GetExtensionFileTaskRunner().get(), this));
+ auto unpacker = base::MakeRefCounted<SandboxedUnpacker>(
+ std::move(connector), Manifest::INTERNAL,
+ 0, /* no special creation flags */
+ temp_dir_, GetExtensionFileTaskRunner().get(), this);
unpacker->StartWithCrx(crx_file_);
}
@@ -173,8 +181,8 @@
base::RunLoop run_loop;
CRXFileInfo file(path);
- scoped_refptr<ValidateCrxHelper> helper(
- new ValidateCrxHelper(file, temp_dir.GetPath(), run_loop.QuitClosure()));
+ auto helper = base::MakeRefCounted<ValidateCrxHelper>(
+ file, temp_dir.GetPath(), run_loop.QuitClosure());
helper->Start();
run_loop.Run();
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index beaa4f8a..dbd097f 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -10,6 +10,7 @@
#include "base/memory/ptr_util.h"
#include "chrome/browser/extensions/blacklist.h"
#include "chrome/browser/extensions/chrome_app_sorting.h"
+#include "chrome/browser/extensions/crx_installer.h"
#include "chrome/browser/extensions/extension_management.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/shared_module_service.h"
@@ -17,6 +18,7 @@
#include "chrome/common/chrome_switches.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
+#include "content/public/common/service_manager_connection.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
@@ -72,6 +74,11 @@
profile_, command_line, install_directory, ExtensionPrefs::Get(profile_),
Blacklist::Get(profile_), autoupdate_enabled, extensions_enabled,
&ready_));
+ if (!test_data_decoder_service_) {
+ test_data_decoder_service_ =
+ std::make_unique<data_decoder::TestDataDecoderService>();
+ }
+ CrxInstaller::set_connector_for_test(test_data_decoder_service_->connector());
extension_service_->ClearProvidersForTesting();
return extension_service_.get();
}
diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h
index 73b4e6d..3a51811b 100644
--- a/chrome/browser/extensions/test_extension_system.h
+++ b/chrome/browser/extensions/test_extension_system.h
@@ -9,6 +9,7 @@
#include "extensions/browser/extension_system.h"
#include "extensions/common/one_shot_event.h"
+#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
@@ -99,6 +100,8 @@
std::unique_ptr<QuotaService> quota_service_;
std::unique_ptr<AppSorting> app_sorting_;
OneShotEvent ready_;
+ std::unique_ptr<data_decoder::TestDataDecoderService>
+ test_data_decoder_service_;
#if defined(OS_CHROMEOS)
std::unique_ptr<chromeos::ScopedTestUserManager> test_user_manager_;
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn
index 7a106d31..24f41ae 100644
--- a/extensions/browser/BUILD.gn
+++ b/extensions/browser/BUILD.gn
@@ -254,6 +254,8 @@
"image_loader.h",
"image_loader_factory.cc",
"image_loader_factory.h",
+ "image_sanitizer.cc",
+ "image_sanitizer.h",
"info_map.cc",
"info_map.h",
"install_flag.h",
@@ -550,6 +552,7 @@
"file_highlighter_unittest.cc",
"file_reader_unittest.cc",
"image_loader_unittest.cc",
+ "image_sanitizer_unittest.cc",
"info_map_unittest.cc",
"lazy_background_task_queue_unittest.cc",
"load_monitoring_extension_host_queue_unittest.cc",
diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS
index bde65cb..64eb946 100644
--- a/extensions/browser/DEPS
+++ b/extensions/browser/DEPS
@@ -23,7 +23,7 @@
"+net",
# This directory contains build flags and does not pull all of PPAPI in.
"+ppapi/features",
- "+services/data_decoder/public/cpp",
+ "+services/data_decoder/public",
"+services/network/public/cpp",
"+services/network/public/interfaces",
"+services/preferences/public/cpp",
diff --git a/extensions/browser/image_sanitizer.cc b/extensions/browser/image_sanitizer.cc
new file mode 100644
index 0000000..886ad701
--- /dev/null
+++ b/extensions/browser/image_sanitizer.cc
@@ -0,0 +1,220 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/image_sanitizer.h"
+
+#include "base/files/file_util.h"
+#include "base/task_runner_util.h"
+#include "extensions/browser/extension_file_task_runner.h"
+#include "extensions/common/extension_resource_path_normalizer.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace extensions {
+
+namespace {
+
+// We don't expect icons and other extension's images to be big.
+// We use this limit to prevent from opening too large images.
+const int kMaxImageCanvas = 4096 * 4096; // 16MB
+
+// Reads the file in |path| and then deletes it.
+// Returns a tuple containing: the file content, whether the read was
+// successful, whether the delete was successful.
+std::tuple<std::vector<uint8_t>, bool, bool> ReadAndDeleteFile(
+ const base::FilePath& path) {
+ std::vector<uint8_t> contents;
+ bool read_success = false;
+ int64_t file_size;
+ if (base::GetFileSize(path, &file_size)) {
+ contents.resize(file_size);
+ read_success = base::ReadFile(
+ path, reinterpret_cast<char*>(contents.data()), file_size);
+ }
+ bool delete_success = base::DeleteFile(path, /*recursive=*/false);
+ return std::make_tuple(std::move(contents), read_success, delete_success);
+}
+
+std::pair<bool, std::vector<unsigned char>> EncodeImage(const SkBitmap& image) {
+ std::vector<unsigned char> image_data;
+ bool success = gfx::PNGCodec::EncodeBGRASkBitmap(
+ image,
+ /*discard_transparency=*/false, &image_data);
+ return std::make_pair(success, std::move(image_data));
+}
+
+int WriteFile(const base::FilePath& path,
+ const std::vector<unsigned char>& data) {
+ return base::WriteFile(path, reinterpret_cast<const char*>(data.data()),
+ base::checked_cast<int>(data.size()));
+}
+
+} // namespace
+
+// static
+std::unique_ptr<ImageSanitizer> ImageSanitizer::CreateAndStart(
+ service_manager::Connector* connector,
+ const service_manager::Identity& identity,
+ const base::FilePath& image_dir,
+ const std::set<base::FilePath>& image_paths,
+ ImageDecodedCallback image_decoded_callback,
+ SanitizationDoneCallback done_callback) {
+ std::unique_ptr<ImageSanitizer> sanitizer(new ImageSanitizer(
+ image_dir, image_paths, std::move(image_decoded_callback),
+ std::move(done_callback)));
+ sanitizer->Start(connector, identity);
+ return sanitizer;
+}
+
+ImageSanitizer::ImageSanitizer(
+ const base::FilePath& image_dir,
+ const std::set<base::FilePath>& image_relative_paths,
+ ImageDecodedCallback image_decoded_callback,
+ SanitizationDoneCallback done_callback)
+ : image_dir_(image_dir),
+ image_paths_(image_relative_paths),
+ image_decoded_callback_(std::move(image_decoded_callback)),
+ done_callback_(std::move(done_callback)),
+ weak_factory_(this) {}
+
+ImageSanitizer::~ImageSanitizer() = default;
+
+void ImageSanitizer::Start(service_manager::Connector* connector,
+ const service_manager::Identity& identity) {
+ if (image_paths_.empty()) {
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ImageSanitizer::ReportSuccess,
+ weak_factory_.GetWeakPtr()));
+ return;
+ }
+
+ connector->BindInterface(identity, &image_decoder_ptr_);
+
+ std::set<base::FilePath> normalized_image_paths;
+ for (const base::FilePath& path : image_paths_) {
+ // Normalize paths as |image_paths_| can contain duplicates like "icon.png"
+ // and "./icon.png" to avoid unpacking the same image twice.
+ base::FilePath normalized_path;
+ if (path.IsAbsolute() || path.ReferencesParent() ||
+ !NormalizeExtensionResourcePath(path, &normalized_path)) {
+ // Report the error asynchronously so the caller stack has chance to
+ // unwind.
+ base::SequencedTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&ImageSanitizer::ReportError,
+ weak_factory_.GetWeakPtr(),
+ Status::kImagePathError, path));
+ return;
+ }
+ normalized_image_paths.insert(normalized_path);
+ }
+ // Update |image_paths_| as some of the path might have been changed by
+ // normalization.
+ image_paths_ = std::move(normalized_image_paths);
+
+ // Note that we use 2 for loops instead of one to prevent a race and flakyness
+ // in tests: if |image_paths_| contains 2 paths, a valid one that points to a
+ // file that does not exist and an invalid one, there is a race that can cause
+ // either error to be reported (kImagePathError or kFileReadError).
+ for (const base::FilePath& path : image_paths_) {
+ base::FilePath full_image_path = image_dir_.Append(path);
+ base::PostTaskAndReplyWithResult(
+ extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
+ base::BindOnce(&ReadAndDeleteFile, full_image_path),
+ base::BindOnce(&ImageSanitizer::ImageFileRead,
+ weak_factory_.GetWeakPtr(), path));
+ }
+}
+
+void ImageSanitizer::ImageFileRead(
+ const base::FilePath& image_path,
+ std::tuple<std::vector<uint8_t>, bool, bool> read_and_delete_result) {
+ if (!std::get<1>(read_and_delete_result)) {
+ ReportError(Status::kFileReadError, image_path);
+ return;
+ }
+ if (!std::get<2>(read_and_delete_result)) {
+ ReportError(Status::kFileDeleteError, image_path);
+ return;
+ }
+ const std::vector<uint8_t>& image_data = std::get<0>(read_and_delete_result);
+ image_decoder_ptr_->DecodeImage(
+ image_data, data_decoder::mojom::ImageCodec::DEFAULT,
+ /*shrink_to_fit=*/false, kMaxImageCanvas, gfx::Size(),
+ base::BindOnce(&ImageSanitizer::ImageDecoded, weak_factory_.GetWeakPtr(),
+ image_path));
+}
+
+void ImageSanitizer::ImageDecoded(const base::FilePath& image_path,
+ const SkBitmap& decoded_image) {
+ if (decoded_image.isNull()) {
+ ReportError(Status::kDecodingError, image_path);
+ return;
+ }
+
+ if (image_decoded_callback_)
+ image_decoded_callback_.Run(image_path, decoded_image);
+
+ // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
+ // though they may originally be .jpg, etc. Figure something out.
+ // http://code.google.com/p/chromium/issues/detail?id=12459
+ base::PostTaskAndReplyWithResult(
+ extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
+ base::BindOnce(&EncodeImage, decoded_image),
+ base::BindOnce(&ImageSanitizer::ImageReencoded,
+ weak_factory_.GetWeakPtr(), image_path));
+}
+
+void ImageSanitizer::ImageReencoded(
+ const base::FilePath& image_path,
+ std::pair<bool, std::vector<unsigned char>> result) {
+ bool success = result.first;
+ std::vector<unsigned char> image_data = std::move(result.second);
+ if (!success) {
+ ReportError(Status::kEncodingError, image_path);
+ return;
+ }
+
+ int size = base::checked_cast<int>(image_data.size());
+ base::PostTaskAndReplyWithResult(
+ extensions::GetExtensionFileTaskRunner().get(), FROM_HERE,
+ base::BindOnce(&WriteFile, image_dir_.Append(image_path),
+ std::move(image_data)),
+ base::BindOnce(&ImageSanitizer::ImageWritten, weak_factory_.GetWeakPtr(),
+ image_path, size));
+}
+
+void ImageSanitizer::ImageWritten(const base::FilePath& image_path,
+ int expected_size,
+ int actual_size) {
+ if (expected_size != actual_size) {
+ ReportError(Status::kFileWriteError, image_path);
+ return;
+ }
+ // We have finished with this path.
+ size_t removed_count = image_paths_.erase(image_path);
+ DCHECK_EQ(1U, removed_count);
+
+ if (image_paths_.empty()) {
+ // This was the last path, we are done.
+ ReportSuccess();
+ }
+}
+
+void ImageSanitizer::ReportSuccess() {
+ CleanUp();
+ std::move(done_callback_).Run(Status::kSuccess, base::FilePath());
+}
+
+void ImageSanitizer::ReportError(Status status, const base::FilePath& path) {
+ CleanUp();
+ // Prevent any other task from reporting, we want to notify only once.
+ weak_factory_.InvalidateWeakPtrs();
+ std::move(done_callback_).Run(status, path);
+}
+
+void ImageSanitizer::CleanUp() {
+ image_decoder_ptr_.reset();
+}
+
+} // namespace extensions
\ No newline at end of file
diff --git a/extensions/browser/image_sanitizer.h b/extensions/browser/image_sanitizer.h
new file mode 100644
index 0000000..ac36aa6
--- /dev/null
+++ b/extensions/browser/image_sanitizer.h
@@ -0,0 +1,116 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef EXTENSIONS_BROWSER_IMAGE_SANITIZER_H_
+#define EXTENSIONS_BROWSER_IMAGE_SANITIZER_H_
+
+#include <cstdint>
+#include <memory>
+#include <set>
+#include <tuple>
+#include <vector>
+
+#include "base/files/file_path.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "services/data_decoder/public/interfaces/image_decoder.mojom.h"
+#include "services/service_manager/public/cpp/identity.h"
+
+class SkBitmap;
+
+namespace service_manager {
+class Connector;
+}
+
+namespace extensions {
+
+// This class takes potentially unsafe images and decodes them in a sandboxed
+// process, then reencodes them so that they can later be safely used in the
+// browser process.
+class ImageSanitizer {
+ public:
+ enum class Status {
+ kSuccess = 0,
+ kImagePathError,
+ kFileReadError,
+ kFileDeleteError,
+ kDecodingError,
+ kEncodingError,
+ kFileWriteError,
+ };
+
+ // Callback invoked when the image sanitization is is done. If status is an
+ // error, |path| points to the file that caused the error.
+ using SanitizationDoneCallback =
+ base::OnceCallback<void(Status status, const base::FilePath& path)>;
+
+ // Called on a background thread when an image has been decoded.
+ using ImageDecodedCallback =
+ base::RepeatingCallback<void(const base::FilePath& path, SkBitmap image)>;
+
+ // Creates an ImageSanitizer and starts the sanitization of the images in
+ // |image_relative_paths|. These paths should be relative and not reference
+ // their parent dir or an kImagePathError will be reported to |done_callback|.
+ // These relative paths are resolved against |image_dir|.
+ // |connector| should be a connector to the ServiceManager usable on the
+ // current thread. |identity| is used when accessing the data decoder service
+ // which is used internally to decode images. It lets callers potentially
+ // share a process when doing unrelated data decoding operations.
+ // |done_callback| is invoked asynchronously when all images have been
+ // sanitized or if an error occurred.
+ // If the returned ImageSanitizer instance is deleted, |done_callback| and
+ // |image_decoded_callback| are not called and the sanitization stops promptly
+ // (some background tasks may still run).
+ static std::unique_ptr<ImageSanitizer> CreateAndStart(
+ service_manager::Connector* connector,
+ const service_manager::Identity& identity,
+ const base::FilePath& image_dir,
+ const std::set<base::FilePath>& image_relative_paths,
+ ImageDecodedCallback image_decoded_callback,
+ SanitizationDoneCallback done_callback);
+
+ ~ImageSanitizer();
+
+ private:
+ ImageSanitizer(const base::FilePath& image_dir,
+ const std::set<base::FilePath>& image_relative_paths,
+ ImageDecodedCallback image_decoded_callback,
+ SanitizationDoneCallback done_callback);
+
+ void Start(service_manager::Connector* connector,
+ const service_manager::Identity& identity);
+
+ void ImageFileRead(
+ const base::FilePath& image_path,
+ std::tuple<std::vector<uint8_t>, bool, bool> read_and_delete_result);
+
+ void ImageDecoded(const base::FilePath& image_path,
+ const SkBitmap& decoded_image);
+
+ void ImageReencoded(const base::FilePath& image_path,
+ std::pair<bool, std::vector<unsigned char>> result);
+
+ void ImageWritten(const base::FilePath& image_path,
+ int expected_size,
+ int actual_size);
+
+ void ReportSuccess();
+ void ReportError(Status status, const base::FilePath& path);
+
+ void CleanUp();
+
+ base::FilePath image_dir_;
+ std::set<base::FilePath> image_paths_;
+ ImageDecodedCallback image_decoded_callback_;
+ SanitizationDoneCallback done_callback_;
+ data_decoder::mojom::ImageDecoderPtr image_decoder_ptr_;
+ base::WeakPtrFactory<ImageSanitizer> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageSanitizer);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_IMAGE_SANITIZER_H_
\ No newline at end of file
diff --git a/extensions/browser/image_sanitizer_unittest.cc b/extensions/browser/image_sanitizer_unittest.cc
new file mode 100644
index 0000000..27eecb3
--- /dev/null
+++ b/extensions/browser/image_sanitizer_unittest.cc
@@ -0,0 +1,211 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "extensions/browser/image_sanitizer.h"
+
+#include <map>
+#include <memory>
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/run_loop.h"
+#include "base/strings/string_number_conversions.h"
+#include "build/build_config.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace extensions {
+
+namespace {
+
+constexpr char kBase64edValidPng[] =
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd"
+ "1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC";
+
+constexpr char kBase64edInvalidPng[] =
+ "Rw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd"
+ "1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC";
+
+class ImageSanitizerTest : public testing::Test {
+ public:
+ ImageSanitizerTest()
+ : thread_bundle_(content::TestBrowserThreadBundle::DEFAULT) {}
+
+ protected:
+ void CreateValidImage(const base::FilePath::StringPieceType& file_name) {
+ ASSERT_TRUE(WriteBase64DataToFile(kBase64edValidPng, file_name));
+ }
+
+ void CreateInvalidImage(const base::FilePath::StringPieceType& file_name) {
+ ASSERT_TRUE(WriteBase64DataToFile(kBase64edInvalidPng, file_name));
+ }
+
+ const base::FilePath& GetImagePath() const { return temp_dir_.GetPath(); }
+
+ void WaitForSanitizationDone() {
+ ASSERT_FALSE(done_callback_);
+ base::RunLoop run_loop;
+ done_callback_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+
+ void CreateAndStartSanitizer(
+ const std::set<base::FilePath>& image_relative_paths) {
+ sanitizer_ = ImageSanitizer::CreateAndStart(
+ test_data_decoder_service_.connector(), service_manager::Identity(),
+ temp_dir_.GetPath(), image_relative_paths,
+ base::BindRepeating(&ImageSanitizerTest::ImageSanitizerDecodedImage,
+ base::Unretained(this)),
+ base::BindOnce(&ImageSanitizerTest::ImageSanitizationDone,
+ base::Unretained(this)));
+ }
+
+ ImageSanitizer::Status last_reported_status() const { return last_status_; }
+
+ const base::FilePath& last_reported_path() const {
+ return last_reported_path_;
+ }
+
+ std::map<base::FilePath, SkBitmap>* decoded_images() {
+ return &decoded_images_;
+ }
+
+ private:
+ bool WriteBase64DataToFile(const std::string& base64_data,
+ const base::FilePath::StringPieceType& file_name) {
+ std::string binary;
+ if (!base::Base64Decode(base64_data, &binary))
+ return false;
+
+ base::FilePath path = temp_dir_.GetPath().Append(file_name);
+ return base::WriteFile(path, binary.data(), binary.size());
+ }
+
+ void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
+
+ void ImageSanitizationDone(ImageSanitizer::Status status,
+ const base::FilePath& path) {
+ last_status_ = status;
+ last_reported_path_ = path;
+ if (done_callback_)
+ std::move(done_callback_).Run();
+ }
+
+ void ImageSanitizerDecodedImage(const base::FilePath& path, SkBitmap image) {
+ EXPECT_EQ(decoded_images()->find(path), decoded_images()->end());
+ (*decoded_images())[path] = image;
+ }
+
+ content::TestBrowserThreadBundle thread_bundle_;
+ data_decoder::TestDataDecoderService test_data_decoder_service_;
+ ImageSanitizer::Status last_status_;
+ base::FilePath last_reported_path_;
+ base::OnceClosure done_callback_;
+ std::unique_ptr<ImageSanitizer> sanitizer_;
+ base::ScopedTempDir temp_dir_;
+ std::map<base::FilePath, SkBitmap> decoded_images_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageSanitizerTest);
+};
+
+} // namespace
+
+TEST_F(ImageSanitizerTest, NoImagesProvided) {
+ CreateAndStartSanitizer(std::set<base::FilePath>());
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kSuccess);
+ EXPECT_TRUE(last_reported_path().empty());
+}
+
+TEST_F(ImageSanitizerTest, InvalidPathAbsolute) {
+ base::FilePath normal_path(FILE_PATH_LITERAL("hello.png"));
+#if defined(OS_WIN)
+ base::FilePath absolute_path(FILE_PATH_LITERAL("c:\\Windows\\win32"));
+#else
+ base::FilePath absolute_path(FILE_PATH_LITERAL("/usr/bin/root"));
+#endif
+ CreateAndStartSanitizer({normal_path, absolute_path});
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kImagePathError);
+ EXPECT_EQ(last_reported_path(), absolute_path);
+}
+
+TEST_F(ImageSanitizerTest, InvalidPathReferenceParent) {
+ base::FilePath good_path(FILE_PATH_LITERAL("hello.png"));
+ base::FilePath bad_path(FILE_PATH_LITERAL("hello"));
+ bad_path = bad_path.Append(base::FilePath::kParentDirectory)
+ .Append(base::FilePath::kParentDirectory)
+ .Append(base::FilePath::kParentDirectory)
+ .Append(FILE_PATH_LITERAL("usr"))
+ .Append(FILE_PATH_LITERAL("bin"));
+ CreateAndStartSanitizer({good_path, bad_path});
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kImagePathError);
+ EXPECT_EQ(last_reported_path(), bad_path);
+}
+
+TEST_F(ImageSanitizerTest, ValidCase) {
+ constexpr std::array<const base::FilePath::CharType* const, 10> kFileNames{
+ {FILE_PATH_LITERAL("image0.png"), FILE_PATH_LITERAL("image1.png"),
+ FILE_PATH_LITERAL("image2.png"), FILE_PATH_LITERAL("image3.png"),
+ FILE_PATH_LITERAL("image4.png"), FILE_PATH_LITERAL("image5.png"),
+ FILE_PATH_LITERAL("image6.png"), FILE_PATH_LITERAL("image7.png"),
+ FILE_PATH_LITERAL("image8.png"), FILE_PATH_LITERAL("image9.png")}};
+ std::set<base::FilePath> paths;
+ for (const base::FilePath::CharType* file_name : kFileNames) {
+ CreateValidImage(file_name);
+ paths.insert(base::FilePath(file_name));
+ }
+ CreateAndStartSanitizer(paths);
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kSuccess);
+ EXPECT_TRUE(last_reported_path().empty());
+ // Make sure the image files are there and non empty, and that the
+ // ImageSanitizerDecodedImage callback was invoked for every image.
+ for (const auto& path : paths) {
+ int64_t file_size = 0;
+ base::FilePath full_path = GetImagePath().Append(path);
+ EXPECT_TRUE(base::GetFileSize(full_path, &file_size));
+ EXPECT_GT(file_size, 0);
+
+ ASSERT_TRUE(base::ContainsKey(*decoded_images(), path));
+ EXPECT_FALSE((*decoded_images())[path].drawsNothing());
+ }
+ // No extra images should have been reported.
+ EXPECT_EQ(decoded_images()->size(), 10U);
+}
+
+TEST_F(ImageSanitizerTest, MissingImage) {
+ constexpr base::FilePath::CharType kGoodPngName[] =
+ FILE_PATH_LITERAL("image.png");
+ constexpr base::FilePath::CharType kNonExistingName[] =
+ FILE_PATH_LITERAL("i_don_t_exist.png");
+ CreateValidImage(kGoodPngName);
+ base::FilePath good_png(kGoodPngName);
+ base::FilePath bad_png(kNonExistingName);
+ CreateAndStartSanitizer({good_png, bad_png});
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kFileReadError);
+ EXPECT_EQ(last_reported_path(), bad_png);
+}
+
+TEST_F(ImageSanitizerTest, InvalidImage) {
+ constexpr base::FilePath::CharType kGoodPngName[] =
+ FILE_PATH_LITERAL("good.png");
+ constexpr base::FilePath::CharType kBadPngName[] =
+ FILE_PATH_LITERAL("bad.png");
+ CreateValidImage(kGoodPngName);
+ CreateInvalidImage(kBadPngName);
+ base::FilePath good_png(kGoodPngName);
+ base::FilePath bad_png(kBadPngName);
+ CreateAndStartSanitizer({good_png, bad_png});
+ WaitForSanitizationDone();
+ EXPECT_EQ(last_reported_status(), ImageSanitizer::Status::kDecodingError);
+ EXPECT_EQ(last_reported_path(), bad_png);
+}
+
+} // namespace extensions
diff --git a/extensions/browser/sandboxed_unpacker.cc b/extensions/browser/sandboxed_unpacker.cc
index 5aa7aa9e..84cdd3d 100644
--- a/extensions/browser/sandboxed_unpacker.cc
+++ b/extensions/browser/sandboxed_unpacker.cc
@@ -15,6 +15,7 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
+#include "base/i18n/rtl.h"
#include "base/json/json_string_value_serializer.h"
#include "base/metrics/histogram_macros.h"
#include "base/path_service.h"
@@ -41,6 +42,8 @@
#include "extensions/common/manifest_handlers/icons_handler.h"
#include "extensions/common/switches.h"
#include "extensions/strings/grit/extensions_strings.h"
+#include "services/data_decoder/public/interfaces/constants.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/codec/png_codec.h"
@@ -182,21 +185,6 @@
return false;
}
-// Read the decoded images back from the file we saved them to.
-// |extension_path| is the path to the extension we unpacked that wrote the
-// data. Returns true on success.
-bool ReadImagesFromFile(const base::FilePath& extension_path,
- DecodedImages* images) {
- base::FilePath path = extension_path.AppendASCII(kDecodedImagesFilename);
- std::string file_str;
- if (!base::ReadFileToString(path, &file_str))
- return false;
-
- IPC::Message pickle(file_str.data(), file_str.size());
- base::PickleIterator iter(pickle);
- return IPC::ReadParam(&pickle, &iter, images);
-}
-
// Read the decoded message catalogs back from the file we saved them to.
// |extension_path| is the path to the extension we unpacked that wrote the
// data. Returns true on success.
@@ -223,12 +211,14 @@
}
SandboxedUnpacker::SandboxedUnpacker(
+ std::unique_ptr<service_manager::Connector> connector,
Manifest::Location location,
int creation_flags,
const base::FilePath& extensions_dir,
const scoped_refptr<base::SequencedTaskRunner>& unpacker_io_task_runner,
SandboxedUnpackerClient* client)
- : client_(client),
+ : connector_(std::move(connector)),
+ client_(client),
extensions_dir_(extensions_dir),
location_(location),
creation_flags_(creation_flags),
@@ -237,6 +227,12 @@
// the utility process kills itself for a bad IPC.
CHECK_GT(location, Manifest::INVALID_LOCATION);
CHECK_LT(location, Manifest::NUM_LOCATIONS);
+
+ // Use a random instance ID to guarantee the connection is to a new data
+ // decoder service (running in its own process).
+ data_decoder_identity_ = service_manager::Identity(
+ data_decoder::mojom::kServiceName, service_manager::mojom::kInheritUserID,
+ base::UnguessableToken::Create().ToString());
}
bool SandboxedUnpacker::CreateTempDirectory() {
@@ -501,21 +497,105 @@
return;
}
- SkBitmap install_icon;
- if (!RewriteImageFiles(&install_icon))
+ // The install icon path may be empty, which is OK, but if it is not it should
+ // be normalized successfully.
+ const std::string& original_install_icon_path =
+ IconsInfo::GetIcons(extension_.get())
+ .Get(extension_misc::EXTENSION_ICON_LARGE,
+ ExtensionIconSet::MATCH_BIGGER);
+ if (!original_install_icon_path.empty() &&
+ !NormalizeExtensionResourcePath(
+ base::FilePath::FromUTF8Unsafe(original_install_icon_path),
+ &install_icon_path_)) {
+ // Invalid path for browser image.
+ ReportFailure(INVALID_PATH_FOR_BROWSER_IMAGE,
+ l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
return;
-
- if (!RewriteCatalogFiles())
- return;
-
- // Index and persist ruleset for the Declarative Net Request API.
- base::Optional<int> dnr_ruleset_checksum;
- if (!IndexAndPersistRulesIfNeeded(std::move(json_ruleset),
- &dnr_ruleset_checksum)) {
- return; // Failure was already reported.
}
- ReportSuccess(std::move(manifest), install_icon, dnr_ruleset_checksum);
+ std::set<base::FilePath> image_paths =
+ ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
+ image_sanitizer_ = ImageSanitizer::CreateAndStart(
+ connector_.get(), data_decoder_identity_, extension_root_, image_paths,
+ base::BindRepeating(&SandboxedUnpacker::ImageSanitizerDecodedImage, this),
+ base::BindOnce(&SandboxedUnpacker::ImageSanitizationDone, this,
+ std::move(manifest), std::move(json_ruleset)));
+}
+
+void SandboxedUnpacker::ImageSanitizationDone(
+ std::unique_ptr<base::DictionaryValue> manifest,
+ std::unique_ptr<base::ListValue> json_ruleset,
+ ImageSanitizer::Status status,
+ const base::FilePath& file_path_for_error) {
+ // Release |image_sanitizer_|, it is not useful anymore and reference |this|.
+ // (keep a local ref to this so if |image_sanitizer_| was the last ref to
+ // |this| it stays valid for the duration of the call).
+ scoped_refptr<SandboxedUnpacker> keep_this_alive = this;
+ image_sanitizer_ = nullptr;
+
+ if (status == ImageSanitizer::Status::kSuccess) {
+ if (!RewriteCatalogFiles())
+ return;
+
+ // Index and persist ruleset for the Declarative Net Request API.
+ base::Optional<int> dnr_ruleset_checksum;
+ if (!IndexAndPersistRulesIfNeeded(std::move(json_ruleset),
+ &dnr_ruleset_checksum)) {
+ return; // Failure was already reported.
+ }
+
+ ReportSuccess(std::move(manifest), dnr_ruleset_checksum);
+ return;
+ }
+
+ FailureReason failure_reason = UNPACKER_CLIENT_FAILED;
+ base::string16 error;
+ switch (status) {
+ case ImageSanitizer::Status::kImagePathError:
+ failure_reason = INVALID_PATH_FOR_BROWSER_IMAGE;
+ error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE"));
+ break;
+ case ImageSanitizer::Status::kFileReadError:
+ case ImageSanitizer::Status::kDecodingError:
+ error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
+ base::i18n::GetDisplayStringInLTRDirectionality(
+ file_path_for_error.BaseName().LossyDisplayName()));
+ break;
+ case ImageSanitizer::Status::kFileDeleteError:
+ failure_reason = ERROR_REMOVING_OLD_IMAGE_FILE;
+ error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE"));
+ break;
+ case ImageSanitizer::Status::kEncodingError:
+ failure_reason = ERROR_RE_ENCODING_THEME_IMAGE;
+ error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE"));
+ break;
+ case ImageSanitizer::Status::kFileWriteError:
+ failure_reason = ERROR_SAVING_THEME_IMAGE;
+ error =
+ l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
+ ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE"));
+ break;
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ ReportFailure(failure_reason, error);
+}
+
+void SandboxedUnpacker::ImageSanitizerDecodedImage(const base::FilePath& path,
+ SkBitmap image) {
+ if (path == install_icon_path_)
+ install_icon_ = image;
}
bool SandboxedUnpacker::IndexAndPersistRulesIfNeeded(
@@ -754,7 +834,6 @@
void SandboxedUnpacker::ReportSuccess(
std::unique_ptr<base::DictionaryValue> original_manifest,
- const SkBitmap& install_icon,
const base::Optional<int>& dnr_ruleset_checksum) {
UMA_HISTOGRAM_COUNTS("Extensions.SandboxUnpackSuccess", 1);
@@ -767,7 +846,7 @@
// Client takes ownership of temporary directory, manifest, and extension.
client_->OnUnpackSuccess(temp_dir_.Take(), extension_root_,
std::move(original_manifest), extension_.get(),
- install_icon, dnr_ruleset_checksum);
+ install_icon_, dnr_ruleset_checksum);
extension_ = NULL;
}
@@ -806,131 +885,6 @@
return final_manifest.release();
}
-bool SandboxedUnpacker::RewriteImageFiles(SkBitmap* install_icon) {
- DCHECK(!temp_dir_.GetPath().empty());
-
- DecodedImages images;
- if (!ReadImagesFromFile(temp_dir_.GetPath(), &images)) {
- // Couldn't read image data from disk.
- ReportFailure(COULD_NOT_READ_IMAGE_DATA_FROM_DISK,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("COULD_NOT_READ_IMAGE_DATA_FROM_DISK")));
- return false;
- }
-
- // Delete any images that may be used by the browser. We're going to write
- // out our own versions of the parsed images, and we want to make sure the
- // originals are gone for good.
- std::set<base::FilePath> image_paths =
- ExtensionsClient::Get()->GetBrowserImagePaths(extension_.get());
-
- // Decoded |images| set contains normalized paths and |image_paths| contains
- // original paths. It is required to check sizes of normalized sets.
- std::set<base::FilePath> normalized_image_paths =
- NormalizeExtensionResourcePaths(image_paths);
- if (normalized_image_paths.size() != images.size()) {
- // Decoded images don't match what's in the manifest.
- ReportFailure(
- DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("DECODED_IMAGES_DO_NOT_MATCH_THE_MANIFEST")));
- return false;
- }
-
- // Report if original icons paths invalid. Normalization procedure may skip
- // invalid paths, so it is required to check exactly original paths.
- for (const auto& path : image_paths) {
- if (path.IsAbsolute() || path.ReferencesParent()) {
- // Invalid path for browser image.
- ReportFailure(INVALID_PATH_FOR_BROWSER_IMAGE,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
- return false;
- }
- }
-
- for (const auto& path : normalized_image_paths) {
- if (!base::DeleteFile(extension_root_.Append(path), false)) {
- // Error removing old image file.
- ReportFailure(ERROR_REMOVING_OLD_IMAGE_FILE,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("ERROR_REMOVING_OLD_IMAGE_FILE")));
- return false;
- }
- }
-
- // Get install icon normalized path. Original install icon path may be empty
- // and it is ok. But if original install icon path is not empty, it should be
- // normalized successfully.
- const std::string& install_icon_path =
- IconsInfo::GetIcons(extension_.get())
- .Get(extension_misc::EXTENSION_ICON_LARGE,
- ExtensionIconSet::MATCH_BIGGER);
- base::FilePath normalized_install_icon_path;
- if (!install_icon_path.empty() &&
- !NormalizeExtensionResourcePath(
- base::FilePath::FromUTF8Unsafe(install_icon_path),
- &normalized_install_icon_path)) {
- // Invalid path for browser image.
- ReportFailure(INVALID_PATH_FOR_BROWSER_IMAGE,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("INVALID_PATH_FOR_BROWSER_IMAGE")));
- return false;
- }
-
- // Write our parsed images back to disk as well.
- for (size_t i = 0; i < images.size(); ++i) {
- const SkBitmap& image = std::get<0>(images[i]);
- const base::FilePath& path_suffix = std::get<1>(images[i]);
- if (path_suffix == normalized_install_icon_path)
- *install_icon = image;
-
- if (path_suffix.IsAbsolute() || path_suffix.ReferencesParent()) {
- // Invalid path for bitmap image.
- ReportFailure(INVALID_PATH_FOR_BITMAP_IMAGE,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("INVALID_PATH_FOR_BITMAP_IMAGE")));
- return false;
- }
-
- base::FilePath path = extension_root_.Append(path_suffix);
-
- std::vector<unsigned char> image_data;
- // TODO(mpcomplete): It's lame that we're encoding all images as PNG, even
- // though they may originally be .jpg, etc. Figure something out.
- // http://code.google.com/p/chromium/issues/detail?id=12459
- if (!gfx::PNGCodec::EncodeBGRASkBitmap(image, false, &image_data)) {
- // Error re-encoding theme image.
- ReportFailure(ERROR_RE_ENCODING_THEME_IMAGE,
- l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("ERROR_RE_ENCODING_THEME_IMAGE")));
- return false;
- }
-
- // Note: we're overwriting existing files that the utility process wrote,
- // so we can be sure the directory exists.
- const char* image_data_ptr = reinterpret_cast<const char*>(&image_data[0]);
- int size = base::checked_cast<int>(image_data.size());
- if (base::WriteFile(path, image_data_ptr, size) != size) {
- // Error saving theme image.
- ReportFailure(
- ERROR_SAVING_THEME_IMAGE,
- l10n_util::GetStringFUTF16(IDS_EXTENSION_PACKAGE_INSTALL_ERROR,
- ASCIIToUTF16("ERROR_SAVING_THEME_IMAGE")));
- return false;
- }
- }
-
- return true;
-}
-
bool SandboxedUnpacker::RewriteCatalogFiles() {
base::DictionaryValue catalogs;
if (!ReadMessageCatalogsFromFile(temp_dir_.GetPath(), &catalogs)) {
diff --git a/extensions/browser/sandboxed_unpacker.h b/extensions/browser/sandboxed_unpacker.h
index a8fe285..a9dd506 100644
--- a/extensions/browser/sandboxed_unpacker.h
+++ b/extensions/browser/sandboxed_unpacker.h
@@ -17,8 +17,10 @@
#include "base/time/time.h"
#include "content/public/browser/utility_process_mojo_client.h"
#include "extensions/browser/crx_file_info.h"
+#include "extensions/browser/image_sanitizer.h"
#include "extensions/browser/install/crx_install_error.h"
#include "extensions/common/manifest.h"
+#include "services/service_manager/public/cpp/identity.h"
class SkBitmap;
@@ -28,6 +30,10 @@
class SequencedTaskRunner;
}
+namespace service_manager {
+class Connector;
+}
+
namespace extensions {
class Extension;
@@ -101,6 +107,8 @@
// passing the |location| and |creation_flags| to Extension::Create. The
// |extensions_dir| parameter should specify the directory under which we'll
// create a subdirectory to write the unpacked extension contents.
+ // |connector| must be a fresh connector (not yet associated to any thread) to
+ // the service manager.
// Note: Because this requires disk I/O, the task runner passed should use
// TaskShutdownBehavior::SKIP_ON_SHUTDOWN to ensure that either the task is
// fully run (if initiated before shutdown) or not run at all (if shutdown is
@@ -110,6 +118,7 @@
// TODO(devlin): SKIP_ON_SHUTDOWN is also not quite sufficient for this. We
// should probably instead be using base::ImportantFileWriter or similar.
SandboxedUnpacker(
+ std::unique_ptr<service_manager::Connector> connector,
Manifest::Location location,
int creation_flags,
const base::FilePath& extensions_dir,
@@ -236,9 +245,14 @@
std::unique_ptr<base::ListValue> json_ruleset);
void UnpackExtensionFailed(const base::string16& error);
+ void ImageSanitizationDone(std::unique_ptr<base::DictionaryValue> manifest,
+ std::unique_ptr<base::ListValue> json_ruleset,
+ ImageSanitizer::Status status,
+ const base::FilePath& path);
+ void ImageSanitizerDecodedImage(const base::FilePath& path, SkBitmap image);
+
// Reports unpack success or failure, or unzip failure.
void ReportSuccess(std::unique_ptr<base::DictionaryValue> original_manifest,
- const SkBitmap& install_icon,
const base::Optional<int>& dnr_ruleset_checksum);
void ReportFailure(FailureReason reason, const base::string16& error);
@@ -249,7 +263,6 @@
// Overwrites original files with safe results from utility process.
// Reports error and returns false if it fails.
- bool RewriteImageFiles(SkBitmap* install_icon);
bool RewriteCatalogFiles();
// Cleans up temp directory artifacts.
@@ -263,6 +276,9 @@
std::unique_ptr<base::ListValue> json_ruleset,
base::Optional<int>* dnr_ruleset_checksum);
+ // Connector to the ServiceManager required by the Unzip API.
+ std::unique_ptr<service_manager::Connector> connector_;
+
// If we unpacked a CRX file, we hold on to the path name for use
// in various histograms.
base::FilePath crx_path_for_histograms_;
@@ -307,6 +323,21 @@
std::unique_ptr<content::UtilityProcessMojoClient<mojom::ExtensionUnpacker>>
utility_process_mojo_client_;
+ // The normalized path of the install icon path, retrieved from the manifest.
+ base::FilePath install_icon_path_;
+
+ // The decoded install icon.
+ SkBitmap install_icon_;
+
+ // The identity used to connect to the data decoder service. It is unique to
+ // this SandboxedUnpacker instance so that data decoder operations for
+ // unpacking this extension share the same process, and so that no unrelated
+ // data decoder operation use that process.
+ service_manager::Identity data_decoder_identity_;
+
+ // The ImageSanitizer used to clean-up images.
+ std::unique_ptr<ImageSanitizer> image_sanitizer_;
+
DISALLOW_COPY_AND_ASSIGN(SandboxedUnpacker);
};
diff --git a/extensions/browser/sandboxed_unpacker_unittest.cc b/extensions/browser/sandboxed_unpacker_unittest.cc
index 5e27fe165..40f1508 100644
--- a/extensions/browser/sandboxed_unpacker_unittest.cc
+++ b/extensions/browser/sandboxed_unpacker_unittest.cc
@@ -12,6 +12,7 @@
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "components/crx_file/id_util.h"
@@ -23,12 +24,45 @@
#include "extensions/common/extension.h"
#include "extensions/common/extension_paths.h"
#include "extensions/common/switches.h"
+#include "extensions/test/test_extensions_client.h"
+#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
+#include "services/service_manager/public/cpp/connector.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/zlib/google/zip.h"
namespace extensions {
+namespace {
+
+// Inserts an illegal path into the browser images returned by
+// TestExtensionsClient for any extension.
+class IllegalImagePathInserter
+ : public TestExtensionsClient::BrowserImagePathsFilter {
+ public:
+ IllegalImagePathInserter(TestExtensionsClient* client) : client_(client) {
+ client_->AddBrowserImagePathsFilter(this);
+ }
+
+ virtual ~IllegalImagePathInserter() {
+ client_->RemoveBrowserImagePathsFilter(this);
+ }
+
+ void Filter(const Extension* extension,
+ std::set<base::FilePath>* paths) override {
+ base::FilePath illegal_path =
+ base::FilePath(base::FilePath::kParentDirectory)
+ .AppendASCII(kTempExtensionName)
+ .AppendASCII("product_logo_128.png");
+ paths->insert(illegal_path);
+ }
+
+ private:
+ TestExtensionsClient* client_;
+};
+
+} // namespace
+
class MockSandboxedUnpackerClient : public SandboxedUnpackerClient {
public:
void WaitForUnpack() {
@@ -83,7 +117,8 @@
client_ = new MockSandboxedUnpackerClient;
sandboxed_unpacker_ = new SandboxedUnpacker(
- Manifest::INTERNAL, Extension::NO_FLAGS, extensions_dir_.GetPath(),
+ test_data_decoder_service_.connector()->Clone(), Manifest::INTERNAL,
+ Extension::NO_FLAGS, extensions_dir_.GetPath(),
base::ThreadTaskRunnerHandle::Get(), client_);
}
@@ -145,13 +180,16 @@
sandboxed_unpacker_));
}
- base::FilePath GetInstallPath() {
+ bool InstallSucceeded() const { return !client_->temp_dir().empty(); }
+
+ base::FilePath GetInstallPath() const {
return client_->temp_dir().AppendASCII(kTempExtensionName);
}
- base::string16 GetInstallError() { return client_->unpack_err(); }
+ base::string16 GetInstallError() const { return client_->unpack_err(); }
protected:
+ data_decoder::TestDataDecoderService test_data_decoder_service_;
base::ScopedTempDir extensions_dir_;
MockSandboxedUnpackerClient* client_;
scoped_refptr<SandboxedUnpacker> sandboxed_unpacker_;
@@ -159,6 +197,24 @@
in_process_utility_thread_helper_;
};
+TEST_F(SandboxedUnpackerTest, ImageDecodingError) {
+ const char kExpected[] = "Could not decode image: ";
+ SetupUnpacker("bad_image.crx", "");
+ EXPECT_TRUE(base::StartsWith(GetInstallError(), base::ASCIIToUTF16(kExpected),
+ base::CompareCase::INSENSITIVE_ASCII))
+ << "Expected prefix: \"" << kExpected << "\", actual error: \""
+ << GetInstallError() << "\"";
+}
+
+TEST_F(SandboxedUnpackerTest, BadPathError) {
+ IllegalImagePathInserter inserter(
+ static_cast<TestExtensionsClient*>(ExtensionsClient::Get()));
+ SetupUnpacker("good_package.crx", "");
+ // Install should have failed with an error.
+ EXPECT_FALSE(InstallSucceeded());
+ EXPECT_FALSE(GetInstallError().empty());
+}
+
TEST_F(SandboxedUnpackerTest, NoCatalogsSuccess) {
SetupUnpacker("no_l10n.crx", "");
// Check that there is no _locales folder.
diff --git a/extensions/common/constants.cc b/extensions/common/constants.cc
index ef80d21..57b6606 100644
--- a/extensions/common/constants.cc
+++ b/extensions/common/constants.cc
@@ -29,8 +29,6 @@
const char kTempExtensionName[] = "CRX_INSTALL";
-const char kDecodedImagesFilename[] = "DECODED_IMAGES";
-
const char kDecodedMessageCatalogsFilename[] = "DECODED_MESSAGE_CATALOGS";
const char kGeneratedBackgroundPageFilename[] =
diff --git a/extensions/common/constants.h b/extensions/common/constants.h
index a55a018..f0d6c6ffb 100644
--- a/extensions/common/constants.h
+++ b/extensions/common/constants.h
@@ -46,9 +46,6 @@
// validation before finalizing install.
extern const char kTempExtensionName[];
-// The file to write our decoded images to, relative to the extension_path.
-extern const char kDecodedImagesFilename[];
-
// The file to write our decoded message catalogs to, relative to the
// extension_path.
extern const char kDecodedMessageCatalogsFilename[];
diff --git a/extensions/common/extension_unpacker.mojom b/extensions/common/extension_unpacker.mojom
index 46b2a74e..2ce9482 100644
--- a/extensions/common/extension_unpacker.mojom
+++ b/extensions/common/extension_unpacker.mojom
@@ -21,8 +21,8 @@
// the Declarative Net Request API in |json_ruleset|. The supplied |location|,
// and the |creation_flags| defined by Extension::InitFromValueFlags are
// passed into Extension::Create() when unpacking the extension. Decoded
- // image and message catalog data from the extension is written to files
- // kDecodedImagesFilename and kDecodedMessageCatalogsFilename in |path|.
+ // message catalog data from the extension is written to the
+ // kDecodedMessageCatalogsFilename file in |path|.
// If Unpack() fails for any reason, |error| contains a user-displayable
// explanation of what went wrong.
// |channel| and |type| are needed to initialize the global state of the
diff --git a/extensions/utility/unpacker.cc b/extensions/utility/unpacker.cc
index c43edf7..405bf335 100644
--- a/extensions/utility/unpacker.cc
+++ b/extensions/utility/unpacker.cc
@@ -20,12 +20,10 @@
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/values.h"
-#include "content/public/child/image_decoder_utils.h"
#include "extensions/common/api/declarative_net_request/dnr_manifest_data.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_l10n_util.h"
-#include "extensions/common/extension_resource_path_normalizer.h"
#include "extensions/common/extension_utility_types.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/file_util.h"
@@ -46,54 +44,12 @@
namespace errors = manifest_errors;
namespace keys = manifest_keys;
-// A limit to stop us passing dangerously large canvases to the browser.
-const int kMaxImageCanvas = 4096 * 4096;
-
constexpr const base::FilePath::CharType* kAllowedThemeFiletypes[] = {
FILE_PATH_LITERAL(".bmp"), FILE_PATH_LITERAL(".gif"),
FILE_PATH_LITERAL(".jpeg"), FILE_PATH_LITERAL(".jpg"),
FILE_PATH_LITERAL(".json"), FILE_PATH_LITERAL(".png"),
FILE_PATH_LITERAL(".webp")};
-SkBitmap DecodeImage(const base::FilePath& path) {
- // Read the file from disk.
- std::string file_contents;
- if (!base::PathExists(path) ||
- !base::ReadFileToString(path, &file_contents)) {
- return SkBitmap();
- }
-
- // Decode the image using WebKit's image decoder.
- const unsigned char* data =
- reinterpret_cast<const unsigned char*>(file_contents.data());
- SkBitmap bitmap =
- content::DecodeImage(data, gfx::Size(), file_contents.length());
- if (bitmap.computeByteSize() > kMaxImageCanvas)
- return SkBitmap();
- return bitmap;
-}
-
-bool PathContainsParentDirectory(const base::FilePath& path) {
- const base::FilePath::StringType kSeparators(base::FilePath::kSeparators);
- const base::FilePath::StringType kParentDirectory(
- base::FilePath::kParentDirectory);
- const size_t npos = base::FilePath::StringType::npos;
- const base::FilePath::StringType& value = path.value();
-
- for (size_t i = 0; i < value.length();) {
- i = value.find(kParentDirectory, i);
- if (i != npos) {
- if ((i == 0 || kSeparators.find(value[i - 1]) == npos) &&
- (i + 1 < value.length() || kSeparators.find(value[i + 1]) == npos)) {
- return true;
- }
- ++i;
- }
- }
-
- return false;
-}
-
bool WritePickle(const IPC::Message& pickle, const base::FilePath& dest_path) {
int size = base::checked_cast<int>(pickle.size());
const char* data = static_cast<const char*>(pickle.data());
@@ -103,10 +59,6 @@
} // namespace
-struct Unpacker::InternalData {
- DecodedImages decoded_images;
-};
-
Unpacker::Unpacker(const base::FilePath& working_dir,
const base::FilePath& extension_dir,
const std::string& extension_id,
@@ -116,9 +68,7 @@
extension_dir_(extension_dir),
extension_id_(extension_id),
location_(location),
- creation_flags_(creation_flags) {
- internal_data_.reset(new InternalData());
-}
+ creation_flags_(creation_flags) {}
Unpacker::~Unpacker() {
}
@@ -221,20 +171,6 @@
}
extension->AddInstallWarnings(warnings);
- // Decode any images that the browser needs to display. |GetBrowserImagePaths|
- // can contain duplicates like "icon.png" and "./icon.png". It is required to
- // normalize paths to avoid unpacking of the same icon twice.
- const std::set<base::FilePath> image_paths =
- ExtensionsClient::Get()->GetBrowserImagePaths(extension.get());
- if (!VerifyOriginalImagePaths(image_paths))
- return false; // Error was already reported.
- const std::set<base::FilePath> normalized_image_paths =
- NormalizeExtensionResourcePaths(image_paths);
- for (const base::FilePath& path : normalized_image_paths) {
- if (!AddDecodedImage(path))
- return false; // Error was already reported.
- }
-
// Parse all message catalogs (if any).
parsed_catalogs_.reset(new base::DictionaryValue);
if (!LocaleInfo::GetDefaultLocale(extension.get()).empty()) {
@@ -242,7 +178,7 @@
return false; // Error was already reported.
}
- return ReadJSONRulesetIfNeeded(extension.get()) && DumpImagesToFile() &&
+ return ReadJSONRulesetIfNeeded(extension.get()) &&
DumpMessageCatalogsToFile();
}
@@ -270,19 +206,6 @@
return true;
}
-bool Unpacker::DumpImagesToFile() {
- IPC::Message pickle; // We use a Message so we can use WriteParam.
- IPC::WriteParam(&pickle, internal_data_->decoded_images);
-
- base::FilePath path = working_dir_.AppendASCII(kDecodedImagesFilename);
- if (!WritePickle(pickle, path)) {
- SetError("Could not write image data to disk.");
- return false;
- }
-
- return true;
-}
-
bool Unpacker::DumpMessageCatalogsToFile() {
IPC::Message pickle;
IPC::WriteParam(&pickle, *parsed_catalogs_.get());
@@ -297,35 +220,6 @@
return true;
}
-bool Unpacker::AddDecodedImage(const base::FilePath& path) {
- SkBitmap image_bitmap = DecodeImage(extension_dir_.Append(path));
- if (image_bitmap.isNull()) {
- SetUTF16Error(l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_IMAGE_ERROR,
- base::i18n::GetDisplayStringInLTRDirectionality(
- path.BaseName().LossyDisplayName())));
- return false;
- }
-
- internal_data_->decoded_images.push_back(std::make_tuple(image_bitmap, path));
- return true;
-}
-
-bool Unpacker::VerifyOriginalImagePaths(
- const std::set<base::FilePath>& image_paths) {
- for (const base::FilePath& path : image_paths) {
- // Make sure it's not referencing a file outside the extension's subdir.
- if (path.IsAbsolute() || PathContainsParentDirectory(path)) {
- SetUTF16Error(l10n_util::GetStringFUTF16(
- IDS_EXTENSION_PACKAGE_IMAGE_PATH_ERROR,
- base::i18n::GetDisplayStringInLTRDirectionality(
- path.LossyDisplayName())));
- return false;
- }
- }
- return true;
-}
-
bool Unpacker::ReadMessageCatalog(const base::FilePath& message_path) {
std::string error;
JSONFileValueDeserializer deserializer(message_path);
diff --git a/extensions/utility/unpacker.h b/extensions/utility/unpacker.h
index ec9b2fa..4473110f 100644
--- a/extensions/utility/unpacker.h
+++ b/extensions/utility/unpacker.h
@@ -6,7 +6,6 @@
#define EXTENSIONS_UTILITY_UNPACKER_H_
#include <memory>
-#include <set>
#include <string>
#include <vector>
@@ -47,8 +46,7 @@
std::string* error);
// Runs the processing steps for the extension. On success, this returns true
- // and the decoded images will be in a file at
- // |working_dir|/kDecodedImagesFilename and the decoded messages will be in a
+ // and the decoded messages will be in a
// file at |working_dir|/kDecodedMessageCatalogsFilename.
bool Run();
@@ -70,11 +68,6 @@
// error.
bool ReadJSONRulesetIfNeeded(const Extension* extension);
- // Write the decoded images to kDecodedImagesFilename. We do this instead
- // of sending them over IPC, since they are so large. Returns true on
- // success.
- bool DumpImagesToFile();
-
// Write the decoded messages to kDecodedMessageCatalogsFilename. We do this
// instead of sending them over IPC, since they are so large. Returns true on
// success.
@@ -87,10 +80,6 @@
// images.
bool AddDecodedImage(const base::FilePath& path);
- // Verifies original image paths from extension. Returns true iff all paths
- // are valid. Sets an error if at least one path is invalid.
- bool VerifyOriginalImagePaths(const std::set<base::FilePath>& image_paths);
-
// Parses the catalog at the given path and puts it in our list of parsed
// catalogs.
bool ReadMessageCatalog(const base::FilePath& message_path);
@@ -121,11 +110,6 @@
// extension did not provide one.
std::unique_ptr<base::ListValue> parsed_json_ruleset_;
- // A list of decoded images and the paths where those images came from. Paths
- // are relative to the manifest file.
- struct InternalData;
- std::unique_ptr<InternalData> internal_data_;
-
// Dictionary of relative paths and catalogs per path. Paths are in the form
// of _locales/locale, without messages.json base part.
std::unique_ptr<base::DictionaryValue> parsed_catalogs_;
diff --git a/extensions/utility/unpacker_unittest.cc b/extensions/utility/unpacker_unittest.cc
index 1e31a68..fdd085e 100644
--- a/extensions/utility/unpacker_unittest.cc
+++ b/extensions/utility/unpacker_unittest.cc
@@ -141,61 +141,6 @@
EXPECT_EQ(0U, unpacker_->parsed_catalogs()->size());
}
-namespace {
-
-// Inserts an illegal path into the browser images returned by
-// TestExtensionsClient for any extension.
-class IllegalImagePathInserter
- : public TestExtensionsClient::BrowserImagePathsFilter {
- public:
- IllegalImagePathInserter(TestExtensionsClient* client) : client_(client) {
- client_->AddBrowserImagePathsFilter(this);
- }
-
- virtual ~IllegalImagePathInserter() {
- client_->RemoveBrowserImagePathsFilter(this);
- }
-
- void Filter(const Extension* extension,
- std::set<base::FilePath>* paths) override {
- base::FilePath illegal_path =
- base::FilePath(base::FilePath::kParentDirectory)
- .AppendASCII(kTempExtensionName)
- .AppendASCII("product_logo_128.png");
- paths->insert(illegal_path);
- }
-
- private:
- TestExtensionsClient* client_;
-};
-
-} // namespace
-
-TEST_F(UnpackerTest, BadPathError) {
- const char kExpected[] = "Illegal path (absolute or relative with '..'): ";
- SetupUnpacker("good_package.crx");
- IllegalImagePathInserter inserter(
- static_cast<TestExtensionsClient*>(ExtensionsClient::Get()));
-
- EXPECT_FALSE(unpacker_->Run());
- EXPECT_TRUE(base::StartsWith(unpacker_->error_message(),
- ASCIIToUTF16(kExpected),
- base::CompareCase::INSENSITIVE_ASCII))
- << "Expected prefix: \"" << kExpected << "\", actual error: \""
- << unpacker_->error_message() << "\"";
-}
-
-TEST_F(UnpackerTest, ImageDecodingError) {
- const char kExpected[] = "Could not decode image: ";
- SetupUnpacker("bad_image.crx");
- EXPECT_FALSE(unpacker_->Run());
- EXPECT_TRUE(base::StartsWith(unpacker_->error_message(),
- ASCIIToUTF16(kExpected),
- base::CompareCase::INSENSITIVE_ASCII))
- << "Expected prefix: \"" << kExpected << "\", actual error: \""
- << unpacker_->error_message() << "\"";
-}
-
struct UnzipFileFilterTestCase {
const base::FilePath::CharType* input;
const bool should_unzip;