123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- // 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/containers/contains.h"
- #include "base/files/file_util.h"
- #include "base/files/scoped_temp_dir.h"
- #include "base/memory/ref_counted.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "content/public/test/browser_task_environment.h"
- #include "extensions/browser/extension_file_task_runner.h"
- #include "services/data_decoder/public/cpp/data_decoder.h"
- #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.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 TestClient : public ImageSanitizer::Client {
- public:
- 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_;
- }
- bool done_callback_called() const { return done_callback_called_; }
- bool decoded_image_callback_called() const {
- return decoded_image_callback_called_;
- }
- void SetSanitizationDoneCallback(base::OnceClosure done_callback) {
- ASSERT_FALSE(done_callback_);
- done_callback_ = std::move(done_callback);
- }
- private:
- ~TestClient() override = default;
- data_decoder::DataDecoder* GetDataDecoder() override {
- return &data_decoder_;
- }
- void OnImageSanitizationDone(ImageSanitizer::Status status,
- const base::FilePath& path) override {
- done_callback_called_ = true;
- last_status_ = status;
- last_reported_path_ = path;
- if (done_callback_)
- std::move(done_callback_).Run();
- }
- void OnImageDecoded(const base::FilePath& path, SkBitmap image) override {
- EXPECT_EQ(0u, decoded_images_.count(path));
- decoded_images_[path] = image;
- decoded_image_callback_called_ = true;
- }
- data_decoder::DataDecoder data_decoder_;
- ImageSanitizer::Status last_status_ = ImageSanitizer::Status::kSuccess;
- base::FilePath last_reported_path_;
- base::OnceClosure done_callback_;
- std::map<base::FilePath, SkBitmap> decoded_images_;
- bool done_callback_called_ = false;
- bool decoded_image_callback_called_ = false;
- };
- class ImageSanitizerTest : public testing::Test {
- public:
- ImageSanitizerTest() = default;
- ImageSanitizerTest(const ImageSanitizerTest&) = delete;
- ImageSanitizerTest& operator=(const ImageSanitizerTest&) = delete;
- 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() {
- base::RunLoop run_loop;
- client_->SetSanitizationDoneCallback(run_loop.QuitClosure());
- run_loop.Run();
- }
- void CreateAndStartSanitizer(
- const std::set<base::FilePath>& image_relative_paths) {
- sanitizer_ = ImageSanitizer::CreateAndStart(client_, temp_dir_.GetPath(),
- image_relative_paths,
- GetExtensionFileTaskRunner());
- }
- void ClearSanitizer() { sanitizer_.reset(); }
- TestClient* client() { return client_.get(); }
- data_decoder::test::InProcessDataDecoder& in_process_data_decoder() {
- return in_process_data_decoder_;
- }
- 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()); }
- content::BrowserTaskEnvironment task_environment_;
- data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
- std::unique_ptr<ImageSanitizer> sanitizer_;
- scoped_refptr<TestClient> client_ = base::MakeRefCounted<TestClient>();
- base::ScopedTempDir temp_dir_;
- };
- } // namespace
- TEST_F(ImageSanitizerTest, NoImagesProvided) {
- CreateAndStartSanitizer(std::set<base::FilePath>());
- WaitForSanitizationDone();
- EXPECT_TRUE(client()->done_callback_called());
- EXPECT_EQ(client()->last_reported_status(), ImageSanitizer::Status::kSuccess);
- EXPECT_FALSE(client()->decoded_image_callback_called());
- EXPECT_TRUE(client()->last_reported_path().empty());
- }
- TEST_F(ImageSanitizerTest, InvalidPathAbsolute) {
- base::FilePath normal_path(FILE_PATH_LITERAL("hello.png"));
- #if BUILDFLAG(IS_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(client()->last_reported_status(),
- ImageSanitizer::Status::kImagePathError);
- EXPECT_EQ(client()->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(client()->last_reported_status(),
- ImageSanitizer::Status::kImagePathError);
- EXPECT_EQ(client()->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_TRUE(client()->done_callback_called());
- EXPECT_EQ(client()->last_reported_status(), ImageSanitizer::Status::kSuccess);
- EXPECT_TRUE(client()->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::Contains(*client()->decoded_images(), path));
- EXPECT_FALSE((*client()->decoded_images())[path].drawsNothing());
- }
- // No extra images should have been reported.
- EXPECT_EQ(client()->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(client()->last_reported_status(),
- ImageSanitizer::Status::kFileReadError);
- EXPECT_EQ(client()->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(client()->last_reported_status(),
- ImageSanitizer::Status::kDecodingError);
- EXPECT_EQ(client()->last_reported_path(), bad_png);
- }
- TEST_F(ImageSanitizerTest, NoCallbackAfterDelete) {
- constexpr base::FilePath::CharType kBadPngName[] =
- FILE_PATH_LITERAL("bad.png");
- CreateInvalidImage(kBadPngName);
- base::FilePath bad_png(kBadPngName);
- CreateAndStartSanitizer({bad_png});
- // Delete the sanitizer before we have received the callback.
- ClearSanitizer();
- // Wait a bit and ensure no callback has been called.
- base::RunLoop run_loop;
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
- FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(200));
- run_loop.Run();
- EXPECT_FALSE(client()->done_callback_called());
- EXPECT_FALSE(client()->decoded_image_callback_called());
- }
- // Ensures the sanitizer does not keep a reference to the callbacks to prevent
- // memory leaks. (it's typical to have a ref counted object A own an
- // ImageSanitizer which is given callbacks bound to A, creating a circular
- // reference)
- TEST_F(ImageSanitizerTest, DontHoldOnToCallbacksOnFailure) {
- constexpr base::FilePath::CharType kBadPngName[] =
- FILE_PATH_LITERAL("bad.png");
- CreateInvalidImage(kBadPngName);
- CreateAndStartSanitizer({base::FilePath(kBadPngName)});
- WaitForSanitizationDone();
- // The image sanitizer shouldn't hold any ref-counts at this point (i.e.
- // ImageSanitizerTest::client_ should be the only remaining ref-count).
- EXPECT_TRUE(client()->HasOneRef());
- }
- TEST_F(ImageSanitizerTest, DontHoldOnToCallbacksOnSuccess) {
- constexpr base::FilePath::CharType kGoodPngName[] =
- FILE_PATH_LITERAL("good.png");
- CreateValidImage(kGoodPngName);
- CreateAndStartSanitizer({base::FilePath(kGoodPngName)});
- WaitForSanitizationDone();
- // The image sanitizer shouldn't hold any ref-counts at this point (i.e.
- // ImageSanitizerTest::client_ should be the only remaining ref-count).
- EXPECT_TRUE(client()->HasOneRef());
- }
- // Tests that the callback is invoked if the data decoder service crashes.
- TEST_F(ImageSanitizerTest, DataDecoderServiceCrashes) {
- constexpr base::FilePath::CharType kGoodPngName[] =
- FILE_PATH_LITERAL("good.png");
- in_process_data_decoder().service().SimulateImageDecoderCrashForTesting(true);
- CreateValidImage(kGoodPngName);
- base::FilePath good_png(kGoodPngName);
- CreateAndStartSanitizer({good_png});
- WaitForSanitizationDone();
- EXPECT_EQ(client()->last_reported_status(),
- ImageSanitizer::Status::kDecodingError);
- }
- } // namespace extensions
|