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