Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2650)

Unified Diff: extensions/browser/content_hash_fetcher_unittest.cc

Issue 2336403002: Fix extension content verification out-of-band hash fetching (Closed)
Patch Set: review feedback Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/browser/content_hash_fetcher.cc ('k') | extensions/browser/content_verifier.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/browser/content_hash_fetcher_unittest.cc
diff --git a/extensions/browser/content_hash_fetcher_unittest.cc b/extensions/browser/content_hash_fetcher_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1893309f2d93903daa8d2c4db554c8fe1b59dcae
--- /dev/null
+++ b/extensions/browser/content_hash_fetcher_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright 2016 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 <memory>
+
+#include "base/callback.h"
+#include "base/callback_helpers.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/files/scoped_temp_dir.h"
+#include "base/memory/ptr_util.h"
+#include "base/memory/weak_ptr.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/version.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_browser_thread_bundle.h"
+#include "extensions/browser/content_hash_fetcher.h"
+#include "extensions/browser/content_verifier_delegate.h"
+#include "extensions/browser/extensions_test.h"
+#include "extensions/common/constants.h"
+#include "extensions/common/extension_paths.h"
+#include "extensions/common/file_util.h"
+#include "net/url_request/test_url_request_interceptor.h"
+#include "net/url_request/url_request_interceptor.h"
+#include "net/url_request/url_request_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/zlib/google/zip.h"
+
+namespace extensions {
+
+// Used to hold the result of a callback from the ContentHashFetcher.
+struct ContentHashFetcherResult {
+ std::string extension_id;
+ bool success;
+ bool force;
+ std::set<base::FilePath> mismatch_paths;
+};
+
+// Allows waiting for the callback from a ContentHashFetcher, returning the
+// data that was passed to that callback.
+class ContentHashFetcherWaiter {
+ public:
+ ContentHashFetcherWaiter() : weak_factory_(this) {}
+
+ ContentHashFetcher::FetchCallback GetCallback() {
+ return base::Bind(&ContentHashFetcherWaiter::Callback,
+ weak_factory_.GetWeakPtr());
+ }
+
+ std::unique_ptr<ContentHashFetcherResult> WaitForCallback() {
+ if (!result_) {
+ base::RunLoop run_loop;
+ run_loop_quit_ = run_loop.QuitClosure();
+ run_loop.Run();
+ }
+ return std::move(result_);
+ }
+
+ private:
+ // Matches signature of ContentHashFetcher::FetchCallback.
+ void Callback(const std::string& extension_id,
+ bool success,
+ bool force,
+ const std::set<base::FilePath>& mismatch_paths) {
+ result_ = base::MakeUnique<ContentHashFetcherResult>();
+ result_->extension_id = extension_id;
+ result_->success = success;
+ result_->force = force;
+ result_->mismatch_paths = mismatch_paths;
+ if (run_loop_quit_)
+ base::ResetAndReturn(&run_loop_quit_).Run();
+ }
+
+ base::Closure run_loop_quit_;
+ std::unique_ptr<ContentHashFetcherResult> result_;
+ base::WeakPtrFactory<ContentHashFetcherWaiter> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(ContentHashFetcherWaiter);
+};
+
+// Used in setting up the behavior of our ContentHashFetcher.
+class MockDelegate : public ContentVerifierDelegate {
+ public:
+ ContentVerifierDelegate::Mode ShouldBeVerified(
+ const Extension& extension) override {
+ return ContentVerifierDelegate::ENFORCE_STRICT;
+ }
+
+ ContentVerifierKey GetPublicKey() override {
+ return ContentVerifierKey(kWebstoreSignaturesPublicKey,
+ kWebstoreSignaturesPublicKeySize);
+ }
+
+ GURL GetSignatureFetchUrl(const std::string& extension_id,
+ const base::Version& version) override {
+ std::string url =
+ base::StringPrintf("http://localhost/getsignature?id=%s&version=%s",
+ extension_id.c_str(), version.GetString().c_str());
+ return GURL(url);
+ }
+
+ std::set<base::FilePath> GetBrowserImagePaths(
+ const extensions::Extension* extension) override {
+ ADD_FAILURE() << "Unexpected call for this test";
+ return std::set<base::FilePath>();
+ }
+
+ void VerifyFailed(const std::string& extension_id,
+ ContentVerifyJob::FailureReason reason) override {
+ ADD_FAILURE() << "Unexpected call for this test";
+ }
+};
+
+class ContentHashFetcherTest : public ExtensionsTest {
+ public:
+ ContentHashFetcherTest() {}
+ ~ContentHashFetcherTest() override {}
+
+ void SetUp() override {
+ ExtensionsTest::SetUp();
+ // We need a real IO thread to be able to intercept the network request
+ // for the missing verified_contents.json file.
+ browser_threads_.reset(new content::TestBrowserThreadBundle(
+ content::TestBrowserThreadBundle::REAL_IO_THREAD));
+ request_context_ = new net::TestURLRequestContextGetter(
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::IO));
+ }
+
+ net::URLRequestContextGetter* request_context() {
+ return request_context_.get();
+ }
+
+ // Helper to get files from our subdirectory in the general extensions test
+ // data dir.
+ base::FilePath GetTestPath(const base::FilePath& relative_path) {
+ base::FilePath base_path;
+ EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &base_path));
+ base_path = base_path.AppendASCII("content_hash_fetcher");
+ return base_path.Append(relative_path);
+ }
+
+ // Unzips the extension source from |extension_zip| into a temporary
+ // directory and loads it, returning the resuling Extension object.
+ scoped_refptr<Extension> UnzipToTempDirAndLoad(
+ const base::FilePath& extension_zip) {
+ EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+ base::FilePath destination = temp_dir_.path();
+ EXPECT_TRUE(zip::Unzip(extension_zip, destination));
+
+ std::string error;
+ scoped_refptr<Extension> extension = file_util::LoadExtension(
+ destination, Manifest::INTERNAL, 0 /* flags */, &error);
+ EXPECT_NE(nullptr, extension.get()) << " error:'" << error << "'";
+ return extension;
+ }
+
+ // Registers interception of requests for |url| to respond with the contents
+ // of the file at |response_path|.
+ void RegisterInterception(const GURL& url,
+ const base::FilePath& response_path) {
+ interceptor_ = base::MakeUnique<net::TestURLRequestInterceptor>(
+ url.scheme(), url.host(),
+ content::BrowserThread::GetTaskRunnerForThread(
+ content::BrowserThread::IO),
+ content::BrowserThread::GetBlockingPool());
+ interceptor_->SetResponse(url, response_path);
+ }
+
+ protected:
+ std::unique_ptr<content::TestBrowserThreadBundle> browser_threads_;
+ std::unique_ptr<net::TestURLRequestInterceptor> interceptor_;
+ scoped_refptr<net::TestURLRequestContextGetter> request_context_;
+ base::ScopedTempDir temp_dir_;
+};
+
+// This tests our ability to successfully fetch, parse, and validate a missing
+// verified_contents.json file for an extension.
+TEST_F(ContentHashFetcherTest, MissingVerifiedContents) {
+ // We unzip the extension source to a temp directory to simulate it being
+ // installed there, because the ContentHashFetcher will create the _metadata/
+ // directory within the extension install dir and write the fetched
+ // verified_contents.json file there.
+ base::FilePath test_dir_base = GetTestPath(
+ base::FilePath(FILE_PATH_LITERAL("missing_verified_contents")));
+ scoped_refptr<Extension> extension =
+ UnzipToTempDirAndLoad(test_dir_base.AppendASCII("source.zip"));
+
+ // Make sure there isn't already a verified_contents.json file there.
+ EXPECT_FALSE(
+ base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
+
+ MockDelegate delegate;
+ ContentHashFetcherWaiter waiter;
+ GURL fetch_url =
+ delegate.GetSignatureFetchUrl(extension->id(), *extension->version());
+
+ RegisterInterception(fetch_url,
+ test_dir_base.AppendASCII("verified_contents.json"));
+
+ ContentHashFetcher fetcher(request_context(), &delegate,
+ waiter.GetCallback());
+ fetcher.DoFetch(extension.get(), true /* force */);
+
+ // Make sure the fetch was successful.
+ std::unique_ptr<ContentHashFetcherResult> result = waiter.WaitForCallback();
+ ASSERT_TRUE(result.get());
+ EXPECT_TRUE(result->success);
+ EXPECT_TRUE(result->force);
+ EXPECT_TRUE(result->mismatch_paths.empty());
+
+ // Make sure the verified_contents.json file was written into the extension's
+ // install dir.
+ EXPECT_TRUE(
+ base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
+}
+
+// Similar to MissingVerifiedContents, but tests the case where the extension
+// actually has corruption.
+TEST_F(ContentHashFetcherTest, MissingVerifiedContentsAndCorrupt) {
+ base::FilePath test_dir_base =
+ GetTestPath(base::FilePath()).AppendASCII("missing_verified_contents");
+ scoped_refptr<Extension> extension =
+ UnzipToTempDirAndLoad(test_dir_base.AppendASCII("source.zip"));
+
+ // Tamper with a file in the extension.
+ base::FilePath script_path = extension->path().AppendASCII("script.js");
+ std::string addition = "//hello world";
+ ASSERT_TRUE(
+ base::AppendToFile(script_path, addition.c_str(), addition.size()));
+ MockDelegate delegate;
+ ContentHashFetcherWaiter waiter;
+ GURL fetch_url =
+ delegate.GetSignatureFetchUrl(extension->id(), *extension->version());
+
+ RegisterInterception(fetch_url,
+ test_dir_base.AppendASCII("verified_contents.json"));
+
+ ContentHashFetcher fetcher(request_context(), &delegate,
+ waiter.GetCallback());
+ fetcher.DoFetch(extension.get(), true /* force */);
+
+ // Make sure the fetch was *not* successful.
+ std::unique_ptr<ContentHashFetcherResult> result = waiter.WaitForCallback();
+ ASSERT_NE(nullptr, result.get());
+ EXPECT_TRUE(result->success);
+ EXPECT_TRUE(result->force);
+ EXPECT_TRUE(
+ base::ContainsKey(result->mismatch_paths, script_path.BaseName()));
+
+ // Make sure the verified_contents.json file was written into the extension's
+ // install dir.
+ EXPECT_TRUE(
+ base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
+}
+
+} // namespace extensions
« no previous file with comments | « extensions/browser/content_hash_fetcher.cc ('k') | extensions/browser/content_verifier.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698