1 // Copyright (c) 2012 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/command_line.h" 6 #include "base/memory/scoped_ptr.h" 7 #include "base/message_loop/message_loop.h" 8 #include "base/run_loop.h" 9 #include "base/synchronization/waitable_event.h" 10 #include "chrome/browser/extensions/activity_log/activity_action_constants.h" 11 #include "chrome/browser/extensions/activity_log/activity_log.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/test_extension_system.h" 14 #include "chrome/browser/prerender/prerender_handle.h" 15 #include "chrome/browser/prerender/prerender_manager.h" 16 #include "chrome/browser/prerender/prerender_manager_factory.h" 17 #include "chrome/common/chrome_constants.h" 18 #include "chrome/common/chrome_switches.h" 19 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 20 #include "chrome/test/base/testing_profile.h" 21 #include "content/public/test/test_browser_thread_bundle.h" 22 #include "extensions/browser/extension_registry.h" 23 #include "extensions/common/dom_action_types.h" 24 #include "extensions/common/extension_builder.h" 25 #include "sql/statement.h" 26 #include "testing/gtest/include/gtest/gtest.h" 27 28 #if defined(OS_CHROMEOS) 29 #include "chrome/browser/chromeos/login/users/user_manager.h" 30 #include "chrome/browser/chromeos/settings/cros_settings.h" 31 #include "chrome/browser/chromeos/settings/device_settings_service.h" 32 #endif 33 34 namespace { 35 36 const char kExtensionId[] = "abc"; 37 38 const char* kUrlApiCalls[] = { 39 "HTMLButtonElement.formAction", "HTMLEmbedElement.src", 40 "HTMLFormElement.action", "HTMLFrameElement.src", 41 "HTMLHtmlElement.manifest", "HTMLIFrameElement.src", 42 "HTMLImageElement.longDesc", "HTMLImageElement.src", 43 "HTMLImageElement.lowsrc", "HTMLInputElement.formAction", 44 "HTMLInputElement.src", "HTMLLinkElement.href", 45 "HTMLMediaElement.src", "HTMLMediaElement.currentSrc", 46 "HTMLModElement.cite", "HTMLObjectElement.data", 47 "HTMLQuoteElement.cite", "HTMLScriptElement.src", 48 "HTMLSourceElement.src", "HTMLTrackElement.src", 49 "HTMLVideoElement.poster"}; 50 51 } // namespace 52 53 namespace extensions { 54 55 class ActivityLogTest : public ChromeRenderViewHostTestHarness { 56 protected: 57 virtual void SetUp() OVERRIDE { 58 ChromeRenderViewHostTestHarness::SetUp(); 59 #if defined OS_CHROMEOS 60 test_user_manager_.reset(new chromeos::ScopedTestUserManager()); 61 #endif 62 CommandLine command_line(CommandLine::NO_PROGRAM); 63 CommandLine::ForCurrentProcess()->AppendSwitch( 64 switches::kEnableExtensionActivityLogging); 65 CommandLine::ForCurrentProcess()->AppendSwitch( 66 switches::kEnableExtensionActivityLogTesting); 67 extension_service_ = static_cast<TestExtensionSystem*>( 68 ExtensionSystem::Get(profile()))->CreateExtensionService 69 (&command_line, base::FilePath(), false); 70 base::RunLoop().RunUntilIdle(); 71 } 72 73 virtual void TearDown() OVERRIDE { 74 #if defined OS_CHROMEOS 75 test_user_manager_.reset(); 76 #endif 77 base::RunLoop().RunUntilIdle(); 78 ChromeRenderViewHostTestHarness::TearDown(); 79 } 80 81 static void RetrieveActions_LogAndFetchActions0( 82 scoped_ptr<std::vector<scoped_refptr<Action> > > i) { 83 ASSERT_EQ(0, static_cast<int>(i->size())); 84 } 85 86 static void RetrieveActions_LogAndFetchActions2( 87 scoped_ptr<std::vector<scoped_refptr<Action> > > i) { 88 ASSERT_EQ(2, static_cast<int>(i->size())); 89 } 90 91 void SetPolicy(bool log_arguments) { 92 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 93 if (log_arguments) 94 activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_FULLSTREAM); 95 else 96 activity_log->SetDatabasePolicy(ActivityLogPolicy::POLICY_COUNTS); 97 } 98 99 bool GetDatabaseEnabled() { 100 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 101 return activity_log->IsDatabaseEnabled(); 102 } 103 104 bool GetWatchdogActive() { 105 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 106 return activity_log->IsWatchdogAppActive(); 107 } 108 109 static void Arguments_Prerender( 110 scoped_ptr<std::vector<scoped_refptr<Action> > > i) { 111 ASSERT_EQ(1U, i->size()); 112 scoped_refptr<Action> last = i->front(); 113 114 ASSERT_EQ("odlameecjipmbmbejkplpemijjgpljce", last->extension_id()); 115 ASSERT_EQ(Action::ACTION_CONTENT_SCRIPT, last->action_type()); 116 ASSERT_EQ("[\"script\"]", 117 ActivityLogPolicy::Util::Serialize(last->args())); 118 ASSERT_EQ("http://www.google.com/", last->SerializePageUrl()); 119 ASSERT_EQ("{\"prerender\":true}", 120 ActivityLogPolicy::Util::Serialize(last->other())); 121 ASSERT_EQ("", last->api_name()); 122 ASSERT_EQ("", last->page_title()); 123 ASSERT_EQ("", last->SerializeArgUrl()); 124 } 125 126 static void RetrieveActions_ArgUrlExtraction( 127 scoped_ptr<std::vector<scoped_refptr<Action> > > i) { 128 const base::DictionaryValue* other = NULL; 129 int dom_verb = -1; 130 131 ASSERT_EQ(4U, i->size()); 132 scoped_refptr<Action> action = i->at(0); 133 ASSERT_EQ("XMLHttpRequest.open", action->api_name()); 134 ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]", 135 ActivityLogPolicy::Util::Serialize(action->args())); 136 ASSERT_EQ("http://api.google.com/", action->arg_url().spec()); 137 // Test that the dom_verb field was changed to XHR (from METHOD). This 138 // could be tested on all retrieved XHR actions but it would be redundant, 139 // so just test once. 140 other = action->other(); 141 ASSERT_TRUE(other); 142 ASSERT_TRUE(other->GetInteger(activity_log_constants::kActionDomVerb, 143 &dom_verb)); 144 ASSERT_EQ(DomActionType::XHR, dom_verb); 145 146 action = i->at(1); 147 ASSERT_EQ("XMLHttpRequest.open", action->api_name()); 148 ASSERT_EQ("[\"POST\",\"\\u003Carg_url>\"]", 149 ActivityLogPolicy::Util::Serialize(action->args())); 150 ASSERT_EQ("http://www.google.com/api/", action->arg_url().spec()); 151 152 action = i->at(2); 153 ASSERT_EQ("XMLHttpRequest.open", action->api_name()); 154 ASSERT_EQ("[\"POST\",\"/api/\"]", 155 ActivityLogPolicy::Util::Serialize(action->args())); 156 ASSERT_FALSE(action->arg_url().is_valid()); 157 158 action = i->at(3); 159 ASSERT_EQ("windows.create", action->api_name()); 160 ASSERT_EQ("[{\"url\":\"\\u003Carg_url>\"}]", 161 ActivityLogPolicy::Util::Serialize(action->args())); 162 ASSERT_EQ("http://www.google.co.uk/", action->arg_url().spec()); 163 } 164 165 static void RetrieveActions_ArgUrlApiCalls( 166 scoped_ptr<std::vector<scoped_refptr<Action> > > actions) { 167 size_t api_calls_size = arraysize(kUrlApiCalls); 168 const base::DictionaryValue* other = NULL; 169 int dom_verb = -1; 170 171 ASSERT_EQ(api_calls_size, actions->size()); 172 173 for (size_t i = 0; i < actions->size(); i++) { 174 scoped_refptr<Action> action = actions->at(i); 175 ASSERT_EQ(kExtensionId, action->extension_id()); 176 ASSERT_EQ(Action::ACTION_DOM_ACCESS, action->action_type()); 177 ASSERT_EQ(kUrlApiCalls[i], action->api_name()); 178 ASSERT_EQ("[\"\\u003Carg_url>\"]", 179 ActivityLogPolicy::Util::Serialize(action->args())); 180 ASSERT_EQ("http://www.google.co.uk/", action->arg_url().spec()); 181 other = action->other(); 182 ASSERT_TRUE(other); 183 ASSERT_TRUE( 184 other->GetInteger(activity_log_constants::kActionDomVerb, &dom_verb)); 185 ASSERT_EQ(DomActionType::SETTER, dom_verb); 186 } 187 } 188 189 ExtensionService* extension_service_; 190 191 #if defined OS_CHROMEOS 192 chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; 193 chromeos::ScopedTestCrosSettings test_cros_settings_; 194 scoped_ptr<chromeos::ScopedTestUserManager> test_user_manager_; 195 #endif 196 }; 197 198 TEST_F(ActivityLogTest, Construct) { 199 ASSERT_TRUE(GetDatabaseEnabled()); 200 ASSERT_FALSE(GetWatchdogActive()); 201 } 202 203 TEST_F(ActivityLogTest, LogAndFetchActions) { 204 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 205 scoped_ptr<base::ListValue> args(new base::ListValue()); 206 ASSERT_TRUE(GetDatabaseEnabled()); 207 208 // Write some API calls 209 scoped_refptr<Action> action = new Action(kExtensionId, 210 base::Time::Now(), 211 Action::ACTION_API_CALL, 212 "tabs.testMethod"); 213 activity_log->LogAction(action); 214 action = new Action(kExtensionId, 215 base::Time::Now(), 216 Action::ACTION_DOM_ACCESS, 217 "document.write"); 218 action->set_page_url(GURL("http://www.google.com")); 219 activity_log->LogAction(action); 220 221 activity_log->GetFilteredActions( 222 kExtensionId, 223 Action::ACTION_ANY, 224 "", 225 "", 226 "", 227 0, 228 base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions2)); 229 } 230 231 TEST_F(ActivityLogTest, LogPrerender) { 232 scoped_refptr<const Extension> extension = 233 ExtensionBuilder() 234 .SetManifest(DictionaryBuilder() 235 .Set("name", "Test extension") 236 .Set("version", "1.0.0") 237 .Set("manifest_version", 2)) 238 .Build(); 239 extension_service_->AddExtension(extension.get()); 240 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 241 ASSERT_TRUE(GetDatabaseEnabled()); 242 GURL url("http://www.google.com"); 243 244 prerender::PrerenderManager* prerender_manager = 245 prerender::PrerenderManagerFactory::GetForProfile( 246 Profile::FromBrowserContext(profile())); 247 248 prerender_manager->OnCookieStoreLoaded(); 249 250 const gfx::Size kSize(640, 480); 251 scoped_ptr<prerender::PrerenderHandle> prerender_handle( 252 prerender_manager->AddPrerenderFromLocalPredictor( 253 url, 254 web_contents()->GetController().GetDefaultSessionStorageNamespace(), 255 kSize)); 256 257 const std::vector<content::WebContents*> contentses = 258 prerender_manager->GetAllPrerenderingContents(); 259 ASSERT_EQ(1U, contentses.size()); 260 content::WebContents *contents = contentses[0]; 261 ASSERT_TRUE(prerender_manager->IsWebContentsPrerendering(contents, NULL)); 262 263 TabHelper::ScriptExecutionObserver::ExecutingScriptsMap executing_scripts; 264 executing_scripts[extension->id()].insert("script"); 265 266 static_cast<TabHelper::ScriptExecutionObserver*>(activity_log)-> 267 OnScriptsExecuted(contents, executing_scripts, 0, url); 268 269 activity_log->GetFilteredActions( 270 extension->id(), 271 Action::ACTION_ANY, 272 "", 273 "", 274 "", 275 0, 276 base::Bind(ActivityLogTest::Arguments_Prerender)); 277 278 prerender_manager->CancelAllPrerenders(); 279 } 280 281 TEST_F(ActivityLogTest, ArgUrlExtraction) { 282 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 283 scoped_ptr<base::ListValue> args(new base::ListValue()); 284 285 base::Time now = base::Time::Now(); 286 287 // Submit a DOM API call which should have its URL extracted into the arg_url 288 // field. 289 scoped_refptr<Action> action = new Action(kExtensionId, 290 now, 291 Action::ACTION_DOM_ACCESS, 292 "XMLHttpRequest.open"); 293 action->set_page_url(GURL("http://www.google.com/")); 294 action->mutable_args()->AppendString("POST"); 295 action->mutable_args()->AppendString("http://api.google.com/"); 296 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, 297 DomActionType::METHOD); 298 activity_log->LogAction(action); 299 300 // Submit a DOM API call with a relative URL in the argument, which should be 301 // resolved relative to the page URL. 302 action = new Action(kExtensionId, 303 now - base::TimeDelta::FromSeconds(1), 304 Action::ACTION_DOM_ACCESS, 305 "XMLHttpRequest.open"); 306 action->set_page_url(GURL("http://www.google.com/")); 307 action->mutable_args()->AppendString("POST"); 308 action->mutable_args()->AppendString("/api/"); 309 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, 310 DomActionType::METHOD); 311 activity_log->LogAction(action); 312 313 // Submit a DOM API call with a relative URL but no base page URL against 314 // which to resolve. 315 action = new Action(kExtensionId, 316 now - base::TimeDelta::FromSeconds(2), 317 Action::ACTION_DOM_ACCESS, 318 "XMLHttpRequest.open"); 319 action->mutable_args()->AppendString("POST"); 320 action->mutable_args()->AppendString("/api/"); 321 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, 322 DomActionType::METHOD); 323 activity_log->LogAction(action); 324 325 // Submit an API call with an embedded URL. 326 action = new Action(kExtensionId, 327 now - base::TimeDelta::FromSeconds(3), 328 Action::ACTION_API_CALL, 329 "windows.create"); 330 action->set_args( 331 ListBuilder() 332 .Append(DictionaryBuilder().Set("url", "http://www.google.co.uk")) 333 .Build()); 334 activity_log->LogAction(action); 335 336 activity_log->GetFilteredActions( 337 kExtensionId, 338 Action::ACTION_ANY, 339 "", 340 "", 341 "", 342 -1, 343 base::Bind(ActivityLogTest::RetrieveActions_ArgUrlExtraction)); 344 } 345 346 TEST_F(ActivityLogTest, UninstalledExtension) { 347 scoped_refptr<const Extension> extension = 348 ExtensionBuilder() 349 .SetManifest(DictionaryBuilder() 350 .Set("name", "Test extension") 351 .Set("version", "1.0.0") 352 .Set("manifest_version", 2)) 353 .Build(); 354 355 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 356 scoped_ptr<base::ListValue> args(new base::ListValue()); 357 ASSERT_TRUE(GetDatabaseEnabled()); 358 359 // Write some API calls 360 scoped_refptr<Action> action = new Action(extension->id(), 361 base::Time::Now(), 362 Action::ACTION_API_CALL, 363 "tabs.testMethod"); 364 activity_log->LogAction(action); 365 action = new Action(extension->id(), 366 base::Time::Now(), 367 Action::ACTION_DOM_ACCESS, 368 "document.write"); 369 action->set_page_url(GURL("http://www.google.com")); 370 371 activity_log->OnExtensionUninstalled(NULL, extension); 372 activity_log->GetFilteredActions( 373 extension->id(), 374 Action::ACTION_ANY, 375 "", 376 "", 377 "", 378 -1, 379 base::Bind(ActivityLogTest::RetrieveActions_LogAndFetchActions0)); 380 } 381 382 TEST_F(ActivityLogTest, ArgUrlApiCalls) { 383 ActivityLog* activity_log = ActivityLog::GetInstance(profile()); 384 scoped_ptr<base::ListValue> args(new base::ListValue()); 385 base::Time now = base::Time::Now(); 386 int api_calls_size = arraysize(kUrlApiCalls); 387 scoped_refptr<Action> action; 388 389 for (int i = 0; i < api_calls_size; i++) { 390 action = new Action(kExtensionId, 391 now - base::TimeDelta::FromSeconds(i), 392 Action::ACTION_DOM_ACCESS, 393 kUrlApiCalls[i]); 394 action->mutable_args()->AppendString("http://www.google.co.uk"); 395 action->mutable_other()->SetInteger(activity_log_constants::kActionDomVerb, 396 DomActionType::SETTER); 397 activity_log->LogAction(action); 398 } 399 400 activity_log->GetFilteredActions( 401 kExtensionId, 402 Action::ACTION_ANY, 403 "", 404 "", 405 "", 406 -1, 407 base::Bind(ActivityLogTest::RetrieveActions_ArgUrlApiCalls)); 408 } 409 410 } // namespace extensions 411