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> | 5 #include <list> |
6 #include <set> | 6 #include <set> |
7 #include <string> | 7 #include <string> |
8 | 8 |
| 9 #include "base/callback_helpers.h" |
9 #include "base/macros.h" | 10 #include "base/macros.h" |
| 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/run_loop.h" |
10 #include "base/scoped_observer.h" | 13 #include "base/scoped_observer.h" |
| 14 #include "base/strings/string_split.h" |
| 15 #include "base/threading/thread_task_runner_handle.h" |
11 #include "chrome/browser/extensions/extension_browsertest.h" | 16 #include "chrome/browser/extensions/extension_browsertest.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" |
12 #include "chrome/common/chrome_switches.h" | 18 #include "chrome/common/chrome_switches.h" |
13 #include "content/public/test/test_utils.h" | 19 #include "content/public/test/test_utils.h" |
14 #include "extensions/browser/content_verifier.h" | 20 #include "extensions/browser/content_verifier.h" |
15 #include "extensions/browser/content_verify_job.h" | 21 #include "extensions/browser/content_verify_job.h" |
| 22 #include "extensions/browser/crx_file_info.h" |
16 #include "extensions/browser/extension_prefs.h" | 23 #include "extensions/browser/extension_prefs.h" |
17 #include "extensions/browser/extension_registry.h" | 24 #include "extensions/browser/extension_registry.h" |
18 #include "extensions/browser/extension_registry_observer.h" | 25 #include "extensions/browser/extension_registry_observer.h" |
| 26 #include "extensions/browser/external_install_info.h" |
| 27 #include "extensions/browser/external_provider_interface.h" |
| 28 #include "extensions/browser/management_policy.h" |
| 29 #include "extensions/browser/updater/extension_downloader.h" |
| 30 #include "extensions/browser/updater/extension_downloader_test_delegate.h" |
| 31 #include "extensions/browser/updater/manifest_fetch_data.h" |
| 32 #include "extensions/common/extension_urls.h" |
19 | 33 |
20 namespace extensions { | 34 namespace extensions { |
21 | 35 |
22 namespace { | 36 namespace { |
23 | 37 |
24 // Helper for observing extension unloads. | 38 // Helper for observing extension registry events. |
25 class UnloadObserver : public ExtensionRegistryObserver { | 39 class RegistryObserver : public ExtensionRegistryObserver { |
26 public: | 40 public: |
27 explicit UnloadObserver(ExtensionRegistry* registry) : observer_(this) { | 41 explicit RegistryObserver(ExtensionRegistry* registry) : observer_(this) { |
28 observer_.Add(registry); | 42 observer_.Add(registry); |
29 } | 43 } |
30 ~UnloadObserver() override {} | 44 ~RegistryObserver() override {} |
31 | 45 |
32 void WaitForUnload(const ExtensionId& id) { | 46 // Waits until we've seen an unload for extension with |id|, returning true |
33 if (base::ContainsKey(observed_, id)) | 47 // if we saw one or false otherwise (typically because of test timeout). |
34 return; | 48 bool WaitForUnload(const ExtensionId& id) { |
| 49 if (base::ContainsKey(unloaded_, id)) |
| 50 return true; |
35 | 51 |
36 ASSERT_TRUE(loop_runner_.get() == NULL); | 52 base::RunLoop run_loop; |
37 awaited_id_ = id; | 53 awaited_unload_id_ = id; |
38 loop_runner_ = new content::MessageLoopRunner(); | 54 quit_closure_ = run_loop.QuitClosure(); |
39 loop_runner_->Run(); | 55 run_loop.Run(); |
| 56 return base::ContainsKey(unloaded_, id); |
40 } | 57 } |
41 | 58 |
| 59 // Same as WaitForUnload, but for an install. |
| 60 bool WaitForInstall(const ExtensionId& id) { |
| 61 if (base::ContainsKey(installed_, id)) |
| 62 return true; |
| 63 |
| 64 base::RunLoop run_loop; |
| 65 awaited_install_id_ = id; |
| 66 quit_closure_ = run_loop.QuitClosure(); |
| 67 run_loop.Run(); |
| 68 return base::ContainsKey(installed_, id); |
| 69 } |
| 70 |
| 71 // ExtensionRegistryObserver |
42 void OnExtensionUnloaded(content::BrowserContext* browser_context, | 72 void OnExtensionUnloaded(content::BrowserContext* browser_context, |
43 const Extension* extension, | 73 const Extension* extension, |
44 UnloadedExtensionInfo::Reason reason) override { | 74 UnloadedExtensionInfo::Reason reason) override { |
45 observed_.insert(extension->id()); | 75 unloaded_.insert(extension->id()); |
46 if (awaited_id_ == extension->id()) | 76 if (awaited_unload_id_ == extension->id()) { |
47 loop_runner_->Quit(); | 77 awaited_unload_id_.clear(); |
| 78 base::ResetAndReturn(&quit_closure_).Run(); |
| 79 } |
| 80 } |
| 81 |
| 82 void OnExtensionInstalled(content::BrowserContext* browser_context, |
| 83 const Extension* extension, |
| 84 bool is_update) override { |
| 85 installed_.insert(extension->id()); |
| 86 if (awaited_install_id_ == extension->id()) { |
| 87 awaited_install_id_.clear(); |
| 88 base::ResetAndReturn(&quit_closure_).Run(); |
| 89 } |
48 } | 90 } |
49 | 91 |
50 private: | 92 private: |
51 ExtensionId awaited_id_; | 93 // The id we're waiting for a load/install of respectively. |
52 std::set<ExtensionId> observed_; | 94 ExtensionId awaited_unload_id_; |
53 scoped_refptr<content::MessageLoopRunner> loop_runner_; | 95 ExtensionId awaited_install_id_; |
54 ScopedObserver<ExtensionRegistry, UnloadObserver> observer_; | 96 |
| 97 // The quit closure for stopping a running RunLoop, if we're waiting. |
| 98 base::Closure quit_closure_; |
| 99 |
| 100 // The extension id's we've seen unloaded and installed, respectively. |
| 101 std::set<ExtensionId> unloaded_; |
| 102 std::set<ExtensionId> installed_; |
| 103 |
| 104 ScopedObserver<ExtensionRegistry, RegistryObserver> observer_; |
| 105 |
| 106 DISALLOW_COPY_AND_ASSIGN(RegistryObserver); |
55 }; | 107 }; |
56 | 108 |
57 // Helper for forcing ContentVerifyJob's to return an error. | 109 // Helper for forcing ContentVerifyJob's to return an error. |
58 class JobDelegate : public ContentVerifyJob::TestDelegate { | 110 class JobDelegate : public ContentVerifyJob::TestDelegate { |
59 public: | 111 public: |
60 JobDelegate() | 112 JobDelegate() |
61 : fail_next_read_(false), | 113 : fail_next_read_(false), |
62 fail_next_done_(false), | 114 fail_next_done_(false), |
63 bytes_read_failed_(0), | 115 bytes_read_failed_(0), |
64 done_reading_failed_(0) {} | 116 done_reading_failed_(0) {} |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 loop_runner_ = nullptr; | 295 loop_runner_ = nullptr; |
244 } | 296 } |
245 | 297 |
246 void VerifierObserver::OnFetchComplete(const std::string& extension_id, | 298 void VerifierObserver::OnFetchComplete(const std::string& extension_id, |
247 bool success) { | 299 bool success) { |
248 completed_fetches_.insert(extension_id); | 300 completed_fetches_.insert(extension_id); |
249 if (extension_id == id_to_wait_for_) | 301 if (extension_id == id_to_wait_for_) |
250 loop_runner_->Quit(); | 302 loop_runner_->Quit(); |
251 } | 303 } |
252 | 304 |
| 305 // This lets us intercept requests for update checks of extensions, and |
| 306 // substitute a local file as a simulated response. |
| 307 class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate { |
| 308 public: |
| 309 DownloaderTestDelegate() {} |
| 310 |
| 311 // This makes it so that update check requests for |extension_id| will return |
| 312 // a downloaded file of |crx_path| that is claimed to have version |
| 313 // |version_string|. |
| 314 void AddResponse(const ExtensionId& extension_id, |
| 315 const std::string& version_string, |
| 316 const base::FilePath& crx_path) { |
| 317 responses_[extension_id] = std::make_pair(version_string, crx_path); |
| 318 } |
| 319 |
| 320 const std::vector<std::unique_ptr<ManifestFetchData>>& requests() { |
| 321 return requests_; |
| 322 } |
| 323 |
| 324 // ExtensionDownloaderTestDelegate: |
| 325 void StartUpdateCheck( |
| 326 ExtensionDownloader* downloader, |
| 327 ExtensionDownloaderDelegate* delegate, |
| 328 std::unique_ptr<ManifestFetchData> fetch_data) override { |
| 329 requests_.push_back(std::move(fetch_data)); |
| 330 const ManifestFetchData* data = requests_.back().get(); |
| 331 |
| 332 for (const auto& id : data->extension_ids()) { |
| 333 if (ContainsKey(responses_, id)) { |
| 334 // We use PostTask here instead of calling OnExtensionDownloadFinished |
| 335 // immeditately, because the calling code isn't expecting a synchronous |
| 336 // response (in non-test situations there are at least 2 network |
| 337 // requests needed before a file could be returned). |
| 338 base::ThreadTaskRunnerHandle::Get()->PostTask( |
| 339 FROM_HERE, |
| 340 base::Bind( |
| 341 &ExtensionDownloaderDelegate::OnExtensionDownloadFinished, |
| 342 base::Unretained(delegate), |
| 343 CRXFileInfo(id, responses_[id].second), |
| 344 false /* pass_file_ownership */, GURL(), responses_[id].first, |
| 345 ExtensionDownloaderDelegate::PingResult(), data->request_ids(), |
| 346 ExtensionDownloaderDelegate::InstallCallback())); |
| 347 } |
| 348 } |
| 349 } |
| 350 |
| 351 private: |
| 352 // The requests we've received. |
| 353 std::vector<std::unique_ptr<ManifestFetchData>> requests_; |
| 354 |
| 355 // The prepared responses - this maps an extension id to a (version string, |
| 356 // crx file path) pair. |
| 357 std::map<std::string, std::pair<ExtensionId, base::FilePath>> responses_; |
| 358 |
| 359 DISALLOW_COPY_AND_ASSIGN(DownloaderTestDelegate); |
| 360 }; |
| 361 |
| 362 // This lets us simulate the behavior of an enterprise policy that wants |
| 363 // a given extension to be installed via the webstore. |
| 364 class TestExternalProvider : public ExternalProviderInterface { |
| 365 public: |
| 366 TestExternalProvider(VisitorInterface* visitor, |
| 367 const ExtensionId& extension_id) |
| 368 : visitor_(visitor), extension_id_(extension_id) {} |
| 369 |
| 370 ~TestExternalProvider() override {} |
| 371 |
| 372 // ExternalProviderInterface: |
| 373 void ServiceShutdown() override {} |
| 374 |
| 375 void VisitRegisteredExtension() override { |
| 376 visitor_->OnExternalExtensionUpdateUrlFound( |
| 377 ExternalInstallInfoUpdateUrl( |
| 378 extension_id_, std::string() /* install_parameter */, |
| 379 base::MakeUnique<GURL>(extension_urls::GetWebstoreUpdateUrl()), |
| 380 Manifest::EXTERNAL_POLICY_DOWNLOAD, 0 /* creation_flags */, |
| 381 true /* mark_acknowledged */), |
| 382 true /* is_initial_load */); |
| 383 visitor_->OnExternalProviderReady(this); |
| 384 } |
| 385 |
| 386 bool HasExtension(const ExtensionId& id) const override { |
| 387 return id == std::string("npnbmohejbjohgpjnmjagbafnjhkmgko"); |
| 388 } |
| 389 |
| 390 bool GetExtensionDetails( |
| 391 const ExtensionId& id, |
| 392 Manifest::Location* location, |
| 393 std::unique_ptr<base::Version>* version) const override { |
| 394 ADD_FAILURE() << "Unexpected GetExtensionDetails call; id:" << id; |
| 395 return false; |
| 396 } |
| 397 |
| 398 bool IsReady() const override { return true; } |
| 399 |
| 400 private: |
| 401 VisitorInterface* visitor_; |
| 402 ExtensionId extension_id_; |
| 403 base::Closure quit_closure_; |
| 404 |
| 405 DISALLOW_COPY_AND_ASSIGN(TestExternalProvider); |
| 406 }; |
| 407 |
| 408 // This lets us simulate a policy-installed extension being "force" installed; |
| 409 // ie a user is not allowed to manually uninstall/disable it. |
| 410 class ForceInstallProvider : public ManagementPolicy::Provider { |
| 411 public: |
| 412 explicit ForceInstallProvider(const ExtensionId& id) : id_(id) {} |
| 413 ~ForceInstallProvider() override {} |
| 414 |
| 415 std::string GetDebugPolicyProviderName() const override { |
| 416 return "ForceInstallProvider"; |
| 417 } |
| 418 |
| 419 // MananagementPolicy::Provider: |
| 420 bool UserMayModifySettings(const Extension* extension, |
| 421 base::string16* error) const override { |
| 422 return extension->id() != id_; |
| 423 } |
| 424 |
| 425 private: |
| 426 // The extension id we want to disallow uninstall/disable for. |
| 427 ExtensionId id_; |
| 428 |
| 429 DISALLOW_COPY_AND_ASSIGN(ForceInstallProvider); |
| 430 }; |
| 431 |
253 } // namespace | 432 } // namespace |
254 | 433 |
255 class ContentVerifierTest : public ExtensionBrowserTest { | 434 class ContentVerifierTest : public ExtensionBrowserTest { |
256 public: | 435 public: |
257 ContentVerifierTest() {} | 436 ContentVerifierTest() {} |
258 ~ContentVerifierTest() override {} | 437 ~ContentVerifierTest() override {} |
259 | 438 |
260 void SetUpCommandLine(base::CommandLine* command_line) override { | 439 void SetUpCommandLine(base::CommandLine* command_line) override { |
261 ExtensionBrowserTest::SetUpCommandLine(command_line); | 440 ExtensionBrowserTest::SetUpCommandLine(command_line); |
262 command_line->AppendSwitchASCII( | 441 command_line->AppendSwitchASCII( |
(...skipping 10 matching lines...) Expand all Loading... |
273 ContentVerifyJob::SetDelegateForTests(NULL); | 452 ContentVerifyJob::SetDelegateForTests(NULL); |
274 ContentVerifyJob::SetObserverForTests(NULL); | 453 ContentVerifyJob::SetObserverForTests(NULL); |
275 ExtensionBrowserTest::TearDownOnMainThread(); | 454 ExtensionBrowserTest::TearDownOnMainThread(); |
276 } | 455 } |
277 | 456 |
278 virtual void OpenPageAndWaitForUnload() { | 457 virtual void OpenPageAndWaitForUnload() { |
279 ContentVerifyJob::SetDelegateForTests(&delegate_); | 458 ContentVerifyJob::SetDelegateForTests(&delegate_); |
280 std::string id = "npnbmohejbjohgpjnmjagbafnjhkmgko"; | 459 std::string id = "npnbmohejbjohgpjnmjagbafnjhkmgko"; |
281 delegate_.set_id(id); | 460 delegate_.set_id(id); |
282 unload_observer_.reset( | 461 unload_observer_.reset( |
283 new UnloadObserver(ExtensionRegistry::Get(profile()))); | 462 new RegistryObserver(ExtensionRegistry::Get(profile()))); |
284 const Extension* extension = InstallExtensionFromWebstore( | 463 const Extension* extension = InstallExtensionFromWebstore( |
285 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); | 464 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); |
286 ASSERT_TRUE(extension); | 465 ASSERT_TRUE(extension); |
287 ASSERT_EQ(id, extension->id()); | 466 ASSERT_EQ(id, extension->id()); |
288 page_url_ = extension->GetResourceURL("page.html"); | 467 page_url_ = extension->GetResourceURL("page.html"); |
289 | 468 |
290 // This call passes false for |check_navigation_success|, because checking | 469 // This call passes false for |check_navigation_success|, because checking |
291 // for navigation success needs the WebContents to still exist after the | 470 // for navigation success needs the WebContents to still exist after the |
292 // navigation, whereas this navigation triggers an unload which destroys | 471 // navigation, whereas this navigation triggers an unload which destroys |
293 // the WebContents. | 472 // the WebContents. |
294 AddTabAtIndexToBrowser(browser(), 1, page_url_, ui::PAGE_TRANSITION_LINK, | 473 AddTabAtIndexToBrowser(browser(), 1, page_url_, ui::PAGE_TRANSITION_LINK, |
295 false); | 474 false); |
296 | 475 |
297 unload_observer_->WaitForUnload(id); | 476 EXPECT_TRUE(unload_observer_->WaitForUnload(id)); |
298 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); | 477 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
299 int reasons = prefs->GetDisableReasons(id); | 478 int reasons = prefs->GetDisableReasons(id); |
300 EXPECT_TRUE(reasons & Extension::DISABLE_CORRUPTED); | 479 EXPECT_TRUE(reasons & Extension::DISABLE_CORRUPTED); |
301 | 480 |
302 // This needs to happen before the ExtensionRegistry gets deleted, which | 481 // This needs to happen before the ExtensionRegistry gets deleted, which |
303 // happens before TearDownOnMainThread is called. | 482 // happens before TearDownOnMainThread is called. |
304 unload_observer_.reset(); | 483 unload_observer_.reset(); |
305 } | 484 } |
306 | 485 |
307 protected: | 486 protected: |
308 JobDelegate delegate_; | 487 JobDelegate delegate_; |
309 std::unique_ptr<UnloadObserver> unload_observer_; | 488 std::unique_ptr<RegistryObserver> unload_observer_; |
310 GURL page_url_; | 489 GURL page_url_; |
311 }; | 490 }; |
312 | 491 |
313 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) { | 492 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) { |
314 EXPECT_EQ(0, delegate_.bytes_read_failed()); | 493 EXPECT_EQ(0, delegate_.bytes_read_failed()); |
315 delegate_.fail_next_read(); | 494 delegate_.fail_next_read(); |
316 OpenPageAndWaitForUnload(); | 495 OpenPageAndWaitForUnload(); |
317 EXPECT_EQ(1, delegate_.bytes_read_failed()); | 496 EXPECT_EQ(1, delegate_.bytes_read_failed()); |
318 } | 497 } |
319 | 498 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
416 DisableExtension(id); | 595 DisableExtension(id); |
417 job_observer.ExpectJobResult(id, | 596 job_observer.ExpectJobResult(id, |
418 base::FilePath(FILE_PATH_LITERAL("script.js")), | 597 base::FilePath(FILE_PATH_LITERAL("script.js")), |
419 JobObserver::Result::FAILURE); | 598 JobObserver::Result::FAILURE); |
420 EnableExtension(id); | 599 EnableExtension(id); |
421 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); | 600 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); |
422 | 601 |
423 ContentVerifyJob::SetObserverForTests(NULL); | 602 ContentVerifyJob::SetObserverForTests(NULL); |
424 } | 603 } |
425 | 604 |
| 605 // Tests the case of a corrupt extension that is force-installed by policy and |
| 606 // should not be allowed to be manually uninstalled/disabled by the user. |
| 607 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, PolicyCorrupted) { |
| 608 ExtensionSystem* system = ExtensionSystem::Get(profile()); |
| 609 ExtensionService* service = system->extension_service(); |
| 610 |
| 611 // The id of our test extension. |
| 612 std::string id("npnbmohejbjohgpjnmjagbafnjhkmgko"); |
| 613 |
| 614 // Setup fake policy and update check objects. |
| 615 ForceInstallProvider policy(id); |
| 616 DownloaderTestDelegate downloader; |
| 617 system->management_policy()->RegisterProvider(&policy); |
| 618 ExtensionDownloader::set_test_delegate(&downloader); |
| 619 service->AddProviderForTesting( |
| 620 base::MakeUnique<TestExternalProvider>(service, id)); |
| 621 |
| 622 base::FilePath crx_path = |
| 623 test_data_dir_.AppendASCII("content_verifier/v1.crx"); |
| 624 const Extension* extension = |
| 625 InstallExtension(crx_path, 1, Manifest::EXTERNAL_POLICY_DOWNLOAD); |
| 626 EXPECT_NE(extension, nullptr); |
| 627 |
| 628 downloader.AddResponse(id, extension->VersionString(), crx_path); |
| 629 |
| 630 RegistryObserver registry_observer(ExtensionRegistry::Get(profile())); |
| 631 ContentVerifier* verifier = system->content_verifier(); |
| 632 verifier->VerifyFailed(extension->id(), ContentVerifyJob::HASH_MISMATCH); |
| 633 |
| 634 // Make sure the extension first got disabled due to corruption. |
| 635 EXPECT_TRUE(registry_observer.WaitForUnload(id)); |
| 636 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); |
| 637 int reasons = prefs->GetDisableReasons(id); |
| 638 EXPECT_TRUE(reasons & Extension::DISABLE_CORRUPTED); |
| 639 |
| 640 // Make sure the extension then got re-installed, and that after reinstall it |
| 641 // is no longer disabled due to corruption. |
| 642 EXPECT_TRUE(registry_observer.WaitForInstall(id)); |
| 643 reasons = prefs->GetDisableReasons(id); |
| 644 EXPECT_FALSE(reasons & Extension::DISABLE_CORRUPTED); |
| 645 |
| 646 // Make sure that the update check request properly included a parameter |
| 647 // indicating that this was a corrupt policy reinstall. |
| 648 bool found = false; |
| 649 for (const auto& request : downloader.requests()) { |
| 650 if (request->Includes(id)) { |
| 651 std::string query = request->full_url().query(); |
| 652 for (const auto& part : base::SplitString( |
| 653 query, "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) { |
| 654 if (base::StartsWith(part, "x=", base::CompareCase::SENSITIVE) && |
| 655 part.find(std::string("id%3D") + id) != std::string::npos) { |
| 656 found = true; |
| 657 EXPECT_NE(std::string::npos, part.find("installsource%3Dreinstall")); |
| 658 } |
| 659 } |
| 660 } |
| 661 } |
| 662 EXPECT_TRUE(found); |
| 663 } |
| 664 |
426 } // namespace extensions | 665 } // namespace extensions |
OLD | NEW |