1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "extensions/browser/sandboxed_unpacker.h"
6
7 #include "base/base64.h"
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/path_service.h"
14 #include "base/run_loop.h"
15 #include "base/strings/pattern.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/thread_task_runner_handle.h"
19 #include "base/values.h"
20 #include "components/crx_file/id_util.h"
21 #include "components/services/unzip/content/unzip_service.h"
22 #include "components/services/unzip/in_process_unzipper.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/test/browser_task_environment.h"
25 #include "content/public/test/test_utils.h"
26 #include "extensions/browser/extensions_test.h"
27 #include "extensions/browser/install/crx_install_error.h"
28 #include "extensions/browser/install/sandboxed_unpacker_failure_reason.h"
29 #include "extensions/common/constants.h"
30 #include "extensions/common/extension.h"
31 #include "extensions/common/extension_paths.h"
32 #include "extensions/common/file_util.h"
33 #include "extensions/common/manifest_constants.h"
34 #include "extensions/common/switches.h"
35 #include "extensions/common/value_builder.h"
36 #include "extensions/common/verifier_formats.h"
37 #include "extensions/strings/grit/extensions_strings.h"
38 #include "extensions/test/test_extensions_client.h"
39 #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
40 #include "testing/gmock/include/gmock/gmock.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 #include "third_party/skia/include/core/SkBitmap.h"
43 #include "third_party/zlib/google/zip.h"
44 #include "ui/base/l10n/l10n_util.h"
45
46 namespace extensions {
47
48 namespace {
49
50 // Inserts an illegal path into the browser images returned by
51 // TestExtensionsClient for any extension.
52 class IllegalImagePathInserter
53 : public TestExtensionsClient::BrowserImagePathsFilter {
54 public:
IllegalImagePathInserter(TestExtensionsClient * client)55 IllegalImagePathInserter(TestExtensionsClient* client) : client_(client) {
56 client_->AddBrowserImagePathsFilter(this);
57 }
58
~IllegalImagePathInserter()59 virtual ~IllegalImagePathInserter() {
60 client_->RemoveBrowserImagePathsFilter(this);
61 }
62
Filter(const Extension * extension,std::set<base::FilePath> * paths)63 void Filter(const Extension* extension,
64 std::set<base::FilePath>* paths) override {
65 base::FilePath illegal_path =
66 base::FilePath(base::FilePath::kParentDirectory)
67 .AppendASCII(kTempExtensionName)
68 .AppendASCII("product_logo_128.png");
69 paths->insert(illegal_path);
70 }
71
72 private:
73 TestExtensionsClient* client_;
74 };
75
76 } // namespace
77
78 class MockSandboxedUnpackerClient : public SandboxedUnpackerClient {
79 public:
WaitForUnpack()80 void WaitForUnpack() {
81 scoped_refptr<content::MessageLoopRunner> runner =
82 new content::MessageLoopRunner;
83 quit_closure_ = runner->QuitClosure();
84 runner->Run();
85 }
86
temp_dir() const87 base::FilePath temp_dir() const { return temp_dir_; }
unpack_error_message() const88 base::string16 unpack_error_message() const {
89 if (error_)
90 return error_->message();
91 return base::string16();
92 }
unpack_error_type() const93 CrxInstallErrorType unpack_error_type() const {
94 if (error_)
95 return error_->type();
96 return CrxInstallErrorType::NONE;
97 }
unpack_error_detail() const98 int unpack_error_detail() const {
99 if (error_) {
100 return error_->type() == CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE
101 ? static_cast<int>(error_->sandbox_failure_detail())
102 : static_cast<int>(error_->detail());
103 }
104 return 0;
105 }
106
set_deleted_tracker(bool * deleted_tracker)107 void set_deleted_tracker(bool* deleted_tracker) {
108 deleted_tracker_ = deleted_tracker;
109 }
110
set_should_compute_hashes(bool should_compute_hashes)111 void set_should_compute_hashes(bool should_compute_hashes) {
112 should_compute_hashes_ = should_compute_hashes;
113 }
114
115 private:
~MockSandboxedUnpackerClient()116 ~MockSandboxedUnpackerClient() override {
117 if (deleted_tracker_)
118 *deleted_tracker_ = true;
119 }
120
ShouldComputeHashesForOffWebstoreExtension(scoped_refptr<const Extension> extension,base::OnceCallback<void (bool)> callback)121 void ShouldComputeHashesForOffWebstoreExtension(
122 scoped_refptr<const Extension> extension,
123 base::OnceCallback<void(bool)> callback) override {
124 std::move(callback).Run(should_compute_hashes_);
125 }
126
OnUnpackSuccess(const base::FilePath & temp_dir,const base::FilePath & extension_root,std::unique_ptr<base::DictionaryValue> original_manifest,const Extension * extension,const SkBitmap & install_icon,declarative_net_request::RulesetInstallPrefs ruleset_install_prefs)127 void OnUnpackSuccess(const base::FilePath& temp_dir,
128 const base::FilePath& extension_root,
129 std::unique_ptr<base::DictionaryValue> original_manifest,
130 const Extension* extension,
131 const SkBitmap& install_icon,
132 declarative_net_request::RulesetInstallPrefs
133 ruleset_install_prefs) override {
134 temp_dir_ = temp_dir;
135 std::move(quit_closure_).Run();
136 }
137
OnUnpackFailure(const CrxInstallError & error)138 void OnUnpackFailure(const CrxInstallError& error) override {
139 error_ = error;
140 std::move(quit_closure_).Run();
141 }
142
143 base::Optional<CrxInstallError> error_;
144 base::OnceClosure quit_closure_;
145 base::FilePath temp_dir_;
146 bool* deleted_tracker_ = nullptr;
147 bool should_compute_hashes_ = false;
148 };
149
150 class SandboxedUnpackerTest : public ExtensionsTest {
151 public:
SandboxedUnpackerTest()152 SandboxedUnpackerTest()
153 : ExtensionsTest(content::BrowserTaskEnvironment::IO_MAINLOOP) {}
154
SetUp()155 void SetUp() override {
156 ExtensionsTest::SetUp();
157 ASSERT_TRUE(extensions_dir_.CreateUniqueTempDir());
158 in_process_utility_thread_helper_.reset(
159 new content::InProcessUtilityThreadHelper);
160 // It will delete itself.
161 client_ = new MockSandboxedUnpackerClient;
162
163 InitSandboxedUnpacker();
164
165 // By default, we host an in-process UnzipperImpl to support any service
166 // clients. Tests may explicitly override the launch callback to prevent
167 // this.
168 unzip::SetUnzipperLaunchOverrideForTesting(
169 base::BindRepeating(&unzip::LaunchInProcessUnzipper));
170 }
171
InitSandboxedUnpacker()172 void InitSandboxedUnpacker() {
173 sandboxed_unpacker_ = new SandboxedUnpacker(
174 Manifest::INTERNAL, Extension::NO_FLAGS, extensions_dir_.GetPath(),
175 base::ThreadTaskRunnerHandle::Get(), client_);
176 }
177
TearDown()178 void TearDown() override {
179 unzip::SetUnzipperLaunchOverrideForTesting(base::NullCallback());
180 // Need to destruct SandboxedUnpacker before the message loop since
181 // it posts a task to it.
182 sandboxed_unpacker_ = nullptr;
183 base::RunLoop().RunUntilIdle();
184 ExtensionsTest::TearDown();
185 in_process_utility_thread_helper_.reset();
186 }
187
GetCrxFullPath(const std::string & crx_name)188 base::FilePath GetCrxFullPath(const std::string& crx_name) {
189 base::FilePath full_path;
190 EXPECT_TRUE(base::PathService::Get(extensions::DIR_TEST_DATA, &full_path));
191 full_path = full_path.AppendASCII("unpacker").AppendASCII(crx_name);
192 EXPECT_TRUE(base::PathExists(full_path)) << full_path.value();
193 return full_path;
194 }
195
SetupUnpacker(const std::string & crx_name,const std::string & package_hash)196 void SetupUnpacker(const std::string& crx_name,
197 const std::string& package_hash) {
198 base::FilePath crx_path = GetCrxFullPath(crx_name);
199 extensions::CRXFileInfo crx_info(crx_path, GetTestVerifierFormat());
200 crx_info.expected_hash = package_hash;
201 base::ThreadTaskRunnerHandle::Get()->PostTask(
202 FROM_HERE, base::BindOnce(&SandboxedUnpacker::StartWithCrx,
203 sandboxed_unpacker_, crx_info));
204 client_->WaitForUnpack();
205 }
206
SetupUnpackerWithDirectory(const std::string & crx_name)207 void SetupUnpackerWithDirectory(const std::string& crx_name) {
208 base::ScopedTempDir temp_dir;
209 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
210 base::FilePath crx_path = GetCrxFullPath(crx_name);
211 ASSERT_TRUE(zip::Unzip(crx_path, temp_dir.GetPath()));
212
213 std::string fake_id = crx_file::id_util::GenerateId(crx_name);
214 std::string fake_public_key;
215 base::Base64Encode(std::string(2048, 'k'), &fake_public_key);
216 base::ThreadTaskRunnerHandle::Get()->PostTask(
217 FROM_HERE, base::BindOnce(&SandboxedUnpacker::StartWithDirectory,
218 sandboxed_unpacker_, fake_id, fake_public_key,
219 temp_dir.Take()));
220 client_->WaitForUnpack();
221 }
222
InstallSucceeded() const223 bool InstallSucceeded() const { return !client_->temp_dir().empty(); }
224
GetInstallPath() const225 base::FilePath GetInstallPath() const {
226 return client_->temp_dir().AppendASCII(kTempExtensionName);
227 }
228
GetInstallErrorMessage() const229 base::string16 GetInstallErrorMessage() const {
230 return client_->unpack_error_message();
231 }
232
GetInstallErrorType() const233 CrxInstallErrorType GetInstallErrorType() const {
234 return client_->unpack_error_type();
235 }
236
GetInstallErrorDetail() const237 int GetInstallErrorDetail() const { return client_->unpack_error_detail(); }
238
ExpectInstallErrorContains(const std::string & error)239 void ExpectInstallErrorContains(const std::string& error) {
240 std::string full_error = base::UTF16ToUTF8(client_->unpack_error_message());
241 EXPECT_TRUE(full_error.find(error) != std::string::npos)
242 << "Error message " << full_error << " does not contain " << error;
243 }
244
245 // Unpacks the package |package_name| and checks that |sandboxed_unpacker_|
246 // gets deleted.
TestSandboxedUnpackerDeleted(const std::string & package_name,bool expect_success)247 void TestSandboxedUnpackerDeleted(const std::string& package_name,
248 bool expect_success) {
249 bool client_deleted = false;
250 client_->set_deleted_tracker(&client_deleted);
251 SetupUnpacker(package_name, "");
252 EXPECT_EQ(GetInstallErrorMessage().empty(), expect_success);
253 // Remove our reference to |sandboxed_unpacker_|, it should get deleted
254 // since/ it's the last reference.
255 sandboxed_unpacker_ = nullptr;
256 // The SandboxedUnpacker should have been deleted and deleted the client.
257 EXPECT_TRUE(client_deleted);
258 }
259
SetPublicKey(const std::string & key)260 void SetPublicKey(const std::string& key) {
261 sandboxed_unpacker_->public_key_ = key;
262 }
263
SetExtensionRoot(const base::FilePath & path)264 void SetExtensionRoot(const base::FilePath& path) {
265 sandboxed_unpacker_->extension_root_ = path;
266 }
267
RewriteManifestFile(const base::Value & manifest)268 base::Optional<base::Value> RewriteManifestFile(const base::Value& manifest) {
269 return sandboxed_unpacker_->RewriteManifestFile(manifest);
270 }
271
in_process_data_decoder()272 data_decoder::test::InProcessDataDecoder& in_process_data_decoder() {
273 return in_process_data_decoder_;
274 }
275
276 protected:
277 base::ScopedTempDir extensions_dir_;
278 MockSandboxedUnpackerClient* client_;
279 scoped_refptr<SandboxedUnpacker> sandboxed_unpacker_;
280 std::unique_ptr<content::InProcessUtilityThreadHelper>
281 in_process_utility_thread_helper_;
282
283 data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
284 };
285
TEST_F(SandboxedUnpackerTest,EmptyDefaultLocale)286 TEST_F(SandboxedUnpackerTest, EmptyDefaultLocale) {
287 SetupUnpacker("empty_default_locale.crx", "");
288 ExpectInstallErrorContains(manifest_errors::kInvalidDefaultLocale);
289 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
290 GetInstallErrorType());
291 EXPECT_EQ(
292 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
293 GetInstallErrorDetail());
294 }
295
TEST_F(SandboxedUnpackerTest,HasDefaultLocaleMissingLocalesFolder)296 TEST_F(SandboxedUnpackerTest, HasDefaultLocaleMissingLocalesFolder) {
297 SetupUnpacker("has_default_missing_locales.crx", "");
298 ExpectInstallErrorContains(manifest_errors::kLocalesTreeMissing);
299 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
300 GetInstallErrorType());
301 EXPECT_EQ(
302 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
303 GetInstallErrorDetail());
304 }
305
TEST_F(SandboxedUnpackerTest,InvalidDefaultLocale)306 TEST_F(SandboxedUnpackerTest, InvalidDefaultLocale) {
307 SetupUnpacker("invalid_default_locale.crx", "");
308 ExpectInstallErrorContains(manifest_errors::kInvalidDefaultLocale);
309 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
310 GetInstallErrorType());
311 EXPECT_EQ(
312 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
313 GetInstallErrorDetail());
314 }
315
TEST_F(SandboxedUnpackerTest,MissingDefaultData)316 TEST_F(SandboxedUnpackerTest, MissingDefaultData) {
317 SetupUnpacker("missing_default_data.crx", "");
318 ExpectInstallErrorContains(manifest_errors::kLocalesNoDefaultMessages);
319 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
320 GetInstallErrorType());
321 EXPECT_EQ(
322 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
323 GetInstallErrorDetail());
324 }
325
TEST_F(SandboxedUnpackerTest,MissingDefaultLocaleHasLocalesFolder)326 TEST_F(SandboxedUnpackerTest, MissingDefaultLocaleHasLocalesFolder) {
327 SetupUnpacker("missing_default_has_locales.crx", "");
328 ExpectInstallErrorContains(l10n_util::GetStringUTF8(
329 IDS_EXTENSION_LOCALES_NO_DEFAULT_LOCALE_SPECIFIED));
330 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
331 GetInstallErrorType());
332 EXPECT_EQ(
333 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
334 GetInstallErrorDetail());
335 }
336
TEST_F(SandboxedUnpackerTest,MissingMessagesFile)337 TEST_F(SandboxedUnpackerTest, MissingMessagesFile) {
338 SetupUnpacker("missing_messages_file.crx", "");
339 EXPECT_TRUE(base::MatchPattern(
340 GetInstallErrorMessage(),
341 base::ASCIIToUTF16("*") +
342 base::ASCIIToUTF16(manifest_errors::kLocalesMessagesFileMissing) +
343 base::ASCIIToUTF16("*_locales?en_US?messages.json'.")))
344 << GetInstallErrorMessage();
345 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
346 GetInstallErrorType());
347 EXPECT_EQ(
348 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
349 GetInstallErrorDetail());
350 }
351
TEST_F(SandboxedUnpackerTest,NoLocaleData)352 TEST_F(SandboxedUnpackerTest, NoLocaleData) {
353 SetupUnpacker("no_locale_data.crx", "");
354 ExpectInstallErrorContains(manifest_errors::kLocalesNoDefaultMessages);
355 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
356 GetInstallErrorType());
357 EXPECT_EQ(
358 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
359 GetInstallErrorDetail());
360 }
361
TEST_F(SandboxedUnpackerTest,ImageDecodingError)362 TEST_F(SandboxedUnpackerTest, ImageDecodingError) {
363 const char kExpected[] = "Could not decode image: ";
364 SetupUnpacker("bad_image.crx", "");
365 EXPECT_TRUE(base::StartsWith(GetInstallErrorMessage(),
366 base::ASCIIToUTF16(kExpected),
367 base::CompareCase::INSENSITIVE_ASCII))
368 << "Expected prefix: \"" << kExpected << "\", actual error: \""
369 << GetInstallErrorMessage() << "\"";
370 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
371 GetInstallErrorType());
372 EXPECT_EQ(
373 static_cast<int>(SandboxedUnpackerFailureReason::UNPACKER_CLIENT_FAILED),
374 GetInstallErrorDetail());
375 }
376
TEST_F(SandboxedUnpackerTest,BadPathError)377 TEST_F(SandboxedUnpackerTest, BadPathError) {
378 IllegalImagePathInserter inserter(
379 static_cast<TestExtensionsClient*>(ExtensionsClient::Get()));
380 SetupUnpacker("good_package.crx", "");
381 // Install should have failed with an error.
382 EXPECT_FALSE(InstallSucceeded());
383 EXPECT_FALSE(GetInstallErrorMessage().empty());
384 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
385 GetInstallErrorType());
386 EXPECT_EQ(static_cast<int>(
387 SandboxedUnpackerFailureReason::INVALID_PATH_FOR_BROWSER_IMAGE),
388 GetInstallErrorDetail());
389 }
390
TEST_F(SandboxedUnpackerTest,NoCatalogsSuccess)391 TEST_F(SandboxedUnpackerTest, NoCatalogsSuccess) {
392 SetupUnpacker("no_l10n.crx", "");
393 // Check that there is no _locales folder.
394 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
395 EXPECT_FALSE(base::PathExists(install_path));
396 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
397 }
398
TEST_F(SandboxedUnpackerTest,FromDirNoCatalogsSuccess)399 TEST_F(SandboxedUnpackerTest, FromDirNoCatalogsSuccess) {
400 SetupUnpackerWithDirectory("no_l10n.crx");
401 // Check that there is no _locales folder.
402 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
403 EXPECT_FALSE(base::PathExists(install_path));
404 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
405 }
406
TEST_F(SandboxedUnpackerTest,WithCatalogsSuccess)407 TEST_F(SandboxedUnpackerTest, WithCatalogsSuccess) {
408 SetupUnpacker("good_l10n.crx", "");
409 // Check that there is _locales folder.
410 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
411 EXPECT_TRUE(base::PathExists(install_path));
412 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
413 }
414
TEST_F(SandboxedUnpackerTest,FromDirWithCatalogsSuccess)415 TEST_F(SandboxedUnpackerTest, FromDirWithCatalogsSuccess) {
416 SetupUnpackerWithDirectory("good_l10n.crx");
417 // Check that there is _locales folder.
418 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
419 EXPECT_TRUE(base::PathExists(install_path));
420 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
421 }
422
TEST_F(SandboxedUnpackerTest,FailHashCheck)423 TEST_F(SandboxedUnpackerTest, FailHashCheck) {
424 base::CommandLine::ForCurrentProcess()->AppendSwitch(
425 extensions::switches::kEnableCrxHashCheck);
426 SetupUnpacker("good_l10n.crx", std::string(64, '0'));
427 // Check that there is an error message.
428 EXPECT_FALSE(GetInstallErrorMessage().empty());
429 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
430 GetInstallErrorType());
431 EXPECT_EQ(static_cast<int>(
432 SandboxedUnpackerFailureReason::CRX_HASH_VERIFICATION_FAILED),
433 GetInstallErrorDetail());
434 }
435
TEST_F(SandboxedUnpackerTest,TestRewriteManifestInjections)436 TEST_F(SandboxedUnpackerTest, TestRewriteManifestInjections) {
437 constexpr char kTestKey[] = "test_key";
438 constexpr char kTestVersion[] = "1.2.3";
439 constexpr char kVersionStr[] = "version";
440 SetPublicKey(kTestKey);
441 SetExtensionRoot(extensions_dir_.GetPath());
442 std::string fingerprint = "1.0123456789abcdef";
443 base::WriteFile(extensions_dir_.GetPath().Append(
444 FILE_PATH_LITERAL("manifest.fingerprint")),
445 fingerprint.c_str(),
446 base::checked_cast<int>(fingerprint.size()));
447 base::Optional<base::Value> manifest(RewriteManifestFile(
448 *DictionaryBuilder().Set(kVersionStr, kTestVersion).Build()));
449 auto* key = manifest->FindStringKey("key");
450 auto* version = manifest->FindStringKey(kVersionStr);
451 auto* differential_fingerprint =
452 manifest->FindStringKey("differential_fingerprint");
453 ASSERT_NE(nullptr, key);
454 ASSERT_NE(nullptr, version);
455 ASSERT_NE(nullptr, differential_fingerprint);
456 EXPECT_EQ(kTestKey, *key);
457 EXPECT_EQ(kTestVersion, *version);
458 EXPECT_EQ(fingerprint, *differential_fingerprint);
459 }
460
TEST_F(SandboxedUnpackerTest,InvalidMessagesFile)461 TEST_F(SandboxedUnpackerTest, InvalidMessagesFile) {
462 SetupUnpackerWithDirectory("invalid_messages_file.crx");
463 // Check that there is no _locales folder.
464 base::FilePath install_path = GetInstallPath().Append(kLocaleFolder);
465 EXPECT_FALSE(base::PathExists(install_path));
466 EXPECT_TRUE(base::MatchPattern(
467 GetInstallErrorMessage(),
468 base::ASCIIToUTF16(
469 "*_locales?en_US?messages.json': Line: 4, column: 1,*")))
470 << GetInstallErrorMessage();
471 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
472 GetInstallErrorType());
473 EXPECT_EQ(static_cast<int>(
474 SandboxedUnpackerFailureReason::COULD_NOT_LOCALIZE_EXTENSION),
475 GetInstallErrorDetail());
476 }
477
TEST_F(SandboxedUnpackerTest,PassHashCheck)478 TEST_F(SandboxedUnpackerTest, PassHashCheck) {
479 base::CommandLine::ForCurrentProcess()->AppendSwitch(
480 extensions::switches::kEnableCrxHashCheck);
481 SetupUnpacker(
482 "good_l10n.crx",
483 "614AE3D608F4C2185E9173293AB3F93EE7C7C79C9A2C3CF71F633386A3296A6C");
484 // Check that there is no error message.
485 EXPECT_THAT(GetInstallErrorMessage(), testing::IsEmpty());
486 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
487 }
488
TEST_F(SandboxedUnpackerTest,SkipHashCheck)489 TEST_F(SandboxedUnpackerTest, SkipHashCheck) {
490 SetupUnpacker("good_l10n.crx", "badhash");
491 // Check that there is no error message.
492 EXPECT_THAT(GetInstallErrorMessage(), testing::IsEmpty());
493 EXPECT_EQ(CrxInstallErrorType::NONE, GetInstallErrorType());
494 }
495
496 // The following tests simulate the utility services failling.
TEST_F(SandboxedUnpackerTest,UnzipperServiceFails)497 TEST_F(SandboxedUnpackerTest, UnzipperServiceFails) {
498 // We override the Unzipper's launching behavior to drop the interface
499 // receiver, effectively simulating a crashy service process.
500 unzip::SetUnzipperLaunchOverrideForTesting(base::BindRepeating([]() -> auto {
501 mojo::PendingRemote<unzip::mojom::Unzipper> remote;
502 ignore_result(remote.InitWithNewPipeAndPassReceiver());
503 return remote;
504 }));
505
506 InitSandboxedUnpacker();
507 SetupUnpacker("good_package.crx", "");
508 EXPECT_FALSE(InstallSucceeded());
509 EXPECT_FALSE(GetInstallErrorMessage().empty());
510 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
511 GetInstallErrorType());
512 EXPECT_EQ(static_cast<int>(SandboxedUnpackerFailureReason::UNZIP_FAILED),
513 GetInstallErrorDetail());
514 }
515
TEST_F(SandboxedUnpackerTest,JsonParserFails)516 TEST_F(SandboxedUnpackerTest, JsonParserFails) {
517 in_process_data_decoder().service().SimulateJsonParserCrashForTesting(true);
518 InitSandboxedUnpacker();
519
520 SetupUnpacker("good_package.crx", "");
521 EXPECT_FALSE(InstallSucceeded());
522 EXPECT_FALSE(GetInstallErrorMessage().empty());
523 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
524 GetInstallErrorType());
525 }
526
TEST_F(SandboxedUnpackerTest,ImageDecoderFails)527 TEST_F(SandboxedUnpackerTest, ImageDecoderFails) {
528 in_process_data_decoder().service().SimulateImageDecoderCrashForTesting(true);
529 InitSandboxedUnpacker();
530 SetupUnpacker("good_package.crx", "");
531 EXPECT_FALSE(InstallSucceeded());
532 EXPECT_FALSE(GetInstallErrorMessage().empty());
533 ASSERT_EQ(CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE,
534 GetInstallErrorType());
535 EXPECT_EQ(
536 static_cast<int>(SandboxedUnpackerFailureReason::
537 UTILITY_PROCESS_CRASHED_WHILE_TRYING_TO_INSTALL),
538 GetInstallErrorDetail());
539 }
540
TEST_F(SandboxedUnpackerTest,NoComputeHashes)541 TEST_F(SandboxedUnpackerTest, NoComputeHashes) {
542 client_->set_should_compute_hashes(false);
543 SetupUnpacker("good_package.crx", "");
544 EXPECT_TRUE(InstallSucceeded());
545 EXPECT_TRUE(GetInstallErrorMessage().empty());
546 EXPECT_FALSE(
547 base::PathExists(file_util::GetComputedHashesPath(GetInstallPath())));
548 }
549
TEST_F(SandboxedUnpackerTest,ComputeHashes)550 TEST_F(SandboxedUnpackerTest, ComputeHashes) {
551 client_->set_should_compute_hashes(true);
552 SetupUnpacker("good_package.crx", "");
553 EXPECT_TRUE(InstallSucceeded());
554 EXPECT_TRUE(GetInstallErrorMessage().empty());
555 EXPECT_TRUE(
556 base::PathExists(file_util::GetComputedHashesPath(GetInstallPath())));
557 }
558
559 // SandboxedUnpacker is ref counted and is reference by callbacks and
560 // InterfacePtrs. This tests that it gets deleted as expected (so that no extra
561 // refs are left).
TEST_F(SandboxedUnpackerTest,DeletedOnSuccess)562 TEST_F(SandboxedUnpackerTest, DeletedOnSuccess) {
563 TestSandboxedUnpackerDeleted("good_l10n.crx", /*expect_success=*/true);
564 }
565
TEST_F(SandboxedUnpackerTest,DeletedOnFailure)566 TEST_F(SandboxedUnpackerTest, DeletedOnFailure) {
567 TestSandboxedUnpackerDeleted("bad_image.crx", /*expect_success=*/false);
568 }
569
570 } // namespace extensions
571