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

Side by Side 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 unified diff | 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 »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6
7 #include "base/callback.h"
8 #include "base/callback_helpers.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/path_service.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/version.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/test/test_browser_thread_bundle.h"
20 #include "extensions/browser/content_hash_fetcher.h"
21 #include "extensions/browser/content_verifier_delegate.h"
22 #include "extensions/browser/extensions_test.h"
23 #include "extensions/common/constants.h"
24 #include "extensions/common/extension_paths.h"
25 #include "extensions/common/file_util.h"
26 #include "net/url_request/test_url_request_interceptor.h"
27 #include "net/url_request/url_request_interceptor.h"
28 #include "net/url_request/url_request_test_util.h"
29 #include "testing/gtest/include/gtest/gtest.h"
30 #include "third_party/zlib/google/zip.h"
31
32 namespace extensions {
33
34 // Used to hold the result of a callback from the ContentHashFetcher.
35 struct ContentHashFetcherResult {
36 std::string extension_id;
37 bool success;
38 bool force;
39 std::set<base::FilePath> mismatch_paths;
40 };
41
42 // Allows waiting for the callback from a ContentHashFetcher, returning the
43 // data that was passed to that callback.
44 class ContentHashFetcherWaiter {
45 public:
46 ContentHashFetcherWaiter() : weak_factory_(this) {}
47
48 ContentHashFetcher::FetchCallback GetCallback() {
49 return base::Bind(&ContentHashFetcherWaiter::Callback,
50 weak_factory_.GetWeakPtr());
51 }
52
53 std::unique_ptr<ContentHashFetcherResult> WaitForCallback() {
54 if (!result_) {
55 base::RunLoop run_loop;
56 run_loop_quit_ = run_loop.QuitClosure();
57 run_loop.Run();
58 }
59 return std::move(result_);
60 }
61
62 private:
63 // Matches signature of ContentHashFetcher::FetchCallback.
64 void Callback(const std::string& extension_id,
65 bool success,
66 bool force,
67 const std::set<base::FilePath>& mismatch_paths) {
68 result_ = base::MakeUnique<ContentHashFetcherResult>();
69 result_->extension_id = extension_id;
70 result_->success = success;
71 result_->force = force;
72 result_->mismatch_paths = mismatch_paths;
73 if (run_loop_quit_)
74 base::ResetAndReturn(&run_loop_quit_).Run();
75 }
76
77 base::Closure run_loop_quit_;
78 std::unique_ptr<ContentHashFetcherResult> result_;
79 base::WeakPtrFactory<ContentHashFetcherWaiter> weak_factory_;
80
81 DISALLOW_COPY_AND_ASSIGN(ContentHashFetcherWaiter);
82 };
83
84 // Used in setting up the behavior of our ContentHashFetcher.
85 class MockDelegate : public ContentVerifierDelegate {
86 public:
87 ContentVerifierDelegate::Mode ShouldBeVerified(
88 const Extension& extension) override {
89 return ContentVerifierDelegate::ENFORCE_STRICT;
90 }
91
92 ContentVerifierKey GetPublicKey() override {
93 return ContentVerifierKey(kWebstoreSignaturesPublicKey,
94 kWebstoreSignaturesPublicKeySize);
95 }
96
97 GURL GetSignatureFetchUrl(const std::string& extension_id,
98 const base::Version& version) override {
99 std::string url =
100 base::StringPrintf("http://localhost/getsignature?id=%s&version=%s",
101 extension_id.c_str(), version.GetString().c_str());
102 return GURL(url);
103 }
104
105 std::set<base::FilePath> GetBrowserImagePaths(
106 const extensions::Extension* extension) override {
107 ADD_FAILURE() << "Unexpected call for this test";
108 return std::set<base::FilePath>();
109 }
110
111 void VerifyFailed(const std::string& extension_id,
112 ContentVerifyJob::FailureReason reason) override {
113 ADD_FAILURE() << "Unexpected call for this test";
114 }
115 };
116
117 class ContentHashFetcherTest : public ExtensionsTest {
118 public:
119 ContentHashFetcherTest() {}
120 ~ContentHashFetcherTest() override {}
121
122 void SetUp() override {
123 ExtensionsTest::SetUp();
124 // We need a real IO thread to be able to intercept the network request
125 // for the missing verified_contents.json file.
126 browser_threads_.reset(new content::TestBrowserThreadBundle(
127 content::TestBrowserThreadBundle::REAL_IO_THREAD));
128 request_context_ = new net::TestURLRequestContextGetter(
129 content::BrowserThread::GetTaskRunnerForThread(
130 content::BrowserThread::IO));
131 }
132
133 net::URLRequestContextGetter* request_context() {
134 return request_context_.get();
135 }
136
137 // Helper to get files from our subdirectory in the general extensions test
138 // data dir.
139 base::FilePath GetTestPath(const base::FilePath& relative_path) {
140 base::FilePath base_path;
141 EXPECT_TRUE(PathService::Get(extensions::DIR_TEST_DATA, &base_path));
142 base_path = base_path.AppendASCII("content_hash_fetcher");
143 return base_path.Append(relative_path);
144 }
145
146 // Unzips the extension source from |extension_zip| into a temporary
147 // directory and loads it, returning the resuling Extension object.
148 scoped_refptr<Extension> UnzipToTempDirAndLoad(
149 const base::FilePath& extension_zip) {
150 EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
151 base::FilePath destination = temp_dir_.path();
152 EXPECT_TRUE(zip::Unzip(extension_zip, destination));
153
154 std::string error;
155 scoped_refptr<Extension> extension = file_util::LoadExtension(
156 destination, Manifest::INTERNAL, 0 /* flags */, &error);
157 EXPECT_NE(nullptr, extension.get()) << " error:'" << error << "'";
158 return extension;
159 }
160
161 // Registers interception of requests for |url| to respond with the contents
162 // of the file at |response_path|.
163 void RegisterInterception(const GURL& url,
164 const base::FilePath& response_path) {
165 interceptor_ = base::MakeUnique<net::TestURLRequestInterceptor>(
166 url.scheme(), url.host(),
167 content::BrowserThread::GetTaskRunnerForThread(
168 content::BrowserThread::IO),
169 content::BrowserThread::GetBlockingPool());
170 interceptor_->SetResponse(url, response_path);
171 }
172
173 protected:
174 std::unique_ptr<content::TestBrowserThreadBundle> browser_threads_;
175 std::unique_ptr<net::TestURLRequestInterceptor> interceptor_;
176 scoped_refptr<net::TestURLRequestContextGetter> request_context_;
177 base::ScopedTempDir temp_dir_;
178 };
179
180 // This tests our ability to successfully fetch, parse, and validate a missing
181 // verified_contents.json file for an extension.
182 TEST_F(ContentHashFetcherTest, MissingVerifiedContents) {
183 // We unzip the extension source to a temp directory to simulate it being
184 // installed there, because the ContentHashFetcher will create the _metadata/
185 // directory within the extension install dir and write the fetched
186 // verified_contents.json file there.
187 base::FilePath test_dir_base = GetTestPath(
188 base::FilePath(FILE_PATH_LITERAL("missing_verified_contents")));
189 scoped_refptr<Extension> extension =
190 UnzipToTempDirAndLoad(test_dir_base.AppendASCII("source.zip"));
191
192 // Make sure there isn't already a verified_contents.json file there.
193 EXPECT_FALSE(
194 base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
195
196 MockDelegate delegate;
197 ContentHashFetcherWaiter waiter;
198 GURL fetch_url =
199 delegate.GetSignatureFetchUrl(extension->id(), *extension->version());
200
201 RegisterInterception(fetch_url,
202 test_dir_base.AppendASCII("verified_contents.json"));
203
204 ContentHashFetcher fetcher(request_context(), &delegate,
205 waiter.GetCallback());
206 fetcher.DoFetch(extension.get(), true /* force */);
207
208 // Make sure the fetch was successful.
209 std::unique_ptr<ContentHashFetcherResult> result = waiter.WaitForCallback();
210 ASSERT_TRUE(result.get());
211 EXPECT_TRUE(result->success);
212 EXPECT_TRUE(result->force);
213 EXPECT_TRUE(result->mismatch_paths.empty());
214
215 // Make sure the verified_contents.json file was written into the extension's
216 // install dir.
217 EXPECT_TRUE(
218 base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
219 }
220
221 // Similar to MissingVerifiedContents, but tests the case where the extension
222 // actually has corruption.
223 TEST_F(ContentHashFetcherTest, MissingVerifiedContentsAndCorrupt) {
224 base::FilePath test_dir_base =
225 GetTestPath(base::FilePath()).AppendASCII("missing_verified_contents");
226 scoped_refptr<Extension> extension =
227 UnzipToTempDirAndLoad(test_dir_base.AppendASCII("source.zip"));
228
229 // Tamper with a file in the extension.
230 base::FilePath script_path = extension->path().AppendASCII("script.js");
231 std::string addition = "//hello world";
232 ASSERT_TRUE(
233 base::AppendToFile(script_path, addition.c_str(), addition.size()));
234 MockDelegate delegate;
235 ContentHashFetcherWaiter waiter;
236 GURL fetch_url =
237 delegate.GetSignatureFetchUrl(extension->id(), *extension->version());
238
239 RegisterInterception(fetch_url,
240 test_dir_base.AppendASCII("verified_contents.json"));
241
242 ContentHashFetcher fetcher(request_context(), &delegate,
243 waiter.GetCallback());
244 fetcher.DoFetch(extension.get(), true /* force */);
245
246 // Make sure the fetch was *not* successful.
247 std::unique_ptr<ContentHashFetcherResult> result = waiter.WaitForCallback();
248 ASSERT_NE(nullptr, result.get());
249 EXPECT_TRUE(result->success);
250 EXPECT_TRUE(result->force);
251 EXPECT_TRUE(
252 base::ContainsKey(result->mismatch_paths, script_path.BaseName()));
253
254 // Make sure the verified_contents.json file was written into the extension's
255 // install dir.
256 EXPECT_TRUE(
257 base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
258 }
259
260 } // namespace extensions
OLDNEW
« 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