content_verifier_browsertest.cc revision 1675a649fd7a8b3cb80ffddae2dc181f122353c5
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 3// found in the LICENSE file. 4 5#include "base/scoped_observer.h" 6#include "chrome/browser/extensions/extension_browsertest.h" 7#include "chrome/common/chrome_switches.h" 8#include "content/public/test/test_utils.h" 9#include "extensions/browser/content_verify_job.h" 10#include "extensions/browser/extension_prefs.h" 11#include "extensions/browser/extension_registry.h" 12#include "extensions/browser/extension_registry_observer.h" 13 14namespace extensions { 15 16namespace { 17 18// Helper for observing extension unloads. 19class UnloadObserver : public ExtensionRegistryObserver { 20 public: 21 explicit UnloadObserver(ExtensionRegistry* registry) : observer_(this) { 22 observer_.Add(registry); 23 } 24 virtual ~UnloadObserver() {} 25 26 void WaitForUnload(const ExtensionId& id) { 27 if (ContainsKey(observed_, id)) 28 return; 29 30 ASSERT_TRUE(loop_runner_.get() == NULL); 31 awaited_id_ = id; 32 loop_runner_ = new content::MessageLoopRunner(); 33 loop_runner_->Run(); 34 } 35 36 virtual void OnExtensionUnloaded( 37 content::BrowserContext* browser_context, 38 const Extension* extension, 39 UnloadedExtensionInfo::Reason reason) OVERRIDE { 40 observed_.insert(extension->id()); 41 if (awaited_id_ == extension->id()) 42 loop_runner_->Quit(); 43 } 44 45 private: 46 ExtensionId awaited_id_; 47 std::set<ExtensionId> observed_; 48 scoped_refptr<content::MessageLoopRunner> loop_runner_; 49 ScopedObserver<ExtensionRegistry, UnloadObserver> observer_; 50}; 51 52// Helper for forcing ContentVerifyJob's to return an error. 53class JobDelegate : public ContentVerifyJob::TestDelegate { 54 public: 55 JobDelegate() : fail_next_read_(false), fail_next_done_(false) {} 56 57 virtual ~JobDelegate() {} 58 59 void set_id(const ExtensionId& id) { id_ = id; } 60 void fail_next_read() { fail_next_read_ = true; } 61 void fail_next_done() { fail_next_done_ = true; } 62 63 virtual ContentVerifyJob::FailureReason BytesRead(const ExtensionId& id, 64 int count, 65 const char* data) OVERRIDE { 66 if (id == id_ && fail_next_read_) { 67 fail_next_read_ = false; 68 return ContentVerifyJob::HASH_MISMATCH; 69 } 70 return ContentVerifyJob::NONE; 71 } 72 73 virtual ContentVerifyJob::FailureReason DoneReading( 74 const ExtensionId& id) OVERRIDE { 75 if (id == id_ && fail_next_done_) { 76 fail_next_done_ = false; 77 return ContentVerifyJob::HASH_MISMATCH; 78 } 79 return ContentVerifyJob::NONE; 80 } 81 82 private: 83 DISALLOW_COPY_AND_ASSIGN(JobDelegate); 84 85 ExtensionId id_; 86 bool fail_next_read_; 87 bool fail_next_done_; 88}; 89 90class JobObserver : public ContentVerifyJob::TestObserver { 91 public: 92 JobObserver(); 93 virtual ~JobObserver(); 94 95 // Call this to add an expected job result. 96 void ExpectJobResult(const std::string& extension_id, 97 const base::FilePath& relative_path, 98 bool expected_to_fail); 99 100 // Wait to see expected jobs. Returns true if we saw all jobs finish as 101 // expected, or false if any job completed with non-expected success/failure 102 // status. 103 bool WaitForExpectedJobs(); 104 105 // ContentVerifyJob::TestObserver interface 106 virtual void JobStarted(const std::string& extension_id, 107 const base::FilePath& relative_path) OVERRIDE; 108 109 virtual void JobFinished(const std::string& extension_id, 110 const base::FilePath& relative_path, 111 bool failed) OVERRIDE; 112 113 private: 114 typedef std::pair<std::string, base::FilePath> ExtensionFile; 115 typedef std::map<ExtensionFile, bool> ExpectedJobs; 116 ExpectedJobs expected_jobs_; 117 scoped_refptr<content::MessageLoopRunner> loop_runner_; 118 bool saw_expected_job_results_; 119}; 120 121void JobObserver::ExpectJobResult(const std::string& extension_id, 122 const base::FilePath& relative_path, 123 bool expected_to_fail) { 124 expected_jobs_.insert(std::make_pair( 125 ExtensionFile(extension_id, relative_path), expected_to_fail)); 126} 127 128JobObserver::JobObserver() : saw_expected_job_results_(false) { 129} 130 131JobObserver::~JobObserver() { 132} 133 134bool JobObserver::WaitForExpectedJobs() { 135 if (!expected_jobs_.empty()) { 136 loop_runner_ = new content::MessageLoopRunner(); 137 loop_runner_->Run(); 138 } 139 return saw_expected_job_results_; 140} 141 142void JobObserver::JobStarted(const std::string& extension_id, 143 const base::FilePath& relative_path) { 144} 145 146void JobObserver::JobFinished(const std::string& extension_id, 147 const base::FilePath& relative_path, 148 bool failed) { 149 ExpectedJobs::iterator i = expected_jobs_.find(ExtensionFile( 150 extension_id, relative_path.NormalizePathSeparatorsTo('/'))); 151 if (i != expected_jobs_.end()) { 152 if (failed != i->second) { 153 saw_expected_job_results_ = false; 154 if (loop_runner_.get()) 155 loop_runner_->Quit(); 156 } 157 expected_jobs_.erase(i); 158 if (expected_jobs_.empty()) { 159 saw_expected_job_results_ = true; 160 if (loop_runner_.get()) 161 loop_runner_->Quit(); 162 } 163 } 164} 165 166} // namespace 167 168class ContentVerifierTest : public ExtensionBrowserTest { 169 public: 170 ContentVerifierTest() {} 171 virtual ~ContentVerifierTest() {} 172 173 virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { 174 ExtensionBrowserTest::SetUpCommandLine(command_line); 175 command_line->AppendSwitchASCII( 176 switches::kExtensionContentVerification, 177 switches::kExtensionContentVerificationEnforce); 178 } 179 180 // Setup our unload observer and JobDelegate, and install a test extension. 181 virtual void SetUpOnMainThread() OVERRIDE { 182 ExtensionBrowserTest::SetUpOnMainThread(); 183 } 184 185 virtual void TearDownOnMainThread() override { 186 ContentVerifyJob::SetDelegateForTests(NULL); 187 ContentVerifyJob::SetObserverForTests(NULL); 188 ExtensionBrowserTest::TearDownOnMainThread(); 189 } 190 191 virtual void OpenPageAndWaitForUnload() { 192 unload_observer_.reset( 193 new UnloadObserver(ExtensionRegistry::Get(profile()))); 194 const Extension* extension = InstallExtensionFromWebstore( 195 test_data_dir_.AppendASCII("content_verifier/v1.crx"), 1); 196 ASSERT_TRUE(extension); 197 id_ = extension->id(); 198 page_url_ = extension->GetResourceURL("page.html"); 199 delegate_.set_id(id_); 200 ContentVerifyJob::SetDelegateForTests(&delegate_); 201 AddTabAtIndex(1, page_url_, ui::PAGE_TRANSITION_LINK); 202 unload_observer_->WaitForUnload(id_); 203 ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); 204 int reasons = prefs->GetDisableReasons(id_); 205 EXPECT_TRUE(reasons & Extension::DISABLE_CORRUPTED); 206 207 // This needs to happen before the ExtensionRegistry gets deleted, which 208 // happens before TearDownOnMainThread is called. 209 unload_observer_.reset(); 210 } 211 212 protected: 213 JobDelegate delegate_; 214 scoped_ptr<UnloadObserver> unload_observer_; 215 ExtensionId id_; 216 GURL page_url_; 217}; 218 219IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnRead) { 220 delegate_.fail_next_read(); 221 OpenPageAndWaitForUnload(); 222} 223 224IN_PROC_BROWSER_TEST_F(ContentVerifierTest, FailOnDone) { 225 delegate_.fail_next_done(); 226 OpenPageAndWaitForUnload(); 227} 228 229IN_PROC_BROWSER_TEST_F(ContentVerifierTest, DotSlashPaths) { 230 JobObserver job_observer; 231 ContentVerifyJob::SetObserverForTests(&job_observer); 232 std::string id = "hoipipabpcoomfapcecilckodldhmpgl"; 233 234 job_observer.ExpectJobResult( 235 id, base::FilePath(FILE_PATH_LITERAL("background.js")), false); 236 job_observer.ExpectJobResult( 237 id, base::FilePath(FILE_PATH_LITERAL("page.html")), false); 238 job_observer.ExpectJobResult( 239 id, base::FilePath(FILE_PATH_LITERAL("page.js")), false); 240 job_observer.ExpectJobResult( 241 id, base::FilePath(FILE_PATH_LITERAL("dir/page2.html")), false); 242 job_observer.ExpectJobResult( 243 id, base::FilePath(FILE_PATH_LITERAL("page2.js")), false); 244 245 // Install a test extension we copied from the webstore that has actual 246 // signatures, and contains image paths with leading "./". 247 const Extension* extension = InstallExtensionFromWebstore( 248 test_data_dir_.AppendASCII("content_verifier/dot_slash_paths.crx"), 1); 249 250 ASSERT_TRUE(extension); 251 ASSERT_EQ(extension->id(), id); 252 253 EXPECT_TRUE(job_observer.WaitForExpectedJobs()); 254 255 ContentVerifyJob::SetObserverForTests(NULL); 256} 257 258} // namespace extensions 259