blob: 189a57219599cc3e4778ab0fb1c76675e15f4375 [file] [log] [blame]
[email protected]f5bf1842012-02-15 02:52:261// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]9428edc2009-11-18 18:02:472// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
noelc8702c42017-03-16 08:51:195#include "extensions/browser/sandboxed_unpacker.h"
6
asargentc4fdad22015-08-28 22:44:397#include "base/base64.h"
[email protected]43c05d902013-07-10 21:27:008#include "base/bind.h"
ginkage553af3202015-02-04 12:39:099#include "base/command_line.h"
thestig18dfb7a52014-08-26 10:44:0410#include "base/files/file_util.h"
[email protected]3b63f8f42011-03-28 01:54:1511#include "base/memory/ref_counted.h"
[email protected]9428edc2009-11-18 18:02:4712#include "base/path_service.h"
[email protected]43c05d902013-07-10 21:27:0013#include "base/run_loop.h"
Jay Civelli6d0e68e2018-01-24 16:42:5314#include "base/strings/pattern.h"
[email protected]46acbf12013-06-10 18:43:4215#include "base/strings/string_util.h"
Jay Civelliea8f3df2018-01-24 05:17:3216#include "base/strings/utf_string_conversions.h"
gab273138e2016-05-11 18:09:3917#include "base/threading/thread_task_runner_handle.h"
[email protected]f3a1c642011-07-12 19:15:0318#include "base/values.h"
asargentc4fdad22015-08-28 22:44:3919#include "components/crx_file/id_util.h"
Colin Blundell6a897c72018-03-16 11:14:1420#include "components/services/unzip/public/cpp/test_unzip_service.h"
21#include "components/services/unzip/unzip_service.h"
Alex Kalugina34f8c02017-07-27 11:33:0322#include "content/public/browser/browser_thread.h"
[email protected]43c05d902013-07-10 21:27:0023#include "content/public/test/test_browser_thread_bundle.h"
24#include "content/public/test/test_utils.h"
asargent275faaa2015-01-27 23:43:2925#include "extensions/browser/extensions_test.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"
asargent12a9cab72015-01-16 21:34:1628#include "extensions/common/extension_paths.h"
Jay Civelli6d0e68e2018-01-24 16:42:5329#include "extensions/common/manifest_constants.h"
ginkage553af3202015-02-04 12:39:0930#include "extensions/common/switches.h"
Jay Civelli26a85642018-01-26 21:29:3931#include "extensions/strings/grit/extensions_strings.h"
Jay Civelliea8f3df2018-01-24 05:17:3232#include "extensions/test/test_extensions_client.h"
Jay Civellib6f2cc9c2018-03-10 01:13:5733#include "services/data_decoder/data_decoder_service.h"
Jay Civelliea8f3df2018-01-24 05:17:3234#include "services/data_decoder/public/cpp/test_data_decoder_service.h"
35#include "services/service_manager/public/cpp/connector.h"
Jay Civellib6f2cc9c2018-03-10 01:13:5736#include "services/service_manager/public/cpp/test/test_connector_factory.h"
[email protected]9428edc2009-11-18 18:02:4737#include "testing/gtest/include/gtest/gtest.h"
38#include "third_party/skia/include/core/SkBitmap.h"
asargentc4fdad22015-08-28 22:44:3939#include "third_party/zlib/google/zip.h"
Jay Civelli26a85642018-01-26 21:29:3940#include "ui/base/l10n/l10n_util.h"
[email protected]9428edc2009-11-18 18:02:4741
[email protected]f5ac2742012-07-02 17:50:5842namespace extensions {
43
Jay Civelliea8f3df2018-01-24 05:17:3244namespace {
45
46// Inserts an illegal path into the browser images returned by
47// TestExtensionsClient for any extension.
48class IllegalImagePathInserter
49 : public TestExtensionsClient::BrowserImagePathsFilter {
50 public:
51 IllegalImagePathInserter(TestExtensionsClient* client) : client_(client) {
52 client_->AddBrowserImagePathsFilter(this);
53 }
54
55 virtual ~IllegalImagePathInserter() {
56 client_->RemoveBrowserImagePathsFilter(this);
57 }
58
59 void Filter(const Extension* extension,
60 std::set<base::FilePath>* paths) override {
61 base::FilePath illegal_path =
62 base::FilePath(base::FilePath::kParentDirectory)
63 .AppendASCII(kTempExtensionName)
64 .AppendASCII("product_logo_128.png");
65 paths->insert(illegal_path);
66 }
67
68 private:
69 TestExtensionsClient* client_;
70};
71
72} // namespace
73
[email protected]f5ac2742012-07-02 17:50:5874class MockSandboxedUnpackerClient : public SandboxedUnpackerClient {
[email protected]9428edc2009-11-18 18:02:4775 public:
[email protected]43c05d902013-07-10 21:27:0076 void WaitForUnpack() {
77 scoped_refptr<content::MessageLoopRunner> runner =
78 new content::MessageLoopRunner;
79 quit_closure_ = runner->QuitClosure();
80 runner->Run();
[email protected]9428edc2009-11-18 18:02:4781 }
[email protected]5f2a4752012-04-27 22:18:5882
[email protected]43c05d902013-07-10 21:27:0083 base::FilePath temp_dir() const { return temp_dir_; }
ginkage553af3202015-02-04 12:39:0984 base::string16 unpack_err() const { return error_; }
[email protected]43c05d902013-07-10 21:27:0085
Jay Civelli26a85642018-01-26 21:29:3986 void set_deleted_tracker(bool* deleted_tracker) {
87 deleted_tracker_ = deleted_tracker;
88 }
89
[email protected]43c05d902013-07-10 21:27:0090 private:
Jay Civelli26a85642018-01-26 21:29:3991 ~MockSandboxedUnpackerClient() override {
92 if (deleted_tracker_)
93 *deleted_tracker_ = true;
94 }
[email protected]43c05d902013-07-10 21:27:0095
Karandeep Bhatiaa8930652017-10-11 17:41:1296 void OnUnpackSuccess(
97 const base::FilePath& temp_dir,
98 const base::FilePath& extension_root,
99 std::unique_ptr<base::DictionaryValue> original_manifest,
100 const Extension* extension,
101 const SkBitmap& install_icon,
102 const base::Optional<int>& dnr_ruleset_checksum) override {
[email protected]43c05d902013-07-10 21:27:00103 temp_dir_ = temp_dir;
104 quit_closure_.Run();
[email protected]43c05d902013-07-10 21:27:00105 }
106
ginkage47e603e2015-02-27 08:42:41107 void OnUnpackFailure(const CrxInstallError& error) override {
108 error_ = error.message();
ginkage553af3202015-02-04 12:39:09109 quit_closure_.Run();
[email protected]43c05d902013-07-10 21:27:00110 }
111
ginkage553af3202015-02-04 12:39:09112 base::string16 error_;
[email protected]43c05d902013-07-10 21:27:00113 base::Closure quit_closure_;
114 base::FilePath temp_dir_;
Jay Civelli26a85642018-01-26 21:29:39115 bool* deleted_tracker_ = nullptr;
[email protected]9428edc2009-11-18 18:02:47116};
117
asargent275faaa2015-01-27 23:43:29118class SandboxedUnpackerTest : public ExtensionsTest {
[email protected]9428edc2009-11-18 18:02:47119 public:
danakjaee67172017-06-13 16:37:02120 SandboxedUnpackerTest()
Alex Kalugina34f8c02017-07-27 11:33:03121 : SandboxedUnpackerTest(content::TestBrowserThreadBundle::IO_MAINLOOP) {}
122
123 SandboxedUnpackerTest(content::TestBrowserThreadBundle::Options options)
Lukasz Anforowicz58d0dac2018-03-23 15:48:10124 : ExtensionsTest(options) {}
danakjaee67172017-06-13 16:37:02125
dcheng72191812014-10-28 20:49:56126 void SetUp() override {
asargent275faaa2015-01-27 23:43:29127 ExtensionsTest::SetUp();
128 ASSERT_TRUE(extensions_dir_.CreateUniqueTempDir());
[email protected]c514acf2014-01-09 07:16:52129 in_process_utility_thread_helper_.reset(
130 new content::InProcessUtilityThreadHelper);
[email protected]9428edc2009-11-18 18:02:47131 // It will delete itself.
[email protected]f5ac2742012-07-02 17:50:58132 client_ = new MockSandboxedUnpackerClient;
asargentc4fdad22015-08-28 22:44:39133
Jay Civellib6f2cc9c2018-03-10 01:13:57134 InitSanboxedUnpacker(/*data_decode_service=*/nullptr,
135 /*unzip_service=*/nullptr);
136 }
137
138 void InitSanboxedUnpacker(
139 std::unique_ptr<service_manager::Service> data_decode_service,
140 std::unique_ptr<service_manager::Service> unzip_service) {
141 service_manager::TestConnectorFactory::NameToServiceMap services;
142 if (!data_decode_service)
143 data_decode_service = data_decoder::DataDecoderService::Create();
144 if (!unzip_service)
145 unzip_service = unzip::UnzipService::CreateService();
146 services.insert(
147 std::make_pair("data_decoder", std::move(data_decode_service)));
148 services.insert(std::make_pair("unzip_service", std::move(unzip_service)));
149 test_connector_factory_ =
150 service_manager::TestConnectorFactory::CreateForServices(
151 std::move(services));
152 connector_ = test_connector_factory_->CreateConnector();
153
154 sandboxed_unpacker_ =
155 new SandboxedUnpacker(connector_->Clone(), Manifest::INTERNAL,
156 Extension::NO_FLAGS, extensions_dir_.GetPath(),
157 base::ThreadTaskRunnerHandle::Get(), client_);
[email protected]9428edc2009-11-18 18:02:47158 }
159
dcheng72191812014-10-28 20:49:56160 void TearDown() override {
[email protected]f5ac2742012-07-02 17:50:58161 // Need to destruct SandboxedUnpacker before the message loop since
[email protected]38f285a52010-11-05 21:02:28162 // it posts a task to it.
danakjaee67172017-06-13 16:37:02163 sandboxed_unpacker_ = nullptr;
[email protected]43c05d902013-07-10 21:27:00164 base::RunLoop().RunUntilIdle();
asargent275faaa2015-01-27 23:43:29165 ExtensionsTest::TearDown();
danakjaee67172017-06-13 16:37:02166 in_process_utility_thread_helper_.reset();
[email protected]9428edc2009-11-18 18:02:47167 }
168
asargentc4fdad22015-08-28 22:44:39169 base::FilePath GetCrxFullPath(const std::string& crx_name) {
170 base::FilePath full_path;
171 EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &full_path));
172 full_path = full_path.AppendASCII("unpacker").AppendASCII(crx_name);
173 EXPECT_TRUE(base::PathExists(full_path)) << full_path.value();
174 return full_path;
175 }
176
ginkage553af3202015-02-04 12:39:09177 void SetupUnpacker(const std::string& crx_name,
178 const std::string& package_hash) {
asargentc4fdad22015-08-28 22:44:39179 base::FilePath crx_path = GetCrxFullPath(crx_name);
pranay.kumar7b7ceb182015-05-05 12:12:46180 base::ThreadTaskRunnerHandle::Get()->PostTask(
[email protected]43c05d902013-07-10 21:27:00181 FROM_HERE,
asargentc4fdad22015-08-28 22:44:39182 base::Bind(
183 &SandboxedUnpacker::StartWithCrx, sandboxed_unpacker_,
184 extensions::CRXFileInfo(std::string(), crx_path, package_hash)));
185 client_->WaitForUnpack();
186 }
187
188 void SetupUnpackerWithDirectory(const std::string& crx_name) {
189 base::ScopedTempDir temp_dir;
190 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
191 base::FilePath crx_path = GetCrxFullPath(crx_name);
vabr9142fe22016-09-08 13:19:22192 ASSERT_TRUE(zip::Unzip(crx_path, temp_dir.GetPath()));
asargentc4fdad22015-08-28 22:44:39193
194 std::string fake_id = crx_file::id_util::GenerateId(crx_name);
195 std::string fake_public_key;
196 base::Base64Encode(std::string(2048, 'k'), &fake_public_key);
197 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik07cace42016-09-01 04:21:25198 FROM_HERE,
199 base::Bind(&SandboxedUnpacker::StartWithDirectory, sandboxed_unpacker_,
200 fake_id, fake_public_key, temp_dir.Take()));
[email protected]43c05d902013-07-10 21:27:00201 client_->WaitForUnpack();
[email protected]9428edc2009-11-18 18:02:47202 }
203
Jay Civelliea8f3df2018-01-24 05:17:32204 bool InstallSucceeded() const { return !client_->temp_dir().empty(); }
205
206 base::FilePath GetInstallPath() const {
[email protected]b22c8af62013-07-23 23:17:02207 return client_->temp_dir().AppendASCII(kTempExtensionName);
[email protected]b0b3abd92010-04-30 17:00:09208 }
209
Jay Civelliea8f3df2018-01-24 05:17:32210 base::string16 GetInstallError() const { return client_->unpack_err(); }
ginkage553af3202015-02-04 12:39:09211
Jay Civelli26a85642018-01-26 21:29:39212 void ExpectInstallErrorContains(const std::string& error) {
213 std::string full_error = base::UTF16ToUTF8(client_->unpack_err());
214 EXPECT_TRUE(full_error.find(error) != std::string::npos)
215 << "Error message " << full_error << " does not contain " << error;
216 }
217
218 // Unpacks the package |package_name| and checks that |sandboxed_unpacker_|
219 // gets deleted.
220 void TestSandboxedUnpackerDeleted(const std::string& package_name,
221 bool expect_success) {
222 bool client_deleted = false;
223 client_->set_deleted_tracker(&client_deleted);
224 SetupUnpacker(package_name, "");
225 EXPECT_EQ(GetInstallError().empty(), expect_success);
226 // Remove our reference to |sandboxed_unpacker_|, it should get deleted
227 // since/ it's the last reference.
228 sandboxed_unpacker_ = nullptr;
229 // The SandboxedUnpacker should have been deleted and deleted the client.
230 EXPECT_TRUE(client_deleted);
231 }
232
[email protected]9428edc2009-11-18 18:02:47233 protected:
[email protected]ea1a3f62012-11-16 20:34:23234 base::ScopedTempDir extensions_dir_;
[email protected]f5ac2742012-07-02 17:50:58235 MockSandboxedUnpackerClient* client_;
[email protected]f5ac2742012-07-02 17:50:58236 scoped_refptr<SandboxedUnpacker> sandboxed_unpacker_;
dchengf5d241082016-04-21 03:43:11237 std::unique_ptr<content::InProcessUtilityThreadHelper>
[email protected]c514acf2014-01-09 07:16:52238 in_process_utility_thread_helper_;
Jay Civellib6f2cc9c2018-03-10 01:13:57239 std::unique_ptr<service_manager::TestConnectorFactory>
240 test_connector_factory_;
241 std::unique_ptr<service_manager::Connector> connector_;
[email protected]9428edc2009-11-18 18:02:47242};
243
Jay Civelli26a85642018-01-26 21:29:39244TEST_F(SandboxedUnpackerTest, EmptyDefaultLocale) {
245 SetupUnpacker("empty_default_locale.crx", "");
246 ExpectInstallErrorContains(manifest_errors::kInvalidDefaultLocale);
247}
248
249TEST_F(SandboxedUnpackerTest, HasDefaultLocaleMissingLocalesFolder) {
250 SetupUnpacker("has_default_missing_locales.crx", "");
251 ExpectInstallErrorContains(manifest_errors::kLocalesTreeMissing);
252}
253
254TEST_F(SandboxedUnpackerTest, InvalidDefaultLocale) {
255 SetupUnpacker("invalid_default_locale.crx", "");
256 ExpectInstallErrorContains(manifest_errors::kInvalidDefaultLocale);
257}
258
259TEST_F(SandboxedUnpackerTest, MissingDefaultData) {
260 SetupUnpacker("missing_default_data.crx", "");
261 ExpectInstallErrorContains(manifest_errors::kLocalesNoDefaultMessages);
262}
263
264TEST_F(SandboxedUnpackerTest, MissingDefaultLocaleHasLocalesFolder) {
265 SetupUnpacker("missing_default_has_locales.crx", "");
266 ExpectInstallErrorContains(l10n_util::GetStringUTF8(
267 IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED));
268}
269
270TEST_F(SandboxedUnpackerTest, MissingMessagesFile) {
271 SetupUnpacker("missing_messages_file.crx", "");
272 EXPECT_TRUE(base::MatchPattern(
273 GetInstallError(),
274 base::ASCIIToUTF16("*") +
275 base::ASCIIToUTF16(manifest_errors::kLocalesMessagesFileMissing) +
276 base::ASCIIToUTF16("*_locales?en_US?messages.json'.")))
277 << GetInstallError();
278}
279
280TEST_F(SandboxedUnpackerTest, NoLocaleData) {
281 SetupUnpacker("no_locale_data.crx", "");
282 ExpectInstallErrorContains(manifest_errors::kLocalesNoDefaultMessages);
283}
284
Jay Civelliea8f3df2018-01-24 05:17:32285TEST_F(SandboxedUnpackerTest, ImageDecodingError) {
286 const char kExpected[] = "Could not decode image: ";
287 SetupUnpacker("bad_image.crx", "");
288 EXPECT_TRUE(base::StartsWith(GetInstallError(), base::ASCIIToUTF16(kExpected),
289 base::CompareCase::INSENSITIVE_ASCII))
290 << "Expected prefix: \"" << kExpected << "\", actual error: \""
291 << GetInstallError() << "\"";
292}
293
294TEST_F(SandboxedUnpackerTest, BadPathError) {
295 IllegalImagePathInserter inserter(
296 static_cast<TestExtensionsClient*>(ExtensionsClient::Get()));
297 SetupUnpacker("good_package.crx", "");
298 // Install should have failed with an error.
299 EXPECT_FALSE(InstallSucceeded());
300 EXPECT_FALSE(GetInstallError().empty());
301}
302
[email protected]f5ac2742012-07-02 17:50:58303TEST_F(SandboxedUnpackerTest, NoCatalogsSuccess) {
ginkage553af3202015-02-04 12:39:09304 SetupUnpacker("no_l10n.crx", "");
[email protected]9428edc2009-11-18 18:02:47305 // Check that there is no _locales folder.
asargent275faaa2015-01-27 23:43:29306 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
[email protected]7567484142013-07-11 17:36:07307 EXPECT_FALSE(base::PathExists(install_path));
[email protected]9428edc2009-11-18 18:02:47308}
309
asargentc4fdad22015-08-28 22:44:39310TEST_F(SandboxedUnpackerTest, FromDirNoCatalogsSuccess) {
311 SetupUnpackerWithDirectory("no_l10n.crx");
312 // Check that there is no _locales folder.
313 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
314 EXPECT_FALSE(base::PathExists(install_path));
315}
316
[email protected]11170a772013-12-13 11:38:32317TEST_F(SandboxedUnpackerTest, WithCatalogsSuccess) {
ginkage553af3202015-02-04 12:39:09318 SetupUnpacker("good_l10n.crx", "");
[email protected]43c05d902013-07-10 21:27:00319 // Check that there is _locales folder.
asargent275faaa2015-01-27 23:43:29320 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
[email protected]7567484142013-07-11 17:36:07321 EXPECT_TRUE(base::PathExists(install_path));
[email protected]9428edc2009-11-18 18:02:47322}
[email protected]f5ac2742012-07-02 17:50:58323
asargentc4fdad22015-08-28 22:44:39324TEST_F(SandboxedUnpackerTest, FromDirWithCatalogsSuccess) {
325 SetupUnpackerWithDirectory("good_l10n.crx");
326 // Check that there is _locales folder.
327 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
328 EXPECT_TRUE(base::PathExists(install_path));
329}
330
ginkage553af3202015-02-04 12:39:09331TEST_F(SandboxedUnpackerTest, FailHashCheck) {
332 base::CommandLine::ForCurrentProcess()->AppendSwitch(
333 extensions::switches::kEnableCrxHashCheck);
waffles5918d5f2017-05-23 01:45:28334 SetupUnpacker("good_l10n.crx", std::string(64, '0'));
ginkage553af3202015-02-04 12:39:09335 // Check that there is an error message.
336 EXPECT_NE(base::string16(), GetInstallError());
337}
338
Jay Civelli6d0e68e2018-01-24 16:42:53339TEST_F(SandboxedUnpackerTest, InvalidMessagesFile) {
340 SetupUnpackerWithDirectory("invalid_messages_file.crx");
341 // Check that there is no _locales folder.
342 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
343 EXPECT_FALSE(base::PathExists(install_path));
344 EXPECT_TRUE(base::MatchPattern(
345 GetInstallError(),
346 base::ASCIIToUTF16("*_locales?en_US?messages.json': Line: 2, column: 10,"
347 " Syntax error.'.")))
348 << GetInstallError();
349}
350
ginkage553af3202015-02-04 12:39:09351TEST_F(SandboxedUnpackerTest, PassHashCheck) {
352 base::CommandLine::ForCurrentProcess()->AppendSwitch(
353 extensions::switches::kEnableCrxHashCheck);
354 SetupUnpacker(
355 "good_l10n.crx",
Devlin Cronin1de938972018-04-24 19:17:15356 "614AE3D608F4C2185E9173293AB3F93EE7C7C79C9A2C3CF71F633386A3296A6C");
ginkage553af3202015-02-04 12:39:09357 // Check that there is no error message.
358 EXPECT_EQ(base::string16(), GetInstallError());
359}
360
361TEST_F(SandboxedUnpackerTest, SkipHashCheck) {
362 SetupUnpacker("good_l10n.crx", "badhash");
363 // Check that there is no error message.
364 EXPECT_EQ(base::string16(), GetInstallError());
365}
366
Jay Civellib6f2cc9c2018-03-10 01:13:57367// The following tests simulate the utility services failling.
368TEST_F(SandboxedUnpackerTest, UnzipperServiceFails) {
369 InitSanboxedUnpacker(
370 /*data_decoder_service=*/nullptr,
371 std::make_unique<unzip::CrashyUnzipService>());
372 SetupUnpacker("good_package.crx", "");
373 EXPECT_FALSE(InstallSucceeded());
374 EXPECT_FALSE(GetInstallError().empty());
375}
376
377TEST_F(SandboxedUnpackerTest, JsonParserFails) {
378 InitSanboxedUnpacker(std::make_unique<data_decoder::CrashyDataDecoderService>(
379 /*crash_json=*/true, /*crash_image=*/false),
380 /*unzip_service=*/nullptr);
381 SetupUnpacker("good_package.crx", "");
382 EXPECT_FALSE(InstallSucceeded());
383 EXPECT_FALSE(GetInstallError().empty());
384}
385
386TEST_F(SandboxedUnpackerTest, ImageDecoderFails) {
387 InitSanboxedUnpacker(std::make_unique<data_decoder::CrashyDataDecoderService>(
388 /*crash_json=*/false, /*crash_image=*/true),
389 /*unzip_service=*/nullptr);
390 SetupUnpacker("good_package.crx", "");
391 EXPECT_FALSE(InstallSucceeded());
392 EXPECT_FALSE(GetInstallError().empty());
393}
394
Jay Civelli26a85642018-01-26 21:29:39395// SandboxedUnpacker is ref counted and is reference by callbacks and
396// InterfacePtrs. This tests that it gets deleted as expected (so that no extra
397// refs are left).
398TEST_F(SandboxedUnpackerTest, DeletedOnSuccess) {
399 TestSandboxedUnpackerDeleted("good_l10n.crx", /*expect_success=*/true);
400}
401
402TEST_F(SandboxedUnpackerTest, DeletedOnFailure) {
403 TestSandboxedUnpackerDeleted("bad_image.crx", /*expect_success=*/false);
404}
405
[email protected]f5ac2742012-07-02 17:50:58406} // namespace extensions