// 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 "extensions/common/manifest_handler.h" #include #include #include #include #include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/install_warning.h" #include "extensions/common/scoped_testing_manifest_handler_registry.h" #include "extensions/common/value_builder.h" #include "testing/gtest/include/gtest/gtest.h" namespace extensions { namespace { std::vector SingleKey(const std::string& key) { return std::vector(1, key); } } // namespace class ManifestHandlerTest : public testing::Test { public: class ParsingWatcher { public: // Called when a manifest handler parses. void Record(const std::string& name) { parsed_names_.push_back(name); } const std::vector& parsed_names() { return parsed_names_; } // Returns true if |name_before| was parsed before |name_after|. bool ParsedBefore(const std::string& name_before, const std::string& name_after) { size_t i_before = parsed_names_.size(); size_t i_after = 0; for (size_t i = 0; i < parsed_names_.size(); ++i) { if (parsed_names_[i] == name_before) i_before = i; if (parsed_names_[i] == name_after) i_after = i; } if (i_before < i_after) return true; return false; } private: // The order of manifest handlers that we watched parsing. std::vector parsed_names_; }; class TestManifestHandler : public ManifestHandler { public: TestManifestHandler(const std::string& name, const std::vector& keys, const std::vector& prereqs, ParsingWatcher* watcher) : name_(name), keys_(keys), prereqs_(prereqs), watcher_(watcher) { keys_ptrs_.resize(keys_.size()); std::transform(keys_.begin(), keys_.end(), keys_ptrs_.begin(), [](const std::string& s) { return s.c_str(); }); } bool Parse(Extension* extension, std::u16string* error) override { watcher_->Record(name_); return true; } const std::vector PrerequisiteKeys() const override { return prereqs_; } protected: std::string name_; std::vector keys_; std::vector keys_ptrs_; std::vector prereqs_; ParsingWatcher* watcher_; base::span Keys() const override { return keys_ptrs_; } }; class FailingTestManifestHandler : public TestManifestHandler { public: FailingTestManifestHandler(const std::string& name, const std::vector& keys, const std::vector& prereqs, ParsingWatcher* watcher) : TestManifestHandler(name, keys, prereqs, watcher) { } bool Parse(Extension* extension, std::u16string* error) override { *error = base::ASCIIToUTF16(name_); return false; } }; class AlwaysParseTestManifestHandler : public TestManifestHandler { public: AlwaysParseTestManifestHandler(const std::string& name, const std::vector& keys, const std::vector& prereqs, ParsingWatcher* watcher) : TestManifestHandler(name, keys, prereqs, watcher) { } bool AlwaysParseForType(Manifest::Type type) const override { return true; } }; class TestManifestValidator : public ManifestHandler { public: TestManifestValidator(bool return_value, bool always_validate, std::vector keys) : return_value_(return_value), always_validate_(always_validate), keys_(keys) { keys_ptrs_.resize(keys_.size()); std::transform(keys_.begin(), keys_.end(), keys_ptrs_.begin(), [](const std::string& s) { return s.c_str(); }); } bool Parse(Extension* extension, std::u16string* error) override { return true; } bool Validate(const Extension* extension, std::string* error, std::vector* warnings) const override { return return_value_; } bool AlwaysValidateForType(Manifest::Type type) const override { return always_validate_; } private: base::span Keys() const override { return keys_ptrs_; } protected: bool return_value_; bool always_validate_; std::vector keys_; std::vector keys_ptrs_; }; }; TEST_F(ManifestHandlerTest, DependentHandlers) { ScopedTestingManifestHandlerRegistry scoped_registry; ParsingWatcher watcher; std::vector prereqs; ManifestHandlerRegistry* registry = ManifestHandlerRegistry::Get(); registry->RegisterHandler(std::make_unique( "A", SingleKey("a"), prereqs, &watcher)); registry->RegisterHandler(std::make_unique( "B", SingleKey("b"), prereqs, &watcher)); registry->RegisterHandler(std::make_unique( "J", SingleKey("j"), prereqs, &watcher)); registry->RegisterHandler(std::make_unique( "K", SingleKey("k"), prereqs, &watcher)); prereqs.push_back("c.d"); std::vector keys; keys.push_back("c.e"); keys.push_back("c.z"); registry->RegisterHandler( std::make_unique("C.EZ", keys, prereqs, &watcher)); prereqs.clear(); prereqs.push_back("b"); prereqs.push_back("k"); registry->RegisterHandler(std::make_unique( "C.D", SingleKey("c.d"), prereqs, &watcher)); ManifestHandler::FinalizeRegistration(); scoped_refptr extension = ExtensionBuilder() .SetManifest(DictionaryBuilder() .Set("name", "no name") .Set("version", "0") .Set("manifest_version", 2) .Set("a", 1) .Set("b", 2) .Set("c", DictionaryBuilder() .Set("d", 3) .Set("e", 4) .Set("f", 5) .Build()) .Set("g", 6) .Build()) .Build(); // A, B, C.EZ, C.D, K EXPECT_EQ(5u, watcher.parsed_names().size()); EXPECT_TRUE(watcher.ParsedBefore("B", "C.D")); EXPECT_TRUE(watcher.ParsedBefore("K", "C.D")); EXPECT_TRUE(watcher.ParsedBefore("C.D", "C.EZ")); } TEST_F(ManifestHandlerTest, FailingHandlers) { ScopedTestingManifestHandlerRegistry scoped_registry; // Can't use ExtensionBuilder, because this extension will fail to // be parsed. std::unique_ptr manifest_a( DictionaryBuilder() .Set("name", "no name") .Set("version", "0") .Set("manifest_version", 2) .Set("a", 1) .Build()); // Succeeds when "a" is not recognized. std::string error; scoped_refptr extension = Extension::Create( base::FilePath(), mojom::ManifestLocation::kInvalidLocation, *manifest_a, Extension::NO_FLAGS, &error); EXPECT_TRUE(extension.get()); // Register a handler for "a" that fails. ParsingWatcher watcher; ManifestHandlerRegistry* registry = ManifestHandlerRegistry::Get(); registry->RegisterHandler(std::make_unique( "A", SingleKey("a"), std::vector(), &watcher)); ManifestHandler::FinalizeRegistration(); extension = Extension::Create(base::FilePath(), mojom::ManifestLocation::kInvalidLocation, *manifest_a, Extension::NO_FLAGS, &error); EXPECT_FALSE(extension.get()); EXPECT_EQ("A", error); } TEST_F(ManifestHandlerTest, Validate) { ScopedTestingManifestHandlerRegistry scoped_registry; scoped_refptr extension = ExtensionBuilder() .SetManifest(DictionaryBuilder() .Set("name", "no name") .Set("version", "0") .Set("manifest_version", 2) .Set("a", 1) .Set("b", 2) .Build()) .Build(); EXPECT_TRUE(extension.get()); ManifestHandlerRegistry* registry = ManifestHandlerRegistry::Get(); std::string error; std::vector warnings; // Always validates and fails. registry->RegisterHandler( std::make_unique(false, true, SingleKey("c"))); EXPECT_FALSE( ManifestHandler::ValidateExtension(extension.get(), &error, &warnings)); // Validates "a" and fails. registry->RegisterHandler( std::make_unique(false, true, SingleKey("a"))); EXPECT_FALSE( ManifestHandler::ValidateExtension(extension.get(), &error, &warnings)); } } // namespace extensions