file_util_unittest.cc 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // Copyright 2013 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/common/file_util.h"
  5. #include <stddef.h>
  6. #include <utility>
  7. #include "base/files/file_util.h"
  8. #include "base/files/scoped_temp_dir.h"
  9. #include "base/json/json_string_value_serializer.h"
  10. #include "base/path_service.h"
  11. #include "base/strings/string_util.h"
  12. #include "base/strings/stringprintf.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "base/values.h"
  15. #include "build/build_config.h"
  16. #include "extensions/common/constants.h"
  17. #include "extensions/common/extension.h"
  18. #include "extensions/common/extension_paths.h"
  19. #include "extensions/common/manifest.h"
  20. #include "extensions/common/manifest_constants.h"
  21. #include "extensions/strings/grit/extensions_strings.h"
  22. #include "testing/gmock/include/gmock/gmock.h"
  23. #include "testing/gtest/include/gtest/gtest.h"
  24. #include "third_party/abseil-cpp/absl/types/optional.h"
  25. #include "ui/base/l10n/l10n_util.h"
  26. #include "url/gurl.h"
  27. using extensions::mojom::ManifestLocation;
  28. namespace extensions {
  29. namespace {
  30. constexpr char kManifestContent[] =
  31. R"({
  32. "name": "Underscore folder test",
  33. "version": "1.0",
  34. "manifest_version": 3
  35. })";
  36. const char kCustomManifest[] = "custom_manifest.json";
  37. const base::FilePath::CharType kCustomManifestFilename[] =
  38. FILE_PATH_LITERAL("custom_manifest.json");
  39. scoped_refptr<Extension> LoadExtensionManifest(
  40. const base::DictionaryValue& manifest,
  41. const base::FilePath& manifest_dir,
  42. ManifestLocation location,
  43. int extra_flags,
  44. std::string* error) {
  45. scoped_refptr<Extension> extension =
  46. Extension::Create(manifest_dir, location, manifest, extra_flags, error);
  47. return extension;
  48. }
  49. scoped_refptr<Extension> LoadExtensionManifest(
  50. const std::string& manifest_value,
  51. const base::FilePath& manifest_dir,
  52. ManifestLocation location,
  53. int extra_flags,
  54. std::string* error) {
  55. JSONStringValueDeserializer deserializer(manifest_value);
  56. std::unique_ptr<base::Value> result =
  57. deserializer.Deserialize(nullptr, error);
  58. if (!result.get())
  59. return nullptr;
  60. CHECK_EQ(base::Value::Type::DICTIONARY, result->type());
  61. return LoadExtensionManifest(*base::DictionaryValue::From(std::move(result)),
  62. manifest_dir, location, extra_flags, error);
  63. }
  64. void RunUnderscoreDirectoriesTest(
  65. const std::vector<std::string>& underscore_directories) {
  66. base::ScopedTempDir temp;
  67. ASSERT_TRUE(temp.CreateUniqueTempDir());
  68. base::FilePath ext_path = temp.GetPath();
  69. ASSERT_TRUE(base::CreateDirectory(ext_path));
  70. for (const auto& dir : underscore_directories)
  71. ASSERT_TRUE(base::CreateDirectory(ext_path.AppendASCII(dir)));
  72. ASSERT_TRUE(
  73. base::WriteFile(ext_path.AppendASCII("manifest.json"), kManifestContent));
  74. std::string error;
  75. scoped_refptr<Extension> extension = file_util::LoadExtension(
  76. ext_path, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error);
  77. ASSERT_TRUE(extension) << error;
  78. EXPECT_TRUE(error.empty());
  79. const std::vector<InstallWarning>& warnings = extension->install_warnings();
  80. ASSERT_EQ(1u, warnings.size());
  81. // The warning should report any one of the illegal underscore directories.
  82. bool warning_matched = false;
  83. for (const auto& dir : underscore_directories) {
  84. std::string expected_warning = base::StringPrintf(
  85. "Cannot load extension with file or directory name %s. Filenames "
  86. "starting with \"_\" are reserved for use by the system.",
  87. dir.c_str());
  88. if (expected_warning == warnings[0].message)
  89. warning_matched = true;
  90. }
  91. EXPECT_TRUE(warning_matched)
  92. << "Correct warning not generated for an unpacked extension with "
  93. << base::JoinString(underscore_directories, ",") << " directories.";
  94. }
  95. } // namespace
  96. typedef testing::Test FileUtilTest;
  97. TEST_F(FileUtilTest, InstallUninstallGarbageCollect) {
  98. base::ScopedTempDir temp;
  99. ASSERT_TRUE(temp.CreateUniqueTempDir());
  100. // Create a source extension.
  101. std::string extension_id("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
  102. std::string version("1.0");
  103. base::FilePath src = temp.GetPath().AppendASCII(extension_id);
  104. ASSERT_TRUE(base::CreateDirectory(src));
  105. base::FilePath extension_content;
  106. base::CreateTemporaryFileInDir(src, &extension_content);
  107. ASSERT_TRUE(base::PathExists(extension_content));
  108. // Create a extensions tree.
  109. base::FilePath all_extensions = temp.GetPath().AppendASCII("extensions");
  110. ASSERT_TRUE(base::CreateDirectory(all_extensions));
  111. // Install in empty directory. Should create parent directories as needed.
  112. base::FilePath version_1 =
  113. file_util::InstallExtension(src, extension_id, version, all_extensions);
  114. ASSERT_EQ(
  115. version_1.value(),
  116. all_extensions.AppendASCII(extension_id).AppendASCII("1.0_0").value());
  117. ASSERT_TRUE(base::DirectoryExists(version_1));
  118. ASSERT_TRUE(base::PathExists(version_1.Append(extension_content.BaseName())));
  119. // Should have moved the source.
  120. ASSERT_FALSE(base::DirectoryExists(src));
  121. // Install again. Should create a new one with different name.
  122. ASSERT_TRUE(base::CreateDirectory(src));
  123. base::FilePath version_2 =
  124. file_util::InstallExtension(src, extension_id, version, all_extensions);
  125. ASSERT_EQ(
  126. version_2.value(),
  127. all_extensions.AppendASCII(extension_id).AppendASCII("1.0_1").value());
  128. ASSERT_TRUE(base::DirectoryExists(version_2));
  129. // Should have moved the source.
  130. ASSERT_FALSE(base::DirectoryExists(src));
  131. // Install yet again. Should create a new one with a different name.
  132. ASSERT_TRUE(base::CreateDirectory(src));
  133. base::FilePath version_3 =
  134. file_util::InstallExtension(src, extension_id, version, all_extensions);
  135. ASSERT_EQ(
  136. version_3.value(),
  137. all_extensions.AppendASCII(extension_id).AppendASCII("1.0_2").value());
  138. ASSERT_TRUE(base::DirectoryExists(version_3));
  139. // Uninstall. Should remove entire extension subtree.
  140. file_util::UninstallExtension(all_extensions, extension_id);
  141. ASSERT_FALSE(base::DirectoryExists(version_1.DirName()));
  142. ASSERT_FALSE(base::DirectoryExists(version_2.DirName()));
  143. ASSERT_FALSE(base::DirectoryExists(version_3.DirName()));
  144. ASSERT_TRUE(base::DirectoryExists(all_extensions));
  145. }
  146. TEST_F(FileUtilTest, LoadExtensionWithMetadataFolder) {
  147. RunUnderscoreDirectoriesTest({"_metadata"});
  148. }
  149. TEST_F(FileUtilTest, LoadExtensionWithUnderscoreFolder) {
  150. RunUnderscoreDirectoriesTest({"_badfolder"});
  151. }
  152. TEST_F(FileUtilTest, LoadExtensionWithUnderscoreAndMetadataFolder) {
  153. RunUnderscoreDirectoriesTest({"_metadata", "_badfolder"});
  154. }
  155. TEST_F(FileUtilTest, LoadExtensionWithValidLocales) {
  156. base::FilePath install_dir;
  157. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  158. install_dir = install_dir.AppendASCII("extension_with_locales");
  159. std::string error;
  160. scoped_refptr<Extension> extension(file_util::LoadExtension(
  161. install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  162. ASSERT_TRUE(extension.get() != nullptr);
  163. EXPECT_EQ("The first extension that I made.", extension->description());
  164. }
  165. TEST_F(FileUtilTest, LoadExtensionWithGzippedLocalesAllowed) {
  166. base::FilePath install_dir;
  167. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  168. install_dir = install_dir.AppendASCII("extension_with_gzipped_locales");
  169. std::string error;
  170. scoped_refptr<Extension> extension(file_util::LoadExtension(
  171. install_dir, ManifestLocation::kComponent, Extension::NO_FLAGS, &error));
  172. ASSERT_TRUE(extension.get() != nullptr);
  173. EXPECT_EQ("The first extension that I made.", extension->description());
  174. ASSERT_TRUE(error.empty());
  175. }
  176. TEST_F(FileUtilTest, LoadExtensionWithGzippedLocalesNotAllowed) {
  177. base::FilePath install_dir;
  178. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  179. install_dir = install_dir.AppendASCII("extension_with_gzipped_locales");
  180. std::string error;
  181. scoped_refptr<Extension> extension(file_util::LoadExtension(
  182. install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  183. ASSERT_TRUE(extension.get() == nullptr);
  184. EXPECT_EQ("Catalog file is missing for locale en.", error);
  185. }
  186. TEST_F(FileUtilTest, LoadExtensionWithoutLocalesFolder) {
  187. base::FilePath install_dir;
  188. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  189. install_dir = install_dir.AppendASCII("extension_without_locales");
  190. std::string error;
  191. scoped_refptr<Extension> extension(file_util::LoadExtension(
  192. install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  193. ASSERT_FALSE(extension.get() == nullptr);
  194. EXPECT_TRUE(error.empty());
  195. }
  196. TEST_F(FileUtilTest, CheckIllegalFilenamesNoUnderscores) {
  197. base::ScopedTempDir temp;
  198. ASSERT_TRUE(temp.CreateUniqueTempDir());
  199. base::FilePath src_path = temp.GetPath().AppendASCII("some_dir");
  200. ASSERT_TRUE(base::CreateDirectory(src_path));
  201. std::string data = "{ \"name\": { \"message\": \"foobar\" } }";
  202. ASSERT_EQ(static_cast<int>(data.length()),
  203. base::WriteFile(src_path.AppendASCII("some_file.txt"), data.c_str(),
  204. data.length()));
  205. std::string error;
  206. EXPECT_TRUE(file_util::CheckForIllegalFilenames(temp.GetPath(), &error));
  207. }
  208. TEST_F(FileUtilTest, CheckIllegalFilenamesOnlyReserved) {
  209. base::ScopedTempDir temp;
  210. ASSERT_TRUE(temp.CreateUniqueTempDir());
  211. static const base::FilePath::CharType* const folders[] = {
  212. kLocaleFolder, kPlatformSpecificFolder};
  213. for (size_t i = 0; i < std::size(folders); i++) {
  214. base::FilePath src_path = temp.GetPath().Append(folders[i]);
  215. ASSERT_TRUE(base::CreateDirectory(src_path));
  216. }
  217. std::string error;
  218. EXPECT_TRUE(file_util::CheckForIllegalFilenames(temp.GetPath(), &error));
  219. }
  220. TEST_F(FileUtilTest, CheckIllegalFilenamesReservedAndIllegal) {
  221. base::ScopedTempDir temp;
  222. ASSERT_TRUE(temp.CreateUniqueTempDir());
  223. base::FilePath src_path = temp.GetPath().Append(kLocaleFolder);
  224. ASSERT_TRUE(base::CreateDirectory(src_path));
  225. src_path = temp.GetPath().AppendASCII("_some_dir");
  226. ASSERT_TRUE(base::CreateDirectory(src_path));
  227. std::string error;
  228. EXPECT_FALSE(file_util::CheckForIllegalFilenames(temp.GetPath(), &error));
  229. }
  230. // These tests do not work on Windows, because it is illegal to create a
  231. // file/directory with a Windows reserved name. Because we cannot create a
  232. // file that will cause the test to fail, let's skip the test.
  233. #if !BUILDFLAG(IS_WIN)
  234. TEST_F(FileUtilTest, CheckIllegalFilenamesDirectoryWindowsReserved) {
  235. base::ScopedTempDir temp;
  236. ASSERT_TRUE(temp.CreateUniqueTempDir());
  237. base::FilePath src_path = temp.GetPath().AppendASCII("aux");
  238. ASSERT_TRUE(base::CreateDirectory(src_path));
  239. std::string error;
  240. EXPECT_FALSE(
  241. file_util::CheckForWindowsReservedFilenames(temp.GetPath(), &error));
  242. }
  243. TEST_F(FileUtilTest,
  244. CheckIllegalFilenamesWindowsReservedFilenameWithExtension) {
  245. base::ScopedTempDir temp;
  246. ASSERT_TRUE(temp.CreateUniqueTempDir());
  247. base::FilePath src_path = temp.GetPath().AppendASCII("some_dir");
  248. ASSERT_TRUE(base::CreateDirectory(src_path));
  249. std::string data = "{ \"name\": { \"message\": \"foobar\" } }";
  250. ASSERT_EQ(static_cast<int>(data.length()),
  251. base::WriteFile(src_path.AppendASCII("lpt1.txt"), data.c_str(),
  252. data.length()));
  253. std::string error;
  254. EXPECT_FALSE(
  255. file_util::CheckForWindowsReservedFilenames(temp.GetPath(), &error));
  256. }
  257. #endif
  258. TEST_F(FileUtilTest, LoadExtensionGivesHelpfullErrorOnMissingManifest) {
  259. base::FilePath install_dir;
  260. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  261. install_dir =
  262. install_dir.AppendASCII("file_util").AppendASCII("missing_manifest");
  263. std::string error;
  264. scoped_refptr<Extension> extension(file_util::LoadExtension(
  265. install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  266. ASSERT_TRUE(extension.get() == nullptr);
  267. ASSERT_FALSE(error.empty());
  268. ASSERT_EQ(manifest_errors::kManifestUnreadable, error);
  269. }
  270. TEST_F(FileUtilTest, LoadExtensionGivesHelpfullErrorOnBadManifest) {
  271. base::FilePath install_dir;
  272. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  273. install_dir =
  274. install_dir.AppendASCII("file_util").AppendASCII("bad_manifest");
  275. std::string error;
  276. scoped_refptr<Extension> extension(file_util::LoadExtension(
  277. install_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  278. ASSERT_TRUE(extension.get() == nullptr);
  279. ASSERT_FALSE(error.empty());
  280. ASSERT_NE(std::string::npos,
  281. error.find(manifest_errors::kManifestParseError +
  282. std::string(" Line: 2, column: 16,")));
  283. }
  284. TEST_F(FileUtilTest, ValidateThemeUTF8) {
  285. base::ScopedTempDir temp;
  286. ASSERT_TRUE(temp.CreateUniqueTempDir());
  287. // "aeo" with accents. Use http://0xcc.net/jsescape/ to decode them.
  288. std::string non_ascii_file = "\xC3\xA0\xC3\xA8\xC3\xB2.png";
  289. base::FilePath non_ascii_path =
  290. temp.GetPath().Append(base::FilePath::FromUTF8Unsafe(non_ascii_file));
  291. base::WriteFile(non_ascii_path, "", 0);
  292. std::string kManifest = base::StringPrintf(
  293. "{ \"name\": \"Test\", \"version\": \"1.0\", "
  294. " \"theme\": { \"images\": { \"theme_frame\": \"%s\" } }"
  295. "}",
  296. non_ascii_file.c_str());
  297. std::string error;
  298. scoped_refptr<Extension> extension = LoadExtensionManifest(
  299. kManifest, temp.GetPath(), ManifestLocation::kUnpacked, 0, &error);
  300. ASSERT_TRUE(extension.get()) << error;
  301. std::vector<InstallWarning> warnings;
  302. EXPECT_TRUE(file_util::ValidateExtension(extension.get(), &error, &warnings))
  303. << error;
  304. EXPECT_EQ(0U, warnings.size());
  305. }
  306. TEST_F(FileUtilTest, BackgroundScriptsMustExist) {
  307. base::ScopedTempDir temp;
  308. ASSERT_TRUE(temp.CreateUniqueTempDir());
  309. std::unique_ptr<base::DictionaryValue> value(new base::DictionaryValue());
  310. value->SetStringKey("name", "test");
  311. value->SetStringKey("version", "1");
  312. value->SetIntKey("manifest_version", 2);
  313. base::ListValue* scripts =
  314. value->SetList("background.scripts", std::make_unique<base::ListValue>());
  315. scripts->Append("foo.js");
  316. std::string error;
  317. std::vector<InstallWarning> warnings;
  318. scoped_refptr<Extension> extension = LoadExtensionManifest(
  319. *value, temp.GetPath(), ManifestLocation::kUnpacked, 0, &error);
  320. ASSERT_TRUE(extension.get()) << error;
  321. EXPECT_FALSE(
  322. file_util::ValidateExtension(extension.get(), &error, &warnings));
  323. EXPECT_EQ(l10n_util::GetStringFUTF8(
  324. IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED, u"foo.js"),
  325. error);
  326. EXPECT_EQ(0U, warnings.size());
  327. scripts->ClearList();
  328. scripts->Append("http://google.com/foo.js");
  329. extension = LoadExtensionManifest(*value, temp.GetPath(),
  330. ManifestLocation::kUnpacked, 0, &error);
  331. ASSERT_TRUE(extension.get()) << error;
  332. warnings.clear();
  333. EXPECT_FALSE(
  334. file_util::ValidateExtension(extension.get(), &error, &warnings));
  335. EXPECT_EQ(
  336. l10n_util::GetStringFUTF8(IDS_EXTENSION_LOAD_BACKGROUND_SCRIPT_FAILED,
  337. u"http://google.com/foo.js"),
  338. error);
  339. EXPECT_EQ(0U, warnings.size());
  340. }
  341. // Private key, generated by Chrome specifically for this test, and
  342. // never used elsewhere.
  343. const char private_key[] =
  344. "-----BEGIN PRIVATE KEY-----\n"
  345. "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKt02SR0FYaYy6fpW\n"
  346. "MAA+kU1BgK3d+OmmWfdr+JATIjhRkyeSF4lTd/71JQsyKqPzYkQPi3EeROWM+goTv\n"
  347. "EhJqq07q63BolpsFmlV+S4ny+sBA2B4aWwRYXlBWikdrQSA0mJMzvEHc6nKzBgXik\n"
  348. "QSVbyyBNAsxlDB9WaCxRVOpK3AgMBAAECgYBGvSPlrVtAOAQ2V8j9FqorKZA8SLPX\n"
  349. "IeJC/yzU3RB2nPMjI17aMOvrUHxJUhzMeh4jwabVvSzzDtKFozPGupW3xaI8sQdi2\n"
  350. "WWMTQIk/Q9HHDWoQ9qA6SwX2qWCc5SyjCKqVp78ye+000kqTJYjBsDgXeAlzKcx2B\n"
  351. "4GAAeWonDdkQJBANNb8wrqNWFn7DqyQTfELzcRTRnqQ/r1pdeJo6obzbnwGnlqe3t\n"
  352. "KhLjtJNIGrQg5iC0OVLWFuvPJs0t3z62A1ckCQQDPq2JZuwTwu5Pl4DJ0r9O1FdqN\n"
  353. "JgqPZyMptokCDQ3khLLGakIu+TqB9YtrzI69rJMSG2Egb+6McaDX+dh3XmR/AkB9t\n"
  354. "xJf6qDnmA2td/tMtTc0NOk8Qdg/fD8xbZ/YfYMnVoYYs9pQoilBaWRePDRNURMLYZ\n"
  355. "vHAI0Llmw7tj7jv17pAkEAz44uXRpjRKtllUIvi5pUENAHwDz+HvdpGH68jpU3hmb\n"
  356. "uOwrmnQYxaMReFV68Z2w9DcLZn07f7/R9Wn72z89CxwJAFsDoNaDes4h48bX7plct\n"
  357. "s9ACjmTwcCigZjN2K7AGv7ntCLF3DnV5dK0dTHNaAdD3SbY3jl29Rk2CwiURSX6Ee\n"
  358. "g==\n"
  359. "-----END PRIVATE KEY-----\n";
  360. TEST_F(FileUtilTest, FindPrivateKeyFiles) {
  361. base::ScopedTempDir temp;
  362. ASSERT_TRUE(temp.CreateUniqueTempDir());
  363. base::FilePath src_path = temp.GetPath().AppendASCII("some_dir");
  364. ASSERT_TRUE(base::CreateDirectory(src_path));
  365. ASSERT_EQ(static_cast<int>(std::size(private_key)),
  366. base::WriteFile(src_path.AppendASCII("a_key.pem"), private_key,
  367. std::size(private_key)));
  368. ASSERT_EQ(static_cast<int>(std::size(private_key)),
  369. base::WriteFile(src_path.AppendASCII("second_key.pem"), private_key,
  370. std::size(private_key)));
  371. // Shouldn't find a key with a different extension.
  372. ASSERT_EQ(static_cast<int>(std::size(private_key)),
  373. base::WriteFile(src_path.AppendASCII("key.diff_ext"), private_key,
  374. std::size(private_key)));
  375. // Shouldn't find a key that isn't parsable.
  376. ASSERT_EQ(static_cast<int>(std::size(private_key)) - 30,
  377. base::WriteFile(src_path.AppendASCII("unparsable_key.pem"),
  378. private_key, std::size(private_key) - 30));
  379. std::vector<base::FilePath> private_keys =
  380. file_util::FindPrivateKeyFiles(temp.GetPath());
  381. EXPECT_EQ(2U, private_keys.size());
  382. EXPECT_THAT(private_keys,
  383. testing::Contains(src_path.AppendASCII("a_key.pem")));
  384. EXPECT_THAT(private_keys,
  385. testing::Contains(src_path.AppendASCII("second_key.pem")));
  386. }
  387. TEST_F(FileUtilTest, WarnOnPrivateKey) {
  388. base::ScopedTempDir temp;
  389. ASSERT_TRUE(temp.CreateUniqueTempDir());
  390. base::FilePath ext_path = temp.GetPath().AppendASCII("ext_root");
  391. ASSERT_TRUE(base::CreateDirectory(ext_path));
  392. const char manifest[] =
  393. "{\n"
  394. " \"name\": \"Test Extension\",\n"
  395. " \"version\": \"1.0\",\n"
  396. " \"manifest_version\": 2,\n"
  397. " \"description\": \"The first extension that I made.\"\n"
  398. "}\n";
  399. ASSERT_EQ(static_cast<int>(strlen(manifest)),
  400. base::WriteFile(ext_path.AppendASCII("manifest.json"), manifest,
  401. strlen(manifest)));
  402. ASSERT_EQ(static_cast<int>(strlen(private_key)),
  403. base::WriteFile(ext_path.AppendASCII("a_key.pem"), private_key,
  404. strlen(private_key)));
  405. std::string error;
  406. scoped_refptr<Extension> extension(file_util::LoadExtension(
  407. ext_path, "the_id", ManifestLocation::kExternalPref, Extension::NO_FLAGS,
  408. &error));
  409. ASSERT_TRUE(extension.get()) << error;
  410. ASSERT_EQ(1u, extension->install_warnings().size());
  411. EXPECT_THAT(extension->install_warnings(),
  412. testing::ElementsAre(testing::Field(
  413. &InstallWarning::message,
  414. testing::ContainsRegex(
  415. "extension includes the key file.*ext_root.a_key.pem"))));
  416. // Turn the warning into an error with ERROR_ON_PRIVATE_KEY.
  417. extension = file_util::LoadExtension(ext_path, "the_id",
  418. ManifestLocation::kExternalPref,
  419. Extension::ERROR_ON_PRIVATE_KEY, &error);
  420. EXPECT_FALSE(extension.get());
  421. EXPECT_THAT(error,
  422. testing::ContainsRegex(
  423. "extension includes the key file.*ext_root.a_key.pem"));
  424. }
  425. // Specify a file other than manifest.json
  426. TEST_F(FileUtilTest, SpecifyManifestFile) {
  427. base::ScopedTempDir temp;
  428. ASSERT_TRUE(temp.CreateUniqueTempDir());
  429. base::FilePath ext_path = temp.GetPath().AppendASCII("ext_root");
  430. ASSERT_TRUE(base::CreateDirectory(ext_path));
  431. const char manifest[] =
  432. "{\n"
  433. " \"name\": \"Test Extension\",\n"
  434. " \"version\": \"1.0\",\n"
  435. " \"manifest_version\": 2,\n"
  436. " \"description\": \"The first extension that I made.\"\n"
  437. "}\n";
  438. ASSERT_EQ(static_cast<int>(strlen(manifest)),
  439. base::WriteFile(ext_path.AppendASCII(kCustomManifest), manifest,
  440. strlen(manifest)));
  441. std::string error;
  442. scoped_refptr<Extension> extension(file_util::LoadExtension(
  443. ext_path, kCustomManifestFilename, "the_id",
  444. ManifestLocation::kExternalPref, Extension::NO_FLAGS, &error));
  445. ASSERT_TRUE(extension.get()) << error;
  446. ASSERT_EQ(0u, extension->install_warnings().size());
  447. }
  448. // Try to install an extension with a zero-length icon file.
  449. TEST_F(FileUtilTest, CheckZeroLengthAndMissingIconFile) {
  450. base::FilePath install_dir;
  451. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  452. base::FilePath ext_dir =
  453. install_dir.AppendASCII("file_util").AppendASCII("bad_icon");
  454. std::string error;
  455. scoped_refptr<Extension> extension(file_util::LoadExtension(
  456. ext_dir, ManifestLocation::kInternal, Extension::NO_FLAGS, &error));
  457. ASSERT_FALSE(extension);
  458. }
  459. // Try to install an unpacked extension with a zero-length icon file.
  460. TEST_F(FileUtilTest, CheckZeroLengthAndMissingIconFileUnpacked) {
  461. base::FilePath install_dir;
  462. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  463. base::FilePath ext_dir =
  464. install_dir.AppendASCII("file_util").AppendASCII("bad_icon");
  465. std::string error;
  466. scoped_refptr<Extension> extension(file_util::LoadExtension(
  467. ext_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  468. EXPECT_FALSE(extension);
  469. EXPECT_EQ("Could not load icon 'missing-icon.png' specified in 'icons'.",
  470. error);
  471. }
  472. // Try to install an unpacked extension with an invisible icon. This
  473. // should fail.
  474. TEST_F(FileUtilTest, CheckInvisibleIconFileUnpacked) {
  475. base::FilePath install_dir;
  476. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  477. base::FilePath ext_dir =
  478. install_dir.AppendASCII("file_util").AppendASCII("invisible_icon");
  479. // Set the flag that enables the error.
  480. file_util::SetReportErrorForInvisibleIconForTesting(true);
  481. std::string error;
  482. scoped_refptr<Extension> extension(file_util::LoadExtension(
  483. ext_dir, ManifestLocation::kUnpacked, Extension::NO_FLAGS, &error));
  484. file_util::SetReportErrorForInvisibleIconForTesting(false);
  485. EXPECT_FALSE(extension);
  486. EXPECT_EQ(
  487. "Icon 'invisible_icon.png' specified in 'icons' is not "
  488. "sufficiently visible.",
  489. error);
  490. }
  491. // Try to install a packed extension with an invisible icon. This should
  492. // succeed.
  493. TEST_F(FileUtilTest, CheckInvisibleIconFilePacked) {
  494. base::FilePath install_dir;
  495. ASSERT_TRUE(base::PathService::Get(DIR_TEST_DATA, &install_dir));
  496. base::FilePath ext_dir =
  497. install_dir.AppendASCII("file_util").AppendASCII("invisible_icon");
  498. // Set the flag that enables the error.
  499. file_util::SetReportErrorForInvisibleIconForTesting(true);
  500. std::string error;
  501. scoped_refptr<Extension> extension(file_util::LoadExtension(
  502. ext_dir, ManifestLocation::kInternal, Extension::NO_FLAGS, &error));
  503. file_util::SetReportErrorForInvisibleIconForTesting(false);
  504. EXPECT_TRUE(extension);
  505. EXPECT_TRUE(error.empty());
  506. }
  507. TEST_F(FileUtilTest, ExtensionURLToRelativeFilePath) {
  508. #define URL_PREFIX "chrome-extension://extension-id/"
  509. struct TestCase {
  510. const char* url;
  511. const char* expected_relative_path;
  512. } test_cases[] = {
  513. {URL_PREFIX "simple.html", "simple.html"},
  514. {URL_PREFIX "directory/to/file.html", "directory/to/file.html"},
  515. {URL_PREFIX "escape%20spaces.html", "escape spaces.html"},
  516. {URL_PREFIX "%C3%9Cber.html",
  517. "\xC3\x9C"
  518. "ber.html"},
  519. #if BUILDFLAG(IS_WIN)
  520. {URL_PREFIX "C%3A/simple.html", ""},
  521. #endif
  522. {URL_PREFIX "////simple.html", "simple.html"},
  523. {URL_PREFIX "/simple.html", "simple.html"},
  524. {URL_PREFIX "\\simple.html", "simple.html"},
  525. {URL_PREFIX "\\\\foo\\simple.html", "foo/simple.html"},
  526. // Escaped file paths result in failure.
  527. {URL_PREFIX "..%2f..%2fsimple.html", ""},
  528. // Escaped things that look like escaped file paths, on the other hand,
  529. // should work.
  530. {URL_PREFIX "..%252f..%252fsimple.html", "..%2f..%2fsimple.html"},
  531. // This is a UTF-8 lock icon, which is unsafe to display in the omnibox, but
  532. // is a valid, if unusual, file name.
  533. {URL_PREFIX "%F0%9F%94%93.html", "\xF0\x9F\x94\x93.html"},
  534. };
  535. #undef URL_PREFIX
  536. for (size_t i = 0; i < std::size(test_cases); ++i) {
  537. GURL url(test_cases[i].url);
  538. base::FilePath expected_path =
  539. base::FilePath::FromUTF8Unsafe(test_cases[i].expected_relative_path);
  540. base::FilePath actual_path = file_util::ExtensionURLToRelativeFilePath(url);
  541. EXPECT_FALSE(actual_path.IsAbsolute()) <<
  542. " For the path " << actual_path.value();
  543. EXPECT_EQ(expected_path.value(), actual_path.value()) <<
  544. " For the path " << url;
  545. }
  546. }
  547. } // namespace extensions