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

Side by Side Diff: chrome/browser/extensions/content_verifier_browsertest.cc

Issue 1250473002: Fix content verifier problem with content scripts (reland) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: removed logging and reworked test to elimnate flakiness Created 5 years, 4 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 | « no previous file | chrome/browser/extensions/extension_browsertest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include <list>
6 #include <set>
7 #include <string>
8
5 #include "base/scoped_observer.h" 9 #include "base/scoped_observer.h"
6 #include "chrome/browser/extensions/extension_browsertest.h" 10 #include "chrome/browser/extensions/extension_browsertest.h"
7 #include "chrome/common/chrome_switches.h" 11 #include "chrome/common/chrome_switches.h"
8 #include "content/public/test/test_utils.h" 12 #include "content/public/test/test_utils.h"
13 #include "extensions/browser/content_verifier.h"
9 #include "extensions/browser/content_verify_job.h" 14 #include "extensions/browser/content_verify_job.h"
10 #include "extensions/browser/extension_prefs.h" 15 #include "extensions/browser/extension_prefs.h"
11 #include "extensions/browser/extension_registry.h" 16 #include "extensions/browser/extension_registry.h"
12 #include "extensions/browser/extension_registry_observer.h" 17 #include "extensions/browser/extension_registry_observer.h"
13 18
14 namespace extensions { 19 namespace extensions {
15 20
16 namespace { 21 namespace {
17 22
18 // Helper for observing extension unloads. 23 // Helper for observing extension unloads.
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 ExtensionId id_; 88 ExtensionId id_;
84 bool fail_next_read_; 89 bool fail_next_read_;
85 bool fail_next_done_; 90 bool fail_next_done_;
86 }; 91 };
87 92
88 class JobObserver : public ContentVerifyJob::TestObserver { 93 class JobObserver : public ContentVerifyJob::TestObserver {
89 public: 94 public:
90 JobObserver(); 95 JobObserver();
91 virtual ~JobObserver(); 96 virtual ~JobObserver();
92 97
98 enum class Result { SUCCESS, FAILURE };
99
93 // Call this to add an expected job result. 100 // Call this to add an expected job result.
94 void ExpectJobResult(const std::string& extension_id, 101 void ExpectJobResult(const std::string& extension_id,
95 const base::FilePath& relative_path, 102 const base::FilePath& relative_path,
96 bool expected_to_fail); 103 Result expected_result);
97 104
98 // Wait to see expected jobs. Returns true if we saw all jobs finish as 105 // Wait to see expected jobs. Returns true when we've seen all expected jobs
99 // expected, or false if any job completed with non-expected success/failure 106 // finish, or false if there was an error or timeout.
100 // status.
101 bool WaitForExpectedJobs(); 107 bool WaitForExpectedJobs();
102 108
103 // ContentVerifyJob::TestObserver interface 109 // ContentVerifyJob::TestObserver interface
104 void JobStarted(const std::string& extension_id, 110 void JobStarted(const std::string& extension_id,
105 const base::FilePath& relative_path) override; 111 const base::FilePath& relative_path) override;
106 112
107 void JobFinished(const std::string& extension_id, 113 void JobFinished(const std::string& extension_id,
108 const base::FilePath& relative_path, 114 const base::FilePath& relative_path,
109 bool failed) override; 115 bool failed) override;
110 116
111 private: 117 private:
112 typedef std::pair<std::string, base::FilePath> ExtensionFile; 118 struct ExpectedResult {
113 typedef std::map<ExtensionFile, bool> ExpectedJobs; 119 public:
114 ExpectedJobs expected_jobs_; 120 std::string extension_id;
121 base::FilePath path;
122 Result result;
123
124 ExpectedResult(const std::string& extension_id, const base::FilePath& path,
125 Result result) {
126 this->extension_id = extension_id;
127 this->path = path;
128 this->result = result;
129 }
130 };
131 std::list<ExpectedResult> expectations_;
132 content::BrowserThread::ID creation_thread_;
115 scoped_refptr<content::MessageLoopRunner> loop_runner_; 133 scoped_refptr<content::MessageLoopRunner> loop_runner_;
116 bool saw_expected_job_results_;
117 }; 134 };
118 135
119 void JobObserver::ExpectJobResult(const std::string& extension_id, 136 void JobObserver::ExpectJobResult(const std::string& extension_id,
120 const base::FilePath& relative_path, 137 const base::FilePath& relative_path,
121 bool expected_to_fail) { 138 Result expected_result) {
122 expected_jobs_.insert(std::make_pair( 139 expectations_.push_back(ExpectedResult(
123 ExtensionFile(extension_id, relative_path), expected_to_fail)); 140 extension_id, relative_path, expected_result));
124 } 141 }
125 142
126 JobObserver::JobObserver() : saw_expected_job_results_(false) { 143 JobObserver::JobObserver() {
144 EXPECT_TRUE(
145 content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_));
127 } 146 }
128 147
129 JobObserver::~JobObserver() { 148 JobObserver::~JobObserver() {
130 } 149 }
131 150
132 bool JobObserver::WaitForExpectedJobs() { 151 bool JobObserver::WaitForExpectedJobs() {
133 if (!expected_jobs_.empty()) { 152 EXPECT_TRUE(content::BrowserThread::CurrentlyOn(creation_thread_));
153 if (!expectations_.empty()) {
134 loop_runner_ = new content::MessageLoopRunner(); 154 loop_runner_ = new content::MessageLoopRunner();
135 loop_runner_->Run(); 155 loop_runner_->Run();
156 loop_runner_ = nullptr;
136 } 157 }
137 return saw_expected_job_results_; 158 return expectations_.empty();
138 } 159 }
139 160
140 void JobObserver::JobStarted(const std::string& extension_id, 161 void JobObserver::JobStarted(const std::string& extension_id,
141 const base::FilePath& relative_path) { 162 const base::FilePath& relative_path) {
142 } 163 }
143 164
144 void JobObserver::JobFinished(const std::string& extension_id, 165 void JobObserver::JobFinished(const std::string& extension_id,
145 const base::FilePath& relative_path, 166 const base::FilePath& relative_path,
146 bool failed) { 167 bool failed) {
147 ExpectedJobs::iterator i = expected_jobs_.find(ExtensionFile( 168 if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
148 extension_id, relative_path.NormalizePathSeparatorsTo('/'))); 169 content::BrowserThread::PostTask(
149 if (i != expected_jobs_.end()) { 170 creation_thread_, FROM_HERE,
150 if (failed != i->second) { 171 base::Bind(&JobObserver::JobFinished, base::Unretained(this),
151 saw_expected_job_results_ = false; 172 extension_id, relative_path, failed));
152 if (loop_runner_.get()) 173 return;
153 loop_runner_->Quit(); 174 }
175 Result result = failed ? Result::FAILURE : Result::SUCCESS;
176 bool found = false;
177 for (std::list<ExpectedResult>::iterator i = expectations_.begin();
178 i != expectations_.end(); ++i) {
179 if (i->extension_id == extension_id && i->path == relative_path &&
180 i->result == result) {
181 found = true;
182 expectations_.erase(i);
183 break;
154 } 184 }
155 expected_jobs_.erase(i); 185 }
156 if (expected_jobs_.empty()) { 186 if (found) {
157 saw_expected_job_results_ = true; 187 if (expectations_.empty() && loop_runner_.get())
158 if (loop_runner_.get()) 188 loop_runner_->Quit();
159 loop_runner_->Quit(); 189 } else {
160 } 190 LOG(WARNING) << "Ignoring unexpected JobFinished " << extension_id << "/"
191 << relative_path.value() << " failed:" << failed;
161 } 192 }
162 } 193 }
163 194
195 class VerifierObserver : public ContentVerifier::TestObserver {
196 public:
197 VerifierObserver();
198 virtual ~VerifierObserver();
199
200 const std::set<std::string>& completed_fetches() {
201 return completed_fetches_;
202 }
203
204 // Returns when we've seen OnFetchComplete for |extension_id|.
205 void WaitForFetchComplete(const std::string& extension_id);
206
207 // ContentVerifier::TestObserver
208 void OnFetchComplete(const std::string& extension_id, bool success) override;
209
210 private:
211 std::set<std::string> completed_fetches_;
212 std::string id_to_wait_for_;
213 scoped_refptr<content::MessageLoopRunner> loop_runner_;
214 };
215
216 VerifierObserver::VerifierObserver() {
217 }
218
219 VerifierObserver::~VerifierObserver() {
220 }
221
222 void VerifierObserver::WaitForFetchComplete(const std::string& extension_id) {
223 EXPECT_TRUE(id_to_wait_for_.empty());
224 EXPECT_EQ(loop_runner_.get(), nullptr);
225 id_to_wait_for_ = extension_id;
226 loop_runner_ = new content::MessageLoopRunner();
227 loop_runner_->Run();
228 id_to_wait_for_.clear();
229 loop_runner_ = nullptr;
230 }
231
232 void VerifierObserver::OnFetchComplete(const std::string& extension_id,
233 bool success) {
234 completed_fetches_.insert(extension_id);
235 if (extension_id == id_to_wait_for_)
236 loop_runner_->Quit();
237 }
238
164 } // namespace 239 } // namespace
165 240
166 class ContentVerifierTest : public ExtensionBrowserTest { 241 class ContentVerifierTest : public ExtensionBrowserTest {
167 public: 242 public:
168 ContentVerifierTest() {} 243 ContentVerifierTest() {}
169 ~ContentVerifierTest() override {} 244 ~ContentVerifierTest() override {}
170 245
171 void SetUpCommandLine(base::CommandLine* command_line) override { 246 void SetUpCommandLine(base::CommandLine* command_line) override {
172 ExtensionBrowserTest::SetUpCommandLine(command_line); 247 ExtensionBrowserTest::SetUpCommandLine(command_line);
173 command_line->AppendSwitchASCII( 248 command_line->AppendSwitchASCII(
174 switches::kExtensionContentVerification, 249 switches::kExtensionContentVerification,
175 switches::kExtensionContentVerificationEnforce); 250 switches::kExtensionContentVerificationEnforce);
176 } 251 }
177 252
178 // Setup our unload observer and JobDelegate, and install a test extension.
179 void SetUpOnMainThread() override { 253 void SetUpOnMainThread() override {
180 ExtensionBrowserTest::SetUpOnMainThread(); 254 ExtensionBrowserTest::SetUpOnMainThread();
181 } 255 }
182 256
183 void TearDownOnMainThread() override { 257 void TearDownOnMainThread() override {
258 ContentVerifier::SetObserverForTests(NULL);
184 ContentVerifyJob::SetDelegateForTests(NULL); 259 ContentVerifyJob::SetDelegateForTests(NULL);
185 ContentVerifyJob::SetObserverForTests(NULL); 260 ContentVerifyJob::SetObserverForTests(NULL);
186 ExtensionBrowserTest::TearDownOnMainThread(); 261 ExtensionBrowserTest::TearDownOnMainThread();
187 } 262 }
188 263
189 virtual void OpenPageAndWaitForUnload() { 264 virtual void OpenPageAndWaitForUnload() {
190 unload_observer_.reset( 265 unload_observer_.reset(
191 new UnloadObserver(ExtensionRegistry::Get(profile()))); 266 new UnloadObserver(ExtensionRegistry::Get(profile())));
192 const Extension* extension = InstallExtensionFromWebstore( 267 const Extension* extension = InstallExtensionFromWebstore(
193 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); 268 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1);
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 delegate_.fail_next_done(); 305 delegate_.fail_next_done();
231 OpenPageAndWaitForUnload(); 306 OpenPageAndWaitForUnload();
232 } 307 }
233 308
234 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) { 309 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) {
235 JobObserver job_observer; 310 JobObserver job_observer;
236 ContentVerifyJob::SetObserverForTests(&job_observer); 311 ContentVerifyJob::SetObserverForTests(&job_observer);
237 std::string id = "hoipipabpcoomfapcecilckodldhmpgl"; 312 std::string id = "hoipipabpcoomfapcecilckodldhmpgl";
238 313
239 job_observer.ExpectJobResult( 314 job_observer.ExpectJobResult(
240 id, base::FilePath(FILE_PATH_LITERAL("background.js")), false); 315 id, base::FilePath(FILE_PATH_LITERAL("background.js")),
316 JobObserver::Result::SUCCESS);
317 job_observer.ExpectJobResult(id,
318 base::FilePath(FILE_PATH_LITERAL("page.html")),
319 JobObserver::Result::SUCCESS);
320 job_observer.ExpectJobResult(id, base::FilePath(FILE_PATH_LITERAL("page.js")),
321 JobObserver::Result::SUCCESS);
241 job_observer.ExpectJobResult( 322 job_observer.ExpectJobResult(
242 id, base::FilePath(FILE_PATH_LITERAL("page.html")), false); 323 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")),
243 job_observer.ExpectJobResult( 324 JobObserver::Result::SUCCESS);
244 id, base::FilePath(FILE_PATH_LITERAL("page.js")), false); 325 job_observer.ExpectJobResult(id,
245 job_observer.ExpectJobResult( 326 base::FilePath(FILE_PATH_LITERAL("page2.js")),
246 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")), false); 327 JobObserver::Result::SUCCESS);
247 job_observer.ExpectJobResult(
248 id, base::FilePath(FILE_PATH_LITERAL("page2.js")), false);
249 328
250 // Install a test extension we copied from the webstore that has actual 329 // Install a test extension we copied from the webstore that has actual
251 // signatures, and contains image paths with leading "./". 330 // signatures, and contains image paths with leading "./".
252 const Extension* extension = InstallExtensionFromWebstore( 331 const Extension* extension = InstallExtensionFromWebstore(
253 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1); 332 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1);
254 333
255 ASSERT_TRUE(extension); 334 ASSERT_TRUE(extension);
256 ASSERT_EQ(extension->id(), id); 335 ASSERT_EQ(extension->id(), id);
257 336
258 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); 337 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
259 338
260 ContentVerifyJob::SetObserverForTests(NULL); 339 ContentVerifyJob::SetObserverForTests(NULL);
261 } 340 }
262 341
342 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, ContentScripts) {
343 VerifierObserver verifier_observer;
344 ContentVerifier::SetObserverForTests(&verifier_observer);
345
346 // Install an extension with content scripts. The initial read of the content
347 // scripts will fail verification because they are read before the content
348 // verification system has completed a one-time processing of the expected
349 // hashes. (The extension only contains the root level hashes of the merkle
350 // tree, but the content verification system builds the entire tree and
351 // caches it in the extension install directory - see ContentHashFetcher for
352 // more details).
353 std::string id = "jmllhlobpjcnnomjlipadejplhmheiif";
354 const Extension* extension = InstallExtensionFromWebstore(
355 test_data_dir_.AppendASCII("content_verifier/content_script.crx"), 1);
356 ASSERT_TRUE(extension);
357 ASSERT_EQ(extension->id(), id);
358
359 // Wait for the content verification code to finish processing the hashes.
360 if (!ContainsKey(verifier_observer.completed_fetches(), id))
361 verifier_observer.WaitForFetchComplete(id);
362
363 // Now disable the extension, since content scripts are read at enable time,
364 // set up our job observer, and re-enable, expecting a success this time.
365 DisableExtension(id);
366 JobObserver job_observer;
367 ContentVerifyJob::SetObserverForTests(&job_observer);
368 job_observer.ExpectJobResult(id,
369 base::FilePath(FILE_PATH_LITERAL("script.js")),
370 JobObserver::Result::SUCCESS);
371 EnableExtension(id);
372 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
373
374 // Now alter the contents of the content script, reload the extension, and
375 // expect to see a job failure due to the content script content hash not
376 // being what was signed by the webstore.
377 base::FilePath scriptfile = extension->path().AppendASCII("script.js");
378 std::string extra = "some_extra_function_call();";
379 ASSERT_TRUE(base::AppendToFile(scriptfile, extra.data(), extra.size()));
380 DisableExtension(id);
381 job_observer.ExpectJobResult(id,
382 base::FilePath(FILE_PATH_LITERAL("script.js")),
383 JobObserver::Result::FAILURE);
384 EnableExtension(id);
385 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
386
387 ContentVerifyJob::SetObserverForTests(NULL);
388 }
389
263 } // namespace extensions 390 } // namespace extensions
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/extension_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698