image_sanitizer_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright 2018 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. #include "extensions/browser/image_sanitizer.h"
  5. #include <map>
  6. #include <memory>
  7. #include "base/base64.h"
  8. #include "base/bind.h"
  9. #include "base/containers/contains.h"
  10. #include "base/files/file_util.h"
  11. #include "base/files/scoped_temp_dir.h"
  12. #include "base/memory/ref_counted.h"
  13. #include "base/run_loop.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/threading/thread_task_runner_handle.h"
  16. #include "build/build_config.h"
  17. #include "content/public/test/browser_task_environment.h"
  18. #include "extensions/browser/extension_file_task_runner.h"
  19. #include "services/data_decoder/public/cpp/data_decoder.h"
  20. #include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h"
  21. #include "testing/gtest/include/gtest/gtest.h"
  22. namespace extensions {
  23. namespace {
  24. constexpr char kBase64edValidPng[] =
  25. "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd"
  26. "1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC";
  27. constexpr char kBase64edInvalidPng[] =
  28. "Rw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd"
  29. "1PeAAAADElEQVQI12P4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC";
  30. class TestClient : public ImageSanitizer::Client {
  31. public:
  32. ImageSanitizer::Status last_reported_status() const { return last_status_; }
  33. const base::FilePath& last_reported_path() const {
  34. return last_reported_path_;
  35. }
  36. std::map<base::FilePath, SkBitmap>* decoded_images() {
  37. return &decoded_images_;
  38. }
  39. bool done_callback_called() const { return done_callback_called_; }
  40. bool decoded_image_callback_called() const {
  41. return decoded_image_callback_called_;
  42. }
  43. void SetSanitizationDoneCallback(base::OnceClosure done_callback) {
  44. ASSERT_FALSE(done_callback_);
  45. done_callback_ = std::move(done_callback);
  46. }
  47. private:
  48. ~TestClient() override = default;
  49. data_decoder::DataDecoder* GetDataDecoder() override {
  50. return &data_decoder_;
  51. }
  52. void OnImageSanitizationDone(ImageSanitizer::Status status,
  53. const base::FilePath& path) override {
  54. done_callback_called_ = true;
  55. last_status_ = status;
  56. last_reported_path_ = path;
  57. if (done_callback_)
  58. std::move(done_callback_).Run();
  59. }
  60. void OnImageDecoded(const base::FilePath& path, SkBitmap image) override {
  61. EXPECT_EQ(0u, decoded_images_.count(path));
  62. decoded_images_[path] = image;
  63. decoded_image_callback_called_ = true;
  64. }
  65. data_decoder::DataDecoder data_decoder_;
  66. ImageSanitizer::Status last_status_ = ImageSanitizer::Status::kSuccess;
  67. base::FilePath last_reported_path_;
  68. base::OnceClosure done_callback_;
  69. std::map<base::FilePath, SkBitmap> decoded_images_;
  70. bool done_callback_called_ = false;
  71. bool decoded_image_callback_called_ = false;
  72. };
  73. class ImageSanitizerTest : public testing::Test {
  74. public:
  75. ImageSanitizerTest() = default;
  76. ImageSanitizerTest(const ImageSanitizerTest&) = delete;
  77. ImageSanitizerTest& operator=(const ImageSanitizerTest&) = delete;
  78. protected:
  79. void CreateValidImage(const base::FilePath::StringPieceType& file_name) {
  80. ASSERT_TRUE(WriteBase64DataToFile(kBase64edValidPng, file_name));
  81. }
  82. void CreateInvalidImage(const base::FilePath::StringPieceType& file_name) {
  83. ASSERT_TRUE(WriteBase64DataToFile(kBase64edInvalidPng, file_name));
  84. }
  85. const base::FilePath& GetImagePath() const { return temp_dir_.GetPath(); }
  86. void WaitForSanitizationDone() {
  87. base::RunLoop run_loop;
  88. client_->SetSanitizationDoneCallback(run_loop.QuitClosure());
  89. run_loop.Run();
  90. }
  91. void CreateAndStartSanitizer(
  92. const std::set<base::FilePath>& image_relative_paths) {
  93. sanitizer_ = ImageSanitizer::CreateAndStart(client_, temp_dir_.GetPath(),
  94. image_relative_paths,
  95. GetExtensionFileTaskRunner());
  96. }
  97. void ClearSanitizer() { sanitizer_.reset(); }
  98. TestClient* client() { return client_.get(); }
  99. data_decoder::test::InProcessDataDecoder& in_process_data_decoder() {
  100. return in_process_data_decoder_;
  101. }
  102. private:
  103. bool WriteBase64DataToFile(const std::string& base64_data,
  104. const base::FilePath::StringPieceType& file_name) {
  105. std::string binary;
  106. if (!base::Base64Decode(base64_data, &binary))
  107. return false;
  108. base::FilePath path = temp_dir_.GetPath().Append(file_name);
  109. return base::WriteFile(path, binary.data(), binary.size());
  110. }
  111. void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
  112. content::BrowserTaskEnvironment task_environment_;
  113. data_decoder::test::InProcessDataDecoder in_process_data_decoder_;
  114. std::unique_ptr<ImageSanitizer> sanitizer_;
  115. scoped_refptr<TestClient> client_ = base::MakeRefCounted<TestClient>();
  116. base::ScopedTempDir temp_dir_;
  117. };
  118. } // namespace
  119. TEST_F(ImageSanitizerTest, NoImagesProvided) {
  120. CreateAndStartSanitizer(std::set<base::FilePath>());
  121. WaitForSanitizationDone();
  122. EXPECT_TRUE(client()->done_callback_called());
  123. EXPECT_EQ(client()->last_reported_status(), ImageSanitizer::Status::kSuccess);
  124. EXPECT_FALSE(client()->decoded_image_callback_called());
  125. EXPECT_TRUE(client()->last_reported_path().empty());
  126. }
  127. TEST_F(ImageSanitizerTest, InvalidPathAbsolute) {
  128. base::FilePath normal_path(FILE_PATH_LITERAL("hello.png"));
  129. #if BUILDFLAG(IS_WIN)
  130. base::FilePath absolute_path(FILE_PATH_LITERAL("c:\\Windows\\win32"));
  131. #else
  132. base::FilePath absolute_path(FILE_PATH_LITERAL("/usr/bin/root"));
  133. #endif
  134. CreateAndStartSanitizer({normal_path, absolute_path});
  135. WaitForSanitizationDone();
  136. EXPECT_EQ(client()->last_reported_status(),
  137. ImageSanitizer::Status::kImagePathError);
  138. EXPECT_EQ(client()->last_reported_path(), absolute_path);
  139. }
  140. TEST_F(ImageSanitizerTest, InvalidPathReferenceParent) {
  141. base::FilePath good_path(FILE_PATH_LITERAL("hello.png"));
  142. base::FilePath bad_path(FILE_PATH_LITERAL("hello"));
  143. bad_path = bad_path.Append(base::FilePath::kParentDirectory)
  144. .Append(base::FilePath::kParentDirectory)
  145. .Append(base::FilePath::kParentDirectory)
  146. .Append(FILE_PATH_LITERAL("usr"))
  147. .Append(FILE_PATH_LITERAL("bin"));
  148. CreateAndStartSanitizer({good_path, bad_path});
  149. WaitForSanitizationDone();
  150. EXPECT_EQ(client()->last_reported_status(),
  151. ImageSanitizer::Status::kImagePathError);
  152. EXPECT_EQ(client()->last_reported_path(), bad_path);
  153. }
  154. TEST_F(ImageSanitizerTest, ValidCase) {
  155. constexpr std::array<const base::FilePath::CharType* const, 10> kFileNames{
  156. {FILE_PATH_LITERAL("image0.png"), FILE_PATH_LITERAL("image1.png"),
  157. FILE_PATH_LITERAL("image2.png"), FILE_PATH_LITERAL("image3.png"),
  158. FILE_PATH_LITERAL("image4.png"), FILE_PATH_LITERAL("image5.png"),
  159. FILE_PATH_LITERAL("image6.png"), FILE_PATH_LITERAL("image7.png"),
  160. FILE_PATH_LITERAL("image8.png"), FILE_PATH_LITERAL("image9.png")}};
  161. std::set<base::FilePath> paths;
  162. for (const base::FilePath::CharType* file_name : kFileNames) {
  163. CreateValidImage(file_name);
  164. paths.insert(base::FilePath(file_name));
  165. }
  166. CreateAndStartSanitizer(paths);
  167. WaitForSanitizationDone();
  168. EXPECT_TRUE(client()->done_callback_called());
  169. EXPECT_EQ(client()->last_reported_status(), ImageSanitizer::Status::kSuccess);
  170. EXPECT_TRUE(client()->last_reported_path().empty());
  171. // Make sure the image files are there and non empty, and that the
  172. // ImageSanitizerDecodedImage callback was invoked for every image.
  173. for (const auto& path : paths) {
  174. int64_t file_size = 0;
  175. base::FilePath full_path = GetImagePath().Append(path);
  176. EXPECT_TRUE(base::GetFileSize(full_path, &file_size));
  177. EXPECT_GT(file_size, 0);
  178. ASSERT_TRUE(base::Contains(*client()->decoded_images(), path));
  179. EXPECT_FALSE((*client()->decoded_images())[path].drawsNothing());
  180. }
  181. // No extra images should have been reported.
  182. EXPECT_EQ(client()->decoded_images()->size(), 10U);
  183. }
  184. TEST_F(ImageSanitizerTest, MissingImage) {
  185. constexpr base::FilePath::CharType kGoodPngName[] =
  186. FILE_PATH_LITERAL("image.png");
  187. constexpr base::FilePath::CharType kNonExistingName[] =
  188. FILE_PATH_LITERAL("i_don_t_exist.png");
  189. CreateValidImage(kGoodPngName);
  190. base::FilePath good_png(kGoodPngName);
  191. base::FilePath bad_png(kNonExistingName);
  192. CreateAndStartSanitizer({good_png, bad_png});
  193. WaitForSanitizationDone();
  194. EXPECT_EQ(client()->last_reported_status(),
  195. ImageSanitizer::Status::kFileReadError);
  196. EXPECT_EQ(client()->last_reported_path(), bad_png);
  197. }
  198. TEST_F(ImageSanitizerTest, InvalidImage) {
  199. constexpr base::FilePath::CharType kGoodPngName[] =
  200. FILE_PATH_LITERAL("good.png");
  201. constexpr base::FilePath::CharType kBadPngName[] =
  202. FILE_PATH_LITERAL("bad.png");
  203. CreateValidImage(kGoodPngName);
  204. CreateInvalidImage(kBadPngName);
  205. base::FilePath good_png(kGoodPngName);
  206. base::FilePath bad_png(kBadPngName);
  207. CreateAndStartSanitizer({good_png, bad_png});
  208. WaitForSanitizationDone();
  209. EXPECT_EQ(client()->last_reported_status(),
  210. ImageSanitizer::Status::kDecodingError);
  211. EXPECT_EQ(client()->last_reported_path(), bad_png);
  212. }
  213. TEST_F(ImageSanitizerTest, NoCallbackAfterDelete) {
  214. constexpr base::FilePath::CharType kBadPngName[] =
  215. FILE_PATH_LITERAL("bad.png");
  216. CreateInvalidImage(kBadPngName);
  217. base::FilePath bad_png(kBadPngName);
  218. CreateAndStartSanitizer({bad_png});
  219. // Delete the sanitizer before we have received the callback.
  220. ClearSanitizer();
  221. // Wait a bit and ensure no callback has been called.
  222. base::RunLoop run_loop;
  223. base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
  224. FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(200));
  225. run_loop.Run();
  226. EXPECT_FALSE(client()->done_callback_called());
  227. EXPECT_FALSE(client()->decoded_image_callback_called());
  228. }
  229. // Ensures the sanitizer does not keep a reference to the callbacks to prevent
  230. // memory leaks. (it's typical to have a ref counted object A own an
  231. // ImageSanitizer which is given callbacks bound to A, creating a circular
  232. // reference)
  233. TEST_F(ImageSanitizerTest, DontHoldOnToCallbacksOnFailure) {
  234. constexpr base::FilePath::CharType kBadPngName[] =
  235. FILE_PATH_LITERAL("bad.png");
  236. CreateInvalidImage(kBadPngName);
  237. CreateAndStartSanitizer({base::FilePath(kBadPngName)});
  238. WaitForSanitizationDone();
  239. // The image sanitizer shouldn't hold any ref-counts at this point (i.e.
  240. // ImageSanitizerTest::client_ should be the only remaining ref-count).
  241. EXPECT_TRUE(client()->HasOneRef());
  242. }
  243. TEST_F(ImageSanitizerTest, DontHoldOnToCallbacksOnSuccess) {
  244. constexpr base::FilePath::CharType kGoodPngName[] =
  245. FILE_PATH_LITERAL("good.png");
  246. CreateValidImage(kGoodPngName);
  247. CreateAndStartSanitizer({base::FilePath(kGoodPngName)});
  248. WaitForSanitizationDone();
  249. // The image sanitizer shouldn't hold any ref-counts at this point (i.e.
  250. // ImageSanitizerTest::client_ should be the only remaining ref-count).
  251. EXPECT_TRUE(client()->HasOneRef());
  252. }
  253. // Tests that the callback is invoked if the data decoder service crashes.
  254. TEST_F(ImageSanitizerTest, DataDecoderServiceCrashes) {
  255. constexpr base::FilePath::CharType kGoodPngName[] =
  256. FILE_PATH_LITERAL("good.png");
  257. in_process_data_decoder().service().SimulateImageDecoderCrashForTesting(true);
  258. CreateValidImage(kGoodPngName);
  259. base::FilePath good_png(kGoodPngName);
  260. CreateAndStartSanitizer({good_png});
  261. WaitForSanitizationDone();
  262. EXPECT_EQ(client()->last_reported_status(),
  263. ImageSanitizer::Status::kDecodingError);
  264. }
  265. } // namespace extensions