Commit 0f709d22 authored by Istiaque Ahmed's avatar Istiaque Ahmed Committed by Commit Bot

[CV] Correctly verify paths in manifest.json for content verification.

This CL makes ContentVerifier::ShouldVerifyAnyPaths take
canonical relative paths instead of unix style relative paths,
so that it can take OS specific canonicalization into account.
This affects all paths specified in manifest.json: background
JS, background html, content script, locales, etc. Previously
ShouldVerifyAnyPaths would have returned false for case
variants of those resources on win/mac, and content-verification
wouldn't run for them as there wouldn't be any ContentVerifyJobs
for them.
An example of this is on win/mac, a script specified as script.js
in manifest.json, but accessed as Script.js on win/mac will
now be verified with this CL.
The same applies to _locales, locale path accessed in different
case now would correctly skip verification (e.g. en_gb instead of
en_GB), instead of incorrectly causing content verification
failure.

ContentVerifierObserver now exposes did_hash_mismatch, which would
be slightly better to observe CVDelegate::VerifyFailed from tests.

This CL adds unittests for the change in content_verifier_unittest.cc
This CL also adds a real world like unittest under //chrome, so
the browser image paths and other manifest paths are verified (and
not mocked like content_verifier_unittest) end-to-end. This is
chrome_content_verifier_unittest.cc

BUG=1051401,796395

Change-Id: I74161a043a6e65b80c7bf06c249cf42813f867a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2049125
Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org>
Reviewed-by: default avatarOleg Davydov <burunduk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#744824}
parent 86972e27
// Copyright 2020 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 <algorithm>
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
#include "chrome/browser/extensions/extension_service_test_with_install.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/testing_profile.h"
#include "extensions/browser/content_verifier/test_utils.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/file_util.h"
namespace extensions {
namespace {
constexpr char kCaseSensitiveManifestPathsCrx[] =
"content_verifier/case_sensitive_manifest_paths.crx";
std::set<base::FilePath> ToFilePaths(const std::set<std::string>& paths) {
std::set<base::FilePath> file_paths;
for (const auto& path : paths)
file_paths.insert(base::FilePath().AppendASCII(path));
return file_paths;
}
bool IsSuperset(const std::set<base::FilePath>& container,
const std::set<base::FilePath>& candidates) {
std::vector<base::FilePath> difference;
std::set_difference(candidates.begin(), candidates.end(), container.begin(),
container.end(), std::back_inserter(difference));
return difference.empty();
}
} // namespace
// Tests are run with //chrome layer so that manifest's //chrome specific bits
// (e.g. browser images, default_icon in actions) are present.
class ChromeContentVerifierTest : public ExtensionServiceTestWithInstall {
public:
void SetUp() override {
ExtensionServiceTestWithInstall::SetUp();
// Note: we need a separate TestingProfile (other than our base class)
// because we need it to build |content_verifier_| below in SetUp().
testing_profile_ = TestingProfile::Builder().Build();
// Set up content verification.
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
command_line->AppendSwitchASCII(
switches::kExtensionContentVerification,
switches::kExtensionContentVerificationEnforce);
auto delegate =
std::make_unique<ChromeContentVerifierDelegate>(browser_context());
delegate_raw_ = delegate.get();
content_verifier_ = base::MakeRefCounted<ContentVerifier>(
browser_context(), std::move(delegate));
info_map()->SetContentVerifier(content_verifier_.get());
content_verifier_->Start();
}
void TearDown() override {
content_verifier_->Shutdown();
ExtensionServiceTestWithInstall::TearDown();
}
testing::AssertionResult InstallExtension(const std::string& crx_path_str) {
if (extension_) {
return testing::AssertionFailure()
<< "Only one extension is allowed to be installed in this test. "
<< "Error while installing crx from: " << crx_path_str;
}
InitializeEmptyExtensionService();
base::FilePath data_dir;
if (!base::PathService::Get(chrome::DIR_TEST_DATA, &data_dir))
return testing::AssertionFailure() << "DIR_TEST_DATA not found";
base::FilePath crx_full_path =
data_dir.AppendASCII("extensions").AppendASCII(crx_path_str);
extension_ = InstallCRX(crx_full_path, INSTALL_NEW);
if (!extension_)
return testing::AssertionFailure()
<< "Failed to install extension at " << crx_full_path;
return testing::AssertionSuccess();
}
void AddExtensionToContentVerifier(
const scoped_refptr<const Extension>& extension,
VerifierObserver* verifier_observer) {
info_map()->AddExtension(extension.get(), base::Time::Now(), false, false);
EXPECT_TRUE(
ExtensionRegistry::Get(browser_context())->AddEnabled(extension));
ExtensionRegistry::Get(browser_context())->TriggerOnLoaded(extension.get());
// Ensure that content verifier has checked hashes from |extension|.
EXPECT_EQ(ChromeContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES,
delegate_raw_->GetVerifierSourceType(*extension));
verifier_observer->EnsureFetchCompleted(extension->id());
}
scoped_refptr<ContentVerifier>& content_verifier() {
return content_verifier_;
}
const scoped_refptr<const Extension>& extension() { return extension_; }
bool ShouldVerifyAnyPaths(
const std::set<base::FilePath>& relative_unix_paths) const {
return content_verifier_->ShouldVerifyAnyPathsForTesting(
extension_->id(), extension_->path(), relative_unix_paths);
}
private:
InfoMap* info_map() {
return ExtensionSystem::Get(browser_context())->info_map();
}
content::BrowserContext* browser_context() { return testing_profile_.get(); }
scoped_refptr<const Extension> extension_;
// Owned by |content_verifier_|.
ChromeContentVerifierDelegate* delegate_raw_ = nullptr;
scoped_refptr<ContentVerifier> content_verifier_;
std::unique_ptr<TestingProfile> testing_profile_;
};
// Tests that an extension with mixed case resources specified in manifest.json
// (messages, browser images, browserAction.default_icon) loads correctly.
TEST_F(ChromeContentVerifierTest, CaseSensitivityInManifestPaths) {
VerifierObserver verifier_observer;
ASSERT_TRUE(InstallExtension(kCaseSensitiveManifestPathsCrx));
// Make sure computed_hashes.json does not exist as this test relies on its
// generation to discover hash_mismatch_unix_paths().
ASSERT_FALSE(
base::PathExists(file_util::GetComputedHashesPath(extension()->path())));
AddExtensionToContentVerifier(extension(), &verifier_observer);
ASSERT_TRUE(
base::PathExists(file_util::GetComputedHashesPath(extension()->path())));
// Known paths that are transcoded in |extension| crx.
std::set<std::string> transcoded_paths = {"_locales/de_AT/messages.json",
"_locales/en_GB/messages.json",
"H.png", "g.png", "i.png"};
// Ensure we've seen known paths as hash-mismatch on FetchComplete.
EXPECT_TRUE(IsSuperset(verifier_observer.hash_mismatch_unix_paths(),
ToFilePaths(transcoded_paths)));
// Sanity check: ensure they are explicitly excluded from verification.
EXPECT_FALSE(ShouldVerifyAnyPaths(ToFilePaths({"_locales/de_AT/messages.json",
"_locales/en_GB/messages.json",
"H.png", "g.png", "i.png"})));
// Make sure we haven't seen ContentVerifier::VerifyFailed
EXPECT_FALSE(verifier_observer.did_hash_mismatch());
// Ensure transcoded paths are handled correctly with different case in
// case-insensitive OS. They should still be excluded from verification (i.e.
// ShouldVerifyAnyPaths should return false for them).
if (!content_verifier_utils::IsFileAccessCaseSensitive()) {
EXPECT_FALSE(ShouldVerifyAnyPaths(ToFilePaths(
{"_locales/de_at/messages.json", "_locales/en_gb/messages.json",
"h.png", "G.png", "I.png"})));
}
// Ensure transcoded paths are handled correctly with dot-space suffix added
// to them in OS that ignores dot-space suffix (win). They should still be
// excluded from verification (i.e. ShouldVerifyAnyPaths should return false
// for them).
if (content_verifier_utils::IsDotSpaceFilenameSuffixIgnored()) {
EXPECT_FALSE(ShouldVerifyAnyPaths(ToFilePaths(
{"_locales/de_AT/messages.json.", "_locales/en_GB/messages.json ",
"H.png .", "g.png ..", "i.png.."})));
// Ensure the same with different case filenames.
if (!content_verifier_utils::IsFileAccessCaseSensitive()) {
EXPECT_FALSE(ShouldVerifyAnyPaths(ToFilePaths(
{"_locales/de_at/messages.json.", "_locales/en_gb/messages.json ",
"h.png .", "G.png ..", "I.png.."})));
}
}
}
// Tests that tampered resources cause verification failure due to hash mismatch
// during OnExtensionLoaded.
TEST_F(ChromeContentVerifierTest, VerifyFailedOnLoad) {
VerifierObserver verifier_observer;
ASSERT_TRUE(InstallExtension(kCaseSensitiveManifestPathsCrx));
// Before ContentVerifier sees |extension|, tamper with a JS file.
{
constexpr char kTamperedContent[] = "// Evil content";
base::FilePath background_script_path =
extension()->path().AppendASCII("d.js");
ASSERT_EQ(static_cast<int>(sizeof(kTamperedContent)),
base::WriteFile(background_script_path, kTamperedContent,
sizeof(kTamperedContent)));
}
AddExtensionToContentVerifier(extension(), &verifier_observer);
// Expect a hash mismatch for tampered d.js file.
EXPECT_TRUE(verifier_observer.did_hash_mismatch());
}
} // namespace extensions
......@@ -4633,6 +4633,7 @@ test("unit_tests") {
"../browser/extensions/chrome_app_icon_unittest.cc",
"../browser/extensions/chrome_app_sorting_unittest.cc",
"../browser/extensions/chrome_component_extension_resource_manager_unittest.cc",
"../browser/extensions/chrome_content_verifier_unittest.cc",
"../browser/extensions/chrome_extension_function_unittest.cc",
"../browser/extensions/chrome_info_map_unittest.cc",
"../browser/extensions/component_loader_unittest.cc",
......
Fetched from Chrome Web Store.
CRX ID = ndbcnocfihoagadmddmdlgbpieadephg
This extension has
- locales with uppercase chars: _locales/en_GB/, _locales/en_US/
- png files that got transcoded
......@@ -24,6 +24,7 @@
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/content_hash_fetcher.h"
#include "extensions/browser/content_hash_reader.h"
#include "extensions/browser/content_verifier/content_verifier_utils.h"
#include "extensions/browser/content_verifier_delegate.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/common/constants.h"
......@@ -39,6 +40,7 @@ namespace extensions {
namespace {
ContentVerifier::TestObserver* g_content_verifier_test_observer = nullptr;
using content_verifier_utils::CanonicalRelativePath;
// This function converts paths like "//foo/bar", "./foo/bar", and
// "/foo/bar" to "foo/bar". It also converts path separators to "/".
......@@ -92,29 +94,34 @@ std::unique_ptr<ContentVerifierIOData::ExtensionData> CreateIOData(
// comparing to actual relative paths work later on.
std::set<base::FilePath> original_image_paths =
delegate->GetBrowserImagePaths(extension);
auto canonicalize_path = [](const base::FilePath& relative_path) {
return content_verifier_utils::CanonicalizeRelativePath(
NormalizeRelativePath(relative_path));
};
auto image_paths = std::make_unique<std::set<base::FilePath>>();
auto image_paths = std::make_unique<std::set<CanonicalRelativePath>>();
for (const auto& path : original_image_paths) {
image_paths->insert(NormalizeRelativePath(path));
image_paths->insert(canonicalize_path(path));
}
auto background_or_content_paths =
std::make_unique<std::set<base::FilePath>>();
std::make_unique<std::set<CanonicalRelativePath>>();
for (const std::string& script :
BackgroundInfo::GetBackgroundScripts(extension)) {
background_or_content_paths->insert(
extension->GetResource(script).relative_path());
canonicalize_path(extension->GetResource(script).relative_path()));
}
if (BackgroundInfo::HasBackgroundPage(extension)) {
background_or_content_paths->insert(
extensions::file_util::ExtensionURLToRelativeFilePath(
BackgroundInfo::GetBackgroundURL(extension)));
canonicalize_path(extensions::file_util::ExtensionURLToRelativeFilePath(
BackgroundInfo::GetBackgroundURL(extension))));
}
for (const std::unique_ptr<UserScript>& script :
ContentScriptsInfo::GetContentScripts(extension)) {
for (const std::unique_ptr<UserScript::File>& js_file :
script->js_scripts()) {
background_or_content_paths->insert(js_file->relative_path());
background_or_content_paths->insert(
canonicalize_path(js_file->relative_path()));
}
}
......@@ -123,6 +130,27 @@ std::unique_ptr<ContentVerifierIOData::ExtensionData> CreateIOData(
extension->version(), source_type);
}
// Returns all locales, possibly with lowercasing them for case-insensitive OS.
std::set<std::string> GetAllLocaleCandidates() {
std::set<std::string> all_locales;
// TODO(asargent) - see if we can cache this list longer to avoid
// having to fetch it more than once for a given run of the
// browser. Maybe it can never change at runtime? (Or if it can, maybe
// there is an event we can listen for to know to drop our cache).
extension_l10n_util::GetAllLocales(&all_locales);
if (content_verifier_utils::IsFileAccessCaseSensitive())
return all_locales;
// Lower-case the locales candidate so we can search in
// case-insensitive manner for win/mac.
std::set<std::string> all_locales_candidate;
std::transform(
all_locales.begin(), all_locales.end(),
std::inserter(all_locales_candidate, all_locales_candidate.begin()),
[](const std::string& locale) { return base::ToLowerASCII(locale); });
return all_locales_candidate;
}
} // namespace
struct ContentVerifier::CacheKey {
......@@ -444,10 +472,10 @@ scoped_refptr<ContentVerifyJob> ContentVerifier::CreateAndStartJobFor(
base::FilePath normalized_unix_path = NormalizeRelativePath(relative_path);
std::set<base::FilePath> unix_paths;
unix_paths.insert(normalized_unix_path);
if (!ShouldVerifyAnyPaths(extension_id, extension_root, unix_paths))
if (!ShouldVerifyAnyPaths(extension_id, extension_root,
{normalized_unix_path})) {
return nullptr;
}
// TODO(asargent) - we can probably get some good performance wins by having
// a cache of ContentHashReader's that we hold onto past the end of each job.
......@@ -604,17 +632,17 @@ void ContentVerifier::OnFetchComplete(
const scoped_refptr<const ContentHash>& content_hash) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
ExtensionId extension_id = content_hash->extension_id();
if (g_content_verifier_test_observer) {
g_content_verifier_test_observer->OnFetchComplete(
extension_id, content_hash->succeeded());
}
VLOG(1) << "OnFetchComplete " << extension_id
<< " success:" << content_hash->succeeded();
const bool did_hash_mismatch =
ShouldVerifyAnyPaths(extension_id, content_hash->extension_root(),
content_hash->hash_mismatch_unix_paths());
if (g_content_verifier_test_observer) {
g_content_verifier_test_observer->OnFetchComplete(content_hash,
did_hash_mismatch);
}
if (!did_hash_mismatch)
return;
......@@ -683,20 +711,29 @@ bool ContentVerifier::ShouldVerifyAnyPaths(
if (!data)
return false;
const std::set<base::FilePath>& browser_images = *(data->browser_image_paths);
const std::set<base::FilePath>& background_or_content_paths =
*(data->background_or_content_paths);
const std::set<CanonicalRelativePath>& browser_images =
*(data->canonical_browser_image_paths);
const std::set<CanonicalRelativePath>& background_or_content_paths =
*(data->canonical_background_or_content_paths);
std::unique_ptr<std::set<std::string>> all_locales;
base::Optional<std::set<std::string>> all_locale_candidates;
const base::FilePath manifest_file(kManifestFilename);
const CanonicalRelativePath manifest_file =
content_verifier_utils::CanonicalizeRelativePath(
base::FilePath(kManifestFilename));
const base::FilePath messages_file(kMessagesFilename);
const base::FilePath locales_relative_dir(kLocaleFolder);
const CanonicalRelativePath indexed_ruleset_path =
content_verifier_utils::CanonicalizeRelativePath(
file_util::GetIndexedRulesetRelativePath());
for (const base::FilePath& relative_unix_path : relative_unix_paths) {
if (relative_unix_path.empty())
continue;
if (relative_unix_path == manifest_file)
CanonicalRelativePath canonical_path_value =
content_verifier_utils::CanonicalizeRelativePath(relative_unix_path);
if (canonical_path_value == manifest_file)
continue;
// JavaScript and HTML files should always be verified.
......@@ -707,37 +744,27 @@ bool ContentVerifier::ShouldVerifyAnyPaths(
// Background pages, scripts and content scripts should always be verified
// regardless of their file type.
if (base::Contains(background_or_content_paths, relative_unix_path))
if (base::Contains(background_or_content_paths, canonical_path_value))
return true;
if (base::Contains(browser_images, relative_unix_path))
if (base::Contains(browser_images, canonical_path_value))
continue;
base::FilePath relative_path = relative_unix_path.NormalizePathSeparators();
base::FilePath full_path = extension_root.Append(relative_path);
if (full_path ==
extension_root.Append(file_util::GetIndexedRulesetRelativePath())) {
if (canonical_path_value == indexed_ruleset_path)
continue;
}
if (locales_relative_dir.IsParent(relative_path)) {
if (!all_locales) {
// TODO(asargent) - see if we can cache this list longer to avoid
// having to fetch it more than once for a given run of the
// browser. Maybe it can never change at runtime? (Or if it can, maybe
// there is an event we can listen for to know to drop our cache).
all_locales.reset(new std::set<std::string>);
extension_l10n_util::GetAllLocales(all_locales.get());
}
const base::FilePath canonical_path(canonical_path_value.value());
if (locales_relative_dir.IsParent(canonical_path)) {
if (!all_locale_candidates)
all_locale_candidates = GetAllLocaleCandidates();
// Since message catalogs get transcoded during installation, we want
// to skip those paths. See if this path looks like
// _locales/<some locale>/messages.json - if so then skip it.
if (relative_path.BaseName() == messages_file &&
relative_path.DirName().DirName() == locales_relative_dir &&
base::Contains(*all_locales,
relative_path.DirName().BaseName().MaybeAsASCII())) {
if (canonical_path.BaseName() == messages_file &&
canonical_path.DirName().DirName() == locales_relative_dir &&
base::Contains(*all_locale_candidates,
canonical_path.DirName().BaseName().MaybeAsASCII())) {
continue;
}
}
......
......@@ -64,8 +64,9 @@ class ContentVerifier : public base::RefCountedThreadSafe<ContentVerifier>,
public:
class TestObserver {
public:
virtual void OnFetchComplete(const std::string& extension_id,
bool success) = 0;
virtual void OnFetchComplete(
const scoped_refptr<const ContentHash>& content_hash,
bool did_hash_mismatch) = 0;
};
static void SetObserverForTests(TestObserver* observer);
......
......@@ -18,6 +18,7 @@
#include "crypto/sha2.h"
#include "extensions/browser/content_hash_fetcher.h"
#include "extensions/browser/content_hash_tree.h"
#include "extensions/browser/content_verifier/content_verifier_utils.h"
#include "extensions/browser/extension_file_task_runner.h"
#include "extensions/common/file_util.h"
#include "net/base/load_flags.h"
......@@ -324,18 +325,20 @@ std::set<base::FilePath> ContentHash::GetMismatchedComputedHashes(
DCHECK(computed_hashes_data);
if (source_type_ !=
ContentVerifierDelegate::VerifierSourceType::SIGNED_HASHES) {
return std::set<base::FilePath>();
return {};
}
std::set<base::FilePath> mismatched_hashes;
for (const auto& resource_info : computed_hashes_data->items()) {
const ComputedHashes::Data::HashInfo& hash_info = resource_info.second;
const content_verifier_utils::CanonicalRelativePath&
canonical_relative_path = resource_info.first;
std::string root = ComputeTreeHashRoot(hash_info.hashes,
block_size_ / crypto::kSHA256Length);
if (!verified_contents_->TreeHashRootEquals(hash_info.relative_unix_path,
root)) {
if (!verified_contents_->TreeHashRootEqualsForCanonicalPath(
canonical_relative_path, root)) {
mismatched_hashes.insert(hash_info.relative_unix_path);
}
}
......
......@@ -248,16 +248,20 @@ void VerifierObserver::EnsureFetchCompleted(const ExtensionId& extension_id) {
loop_runner_ = nullptr;
}
void VerifierObserver::OnFetchComplete(const ExtensionId& extension_id,
bool success) {
void VerifierObserver::OnFetchComplete(
const scoped_refptr<const ContentHash>& content_hash,
bool did_hash_mismatch) {
if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
base::PostTask(
FROM_HERE, {creation_thread_},
base::PostTask(FROM_HERE, {creation_thread_},
base::BindOnce(&VerifierObserver::OnFetchComplete,
base::Unretained(this), extension_id, success));
base::Unretained(this), content_hash,
did_hash_mismatch));
return;
}
const ExtensionId extension_id = content_hash->extension_id();
completed_fetches_.insert(extension_id);
content_hash_ = content_hash;
did_hash_mismatch_ = did_hash_mismatch;
if (extension_id == id_to_wait_for_) {
DCHECK(loop_runner_);
loop_runner_->Quit();
......
......@@ -183,16 +183,25 @@ class VerifierObserver : public ContentVerifier::TestObserver {
VerifierObserver();
virtual ~VerifierObserver();
const std::set<base::FilePath>& hash_mismatch_unix_paths() {
DCHECK(content_hash_);
return content_hash_->hash_mismatch_unix_paths();
}
bool did_hash_mismatch() const { return did_hash_mismatch_; }
// Ensures that |extension_id| has seen OnFetchComplete, waits for it to
// complete if it hasn't already.
void EnsureFetchCompleted(const ExtensionId& extension_id);
// ContentVerifier::TestObserver
void OnFetchComplete(const ExtensionId& extension_id, bool success) override;
void OnFetchComplete(const scoped_refptr<const ContentHash>& content_hash,
bool did_hash_mismatch) override;
private:
std::set<ExtensionId> completed_fetches_;
ExtensionId id_to_wait_for_;
scoped_refptr<const ContentHash> content_hash_;
bool did_hash_mismatch_ = true;
// Created and accessed on |creation_thread_|.
scoped_refptr<content::MessageLoopRunner> loop_runner_;
......
......@@ -11,12 +11,15 @@
namespace extensions {
ContentVerifierIOData::ExtensionData::ExtensionData(
std::unique_ptr<std::set<base::FilePath>> browser_image_paths,
std::unique_ptr<std::set<base::FilePath>> background_or_content_paths,
std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_browser_image_paths,
std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_background_or_content_paths,
const base::Version& version,
ContentVerifierDelegate::VerifierSourceType source_type)
: browser_image_paths(std::move(browser_image_paths)),
background_or_content_paths(std::move(background_or_content_paths)),
: canonical_browser_image_paths(std::move(canonical_browser_image_paths)),
canonical_background_or_content_paths(
std::move(canonical_background_or_content_paths)),
version(version),
source_type(source_type) {}
......@@ -32,7 +35,7 @@ ContentVerifierIOData::~ContentVerifierIOData() {
void ContentVerifierIOData::AddData(const std::string& extension_id,
std::unique_ptr<ExtensionData> data) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
CHECK(data->browser_image_paths.get());
CHECK(data->canonical_browser_image_paths.get());
data_map_[extension_id] = std::move(data);
}
......
......@@ -13,25 +13,32 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/version.h"
#include "extensions/browser/content_verifier/content_verifier_utils.h"
#include "extensions/browser/content_verifier_delegate.h"
namespace extensions {
using CanonicalRelativePath = content_verifier_utils::CanonicalRelativePath;
// A helper class for keeping track of data for the ContentVerifier that should
// only be accessed on the IO thread.
class ContentVerifierIOData {
public:
struct ExtensionData {
// Set of images file paths used within the browser process.
std::unique_ptr<std::set<base::FilePath>> browser_image_paths;
// Set of file paths used as background scripts, pages or content scripts.
std::unique_ptr<std::set<base::FilePath>> background_or_content_paths;
// Set of canonical file paths used as images within the browser process.
std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_browser_image_paths;
// Set of canonical file paths used as background scripts, pages or
// content scripts.
std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_background_or_content_paths;
base::Version version;
ContentVerifierDelegate::VerifierSourceType source_type;
ExtensionData(
std::unique_ptr<std::set<base::FilePath>> browser_image_paths,
std::unique_ptr<std::set<base::FilePath>> background_or_content_paths,
ExtensionData(std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_browser_image_paths,
std::unique_ptr<std::set<CanonicalRelativePath>>
canonical_background_or_content_paths,
const base::Version& version,
ContentVerifierDelegate::VerifierSourceType source_type);
~ExtensionData();
......
......@@ -194,7 +194,7 @@ bool VerifiedContents::HasTreeHashRoot(
bool VerifiedContents::TreeHashRootEquals(const base::FilePath& relative_path,
const std::string& expected) const {
return TreeHashRootEqualsImpl(
return TreeHashRootEqualsForCanonicalPath(
content_verifier_utils::CanonicalizeRelativePath(relative_path),
expected);
}
......@@ -333,7 +333,7 @@ bool VerifiedContents::VerifySignature(const std::string& protected_value,
return true;
}
bool VerifiedContents::TreeHashRootEqualsImpl(
bool VerifiedContents::TreeHashRootEqualsForCanonicalPath(
const content_verifier_utils::CanonicalRelativePath&
canonical_relative_path,
const std::string& expected) const {
......
......@@ -48,6 +48,10 @@ class VerifiedContents {
bool TreeHashRootEquals(const base::FilePath& relative_path,
const std::string& expected) const;
bool TreeHashRootEqualsForCanonicalPath(
const CanonicalRelativePath& canonical_relative_path,
const std::string& expected) const;
// If InitFrom has not been called yet, or was used in "ignore invalid
// signature" mode, this can return false.
bool valid_signature() { return valid_signature_; }
......@@ -68,10 +72,6 @@ class VerifiedContents {
const std::string& payload,
const std::string& signature_bytes);
bool TreeHashRootEqualsImpl(
const CanonicalRelativePath& canonical_relative_path,
const std::string& expected) const;
// The public key we should use for signature verification.
base::span<const uint8_t> public_key_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment