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

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

Issue 2299203004: Attempt to repair corrupt enterprise policy force-installed extensions (Closed)
Patch Set: fix chromeos compile problem Created 4 years, 3 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
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> 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
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 std::string& extension_id,
lazyboy 2016/09/02 19:21:53 ExtensionId instead of std::string for extension i
asargent_no_longer_on_chrome 2016/09/09 03:30:43 Done.
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<std::string, 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 std::string& 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 std::string& id) const override {
387 return id == std::string("npnbmohejbjohgpjnmjagbafnjhkmgko");
388 }
389
390 bool GetExtensionDetails(
391 const std::string& 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 std::string 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 std::string& 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 std::string 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
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
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 // Test the case of a corrupt extension that is force-installed by policy and
lazyboy 2016/09/02 19:21:52 nit: Tests the ...
asargent_no_longer_on_chrome 2016/09/09 03:30:43 Done.
606 // should not be allowed to be manually uninstalled/disabled by the user.
607 IN_PROC_BROWSER_TEST_F(ContentVerifierTest, PolicyCorrupted) {
lazyboy 2016/09/02 19:21:53 Nice and thorough test!
asargent_no_longer_on_chrome 2016/09/09 03:30:43 thanks!
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 (part.find("x=") == 0 &&
lazyboy 2016/09/02 19:21:53 base::StartWith(part, "x=")
asargent_no_longer_on_chrome 2016/09/09 03:30:43 Done.
655 part.find(std::string("id%3D") + id) != std::string::npos) {
656 found = true;
657 EXPECT_NE(std::string::npos, part.find("%26cpr"));
658 }
659 }
660 }
661 }
662 EXPECT_TRUE(found);
663 }
664
426 } // namespace extensions 665 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698