summaryrefslogtreecommitdiff
path: root/chromium/chrome/common/extensions/extension_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/chrome/common/extensions/extension_unittest.cc')
-rw-r--r--chromium/chrome/common/extensions/extension_unittest.cc447
1 files changed, 447 insertions, 0 deletions
diff --git a/chromium/chrome/common/extensions/extension_unittest.cc b/chromium/chrome/common/extensions/extension_unittest.cc
new file mode 100644
index 00000000000..09fe2826445
--- /dev/null
+++ b/chromium/chrome/common/extensions/extension_unittest.cc
@@ -0,0 +1,447 @@
+// Copyright (c) 2013 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 <stddef.h>
+
+#include "base/files/file_util.h"
+#include "base/format_macros.h"
+#include "base/path_service.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "build/build_config.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/extensions/command.h"
+#include "chrome/common/extensions/extension_test_util.h"
+#include "chrome/common/url_constants.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/crx_file/id_util.h"
+#include "extensions/common/extension.h"
+#include "extensions/common/extension_builder.h"
+#include "extensions/common/extension_resource.h"
+#include "extensions/common/file_util.h"
+#include "extensions/common/manifest.h"
+#include "extensions/common/manifest_handlers/content_scripts_handler.h"
+#include "extensions/common/permissions/permissions_data.h"
+#include "extensions/common/value_builder.h"
+#include "extensions/test/test_extension_dir.h"
+#include "net/base/mime_sniffer.h"
+#include "net/dns/mock_host_resolver.h"
+#include "skia/ext/image_operations.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "url/gurl.h"
+
+using extension_test_util::LoadManifest;
+using extension_test_util::LoadManifestStrict;
+using base::FilePath;
+
+namespace extensions {
+
+// We persist location values in the preferences, so this is a sanity test that
+// someone doesn't accidentally change them.
+TEST(ExtensionTest, LocationValuesTest) {
+ ASSERT_EQ(0, Manifest::INVALID_LOCATION);
+ ASSERT_EQ(1, Manifest::INTERNAL);
+ ASSERT_EQ(2, Manifest::EXTERNAL_PREF);
+ ASSERT_EQ(3, Manifest::EXTERNAL_REGISTRY);
+ ASSERT_EQ(4, Manifest::UNPACKED);
+ ASSERT_EQ(5, Manifest::COMPONENT);
+ ASSERT_EQ(6, Manifest::EXTERNAL_PREF_DOWNLOAD);
+ ASSERT_EQ(7, Manifest::EXTERNAL_POLICY_DOWNLOAD);
+ ASSERT_EQ(8, Manifest::COMMAND_LINE);
+ ASSERT_EQ(9, Manifest::EXTERNAL_POLICY);
+}
+
+TEST(ExtensionTest, LocationPriorityTest) {
+ for (int i = 0; i < Manifest::NUM_LOCATIONS; i++) {
+ Manifest::Location loc = static_cast<Manifest::Location>(i);
+
+ // INVALID is not a valid location.
+ if (loc == Manifest::INVALID_LOCATION)
+ continue;
+
+ // Comparing a location that has no rank will hit a CHECK. Do a
+ // compare with every valid location, to be sure each one is covered.
+
+ // Check that no install source can override a componenet extension.
+ ASSERT_EQ(Manifest::COMPONENT,
+ Manifest::GetHigherPriorityLocation(Manifest::COMPONENT, loc));
+ ASSERT_EQ(Manifest::COMPONENT,
+ Manifest::GetHigherPriorityLocation(loc, Manifest::COMPONENT));
+
+ // Check that any source can override a user install. This might change
+ // in the future, in which case this test should be updated.
+ ASSERT_EQ(loc,
+ Manifest::GetHigherPriorityLocation(Manifest::INTERNAL, loc));
+ ASSERT_EQ(loc,
+ Manifest::GetHigherPriorityLocation(loc, Manifest::INTERNAL));
+ }
+
+ // Check a few interesting cases that we know can happen:
+ ASSERT_EQ(Manifest::EXTERNAL_POLICY_DOWNLOAD,
+ Manifest::GetHigherPriorityLocation(
+ Manifest::EXTERNAL_POLICY_DOWNLOAD,
+ Manifest::EXTERNAL_PREF));
+
+ ASSERT_EQ(Manifest::EXTERNAL_PREF,
+ Manifest::GetHigherPriorityLocation(
+ Manifest::INTERNAL,
+ Manifest::EXTERNAL_PREF));
+}
+
+TEST(ExtensionTest, EnsureNewLinesInExtensionNameAreCollapsed) {
+ DictionaryBuilder manifest;
+ std::string unsanitized_name = "Test\n\n\n\n\n\n\n\n\n\n\n\nNew lines\u0085";
+ manifest.Set("name", unsanitized_name)
+ .Set("manifest_version", 2)
+ .Set("description", "some description");
+ scoped_refptr<const Extension> extension =
+ ExtensionBuilder()
+ .SetManifest(manifest.Build())
+ .MergeManifest(DictionaryBuilder().Set("version", "0.1").Build())
+ .Build();
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("TestNew lines", extension->name());
+ // Ensure that non-localized name is not sanitized.
+ EXPECT_EQ(unsanitized_name, extension->non_localized_name());
+}
+
+TEST(ExtensionTest, EnsureWhitespacesInExtensionNameAreCollapsed) {
+ DictionaryBuilder manifest;
+ std::string unsanitized_name = "Test Whitespace";
+ manifest.Set("name", unsanitized_name)
+ .Set("manifest_version", 2)
+ .Set("description", "some description");
+ scoped_refptr<const Extension> extension =
+ ExtensionBuilder()
+ .SetManifest(manifest.Build())
+ .MergeManifest(DictionaryBuilder().Set("version", "0.1").Build())
+ .Build();
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("Test Whitespace", extension->name());
+ // Ensure that non-localized name is not sanitized.
+ EXPECT_EQ(unsanitized_name, extension->non_localized_name());
+}
+
+// TODO(crbug.com/794252): Disallow empty extension names from being locally
+// loaded.
+TEST(ExtensionTest, EmptyName) {
+ DictionaryBuilder manifest1;
+ manifest1.Set("name", "")
+ .Set("manifest_version", 2)
+ .Set("description", "some description");
+ scoped_refptr<const Extension> extension =
+ ExtensionBuilder()
+ .SetManifest(manifest1.Build())
+ .MergeManifest(DictionaryBuilder().Set("version", "0.1").Build())
+ .Build();
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("", extension->name());
+
+ DictionaryBuilder manifest2;
+ manifest2.Set("name", " ")
+ .Set("manifest_version", 2)
+ .Set("description", "some description");
+ extension =
+ ExtensionBuilder()
+ .SetManifest(manifest2.Build())
+ .MergeManifest(DictionaryBuilder().Set("version", "0.1").Build())
+ .Build();
+ ASSERT_TRUE(extension.get());
+ EXPECT_EQ("", extension->name());
+}
+
+TEST(ExtensionTest, RTLNameInLTRLocale) {
+ // Test the case when a directional override is the first character.
+ auto run_rtl_test = [](const wchar_t* name, const wchar_t* expected) {
+ SCOPED_TRACE(
+ base::StringPrintf("Name: %ls, Expected: %ls", name, expected));
+ DictionaryBuilder manifest;
+ manifest.Set("name", base::WideToUTF8(name))
+ .Set("manifest_version", 2)
+ .Set("description", "some description")
+ .Set("version",
+ "0.1"); // <NOTE> Moved this here to avoid the MergeManifest call.
+ scoped_refptr<const Extension> extension =
+ ExtensionBuilder().SetManifest(manifest.Build()).Build();
+ ASSERT_TRUE(extension);
+ const int kResourceId = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
+ const base::string16 expected_utf16 = base::WideToUTF16(expected);
+ EXPECT_EQ(l10n_util::GetStringFUTF16(kResourceId, expected_utf16),
+ l10n_util::GetStringFUTF16(kResourceId,
+ base::UTF8ToUTF16(extension->name())));
+ EXPECT_EQ(base::WideToUTF8(expected), extension->name());
+ };
+
+ run_rtl_test(L"\x202emoc.elgoog", L"\x202emoc.elgoog\x202c");
+ run_rtl_test(L"\x202egoogle\x202e.com/\x202eguest",
+ L"\x202egoogle\x202e.com/\x202eguest\x202c\x202c\x202c");
+ run_rtl_test(L"google\x202e.com", L"google\x202e.com\x202c");
+
+ run_rtl_test(L"كبير Google التطبيق",
+#if !defined(OS_WIN)
+ L"\x200e\x202bكبير Google التطبيق\x202c\x200e");
+#else
+ // On Windows for an LTR locale, no changes to the string are
+ // made.
+ L"كبير Google التطبيق");
+#endif // !OS_WIN
+}
+
+TEST(ExtensionTest, GetResourceURLAndPath) {
+ scoped_refptr<Extension> extension = LoadManifestStrict("empty_manifest",
+ "empty.json");
+ EXPECT_TRUE(extension.get());
+
+ EXPECT_EQ(extension->url().spec() + "bar/baz.js",
+ Extension::GetResourceURL(extension->url(), "bar/baz.js").spec());
+ EXPECT_EQ(extension->url().spec() + "baz.js",
+ Extension::GetResourceURL(extension->url(),
+ "bar/../baz.js").spec());
+ EXPECT_EQ(extension->url().spec() + "baz.js",
+ Extension::GetResourceURL(extension->url(), "../baz.js").spec());
+
+ // Test that absolute-looking paths ("/"-prefixed) are pasted correctly.
+ EXPECT_EQ(extension->url().spec() + "test.html",
+ extension->GetResourceURL("/test.html").spec());
+}
+
+TEST(ExtensionTest, GetResource) {
+ const FilePath valid_path_test_cases[] = {
+ FilePath(FILE_PATH_LITERAL("manifest.json")),
+ FilePath(FILE_PATH_LITERAL("a/b/c/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("com/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("lpt/manifest.json")),
+ };
+ const FilePath invalid_path_test_cases[] = {
+ // Directory name
+ FilePath(FILE_PATH_LITERAL("src/")),
+ // Contains a drive letter specification.
+ FilePath(FILE_PATH_LITERAL("C:\\manifest.json")),
+ // Use backslash '\\' as separator.
+ FilePath(FILE_PATH_LITERAL("a\\b\\c\\manifest.json")),
+ // Reserved Characters with extension
+ FilePath(FILE_PATH_LITERAL("mani>fest.json")),
+ FilePath(FILE_PATH_LITERAL("mani<fest.json")),
+ FilePath(FILE_PATH_LITERAL("mani*fest.json")),
+ FilePath(FILE_PATH_LITERAL("mani:fest.json")),
+ FilePath(FILE_PATH_LITERAL("mani?fest.json")),
+ FilePath(FILE_PATH_LITERAL("mani|fest.json")),
+ // Reserved Characters without extension
+ FilePath(FILE_PATH_LITERAL("mani>fest")),
+ FilePath(FILE_PATH_LITERAL("mani<fest")),
+ FilePath(FILE_PATH_LITERAL("mani*fest")),
+ FilePath(FILE_PATH_LITERAL("mani:fest")),
+ FilePath(FILE_PATH_LITERAL("mani?fest")),
+ FilePath(FILE_PATH_LITERAL("mani|fest")),
+ // Reserved Names with extension.
+ FilePath(FILE_PATH_LITERAL("com1.json")),
+ FilePath(FILE_PATH_LITERAL("com9.json")),
+ FilePath(FILE_PATH_LITERAL("LPT1.json")),
+ FilePath(FILE_PATH_LITERAL("LPT9.json")),
+ FilePath(FILE_PATH_LITERAL("CON.json")),
+ FilePath(FILE_PATH_LITERAL("PRN.json")),
+ FilePath(FILE_PATH_LITERAL("AUX.json")),
+ FilePath(FILE_PATH_LITERAL("NUL.json")),
+ // Reserved Names without extension.
+ FilePath(FILE_PATH_LITERAL("com1")),
+ FilePath(FILE_PATH_LITERAL("com9")),
+ FilePath(FILE_PATH_LITERAL("LPT1")),
+ FilePath(FILE_PATH_LITERAL("LPT9")),
+ FilePath(FILE_PATH_LITERAL("CON")),
+ FilePath(FILE_PATH_LITERAL("PRN")),
+ FilePath(FILE_PATH_LITERAL("AUX")),
+ FilePath(FILE_PATH_LITERAL("NUL")),
+ // Reserved Names as directory.
+ FilePath(FILE_PATH_LITERAL("com1/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("com9/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("LPT1/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("LPT9/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("CON/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("PRN/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("AUX/manifest.json")),
+ FilePath(FILE_PATH_LITERAL("NUL/manifest.json")),
+ };
+
+ scoped_refptr<Extension> extension = LoadManifestStrict("empty_manifest",
+ "empty.json");
+ EXPECT_TRUE(extension.get());
+ for (size_t i = 0; i < base::size(valid_path_test_cases); ++i)
+ EXPECT_TRUE(!extension->GetResource(valid_path_test_cases[i]).empty());
+ for (size_t i = 0; i < base::size(invalid_path_test_cases); ++i)
+ EXPECT_TRUE(extension->GetResource(invalid_path_test_cases[i]).empty());
+}
+
+TEST(ExtensionTest, GetAbsolutePathNoError) {
+ scoped_refptr<Extension> extension = LoadManifestStrict("absolute_path",
+ "absolute.json");
+ EXPECT_TRUE(extension.get());
+ std::string err;
+ std::vector<InstallWarning> warnings;
+ EXPECT_TRUE(file_util::ValidateExtension(extension.get(), &err, &warnings));
+ EXPECT_EQ(0U, warnings.size());
+
+ EXPECT_EQ(extension->path().AppendASCII("test.html").value(),
+ extension->GetResource("test.html").GetFilePath().value());
+ EXPECT_EQ(extension->path().AppendASCII("test.js").value(),
+ extension->GetResource("test.js").GetFilePath().value());
+}
+
+
+TEST(ExtensionTest, IdIsValid) {
+ EXPECT_TRUE(crx_file::id_util::IdIsValid("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
+ EXPECT_TRUE(crx_file::id_util::IdIsValid("pppppppppppppppppppppppppppppppp"));
+ EXPECT_TRUE(crx_file::id_util::IdIsValid("abcdefghijklmnopabcdefghijklmnop"));
+ EXPECT_TRUE(crx_file::id_util::IdIsValid("ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP"));
+ EXPECT_FALSE(crx_file::id_util::IdIsValid("abcdefghijklmnopabcdefghijklmno"));
+ EXPECT_FALSE(
+ crx_file::id_util::IdIsValid("abcdefghijklmnopabcdefghijklmnopa"));
+ EXPECT_FALSE(
+ crx_file::id_util::IdIsValid("0123456789abcdef0123456789abcdef"));
+ EXPECT_FALSE(
+ crx_file::id_util::IdIsValid("abcdefghijklmnopabcdefghijklmnoq"));
+ EXPECT_FALSE(
+ crx_file::id_util::IdIsValid("abcdefghijklmnopabcdefghijklmno0"));
+}
+
+// This test ensures that the mimetype sniffing code stays in sync with the
+// actual crx files that we test other parts of the system with.
+TEST(ExtensionTest, MimeTypeSniffing) {
+ auto get_mime_type_from_crx = [](const base::FilePath& file_path) {
+ SCOPED_TRACE(file_path.AsUTF8Unsafe());
+
+ std::string data;
+ EXPECT_TRUE(base::ReadFileToString(file_path, &data));
+
+ std::string result;
+ EXPECT_TRUE(net::SniffMimeType(
+ data.c_str(), data.size(), GURL("http://www.example.com/foo.crx"),
+ std::string(), net::ForceSniffFileUrlsForHtml::kDisabled, &result));
+
+ return result;
+ };
+
+ base::FilePath dir_path;
+ ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &dir_path));
+ dir_path = dir_path.AppendASCII("extensions");
+
+ // First, test an extension packed a long time ago (but in this galaxy).
+ // Specifically, this package is using the crx2 format, whereas modern chrome
+ // uses crx3.
+ EXPECT_EQ(
+ Extension::kMimeType,
+ get_mime_type_from_crx(dir_path.AppendASCII("legacy_crx_package.crx")));
+
+ // Then, an extension whose crx has a bad magic number (it should be Cr24).
+ EXPECT_EQ("application/octet-stream",
+ get_mime_type_from_crx(dir_path.AppendASCII("bad_magic.crx")));
+
+ // Finally, an extension that we pack right. This. Instant.
+ // This verifies that the modern extensions Chrome packs are always
+ // recognized as the extension mime type.
+ // Regression test for https://crbug.com/831284.
+ TestExtensionDir test_dir;
+ test_dir.WriteManifest(R"(
+ {
+ "name": "New extension",
+ "version": "0.2",
+ "manifest_version": 2
+ })");
+ EXPECT_EQ(Extension::kMimeType, get_mime_type_from_crx(test_dir.Pack()));
+}
+
+TEST(ExtensionTest, WantsFileAccess) {
+ scoped_refptr<Extension> extension;
+ GURL file_url("file:///etc/passwd");
+
+ // Ignore the policy delegate for this test.
+ PermissionsData::SetPolicyDelegate(NULL);
+
+ // <all_urls> permission
+ extension = LoadManifest("permissions", "permissions_all_urls.json");
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_FALSE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+ extension = LoadManifest(
+ "permissions", "permissions_all_urls.json", Extension::ALLOW_FILE_ACCESS);
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_TRUE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+
+ // file:///* permission
+ extension = LoadManifest("permissions", "permissions_file_scheme.json");
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_FALSE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+ extension = LoadManifest("permissions",
+ "permissions_file_scheme.json",
+ Extension::ALLOW_FILE_ACCESS);
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_TRUE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+
+ // http://* permission
+ extension = LoadManifest("permissions", "permissions_http_scheme.json");
+ EXPECT_FALSE(extension->wants_file_access());
+ EXPECT_FALSE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+ extension = LoadManifest("permissions",
+ "permissions_http_scheme.json",
+ Extension::ALLOW_FILE_ACCESS);
+ EXPECT_FALSE(extension->wants_file_access());
+ EXPECT_FALSE(
+ extension->permissions_data()->CanAccessPage(file_url, -1, nullptr));
+
+ // <all_urls> content script match
+ extension = LoadManifest("permissions", "content_script_all_urls.json");
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_FALSE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+ extension = LoadManifest("permissions", "content_script_all_urls.json",
+ Extension::ALLOW_FILE_ACCESS);
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_TRUE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+
+ // file:///* content script match
+ extension = LoadManifest("permissions", "content_script_file_scheme.json");
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_FALSE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+ extension = LoadManifest("permissions", "content_script_file_scheme.json",
+ Extension::ALLOW_FILE_ACCESS);
+ EXPECT_TRUE(extension->wants_file_access());
+ EXPECT_TRUE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+
+ // http://* content script match
+ extension = LoadManifest("permissions", "content_script_http_scheme.json");
+ EXPECT_FALSE(extension->wants_file_access());
+ EXPECT_FALSE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+ extension = LoadManifest("permissions", "content_script_http_scheme.json",
+ Extension::ALLOW_FILE_ACCESS);
+ EXPECT_FALSE(extension->wants_file_access());
+ EXPECT_FALSE(extension->permissions_data()->CanRunContentScriptOnPage(
+ file_url, -1, nullptr));
+}
+
+TEST(ExtensionTest, ExtraFlags) {
+ scoped_refptr<Extension> extension;
+ extension = LoadManifest("app", "manifest.json", Extension::FROM_WEBSTORE);
+ EXPECT_TRUE(extension->from_webstore());
+
+ extension = LoadManifest("app", "manifest.json", Extension::FROM_BOOKMARK);
+ EXPECT_TRUE(extension->from_bookmark());
+
+ extension = LoadManifest("app", "manifest.json", Extension::NO_FLAGS);
+ EXPECT_FALSE(extension->from_bookmark());
+ EXPECT_FALSE(extension->from_webstore());
+}
+
+} // namespace extensions