// 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/common/extension.h" #include "base/command_line.h" #include "base/test/scoped_command_line.h" #include "base/test/scoped_feature_list.h" #include "extensions/common/extension_features.h" #include "extensions/common/manifest_constants.h" #include "extensions/common/switches.h" #include "extensions/common/value_builder.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/abseil-cpp/absl/types/optional.h" using extensions::mojom::ManifestLocation; namespace extensions { namespace { testing::AssertionResult RunManifestVersionSuccess( std::unique_ptr manifest, Manifest::Type expected_type, int expected_manifest_version, bool expect_warning = false, Extension::InitFromValueFlags custom_flag = Extension::NO_FLAGS) { std::string error; scoped_refptr extension = Extension::Create(base::FilePath(), ManifestLocation::kInternal, *manifest, custom_flag, &error); if (!extension) { return testing::AssertionFailure() << "Extension creation failed: " << error; } if (extension->GetType() != expected_type) { return testing::AssertionFailure() << "Wrong type: " << extension->GetType(); } if (extension->manifest_version() != expected_manifest_version) { return testing::AssertionFailure() << "Wrong manifest version: " << extension->manifest_version(); } bool has_manifest_version_warning = false; for (const auto& warning : extension->install_warnings()) { if (warning.key == manifest_keys::kManifestVersion) { has_manifest_version_warning = true; break; } } if (has_manifest_version_warning != expect_warning) return testing::AssertionFailure() << "Expected warning: " << expect_warning << ", Found Warning: " << has_manifest_version_warning; return testing::AssertionSuccess(); } testing::AssertionResult RunManifestVersionFailure( std::unique_ptr manifest, Extension::InitFromValueFlags custom_flag = Extension::NO_FLAGS) { std::string error; scoped_refptr extension = Extension::Create(base::FilePath(), ManifestLocation::kInternal, *manifest, custom_flag, &error); if (extension) return testing::AssertionFailure() << "Extension creation succeeded."; return testing::AssertionSuccess(); } testing::AssertionResult RunCreationWithFlags( const base::DictionaryValue* manifest, mojom::ManifestLocation location, Manifest::Type expected_type, Extension::InitFromValueFlags custom_flag = Extension::NO_FLAGS) { std::string error; scoped_refptr extension = Extension::Create( base::FilePath(), location, *manifest, custom_flag, &error); if (!extension) { return testing::AssertionFailure() << "Extension creation failed: " << error; } if (extension->GetType() != expected_type) { return testing::AssertionFailure() << "Wrong type: " << extension->GetType(); } return testing::AssertionSuccess(); } } // namespace // TODO(devlin): Move tests from chrome/common/extensions/extension_unittest.cc // that don't depend on //chrome into here. TEST(ExtensionTest, ExtensionManifestVersions) { auto get_manifest = [](absl::optional manifest_version) { DictionaryBuilder builder; builder.Set("name", "My Extension") .Set("version", "0.1") .Set("description", "An awesome extension"); if (manifest_version) builder.Set("manifest_version", *manifest_version); return builder.Build(); }; const Manifest::Type kType = Manifest::TYPE_EXTENSION; EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(2), kType, 2)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(3), kType, 3)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(4), kType, 4, true /* expect warning */)); // Manifest v1 is deprecated, and should not load. EXPECT_TRUE(RunManifestVersionFailure(get_manifest(1))); // Omitting the key defaults to v1 for extensions. EXPECT_TRUE(RunManifestVersionFailure(get_manifest(absl::nullopt))); // '0' and '-1' are invalid values. EXPECT_TRUE(RunManifestVersionFailure(get_manifest(0))); EXPECT_TRUE(RunManifestVersionFailure(get_manifest(-1))); { // Manifest v1 should only load if a command line switch is used. base::test::ScopedCommandLine command_line; command_line.GetProcessCommandLine()->AppendSwitch( switches::kAllowLegacyExtensionManifests); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(1), kType, 1)); EXPECT_TRUE( RunManifestVersionSuccess(get_manifest(absl::nullopt), kType, 1)); } { // If the requisite feature is disabled, Manifest V3 extensions should // fail to load. base::test::ScopedFeatureList feature_list; feature_list.InitAndDisableFeature( extensions_features::kMv3ExtensionsSupported); EXPECT_TRUE(RunManifestVersionFailure(get_manifest(3))); } } TEST(ExtensionTest, PlatformAppManifestVersions) { auto get_manifest = [](absl::optional manifest_version) { DictionaryBuilder background; background.Set("scripts", ListBuilder().Append("background.js").Build()); DictionaryBuilder builder; builder.Set("name", "My Platform App") .Set("version", "0.1") .Set("description", "A platform app") .Set("app", DictionaryBuilder().Set("background", background.Build()).Build()); if (manifest_version) builder.Set("manifest_version", *manifest_version); return builder.Build(); }; const Manifest::Type kType = Manifest::TYPE_PLATFORM_APP; EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(2), kType, 2)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(3), kType, 3)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(4), kType, 4, true /* expect warning */)); // Omitting the key defaults to v2 for platform apps. EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(absl::nullopt), kType, 2)); // Manifest v1 is deprecated, and should not load. EXPECT_TRUE(RunManifestVersionFailure(get_manifest(1))); // '0' and '-1' are invalid values. EXPECT_TRUE(RunManifestVersionFailure(get_manifest(0))); EXPECT_TRUE(RunManifestVersionFailure(get_manifest(-1))); { // Manifest v1 should not load for platform apps, even with the command line // switch. base::test::ScopedCommandLine command_line; command_line.GetProcessCommandLine()->AppendSwitch( switches::kAllowLegacyExtensionManifests); EXPECT_TRUE(RunManifestVersionFailure(get_manifest(1))); } } TEST(ExtensionTest, HostedAppManifestVersions) { auto get_manifest = [](absl::optional manifest_version) { DictionaryBuilder builder; DictionaryBuilder app; app.Set("urls", ListBuilder().Append("http://example.com").Build()); builder.Set("name", "My Hosted App") .Set("version", "0.1") .Set("description", "A hosted app") .Set("app", app.Build()); if (manifest_version) builder.Set("manifest_version", *manifest_version); return builder.Build(); }; const Manifest::Type kType = Manifest::TYPE_HOSTED_APP; EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(2), kType, 2)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(3), kType, 3)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(4), kType, 4, true /* expect warning */)); // Manifest v1 is deprecated, but should still load for hosted apps. EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(1), kType, 1)); // Omitting the key defaults to v1 for hosted apps, and v1 is still allowed. EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(absl::nullopt), kType, 1)); // Requiring the modern manifest version should make hosted apps require v2. EXPECT_TRUE(RunManifestVersionFailure( get_manifest(1), Extension::REQUIRE_MODERN_MANIFEST_VERSION)); } TEST(ExtensionTest, UserScriptManifestVersions) { auto get_manifest = [](absl::optional manifest_version) { DictionaryBuilder builder; builder.Set("name", "My Extension") .Set("version", "0.1") .Set("description", "An awesome extension") .Set("converted_from_user_script", true); if (manifest_version) builder.Set("manifest_version", *manifest_version); return builder.Build(); }; const Manifest::Type kType = Manifest::TYPE_USER_SCRIPT; EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(2), kType, 2)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(3), kType, 3)); EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(4), kType, 4, true /* expect warning */)); // Manifest v1 is deprecated, but should still load for user scripts. EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(1), kType, 1)); // Omitting the key defaults to v1 for user scripts, but v1 is still allowed. EXPECT_TRUE(RunManifestVersionSuccess(get_manifest(absl::nullopt), kType, 1)); // Requiring the modern manifest version should make user scripts require v2. EXPECT_TRUE(RunManifestVersionFailure( get_manifest(1), Extension::REQUIRE_MODERN_MANIFEST_VERSION)); } TEST(ExtensionTest, LoginScreenFlag) { DictionaryBuilder builder; builder.Set("name", "My Extension") .Set("version", "0.1") .Set("description", "An awesome extension") .Set("manifest_version", 2); std::unique_ptr manifest = builder.Build(); EXPECT_TRUE( RunCreationWithFlags(manifest.get(), ManifestLocation::kExternalPolicy, Manifest::TYPE_EXTENSION, Extension::NO_FLAGS)); EXPECT_TRUE(RunCreationWithFlags( manifest.get(), ManifestLocation::kExternalPolicy, Manifest::TYPE_LOGIN_SCREEN_EXTENSION, Extension::FOR_LOGIN_SCREEN)); } } // namespace extensions