#ifndef EXTENSIONS_BROWSER_CONTENT_VERIFIER_TEST_UTILS_H_
#define EXTENSIONS_BROWSER_CONTENT_VERIFIER_TEST_UTILS_H_
#include <list>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/run_loop.h"
#include "base/task/sequenced_task_runner.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/content_hash_reader.h"
#include "extensions/browser/content_verifier/content_hash.h"
#include "extensions/browser/content_verifier/content_verifier.h"
#include "extensions/browser/content_verifier/content_verifier_delegate.h"
#include "extensions/browser/content_verifier/content_verify_job.h"
#include "extensions/common/extension_id.h"
#include "extensions/test/test_extension_dir.h"
namespace extensions {
class Extension;
class TestContentVerifySingleJobObserver {
public:
TestContentVerifySingleJobObserver(const ExtensionId& extension_id,
const base::FilePath& relative_path);
~TestContentVerifySingleJobObserver();
TestContentVerifySingleJobObserver(
const TestContentVerifySingleJobObserver&) = delete;
TestContentVerifySingleJobObserver& operator=(
const TestContentVerifySingleJobObserver&) = delete;
[[nodiscard]] ContentVerifyJob::FailureReason WaitForJobFinished();
ContentHashReader::InitStatus WaitForOnHashesReady();
private:
class ObserverClient : public ContentVerifyJob::TestObserver {
public:
ObserverClient(const ExtensionId& extension_id,
const base::FilePath& relative_path);
ObserverClient(const ObserverClient&) = delete;
ObserverClient& operator=(const ObserverClient&) = delete;
void JobFinished(const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentVerifyJob::FailureReason reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
const ContentHashReader& hash_reader) override;
[[nodiscard]] ContentVerifyJob::FailureReason WaitForJobFinished();
ContentHashReader::InitStatus WaitForOnHashesReady();
private:
~ObserverClient() override;
void OnHashesReadyOnCreationThread(
const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentHashReader::InitStatus content_hash_status);
content::BrowserThread::ID creation_thread_;
base::RunLoop job_finished_run_loop_;
base::RunLoop on_hashes_ready_run_loop_;
ExtensionId extension_id_;
base::FilePath relative_path_;
std::optional<ContentVerifyJob::FailureReason> failure_reason_;
bool seen_on_hashes_ready_ = false;
ContentHashReader::InitStatus hashes_status_;
};
scoped_refptr<ObserverClient> client_;
};
class TestContentVerifyJobObserver {
public:
TestContentVerifyJobObserver();
~TestContentVerifyJobObserver();
TestContentVerifyJobObserver(const TestContentVerifyJobObserver&) = delete;
TestContentVerifyJobObserver& operator=(const TestContentVerifyJobObserver&) =
delete;
enum class Result { SUCCESS, FAILURE };
void ExpectJobResult(const ExtensionId& extension_id,
const base::FilePath& relative_path,
Result expected_result);
bool WaitForExpectedJobs();
private:
class ObserverClient : public ContentVerifyJob::TestObserver {
public:
ObserverClient();
ObserverClient(const ObserverClient&) = delete;
ObserverClient& operator=(const ObserverClient&) = delete;
void JobFinished(const ExtensionId& extension_id,
const base::FilePath& relative_path,
ContentVerifyJob::FailureReason failure_reason) override;
void OnHashesReady(const ExtensionId& extension_id,
const base::FilePath& relative_path,
const ContentHashReader& hash_reader) override {}
void ExpectJobResult(const ExtensionId& extension_id,
const base::FilePath& relative_path,
Result expected_result);
bool WaitForExpectedJobs();
private:
struct ExpectedResult {
public:
ExtensionId extension_id;
base::FilePath path;
Result result;
ExpectedResult(const ExtensionId& extension_id,
const base::FilePath& path,
Result result)
: extension_id(extension_id), path(path), result(result) {}
};
~ObserverClient() override;
std::list<ExpectedResult> expectations_;
content::BrowserThread::ID creation_thread_;
base::OnceClosure job_quit_closure_;
};
scoped_refptr<ObserverClient> client_;
};
class MockContentVerifierDelegate : public ContentVerifierDelegate {
public:
MockContentVerifierDelegate();
MockContentVerifierDelegate(const MockContentVerifierDelegate&) = delete;
MockContentVerifierDelegate& operator=(const MockContentVerifierDelegate&) =
delete;
~MockContentVerifierDelegate() override;
VerifierSourceType GetVerifierSourceType(const Extension& extension) override;
ContentVerifierKey GetPublicKey() override;
GURL GetSignatureFetchUrl(const ExtensionId& extension_id,
const base::Version& version) override;
std::set<base::FilePath> GetBrowserImagePaths(
const extensions::Extension* extension) override;
void VerifyFailed(const ExtensionId& extension_id,
ContentVerifyJob::FailureReason reason) override;
void Shutdown() override;
void SetVerifierSourceType(VerifierSourceType type);
void SetVerifierKey(std::vector<uint8_t> key);
private:
VerifierSourceType verifier_source_type_ = VerifierSourceType::SIGNED_HASHES;
std::vector<uint8_t> verifier_key_;
};
class VerifierObserver : public ContentVerifier::TestObserver {
public:
VerifierObserver();
VerifierObserver(const VerifierObserver&) = delete;
VerifierObserver& operator=(const VerifierObserver&) = delete;
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_; }
void EnsureFetchCompleted(const ExtensionId& extension_id);
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;
scoped_refptr<content::MessageLoopRunner> loop_runner_;
content::BrowserThread::ID creation_thread_;
base::WeakPtrFactory<VerifierObserver> weak_ptr_factory_{this};
};
struct ContentHashResult {
ContentHashResult(const ExtensionId& extension_id,
bool success,
bool was_cancelled,
const std::set<base::FilePath> mismatch_paths);
~ContentHashResult();
ExtensionId extension_id;
bool success;
bool was_cancelled;
std::set<base::FilePath> mismatch_paths;
};
class ContentHashWaiter {
public:
ContentHashWaiter();
ContentHashWaiter(const ContentHashWaiter&) = delete;
ContentHashWaiter& operator=(const ContentHashWaiter&) = delete;
~ContentHashWaiter();
std::unique_ptr<ContentHashResult> CreateAndWaitForCallback(
ContentHash::FetchKey key,
ContentVerifierDelegate::VerifierSourceType source_type);
private:
void CreatedCallback(scoped_refptr<ContentHash> content_hash,
bool was_cancelled);
void CreateContentHash(
ContentHash::FetchKey key,
ContentVerifierDelegate::VerifierSourceType source_type);
scoped_refptr<base::SequencedTaskRunner> reply_task_runner_;
base::RunLoop run_loop_;
std::unique_ptr<ContentHashResult> result_;
};
namespace content_verifier_test_utils {
class TestExtensionBuilder {
public:
TestExtensionBuilder();
explicit TestExtensionBuilder(const ExtensionId& extension_id);
~TestExtensionBuilder();
TestExtensionBuilder(const TestExtensionBuilder&) = delete;
TestExtensionBuilder& operator=(const TestExtensionBuilder&) = delete;
void AddResource(base::FilePath::StringType relative_path,
std::string contents);
void WriteManifest();
void WriteResource(base::FilePath::StringType relative_path,
std::string contents);
void WriteComputedHashes();
std::string CreateVerifiedContents() const;
void WriteVerifiedContents();
std::vector<uint8_t> GetTestContentVerifierPublicKey() const;
base::FilePath extension_path() const {
return extension_dir_.UnpackedPath();
}
const ExtensionId& extension_id() const { return extension_id_; }
private:
struct ExtensionResource {
ExtensionResource(base::FilePath relative_path, std::string contents)
: relative_path(std::move(relative_path)),
contents(std::move(contents)) {}
base::FilePath relative_path;
std::string contents;
};
std::unique_ptr<base::Value> CreateVerifiedContentsPayload() const;
ExtensionId extension_id_;
std::vector<ExtensionResource> extension_resources_;
TestExtensionDir extension_dir_;
};
scoped_refptr<Extension> UnzipToDirAndLoadExtension(
const base::FilePath& extension_zip,
const base::FilePath& unzip_dir);
}
}
#endif