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 <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 Loading... |
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 Loading... |
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 |
OLD | NEW |