blob: 256abe89b0c2ad0e35808ded61d224af2472b484 [file] [log] [blame]
[email protected]fd3df7782014-05-08 23:54:271// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
asargent678123d22015-07-31 23:24:105#include <list>
6#include <set>
7#include <string>
8
asargent6014bdf2016-11-30 21:50:169#include "base/bind_helpers.h"
asargent56282ab72016-09-09 16:58:0310#include "base/callback_helpers.h"
avia2f4804a2015-12-24 23:11:1311#include "base/macros.h"
asargent56282ab72016-09-09 16:58:0312#include "base/memory/ptr_util.h"
13#include "base/run_loop.h"
[email protected]fd3df7782014-05-08 23:54:2714#include "base/scoped_observer.h"
asargent56282ab72016-09-09 16:58:0315#include "base/strings/string_split.h"
jam3f2d3932017-04-26 20:28:5116#include "base/threading/thread_restrictions.h"
asargent56282ab72016-09-09 16:58:0317#include "base/threading/thread_task_runner_handle.h"
asargent0de8a8bc2016-11-22 22:58:0218#include "chrome/browser/extensions/browsertest_util.h"
asargent6014bdf2016-11-30 21:50:1619#include "chrome/browser/extensions/chrome_content_verifier_delegate.h"
[email protected]fd3df7782014-05-08 23:54:2720#include "chrome/browser/extensions/extension_browsertest.h"
asargent0de8a8bc2016-11-22 22:58:0221#include "chrome/browser/extensions/extension_management_test_util.h"
asargent56282ab72016-09-09 16:58:0322#include "chrome/browser/extensions/extension_service.h"
lazyboy77214d3c2017-04-04 16:46:1223#include "chrome/browser/extensions/policy_extension_reinstaller.h"
[email protected]8bb62162014-06-23 09:45:5024#include "chrome/common/chrome_switches.h"
jamc958dad12017-05-23 17:05:4425#include "chrome/test/base/ui_test_utils.h"
asargent0de8a8bc2016-11-22 22:58:0226#include "components/policy/core/browser/browser_policy_connector.h"
27#include "components/policy/core/common/mock_configuration_policy_provider.h"
Justin Donnelly5ce949f2017-05-12 22:03:3828#include "content/public/common/browser_side_navigation_policy.h"
[email protected]fd3df7782014-05-08 23:54:2729#include "content/public/test/test_utils.h"
asargent678123d22015-07-31 23:24:1030#include "extensions/browser/content_verifier.h"
[email protected]fd3df7782014-05-08 23:54:2731#include "extensions/browser/content_verify_job.h"
asargent56282ab72016-09-09 16:58:0332#include "extensions/browser/crx_file_info.h"
[email protected]fd3df7782014-05-08 23:54:2733#include "extensions/browser/extension_prefs.h"
34#include "extensions/browser/extension_registry.h"
35#include "extensions/browser/extension_registry_observer.h"
asargent56282ab72016-09-09 16:58:0336#include "extensions/browser/external_install_info.h"
37#include "extensions/browser/external_provider_interface.h"
38#include "extensions/browser/management_policy.h"
39#include "extensions/browser/updater/extension_downloader.h"
40#include "extensions/browser/updater/extension_downloader_test_delegate.h"
41#include "extensions/browser/updater/manifest_fetch_data.h"
42#include "extensions/common/extension_urls.h"
[email protected]fd3df7782014-05-08 23:54:2743
[email protected]fd3df7782014-05-08 23:54:2744namespace extensions {
45
46namespace {
47
asargent56282ab72016-09-09 16:58:0348// Helper for observing extension registry events.
49class RegistryObserver : public ExtensionRegistryObserver {
[email protected]fd3df7782014-05-08 23:54:2750 public:
asargent56282ab72016-09-09 16:58:0351 explicit RegistryObserver(ExtensionRegistry* registry) : observer_(this) {
[email protected]fd3df7782014-05-08 23:54:2752 observer_.Add(registry);
53 }
asargent56282ab72016-09-09 16:58:0354 ~RegistryObserver() override {}
[email protected]fd3df7782014-05-08 23:54:2755
asargent56282ab72016-09-09 16:58:0356 // Waits until we've seen an unload for extension with |id|, returning true
57 // if we saw one or false otherwise (typically because of test timeout).
58 bool WaitForUnload(const ExtensionId& id) {
59 if (base::ContainsKey(unloaded_, id))
60 return true;
[email protected]fd3df7782014-05-08 23:54:2761
asargent56282ab72016-09-09 16:58:0362 base::RunLoop run_loop;
63 awaited_unload_id_ = id;
64 quit_closure_ = run_loop.QuitClosure();
65 run_loop.Run();
66 return base::ContainsKey(unloaded_, id);
[email protected]fd3df7782014-05-08 23:54:2767 }
68
asargent56282ab72016-09-09 16:58:0369 // Same as WaitForUnload, but for an install.
70 bool WaitForInstall(const ExtensionId& id) {
71 if (base::ContainsKey(installed_, id))
72 return true;
73
74 base::RunLoop run_loop;
75 awaited_install_id_ = id;
76 quit_closure_ = run_loop.QuitClosure();
77 run_loop.Run();
78 return base::ContainsKey(installed_, id);
79 }
80
81 // ExtensionRegistryObserver
dchengae36a4a2014-10-21 12:36:3682 void OnExtensionUnloaded(content::BrowserContext* browser_context,
83 const Extension* extension,
limasdf0deef2042017-05-03 19:17:1784 UnloadedExtensionReason reason) override {
asargent56282ab72016-09-09 16:58:0385 unloaded_.insert(extension->id());
86 if (awaited_unload_id_ == extension->id()) {
87 awaited_unload_id_.clear();
88 base::ResetAndReturn(&quit_closure_).Run();
89 }
90 }
91
92 void OnExtensionInstalled(content::BrowserContext* browser_context,
93 const Extension* extension,
94 bool is_update) override {
95 installed_.insert(extension->id());
96 if (awaited_install_id_ == extension->id()) {
97 awaited_install_id_.clear();
98 base::ResetAndReturn(&quit_closure_).Run();
99 }
[email protected]fd3df7782014-05-08 23:54:27100 }
101
102 private:
asargent56282ab72016-09-09 16:58:03103 // The id we're waiting for a load/install of respectively.
104 ExtensionId awaited_unload_id_;
105 ExtensionId awaited_install_id_;
106
107 // The quit closure for stopping a running RunLoop, if we're waiting.
108 base::Closure quit_closure_;
109
110 // The extension id's we've seen unloaded and installed, respectively.
111 std::set<ExtensionId> unloaded_;
112 std::set<ExtensionId> installed_;
113
114 ScopedObserver<ExtensionRegistry, RegistryObserver> observer_;
115
116 DISALLOW_COPY_AND_ASSIGN(RegistryObserver);
[email protected]fd3df7782014-05-08 23:54:27117};
118
119// Helper for forcing ContentVerifyJob's to return an error.
120class JobDelegate : public ContentVerifyJob::TestDelegate {
121 public:
asargentb8af8152015-11-02 17:56:37122 JobDelegate()
123 : fail_next_read_(false),
124 fail_next_done_(false),
125 bytes_read_failed_(0),
126 done_reading_failed_(0) {}
[email protected]fd3df7782014-05-08 23:54:27127
lazyboyd6dbb262017-03-30 00:57:30128 ~JobDelegate() override {}
[email protected]fd3df7782014-05-08 23:54:27129
130 void set_id(const ExtensionId& id) { id_ = id; }
131 void fail_next_read() { fail_next_read_ = true; }
132 void fail_next_done() { fail_next_done_ = true; }
133
asargentb8af8152015-11-02 17:56:37134 // Return the number of BytesRead/DoneReading calls we actually failed,
135 // respectively.
136 int bytes_read_failed() { return bytes_read_failed_; }
137 int done_reading_failed() { return done_reading_failed_; }
138
dchengae36a4a2014-10-21 12:36:36139 ContentVerifyJob::FailureReason BytesRead(const ExtensionId& id,
140 int count,
141 const char* data) override {
[email protected]fd3df7782014-05-08 23:54:27142 if (id == id_ && fail_next_read_) {
143 fail_next_read_ = false;
asargentb8af8152015-11-02 17:56:37144 bytes_read_failed_++;
[email protected]fd3df7782014-05-08 23:54:27145 return ContentVerifyJob::HASH_MISMATCH;
146 }
147 return ContentVerifyJob::NONE;
148 }
149
dchengae36a4a2014-10-21 12:36:36150 ContentVerifyJob::FailureReason DoneReading(const ExtensionId& id) override {
[email protected]fd3df7782014-05-08 23:54:27151 if (id == id_ && fail_next_done_) {
152 fail_next_done_ = false;
asargentb8af8152015-11-02 17:56:37153 done_reading_failed_++;
[email protected]fd3df7782014-05-08 23:54:27154 return ContentVerifyJob::HASH_MISMATCH;
155 }
156 return ContentVerifyJob::NONE;
157 }
158
159 private:
[email protected]fd3df7782014-05-08 23:54:27160 ExtensionId id_;
161 bool fail_next_read_;
162 bool fail_next_done_;
asargentb8af8152015-11-02 17:56:37163 int bytes_read_failed_;
164 int done_reading_failed_;
165
166 DISALLOW_COPY_AND_ASSIGN(JobDelegate);
[email protected]fd3df7782014-05-08 23:54:27167};
168
asargent7cc29ce32014-10-09 21:27:57169class JobObserver : public ContentVerifyJob::TestObserver {
170 public:
171 JobObserver();
172 virtual ~JobObserver();
173
asargent678123d22015-07-31 23:24:10174 enum class Result { SUCCESS, FAILURE };
175
asargent7cc29ce32014-10-09 21:27:57176 // Call this to add an expected job result.
177 void ExpectJobResult(const std::string& extension_id,
178 const base::FilePath& relative_path,
asargent678123d22015-07-31 23:24:10179 Result expected_result);
asargent7cc29ce32014-10-09 21:27:57180
asargent678123d22015-07-31 23:24:10181 // Wait to see expected jobs. Returns true when we've seen all expected jobs
182 // finish, or false if there was an error or timeout.
asargent7cc29ce32014-10-09 21:27:57183 bool WaitForExpectedJobs();
184
185 // ContentVerifyJob::TestObserver interface
dchengae36a4a2014-10-21 12:36:36186 void JobStarted(const std::string& extension_id,
187 const base::FilePath& relative_path) override;
asargent7cc29ce32014-10-09 21:27:57188
dchengae36a4a2014-10-21 12:36:36189 void JobFinished(const std::string& extension_id,
190 const base::FilePath& relative_path,
lazyboyd6dbb262017-03-30 00:57:30191 ContentVerifyJob::FailureReason failure_reason) override;
asargent7cc29ce32014-10-09 21:27:57192
193 private:
asargent678123d22015-07-31 23:24:10194 struct ExpectedResult {
195 public:
196 std::string extension_id;
197 base::FilePath path;
198 Result result;
199
200 ExpectedResult(const std::string& extension_id, const base::FilePath& path,
201 Result result) {
202 this->extension_id = extension_id;
203 this->path = path;
204 this->result = result;
205 }
206 };
207 std::list<ExpectedResult> expectations_;
208 content::BrowserThread::ID creation_thread_;
asargent7cc29ce32014-10-09 21:27:57209 scoped_refptr<content::MessageLoopRunner> loop_runner_;
asargent7cc29ce32014-10-09 21:27:57210};
211
212void JobObserver::ExpectJobResult(const std::string& extension_id,
213 const base::FilePath& relative_path,
asargent678123d22015-07-31 23:24:10214 Result expected_result) {
215 expectations_.push_back(ExpectedResult(
216 extension_id, relative_path, expected_result));
asargent7cc29ce32014-10-09 21:27:57217}
218
asargent678123d22015-07-31 23:24:10219JobObserver::JobObserver() {
220 EXPECT_TRUE(
221 content::BrowserThread::GetCurrentThreadIdentifier(&creation_thread_));
lazyboyb4c713072017-03-31 23:58:14222 ContentVerifyJob::SetObserverForTests(this);
asargent7cc29ce32014-10-09 21:27:57223}
224
225JobObserver::~JobObserver() {
lazyboyb4c713072017-03-31 23:58:14226 ContentVerifyJob::SetObserverForTests(nullptr);
asargent7cc29ce32014-10-09 21:27:57227}
228
229bool JobObserver::WaitForExpectedJobs() {
asargent678123d22015-07-31 23:24:10230 EXPECT_TRUE(content::BrowserThread::CurrentlyOn(creation_thread_));
231 if (!expectations_.empty()) {
asargent7cc29ce32014-10-09 21:27:57232 loop_runner_ = new content::MessageLoopRunner();
233 loop_runner_->Run();
asargent678123d22015-07-31 23:24:10234 loop_runner_ = nullptr;
asargent7cc29ce32014-10-09 21:27:57235 }
asargent678123d22015-07-31 23:24:10236 return expectations_.empty();
asargent7cc29ce32014-10-09 21:27:57237}
238
239void JobObserver::JobStarted(const std::string& extension_id,
240 const base::FilePath& relative_path) {
241}
242
243void JobObserver::JobFinished(const std::string& extension_id,
244 const base::FilePath& relative_path,
lazyboyd6dbb262017-03-30 00:57:30245 ContentVerifyJob::FailureReason failure_reason) {
asargent678123d22015-07-31 23:24:10246 if (!content::BrowserThread::CurrentlyOn(creation_thread_)) {
247 content::BrowserThread::PostTask(
248 creation_thread_, FROM_HERE,
tzik8d880ee2017-04-20 19:46:24249 base::BindOnce(&JobObserver::JobFinished, base::Unretained(this),
250 extension_id, relative_path, failure_reason));
asargent678123d22015-07-31 23:24:10251 return;
252 }
lazyboyd6dbb262017-03-30 00:57:30253 Result result = failure_reason == ContentVerifyJob::NONE ? Result::SUCCESS
254 : Result::FAILURE;
asargent678123d22015-07-31 23:24:10255 bool found = false;
256 for (std::list<ExpectedResult>::iterator i = expectations_.begin();
257 i != expectations_.end(); ++i) {
258 if (i->extension_id == extension_id && i->path == relative_path &&
259 i->result == result) {
260 found = true;
261 expectations_.erase(i);
262 break;
asargent7cc29ce32014-10-09 21:27:57263 }
264 }
asargent678123d22015-07-31 23:24:10265 if (found) {
266 if (expectations_.empty() && loop_runner_.get())
267 loop_runner_->Quit();
268 } else {
269 LOG(WARNING) << "Ignoring unexpected JobFinished " << extension_id << "/"
lazyboyd6dbb262017-03-30 00:57:30270 << relative_path.value()
271 << " failure_reason:" << failure_reason;
asargent678123d22015-07-31 23:24:10272 }
273}
274
275class VerifierObserver : public ContentVerifier::TestObserver {
276 public:
277 VerifierObserver();
278 virtual ~VerifierObserver();
279
280 const std::set<std::string>& completed_fetches() {
281 return completed_fetches_;
282 }
283
284 // Returns when we've seen OnFetchComplete for |extension_id|.
285 void WaitForFetchComplete(const std::string& extension_id);
286
287 // ContentVerifier::TestObserver
288 void OnFetchComplete(const std::string& extension_id, bool success) override;
289
290 private:
291 std::set<std::string> completed_fetches_;
292 std::string id_to_wait_for_;
293 scoped_refptr<content::MessageLoopRunner> loop_runner_;
294};
295
296VerifierObserver::VerifierObserver() {
lazyboyb4c713072017-03-31 23:58:14297 ContentVerifier::SetObserverForTests(this);
asargent678123d22015-07-31 23:24:10298}
299
300VerifierObserver::~VerifierObserver() {
lazyboyb4c713072017-03-31 23:58:14301 ContentVerifier::SetObserverForTests(nullptr);
asargent678123d22015-07-31 23:24:10302}
303
304void VerifierObserver::WaitForFetchComplete(const std::string& extension_id) {
305 EXPECT_TRUE(id_to_wait_for_.empty());
306 EXPECT_EQ(loop_runner_.get(), nullptr);
307 id_to_wait_for_ = extension_id;
308 loop_runner_ = new content::MessageLoopRunner();
309 loop_runner_->Run();
310 id_to_wait_for_.clear();
311 loop_runner_ = nullptr;
312}
313
314void VerifierObserver::OnFetchComplete(const std::string& extension_id,
315 bool success) {
316 completed_fetches_.insert(extension_id);
317 if (extension_id == id_to_wait_for_)
318 loop_runner_->Quit();
asargent7cc29ce32014-10-09 21:27:57319}
320
asargent56282ab72016-09-09 16:58:03321// This lets us intercept requests for update checks of extensions, and
322// substitute a local file as a simulated response.
323class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate {
324 public:
325 DownloaderTestDelegate() {}
326
327 // This makes it so that update check requests for |extension_id| will return
328 // a downloaded file of |crx_path| that is claimed to have version
329 // |version_string|.
330 void AddResponse(const ExtensionId& extension_id,
331 const std::string& version_string,
332 const base::FilePath& crx_path) {
333 responses_[extension_id] = std::make_pair(version_string, crx_path);
334 }
335
336 const std::vector<std::unique_ptr<ManifestFetchData>>& requests() {
337 return requests_;
338 }
339
340 // ExtensionDownloaderTestDelegate:
341 void StartUpdateCheck(
342 ExtensionDownloader* downloader,
343 ExtensionDownloaderDelegate* delegate,
344 std::unique_ptr<ManifestFetchData> fetch_data) override {
345 requests_.push_back(std::move(fetch_data));
346 const ManifestFetchData* data = requests_.back().get();
347
348 for (const auto& id : data->extension_ids()) {
349 if (ContainsKey(responses_, id)) {
350 // We use PostTask here instead of calling OnExtensionDownloadFinished
351 // immeditately, because the calling code isn't expecting a synchronous
352 // response (in non-test situations there are at least 2 network
353 // requests needed before a file could be returned).
354 base::ThreadTaskRunnerHandle::Get()->PostTask(
355 FROM_HERE,
tzik8d880ee2017-04-20 19:46:24356 base::BindOnce(
asargent56282ab72016-09-09 16:58:03357 &ExtensionDownloaderDelegate::OnExtensionDownloadFinished,
358 base::Unretained(delegate),
359 CRXFileInfo(id, responses_[id].second),
360 false /* pass_file_ownership */, GURL(), responses_[id].first,
361 ExtensionDownloaderDelegate::PingResult(), data->request_ids(),
362 ExtensionDownloaderDelegate::InstallCallback()));
363 }
364 }
365 }
366
367 private:
368 // The requests we've received.
369 std::vector<std::unique_ptr<ManifestFetchData>> requests_;
370
371 // The prepared responses - this maps an extension id to a (version string,
372 // crx file path) pair.
373 std::map<std::string, std::pair<ExtensionId, base::FilePath>> responses_;
374
375 DISALLOW_COPY_AND_ASSIGN(DownloaderTestDelegate);
376};
377
378// This lets us simulate the behavior of an enterprise policy that wants
379// a given extension to be installed via the webstore.
380class TestExternalProvider : public ExternalProviderInterface {
381 public:
382 TestExternalProvider(VisitorInterface* visitor,
383 const ExtensionId& extension_id)
384 : visitor_(visitor), extension_id_(extension_id) {}
385
386 ~TestExternalProvider() override {}
387
388 // ExternalProviderInterface:
389 void ServiceShutdown() override {}
390
391 void VisitRegisteredExtension() override {
392 visitor_->OnExternalExtensionUpdateUrlFound(
393 ExternalInstallInfoUpdateUrl(
394 extension_id_, std::string() /* install_parameter */,
395 base::MakeUnique<GURL>(extension_urls::GetWebstoreUpdateUrl()),
396 Manifest::EXTERNAL_POLICY_DOWNLOAD, 0 /* creation_flags */,
397 true /* mark_acknowledged */),
398 true /* is_initial_load */);
399 visitor_->OnExternalProviderReady(this);
400 }
401
402 bool HasExtension(const ExtensionId& id) const override {
403 return id == std::string("npnbmohejbjohgpjnmjagbafnjhkmgko");
404 }
405
406 bool GetExtensionDetails(
407 const ExtensionId& id,
408 Manifest::Location* location,
409 std::unique_ptr<base::Version>* version) const override {
410 ADD_FAILURE() << "Unexpected GetExtensionDetails call; id:" << id;
411 return false;
412 }
413
414 bool IsReady() const override { return true; }
415
416 private:
417 VisitorInterface* visitor_;
418 ExtensionId extension_id_;
419 base::Closure quit_closure_;
420
421 DISALLOW_COPY_AND_ASSIGN(TestExternalProvider);
422};
423
424// This lets us simulate a policy-installed extension being "force" installed;
425// ie a user is not allowed to manually uninstall/disable it.
426class ForceInstallProvider : public ManagementPolicy::Provider {
427 public:
428 explicit ForceInstallProvider(const ExtensionId& id) : id_(id) {}
429 ~ForceInstallProvider() override {}
430
431 std::string GetDebugPolicyProviderName() const override {
432 return "ForceInstallProvider";
433 }
434
435 // MananagementPolicy::Provider:
436 bool UserMayModifySettings(const Extension* extension,
437 base::string16* error) const override {
438 return extension->id() != id_;
439 }
asargent0de8a8bc2016-11-22 22:58:02440 bool MustRemainEnabled(const Extension* extension,
441 base::string16* error) const override {
442 return extension->id() == id_;
443 }
asargent56282ab72016-09-09 16:58:03444
445 private:
446 // The extension id we want to disallow uninstall/disable for.
447 ExtensionId id_;
448
449 DISALLOW_COPY_AND_ASSIGN(ForceInstallProvider);
450};
451
lazyboyb4c713072017-03-31 23:58:14452class ScopedContentVerifyJobDelegateOverride {
453 public:
454 explicit ScopedContentVerifyJobDelegateOverride(JobDelegate* delegate) {
455 ContentVerifyJob::SetDelegateForTests(delegate);
456 }
457 ~ScopedContentVerifyJobDelegateOverride() {
458 ContentVerifyJob::SetDelegateForTests(nullptr);
459 }
460
461 private:
462 DISALLOW_COPY_AND_ASSIGN(ScopedContentVerifyJobDelegateOverride);
463};
464
[email protected]fd3df7782014-05-08 23:54:27465} // namespace
466
467class ContentVerifierTest : public ExtensionBrowserTest {
468 public:
469 ContentVerifierTest() {}
dcheng72191812014-10-28 20:49:56470 ~ContentVerifierTest() override {}
[email protected]fd3df7782014-05-08 23:54:27471
dchengae36a4a2014-10-21 12:36:36472 void SetUpCommandLine(base::CommandLine* command_line) override {
[email protected]fd3df7782014-05-08 23:54:27473 ExtensionBrowserTest::SetUpCommandLine(command_line);
474 command_line->AppendSwitchASCII(
475 switches::kExtensionContentVerification,
476 switches::kExtensionContentVerificationEnforce);
477 }
478
asargent7cc29ce32014-10-09 21:27:57479 virtual void OpenPageAndWaitForUnload() {
lazyboyb4c713072017-03-31 23:58:14480 ScopedContentVerifyJobDelegateOverride scoped_delegate(&delegate_);
asargentb8af8152015-11-02 17:56:37481 std::string id = "npnbmohejbjohgpjnmjagbafnjhkmgko";
482 delegate_.set_id(id);
lazyboyb4c713072017-03-31 23:58:14483
484 // |unload_observer| needs to destroy before the ExtensionRegistry gets
485 // deleted, which happens before TearDownOnMainThread is called.
486 RegistryObserver unload_observer(ExtensionRegistry::Get(profile()));
[email protected]fd3df7782014-05-08 23:54:27487 const Extension* extension = InstallExtensionFromWebstore(
488 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1);
489 ASSERT_TRUE(extension);
asargentb8af8152015-11-02 17:56:37490 ASSERT_EQ(id, extension->id());
[email protected]fd3df7782014-05-08 23:54:27491 page_url_ = extension->GetResourceURL("page.html");
jamc958dad12017-05-23 17:05:44492 // Wait for 0 navigations to complete because with PlzNavigate it's racy
493 // when the didstop IPC arrives relative to the tab being closed. The
494 // wait call below is what the tests care about.
495 ui_test_utils::NavigateToURLWithDispositionBlockUntilNavigationsComplete(
496 browser(), page_url_, 0, WindowOpenDisposition::NEW_FOREGROUND_TAB,
497 ui_test_utils::BROWSER_TEST_NONE);
alexmos0785dd762014-11-13 19:59:38498
lazyboyb4c713072017-03-31 23:58:14499 EXPECT_TRUE(unload_observer.WaitForUnload(id));
[email protected]fd3df7782014-05-08 23:54:27500 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
asargentb8af8152015-11-02 17:56:37501 int reasons = prefs->GetDisableReasons(id);
Minh X. Nguyen45479012017-08-18 21:35:36502 EXPECT_TRUE(reasons & disable_reason::DISABLE_CORRUPTED);
[email protected]fd3df7782014-05-08 23:54:27503 }
504
lazyboyd05d1e82017-04-24 21:20:42505 void TestContentScriptExtension(const std::string& crx_relpath,
506 const std::string& id,
507 const std::string& script_relpath) {
508 VerifierObserver verifier_observer;
509
510 // Install the extension with content scripts. The initial read of the
511 // content scripts will fail verification because they are read before the
512 // content verification system has completed a one-time processing of the
513 // expected hashes. (The extension only contains the root level hashes of
514 // the merkle tree, but the content verification system builds the entire
515 // tree and caches it in the extension install directory - see
516 // ContentHashFetcher for more details).
517 const Extension* extension = InstallExtensionFromWebstore(
518 test_data_dir_.AppendASCII(crx_relpath), 1);
519 ASSERT_TRUE(extension);
520 EXPECT_EQ(id, extension->id());
521
522 // Wait for the content verification code to finish processing the hashes.
523 if (!base::ContainsKey(verifier_observer.completed_fetches(), id))
524 verifier_observer.WaitForFetchComplete(id);
525
526 // Now disable the extension, since content scripts are read at enable time,
527 // set up our job observer, and re-enable, expecting a success this time.
528 DisableExtension(id);
529 JobObserver job_observer;
530 base::FilePath script_relfilepath =
531 base::FilePath().AppendASCII(script_relpath);
532 job_observer.ExpectJobResult(id, script_relfilepath,
533 JobObserver::Result::SUCCESS);
534 EnableExtension(id);
535 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
536
537 // Now alter the contents of the content script, reload the extension, and
538 // expect to see a job failure due to the content script content hash not
539 // being what was signed by the webstore.
540 base::FilePath scriptfile = extension->path().AppendASCII(script_relpath);
541 std::string extra = "some_extra_function_call();";
jam3f2d3932017-04-26 20:28:51542 {
543 base::ThreadRestrictions::ScopedAllowIO allow_io;
544 ASSERT_TRUE(base::AppendToFile(scriptfile, extra.data(), extra.size()));
545 }
lazyboyd05d1e82017-04-24 21:20:42546 DisableExtension(id);
547 job_observer.ExpectJobResult(id, script_relfilepath,
548 JobObserver::Result::FAILURE);
549 EnableExtension(id);
550 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
551 }
552
[email protected]fd3df7782014-05-08 23:54:27553 protected:
554 JobDelegate delegate_;
[email protected]fd3df7782014-05-08 23:54:27555 GURL page_url_;
556};
557
558IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) {
asargentb8af8152015-11-02 17:56:37559 EXPECT_EQ(0, delegate_.bytes_read_failed());
[email protected]fd3df7782014-05-08 23:54:27560 delegate_.fail_next_read();
561 OpenPageAndWaitForUnload();
asargentb8af8152015-11-02 17:56:37562 EXPECT_EQ(1, delegate_.bytes_read_failed());
[email protected]fd3df7782014-05-08 23:54:27563}
564
565IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnDone) {
asargentb8af8152015-11-02 17:56:37566 EXPECT_EQ(0, delegate_.done_reading_failed());
[email protected]fd3df7782014-05-08 23:54:27567 delegate_.fail_next_done();
568 OpenPageAndWaitForUnload();
asargentb8af8152015-11-02 17:56:37569 EXPECT_EQ(1, delegate_.done_reading_failed());
[email protected]fd3df7782014-05-08 23:54:27570}
571
asargent7cc29ce32014-10-09 21:27:57572IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) {
573 JobObserver job_observer;
asargent7cc29ce32014-10-09 21:27:57574 std::string id = "hoipipabpcoomfapcecilckodldhmpgl";
575
576 job_observer.ExpectJobResult(
asargent678123d22015-07-31 23:24:10577 id, base::FilePath(FILE_PATH_LITERAL("background.js")),
578 JobObserver::Result::SUCCESS);
579 job_observer.ExpectJobResult(id,
580 base::FilePath(FILE_PATH_LITERAL("page.html")),
581 JobObserver::Result::SUCCESS);
582 job_observer.ExpectJobResult(id, base::FilePath(FILE_PATH_LITERAL("page.js")),
583 JobObserver::Result::SUCCESS);
asargent7cc29ce32014-10-09 21:27:57584 job_observer.ExpectJobResult(
asargent678123d22015-07-31 23:24:10585 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")),
586 JobObserver::Result::SUCCESS);
587 job_observer.ExpectJobResult(id,
588 base::FilePath(FILE_PATH_LITERAL("page2.js")),
589 JobObserver::Result::SUCCESS);
asargent73d92c72015-09-10 19:20:12590 job_observer.ExpectJobResult(id, base::FilePath(FILE_PATH_LITERAL("cs1.js")),
591 JobObserver::Result::SUCCESS);
592 job_observer.ExpectJobResult(id, base::FilePath(FILE_PATH_LITERAL("cs2.js")),
593 JobObserver::Result::SUCCESS);
594
595 VerifierObserver verifier_observer;
asargent7cc29ce32014-10-09 21:27:57596
597 // Install a test extension we copied from the webstore that has actual
asargent73d92c72015-09-10 19:20:12598 // signatures, and contains paths with a leading "./" in various places.
asargent7cc29ce32014-10-09 21:27:57599 const Extension* extension = InstallExtensionFromWebstore(
600 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1);
601
602 ASSERT_TRUE(extension);
603 ASSERT_EQ(extension->id(), id);
604
asargent73d92c72015-09-10 19:20:12605 // The content scripts might fail verification the first time since the
606 // one-time processing might not be finished yet - if that's the case then
607 // we want to wait until that work is done.
skyostil32fa6b3f92016-08-12 13:15:43608 if (!base::ContainsKey(verifier_observer.completed_fetches(), id))
asargent73d92c72015-09-10 19:20:12609 verifier_observer.WaitForFetchComplete(id);
610
611 // Now disable/re-enable the extension to cause the content scripts to be
612 // read again.
613 DisableExtension(id);
614 EnableExtension(id);
615
asargent7cc29ce32014-10-09 21:27:57616 EXPECT_TRUE(job_observer.WaitForExpectedJobs());
asargent7cc29ce32014-10-09 21:27:57617}
618
asargent678123d22015-07-31 23:24:10619IN_PROC_BROWSER_TEST_F(ContentVerifierTest, ContentScripts) {
lazyboyd05d1e82017-04-24 21:20:42620 TestContentScriptExtension("content_verifier/content_script.crx",
621 "jmllhlobpjcnnomjlipadejplhmheiif", "script.js");
622}
asargent678123d22015-07-31 23:24:10623
lazyboyd05d1e82017-04-24 21:20:42624IN_PROC_BROWSER_TEST_F(ContentVerifierTest, ContentScriptsInLocales) {
625 TestContentScriptExtension("content_verifier/content_script_locales.crx",
626 "jaghonccckpcikmliipifpoodmeofoon",
627 "_locales/en/content_script.js");
asargent678123d22015-07-31 23:24:10628}
629
asargent56282ab72016-09-09 16:58:03630// Tests the case of a corrupt extension that is force-installed by policy and
631// should not be allowed to be manually uninstalled/disabled by the user.
632IN_PROC_BROWSER_TEST_F(ContentVerifierTest, PolicyCorrupted) {
633 ExtensionSystem* system = ExtensionSystem::Get(profile());
634 ExtensionService* service = system->extension_service();
635
636 // The id of our test extension.
637 std::string id("npnbmohejbjohgpjnmjagbafnjhkmgko");
638
639 // Setup fake policy and update check objects.
640 ForceInstallProvider policy(id);
641 DownloaderTestDelegate downloader;
642 system->management_policy()->RegisterProvider(&policy);
643 ExtensionDownloader::set_test_delegate(&downloader);
644 service->AddProviderForTesting(
645 base::MakeUnique<TestExternalProvider>(service, id));
646
647 base::FilePath crx_path =
648 test_data_dir_.AppendASCII("content_verifier/v1.crx");
649 const Extension* extension =
650 InstallExtension(crx_path, 1, Manifest::EXTERNAL_POLICY_DOWNLOAD);
651 EXPECT_NE(extension, nullptr);
652
653 downloader.AddResponse(id, extension->VersionString(), crx_path);
654
655 RegistryObserver registry_observer(ExtensionRegistry::Get(profile()));
656 ContentVerifier* verifier = system->content_verifier();
657 verifier->VerifyFailed(extension->id(), ContentVerifyJob::HASH_MISMATCH);
658
659 // Make sure the extension first got disabled due to corruption.
660 EXPECT_TRUE(registry_observer.WaitForUnload(id));
661 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
662 int reasons = prefs->GetDisableReasons(id);
Minh X. Nguyen45479012017-08-18 21:35:36663 EXPECT_TRUE(reasons & disable_reason::DISABLE_CORRUPTED);
asargent56282ab72016-09-09 16:58:03664
665 // Make sure the extension then got re-installed, and that after reinstall it
666 // is no longer disabled due to corruption.
667 EXPECT_TRUE(registry_observer.WaitForInstall(id));
668 reasons = prefs->GetDisableReasons(id);
Minh X. Nguyen45479012017-08-18 21:35:36669 EXPECT_FALSE(reasons & disable_reason::DISABLE_CORRUPTED);
asargent56282ab72016-09-09 16:58:03670
671 // Make sure that the update check request properly included a parameter
672 // indicating that this was a corrupt policy reinstall.
673 bool found = false;
674 for (const auto& request : downloader.requests()) {
675 if (request->Includes(id)) {
676 std::string query = request->full_url().query();
677 for (const auto& part : base::SplitString(
678 query, "&", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL)) {
679 if (base::StartsWith(part, "x=", base::CompareCase::SENSITIVE) &&
680 part.find(std::string("id%3D") + id) != std::string::npos) {
681 found = true;
682 EXPECT_NE(std::string::npos, part.find("installsource%3Dreinstall"));
683 }
684 }
685 }
686 }
687 EXPECT_TRUE(found);
688}
689
asargent0de8a8bc2016-11-22 22:58:02690class ContentVerifierPolicyTest : public ContentVerifierTest {
691 public:
692 // We need to do this work here because the force-install policy values are
693 // checked pretty early on in the startup of the ExtensionService, which
694 // happens between SetUpInProcessBrowserTestFixture and SetUpOnMainThread.
695 void SetUpInProcessBrowserTestFixture() override {
696 ContentVerifierTest::SetUpInProcessBrowserTestFixture();
697
698 EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
699 .WillRepeatedly(testing::Return(true));
700
701 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
702 &policy_provider_);
703 ExtensionManagementPolicyUpdater management_policy(&policy_provider_);
704 management_policy.SetIndividualExtensionAutoInstalled(
705 id_, extension_urls::kChromeWebstoreUpdateURL, true /* forced */);
706
707 ExtensionDownloader::set_test_delegate(&downloader_);
708 base::FilePath crx_path =
709 test_data_dir_.AppendASCII("content_verifier/v1.crx");
710 std::string version = "2";
711 downloader_.AddResponse(id_, version, crx_path);
712 }
713
714 void SetUpOnMainThread() override {
715 extensions::browsertest_util::CreateAndInitializeLocalCache();
716 }
717
718 protected:
719 // The id of the extension we want to have force-installed.
720 std::string id_ = "npnbmohejbjohgpjnmjagbafnjhkmgko";
721
722 private:
723 policy::MockConfigurationPolicyProvider policy_provider_;
724 DownloaderTestDelegate downloader_;
725};
726
727// We want to test what happens at startup with a corroption-disabled policy
728// force installed extension. So we set that up in the PRE test here.
729IN_PROC_BROWSER_TEST_F(ContentVerifierPolicyTest,
asargentba52f322016-11-30 18:26:06730 PRE_PolicyCorruptedOnStartup) {
asargent0de8a8bc2016-11-22 22:58:02731 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
732 RegistryObserver registry_observer(registry);
733
734 // Wait for the extension to be installed by policy we set up in
735 // SetUpInProcessBrowserTestFixture.
736 if (!registry->GetInstalledExtension(id_)) {
737 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
738 }
739
740 // Simulate corruption of the extension so that we can test what happens
741 // at startup in the non-PRE test.
742 ExtensionSystem* system = ExtensionSystem::Get(profile());
743 ContentVerifier* verifier = system->content_verifier();
744 verifier->VerifyFailed(id_, ContentVerifyJob::HASH_MISMATCH);
745 EXPECT_TRUE(registry_observer.WaitForUnload(id_));
746 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
747 int reasons = prefs->GetDisableReasons(id_);
Minh X. Nguyen45479012017-08-18 21:35:36748 EXPECT_TRUE(reasons & disable_reason::DISABLE_CORRUPTED);
asargent0de8a8bc2016-11-22 22:58:02749}
750
751// Now actually test what happens on the next startup after the PRE test above.
asargentba52f322016-11-30 18:26:06752IN_PROC_BROWSER_TEST_F(ContentVerifierPolicyTest, PolicyCorruptedOnStartup) {
753 // Depdending on timing, the extension may have already been reinstalled
754 // between SetUpInProcessBrowserTestFixture and now (usually not during local
755 // testing on a developer machine, but sometimes on a heavily loaded system
756 // such as the build waterfall / trybots). If the reinstall didn't already
757 // happen, wait for it.
asargent0de8a8bc2016-11-22 22:58:02758 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile());
asargent0de8a8bc2016-11-22 22:58:02759 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
asargentba52f322016-11-30 18:26:06760 int disable_reasons = prefs->GetDisableReasons(id_);
Minh X. Nguyen45479012017-08-18 21:35:36761 if (disable_reasons & disable_reason::DISABLE_CORRUPTED) {
asargentba52f322016-11-30 18:26:06762 RegistryObserver registry_observer(registry);
763 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
764 disable_reasons = prefs->GetDisableReasons(id_);
765 }
Minh X. Nguyen45479012017-08-18 21:35:36766 EXPECT_FALSE(disable_reasons & disable_reason::DISABLE_CORRUPTED);
asargentba52f322016-11-30 18:26:06767 EXPECT_TRUE(registry->enabled_extensions().Contains(id_));
asargent0de8a8bc2016-11-22 22:58:02768}
769
asargent6014bdf2016-11-30 21:50:16770namespace {
771
772// A helper for intercepting the normal action that
773// ChromeContentVerifierDelegate would take on discovering corruption, letting
774// us track the delay for each consecutive reinstall.
775class DelayTracker {
776 public:
lazyboy77214d3c2017-04-04 16:46:12777 DelayTracker()
778 : action_(base::Bind(&DelayTracker::ReinstallAction,
779 base::Unretained(this))) {
780 PolicyExtensionReinstaller::set_policy_reinstall_action_for_test(&action_);
781 }
782
783 ~DelayTracker() {
784 PolicyExtensionReinstaller::set_policy_reinstall_action_for_test(nullptr);
785 }
asargent6014bdf2016-11-30 21:50:16786
787 const std::vector<base::TimeDelta>& calls() { return calls_; }
788
lazyboy77214d3c2017-04-04 16:46:12789 void ReinstallAction(const base::Closure& callback, base::TimeDelta delay) {
790 saved_callback_ = callback;
791 calls_.push_back(delay);
792 }
793
794 void Proceed() {
795 ASSERT_TRUE(saved_callback_);
796 ASSERT_TRUE(!saved_callback_->is_null());
797 // Run() will set |saved_callback_| again, so use a temporary: |callback|.
798 base::Closure callback = saved_callback_.value();
799 saved_callback_.reset();
800 callback.Run();
801 }
asargent6014bdf2016-11-30 21:50:16802
803 private:
804 std::vector<base::TimeDelta> calls_;
lazyboy77214d3c2017-04-04 16:46:12805 base::Optional<base::Closure> saved_callback_;
806 PolicyExtensionReinstaller::ReinstallCallback action_;
asargent6014bdf2016-11-30 21:50:16807
808 DISALLOW_COPY_AND_ASSIGN(DelayTracker);
809};
810
811} // namespace
812
813IN_PROC_BROWSER_TEST_F(ContentVerifierPolicyTest, Backoff) {
814 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
815 ExtensionSystem* system = ExtensionSystem::Get(profile());
asargent6014bdf2016-11-30 21:50:16816 ContentVerifier* verifier = system->content_verifier();
817
818 // Wait for the extension to be installed by the policy we set up in
819 // SetUpInProcessBrowserTestFixture.
820 if (!registry->GetInstalledExtension(id_)) {
821 RegistryObserver registry_observer(registry);
822 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
823 }
824
825 // Setup to intercept reinstall action, so we can see what the delay would
826 // have been for the real action.
827 DelayTracker delay_tracker;
asargent6014bdf2016-11-30 21:50:16828
829 // Do 4 iterations of disabling followed by reinstall.
830 const size_t iterations = 4;
831 for (size_t i = 0; i < iterations; i++) {
832 RegistryObserver registry_observer(registry);
833 verifier->VerifyFailed(id_, ContentVerifyJob::HASH_MISMATCH);
834 EXPECT_TRUE(registry_observer.WaitForUnload(id_));
lazyboy77214d3c2017-04-04 16:46:12835 // Resolve the request to |delay_tracker|, so the reinstallation can
836 // proceed.
837 delay_tracker.Proceed();
asargent6014bdf2016-11-30 21:50:16838 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
839 }
840 const std::vector<base::TimeDelta>& calls = delay_tracker.calls();
841
lazyboy77214d3c2017-04-04 16:46:12842 // After |delay_tracker| resolves the 4 (|iterations|) reinstallation
843 // requests, it will get an additional request (right away) for retrying
844 // reinstallation.
845 // Note: the additional request in non-test environment will arrive with
846 // a (backoff) delay. But during test, |delay_tracker| issues the request
847 // immediately.
848 ASSERT_EQ(iterations, calls.size() - 1);
asargent6014bdf2016-11-30 21:50:16849 // Assert that the first reinstall action happened with a delay of 0, and
850 // then kept growing each additional time.
asargent6014bdf2016-11-30 21:50:16851 EXPECT_EQ(base::TimeDelta(), delay_tracker.calls()[0]);
852 for (size_t i = 1; i < delay_tracker.calls().size(); i++) {
853 EXPECT_LT(calls[i - 1], calls[i]);
854 }
855}
856
lazyboy77214d3c2017-04-04 16:46:12857// Tests that if CheckForExternalUpdates() fails, then we retry reinstalling
858// corrupted policy extensions. For example: if network is unavailable,
859// CheckForExternalUpdates() will fail.
860IN_PROC_BROWSER_TEST_F(ContentVerifierPolicyTest, FailedUpdateRetries) {
861 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
862 ExtensionSystem* system = ExtensionSystem::Get(profile());
863 ExtensionService* service = system->extension_service();
864 ContentVerifier* verifier = system->content_verifier();
865
866 // Wait for the extension to be installed by the policy we set up in
867 // SetUpInProcessBrowserTestFixture.
868 if (!registry->GetInstalledExtension(id_)) {
869 RegistryObserver registry_observer(registry);
870 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
871 }
872
873 DelayTracker delay_tracker;
874 service->set_external_updates_disabled_for_test(true);
875 RegistryObserver registry_observer(registry);
876 verifier->VerifyFailed(id_, ContentVerifyJob::HASH_MISMATCH);
877 EXPECT_TRUE(registry_observer.WaitForUnload(id_));
878
879 const std::vector<base::TimeDelta>& calls = delay_tracker.calls();
880 ASSERT_EQ(1u, calls.size());
881 EXPECT_EQ(base::TimeDelta(), delay_tracker.calls()[0]);
882
883 delay_tracker.Proceed();
884
885 // Remove the override and set ExtensionService to update again. The extension
886 // should be now installed.
887 PolicyExtensionReinstaller::set_policy_reinstall_action_for_test(nullptr);
888 service->set_external_updates_disabled_for_test(false);
889 delay_tracker.Proceed();
890
891 EXPECT_TRUE(registry_observer.WaitForInstall(id_));
892}
893
[email protected]fd3df7782014-05-08 23:54:27894} // namespace extensions