OLD | NEW |
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" | |
10 #include "extensions/browser/content_verify_job.h" | 9 #include "extensions/browser/content_verify_job.h" |
11 #include "extensions/browser/extension_prefs.h" | 10 #include "extensions/browser/extension_prefs.h" |
12 #include "extensions/browser/extension_registry.h" | 11 #include "extensions/browser/extension_registry.h" |
13 #include "extensions/browser/extension_registry_observer.h" | 12 #include "extensions/browser/extension_registry_observer.h" |
14 | 13 |
15 namespace extensions { | 14 namespace extensions { |
16 | 15 |
17 namespace { | 16 namespace { |
18 | 17 |
19 // Helper for observing extension unloads. | 18 // Helper for observing extension unloads. |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 ExtensionId id_; | 83 ExtensionId id_; |
85 bool fail_next_read_; | 84 bool fail_next_read_; |
86 bool fail_next_done_; | 85 bool fail_next_done_; |
87 }; | 86 }; |
88 | 87 |
89 class JobObserver : public ContentVerifyJob::TestObserver { | 88 class JobObserver : public ContentVerifyJob::TestObserver { |
90 public: | 89 public: |
91 JobObserver(); | 90 JobObserver(); |
92 virtual ~JobObserver(); | 91 virtual ~JobObserver(); |
93 | 92 |
94 enum class Result { SUCCESS, FAILURE }; | |
95 | |
96 // Call this to add an expected job result. | 93 // Call this to add an expected job result. |
97 void ExpectJobResult(const std::string& extension_id, | 94 void ExpectJobResult(const std::string& extension_id, |
98 const base::FilePath& relative_path, | 95 const base::FilePath& relative_path, |
99 Result expected_result); | 96 bool expected_to_fail); |
100 | 97 |
101 // Wait to see expected jobs. Returns true if we saw all jobs finish as | 98 // Wait to see expected jobs. Returns true if we saw all jobs finish as |
102 // expected, or false if any job completed with non-expected success/failure | 99 // expected, or false if any job completed with non-expected success/failure |
103 // status. | 100 // status. |
104 bool WaitForExpectedJobs(); | 101 bool WaitForExpectedJobs(); |
105 | 102 |
106 // ContentVerifyJob::TestObserver interface | 103 // ContentVerifyJob::TestObserver interface |
107 void JobStarted(const std::string& extension_id, | 104 void JobStarted(const std::string& extension_id, |
108 const base::FilePath& relative_path) override; | 105 const base::FilePath& relative_path) override; |
109 | 106 |
110 void JobFinished(const std::string& extension_id, | 107 void JobFinished(const std::string& extension_id, |
111 const base::FilePath& relative_path, | 108 const base::FilePath& relative_path, |
112 bool failed) override; | 109 bool failed) override; |
113 | 110 |
114 private: | 111 private: |
115 typedef std::pair<std::string, base::FilePath> ExtensionFile; | 112 typedef std::pair<std::string, base::FilePath> ExtensionFile; |
116 typedef std::map<ExtensionFile, Result> ExpectedJobs; | 113 typedef std::map<ExtensionFile, bool> ExpectedJobs; |
117 ExpectedJobs expected_jobs_; | 114 ExpectedJobs expected_jobs_; |
118 scoped_refptr<content::MessageLoopRunner> loop_runner_; | 115 scoped_refptr<content::MessageLoopRunner> loop_runner_; |
119 // Used to track when jobs unexpectedly fail when expected to succeed, or | 116 bool saw_expected_job_results_; |
120 // vice versa. | |
121 int unexpected_job_results_; | |
122 content::BrowserThread::ID creation_thread_; | |
123 }; | 117 }; |
124 | 118 |
125 void JobObserver::ExpectJobResult(const std::string& extension_id, | 119 void JobObserver::ExpectJobResult(const std::string& extension_id, |
126 const base::FilePath& relative_path, | 120 const base::FilePath& relative_path, |
127 Result expected_result) { | 121 bool expected_to_fail) { |
128 expected_jobs_.insert(std::make_pair( | 122 expected_jobs_.insert(std::make_pair( |
129 ExtensionFile(extension_id, relative_path), expected_result)); | 123 ExtensionFile(extension_id, relative_path), expected_to_fail)); |
130 } | 124 } |
131 | 125 |
132 JobObserver::JobObserver() : unexpected_job_results_(0) { | 126 JobObserver::JobObserver() : saw_expected_job_results_(false) { |
133 EXPECT_TRUE( | |
134 content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_)); | |
135 } | 127 } |
136 | 128 |
137 JobObserver::~JobObserver() { | 129 JobObserver::~JobObserver() { |
138 } | 130 } |
139 | 131 |
140 bool JobObserver::WaitForExpectedJobs() { | 132 bool JobObserver::WaitForExpectedJobs() { |
141 EXPECT_TRUE(content::BrowserThread::CurrentlyOn(creation_thread_)); | |
142 if (!expected_jobs_.empty()) { | 133 if (!expected_jobs_.empty()) { |
143 loop_runner_ = new content::MessageLoopRunner(); | 134 loop_runner_ = new content::MessageLoopRunner(); |
144 loop_runner_->Run(); | 135 loop_runner_->Run(); |
145 loop_runner_ = nullptr; | |
146 } | 136 } |
147 return unexpected_job_results_ == 0; | 137 return saw_expected_job_results_; |
148 } | 138 } |
149 | 139 |
150 void JobObserver::JobStarted(const std::string& extension_id, | 140 void JobObserver::JobStarted(const std::string& extension_id, |
151 const base::FilePath& relative_path) { | 141 const base::FilePath& relative_path) { |
152 } | 142 } |
153 | 143 |
154 void JobObserver::JobFinished(const std::string& extension_id, | 144 void JobObserver::JobFinished(const std::string& extension_id, |
155 const base::FilePath& relative_path, | 145 const base::FilePath& relative_path, |
156 bool failed) { | 146 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 } | |
164 ExpectedJobs::iterator i = expected_jobs_.find(ExtensionFile( | 147 ExpectedJobs::iterator i = expected_jobs_.find(ExtensionFile( |
165 extension_id, relative_path.NormalizePathSeparatorsTo('/'))); | 148 extension_id, relative_path.NormalizePathSeparatorsTo('/'))); |
166 if (i != expected_jobs_.end()) { | 149 if (i != expected_jobs_.end()) { |
167 if ((failed && i->second != Result::FAILURE) || | 150 if (failed != i->second) { |
168 (!failed && i->second != Result::SUCCESS)) { | 151 saw_expected_job_results_ = false; |
169 unexpected_job_results_ += 1; | |
170 if (loop_runner_.get()) | 152 if (loop_runner_.get()) |
171 loop_runner_->Quit(); | 153 loop_runner_->Quit(); |
172 } | 154 } |
173 expected_jobs_.erase(i); | 155 expected_jobs_.erase(i); |
174 if (expected_jobs_.empty()) { | 156 if (expected_jobs_.empty()) { |
| 157 saw_expected_job_results_ = true; |
175 if (loop_runner_.get()) | 158 if (loop_runner_.get()) |
176 loop_runner_->Quit(); | 159 loop_runner_->Quit(); |
177 } | 160 } |
178 } | 161 } |
179 } | 162 } |
180 | 163 |
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 | |
225 } // namespace | 164 } // namespace |
226 | 165 |
227 class ContentVerifierTest : public ExtensionBrowserTest { | 166 class ContentVerifierTest : public ExtensionBrowserTest { |
228 public: | 167 public: |
229 ContentVerifierTest() {} | 168 ContentVerifierTest() {} |
230 ~ContentVerifierTest() override {} | 169 ~ContentVerifierTest() override {} |
231 | 170 |
232 void SetUpCommandLine(base::CommandLine* command_line) override { | 171 void SetUpCommandLine(base::CommandLine* command_line) override { |
233 ExtensionBrowserTest::SetUpCommandLine(command_line); | 172 ExtensionBrowserTest::SetUpCommandLine(command_line); |
234 command_line->AppendSwitchASCII( | 173 command_line->AppendSwitchASCII( |
235 switches::kExtensionContentVerification, | 174 switches::kExtensionContentVerification, |
236 switches::kExtensionContentVerificationEnforce); | 175 switches::kExtensionContentVerificationEnforce); |
237 } | 176 } |
238 | 177 |
| 178 // Setup our unload observer and JobDelegate, and install a test extension. |
239 void SetUpOnMainThread() override { | 179 void SetUpOnMainThread() override { |
240 ExtensionBrowserTest::SetUpOnMainThread(); | 180 ExtensionBrowserTest::SetUpOnMainThread(); |
241 } | 181 } |
242 | 182 |
243 void TearDownOnMainThread() override { | 183 void TearDownOnMainThread() override { |
244 ContentVerifier::SetObserverForTests(NULL); | |
245 ContentVerifyJob::SetDelegateForTests(NULL); | 184 ContentVerifyJob::SetDelegateForTests(NULL); |
246 ContentVerifyJob::SetObserverForTests(NULL); | 185 ContentVerifyJob::SetObserverForTests(NULL); |
247 ExtensionBrowserTest::TearDownOnMainThread(); | 186 ExtensionBrowserTest::TearDownOnMainThread(); |
248 } | 187 } |
249 | 188 |
250 virtual void OpenPageAndWaitForUnload() { | 189 virtual void OpenPageAndWaitForUnload() { |
251 unload_observer_.reset( | 190 unload_observer_.reset( |
252 new UnloadObserver(ExtensionRegistry::Get(profile()))); | 191 new UnloadObserver(ExtensionRegistry::Get(profile()))); |
253 const Extension* extension = InstallExtensionFromWebstore( | 192 const Extension* extension = InstallExtensionFromWebstore( |
254 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); | 193 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 delegate_.fail_next_done(); | 230 delegate_.fail_next_done(); |
292 OpenPageAndWaitForUnload(); | 231 OpenPageAndWaitForUnload(); |
293 } | 232 } |
294 | 233 |
295 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) { | 234 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) { |
296 JobObserver job_observer; | 235 JobObserver job_observer; |
297 ContentVerifyJob::SetObserverForTests(&job_observer); | 236 ContentVerifyJob::SetObserverForTests(&job_observer); |
298 std::string id = "hoipipabpcoomfapcecilckodldhmpgl"; | 237 std::string id = "hoipipabpcoomfapcecilckodldhmpgl"; |
299 | 238 |
300 job_observer.ExpectJobResult( | 239 job_observer.ExpectJobResult( |
301 id, base::FilePath(FILE_PATH_LITERAL("background.js")), | 240 id, base::FilePath(FILE_PATH_LITERAL("background.js")), false); |
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); | |
308 job_observer.ExpectJobResult( | 241 job_observer.ExpectJobResult( |
309 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")), | 242 id, base::FilePath(FILE_PATH_LITERAL("page.html")), false); |
310 JobObserver::Result::SUCCESS); | 243 job_observer.ExpectJobResult( |
311 job_observer.ExpectJobResult(id, | 244 id, base::FilePath(FILE_PATH_LITERAL("page.js")), false); |
312 base::FilePath(FILE_PATH_LITERAL("page2.js")), | 245 job_observer.ExpectJobResult( |
313 JobObserver::Result::SUCCESS); | 246 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")), false); |
| 247 job_observer.ExpectJobResult( |
| 248 id, base::FilePath(FILE_PATH_LITERAL("page2.js")), false); |
314 | 249 |
315 // Install a test extension we copied from the webstore that has actual | 250 // Install a test extension we copied from the webstore that has actual |
316 // signatures, and contains image paths with leading "./". | 251 // signatures, and contains image paths with leading "./". |
317 const Extension* extension = InstallExtensionFromWebstore( | 252 const Extension* extension = InstallExtensionFromWebstore( |
318 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1); | 253 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1); |
319 | 254 |
320 ASSERT_TRUE(extension); | 255 ASSERT_TRUE(extension); |
321 ASSERT_EQ(extension->id(), id); | 256 ASSERT_EQ(extension->id(), id); |
322 | 257 |
323 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); | 258 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); |
324 | 259 |
325 ContentVerifyJob::SetObserverForTests(NULL); | 260 ContentVerifyJob::SetObserverForTests(NULL); |
326 } | 261 } |
327 | 262 |
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 | |
376 } // namespace extensions | 263 } // namespace extensions |
OLD | NEW |