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

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

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