OLD | NEW |
(Empty) | |
| 1 // Copyright 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/run_loop.h" |
| 6 #include "base/stringprintf.h" |
| 7 #include "chrome/browser/extensions/blacklist.h" |
| 8 #include "chrome/browser/extensions/extension_browsertest.h" |
| 9 #include "chrome/browser/extensions/extension_service.h" |
| 10 #include "chrome/browser/extensions/extension_system.h" |
| 11 #include "chrome/browser/ui/browser.h" |
| 12 #include "chrome/common/chrome_notification_types.h" |
| 13 #include "chrome/common/extensions/extension.h" |
| 14 #include "chrome/common/extensions/extension_constants.h" |
| 15 #include "content/public/browser/notification_details.h" |
| 16 #include "content/public/browser/notification_observer.h" |
| 17 #include "content/public/browser/notification_registrar.h" |
| 18 #include "content/public/browser/notification_source.h" |
| 19 |
| 20 namespace extensions { |
| 21 |
| 22 namespace { |
| 23 |
| 24 // Records notifications, but only for extensions with specific IDs. |
| 25 class FilteringNotificationObserver : public content::NotificationObserver { |
| 26 public: |
| 27 FilteringNotificationObserver( |
| 28 content::NotificationSource source, |
| 29 const std::set<std::string>& extension_ids) |
| 30 : extension_ids_(extension_ids) { |
| 31 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, source); |
| 32 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_INSTALLED, source); |
| 33 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED, source); |
| 34 } |
| 35 |
| 36 // Checks then clears notifications for our extensions. |
| 37 testing::AssertionResult CheckNotifications(chrome::NotificationType type) { |
| 38 return CheckNotifications(std::vector<chrome::NotificationType>(1, type)); |
| 39 } |
| 40 |
| 41 // Checks then clears notifications for our extensions. |
| 42 testing::AssertionResult CheckNotifications(chrome::NotificationType t1, |
| 43 chrome::NotificationType t2) { |
| 44 std::vector<chrome::NotificationType> types; |
| 45 types.push_back(t1); |
| 46 types.push_back(t2); |
| 47 return CheckNotifications(types); |
| 48 } |
| 49 |
| 50 // Checks then clears notifications for our extensions. |
| 51 testing::AssertionResult CheckNotifications(chrome::NotificationType t1, |
| 52 chrome::NotificationType t2, |
| 53 chrome::NotificationType t3) { |
| 54 std::vector<chrome::NotificationType> types; |
| 55 types.push_back(t1); |
| 56 types.push_back(t2); |
| 57 types.push_back(t3); |
| 58 return CheckNotifications(types); |
| 59 } |
| 60 |
| 61 // Checks then clears notifications for our extensions. |
| 62 testing::AssertionResult CheckNotifications(chrome::NotificationType t1, |
| 63 chrome::NotificationType t2, |
| 64 chrome::NotificationType t3, |
| 65 chrome::NotificationType t4, |
| 66 chrome::NotificationType t5, |
| 67 chrome::NotificationType t6) { |
| 68 std::vector<chrome::NotificationType> types; |
| 69 types.push_back(t1); |
| 70 types.push_back(t2); |
| 71 types.push_back(t3); |
| 72 types.push_back(t4); |
| 73 types.push_back(t5); |
| 74 types.push_back(t6); |
| 75 return CheckNotifications(types); |
| 76 } |
| 77 |
| 78 private: |
| 79 // content::NotificationObserver implementation. |
| 80 virtual void Observe(int type, |
| 81 const content::NotificationSource& source, |
| 82 const content::NotificationDetails& details) OVERRIDE { |
| 83 switch (type) { |
| 84 case chrome::NOTIFICATION_EXTENSION_INSTALLED: { |
| 85 const Extension* extension = |
| 86 content::Details<const Extension>(details).ptr(); |
| 87 if (extension_ids_.count(extension->id())) |
| 88 notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| 89 break; |
| 90 } |
| 91 |
| 92 case chrome::NOTIFICATION_EXTENSION_LOADED: { |
| 93 const Extension* extension = |
| 94 content::Details<const Extension>(details).ptr(); |
| 95 if (extension_ids_.count(extension->id())) |
| 96 notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| 97 break; |
| 98 } |
| 99 |
| 100 case chrome::NOTIFICATION_EXTENSION_UNLOADED: { |
| 101 UnloadedExtensionInfo* reason = |
| 102 content::Details<UnloadedExtensionInfo>(details).ptr(); |
| 103 if (extension_ids_.count(reason->extension->id())) { |
| 104 notifications_.push_back(static_cast<chrome::NotificationType>(type)); |
| 105 // The only way that extensions are unloaded in these tests is |
| 106 // by blacklisting. |
| 107 EXPECT_EQ(extension_misc::UNLOAD_REASON_BLACKLIST, |
| 108 reason->reason); |
| 109 } |
| 110 break; |
| 111 } |
| 112 |
| 113 default: |
| 114 NOTREACHED(); |
| 115 break; |
| 116 } |
| 117 } |
| 118 |
| 119 // Checks then clears notifications for our extensions. |
| 120 testing::AssertionResult CheckNotifications( |
| 121 const std::vector<chrome::NotificationType>& types) { |
| 122 testing::AssertionResult result = (notifications_ == types) ? |
| 123 testing::AssertionSuccess() : |
| 124 testing::AssertionFailure() << "Expected " << Str(types) << ", " << |
| 125 "Got " << Str(notifications_); |
| 126 notifications_.clear(); |
| 127 return result; |
| 128 } |
| 129 |
| 130 std::string Str(const std::vector<chrome::NotificationType>& types) { |
| 131 std::string str = "["; |
| 132 bool needs_comma = false; |
| 133 for (std::vector<chrome::NotificationType>::const_iterator it = |
| 134 types.begin(); it != types.end(); ++it) { |
| 135 if (needs_comma) |
| 136 str += ","; |
| 137 needs_comma = true; |
| 138 str += base::StringPrintf("%d", *it); |
| 139 } |
| 140 return str + "]"; |
| 141 } |
| 142 |
| 143 const std::set<std::string> extension_ids_; |
| 144 |
| 145 std::vector<chrome::NotificationType> notifications_; |
| 146 |
| 147 content::NotificationRegistrar registrar_; |
| 148 }; |
| 149 |
| 150 // Stores the paths to CRX files of extensions, and the extension's ID. |
| 151 // Use arbitrary extensions; we're just testing blacklisting behavior. |
| 152 class CrxInfo { |
| 153 public: |
| 154 CrxInfo(const std::string& path, const std::string& id) |
| 155 : path_(path), id_(id) {} |
| 156 |
| 157 const std::string& path() { return path_; } |
| 158 const std::string& id() { return id_; } |
| 159 |
| 160 private: |
| 161 const std::string path_; |
| 162 const std::string id_; |
| 163 }; |
| 164 |
| 165 } // namespace |
| 166 |
| 167 class ExtensionBlacklistBrowserTest : public ExtensionBrowserTest { |
| 168 public: |
| 169 ExtensionBlacklistBrowserTest() |
| 170 : info_a_("install/install.crx", "ogdbpbegnmindpdjfafpmpicikegejdj"), |
| 171 info_b_("autoupdate/v1.crx", "ogjcoiohnmldgjemafoockdghcjciccf"), |
| 172 info_c_("hosted_app.crx", "kbmnembihfiondgfjekmnmcbddelicoi") {} |
| 173 |
| 174 virtual ~ExtensionBlacklistBrowserTest() {} |
| 175 |
| 176 protected: |
| 177 // Returns whether |extension| is strictly safe: in one of ExtensionService's |
| 178 // non-blacklisted extension sets, and not in its blacklisted extensions. |
| 179 testing::AssertionResult IsSafe(const Extension* extension) { |
| 180 std::string id = extension->id(); |
| 181 int include_mask = ExtensionService::INCLUDE_EVERYTHING & |
| 182 ~ExtensionService::INCLUDE_BLACKLISTED; |
| 183 if (!extension_service()->GetExtensionById(id, include_mask)) |
| 184 return testing::AssertionFailure() << id << " is safe"; |
| 185 return IsInValidState(extension); |
| 186 } |
| 187 |
| 188 // Returns whether |extension| is strictly blacklisted: in ExtensionService's |
| 189 // blacklist, and not in any of its other extension sets. |
| 190 testing::AssertionResult IsBlacklisted(const Extension* extension) { |
| 191 std::string id = extension->id(); |
| 192 if (!extension_service()->blacklisted_extensions()->Contains(id)) |
| 193 return testing::AssertionFailure() << id << " is not blacklisted"; |
| 194 return IsInValidState(extension); |
| 195 } |
| 196 |
| 197 std::set<std::string> GetTestExtensionIDs() { |
| 198 std::set<std::string> extension_ids; |
| 199 extension_ids.insert(info_a_.id()); |
| 200 extension_ids.insert(info_b_.id()); |
| 201 extension_ids.insert(info_c_.id()); |
| 202 return extension_ids; |
| 203 } |
| 204 |
| 205 Blacklist* blacklist() { |
| 206 return ExtensionSystem::Get(profile())->blacklist(); |
| 207 } |
| 208 |
| 209 CrxInfo info_a_; |
| 210 CrxInfo info_b_; |
| 211 CrxInfo info_c_; |
| 212 |
| 213 private: |
| 214 // Returns whether |extension| is either installed or blacklisted, but |
| 215 // neither both nor neither. |
| 216 testing::AssertionResult IsInValidState(const Extension* extension) { |
| 217 std::string id = extension->id(); |
| 218 bool is_blacklisted = |
| 219 extension_service()->blacklisted_extensions()->Contains(id); |
| 220 int safe_mask = ExtensionService::INCLUDE_EVERYTHING & |
| 221 ~ExtensionService::INCLUDE_BLACKLISTED; |
| 222 bool is_safe = extension_service()->GetExtensionById(id, safe_mask) != NULL; |
| 223 if (is_blacklisted && is_safe) { |
| 224 return testing::AssertionFailure() << |
| 225 id << " is both safe and in blacklisted_extensions"; |
| 226 } |
| 227 if (!is_blacklisted && !is_safe) { |
| 228 return testing::AssertionFailure() << |
| 229 id << " is neither safe nor in blacklisted_extensions"; |
| 230 } |
| 231 return testing::AssertionSuccess(); |
| 232 } |
| 233 }; |
| 234 |
| 235 // Stage 1: blacklisting when there weren't any extensions installed when the |
| 236 // browser started. |
| 237 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, PRE_Blacklist) { |
| 238 FilteringNotificationObserver notifications( |
| 239 content::NotificationService::AllSources(), GetTestExtensionIDs()); |
| 240 |
| 241 scoped_refptr<const Extension> extension_a = |
| 242 InstallExtension(test_data_dir_.AppendASCII(info_a_.path()), 1); |
| 243 scoped_refptr<const Extension> extension_b = |
| 244 InstallExtension(test_data_dir_.AppendASCII(info_b_.path()), 1); |
| 245 scoped_refptr<const Extension> extension_c = |
| 246 InstallExtension(test_data_dir_.AppendASCII(info_c_.path()), 1); |
| 247 |
| 248 EXPECT_TRUE(notifications.CheckNotifications( |
| 249 chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| 250 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 251 chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| 252 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 253 chrome::NOTIFICATION_EXTENSION_INSTALLED, |
| 254 chrome::NOTIFICATION_EXTENSION_LOADED)); |
| 255 |
| 256 ASSERT_TRUE(extension_a); |
| 257 ASSERT_TRUE(extension_b); |
| 258 ASSERT_EQ(info_a_.id(), extension_a->id()); |
| 259 ASSERT_EQ(info_b_.id(), extension_b->id()); |
| 260 ASSERT_EQ(info_c_.id(), extension_c->id()); |
| 261 |
| 262 std::vector<std::string> empty_vector; |
| 263 std::vector<std::string> vector_a(1, info_a_.id()); |
| 264 std::vector<std::string> vector_b(1, info_b_.id()); |
| 265 std::vector<std::string> vector_c(1, info_c_.id()); |
| 266 std::vector<std::string> vector_ab(1, info_a_.id()); |
| 267 vector_ab.push_back(info_b_.id()); |
| 268 std::vector<std::string> vector_bc(1, info_b_.id()); |
| 269 vector_bc.push_back(info_c_.id()); |
| 270 std::vector<std::string> vector_abc(1, info_a_.id()); |
| 271 vector_abc.push_back(info_b_.id()); |
| 272 vector_abc.push_back(info_c_.id()); |
| 273 |
| 274 EXPECT_TRUE(IsSafe(extension_a)); |
| 275 EXPECT_TRUE(IsSafe(extension_b)); |
| 276 EXPECT_TRUE(IsSafe(extension_c)); |
| 277 |
| 278 // Blacklist a and b. |
| 279 blacklist()->SetFromUpdater(vector_ab, "1"); |
| 280 base::RunLoop().RunUntilIdle(); |
| 281 |
| 282 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 283 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 284 EXPECT_TRUE(IsSafe(extension_c)); |
| 285 EXPECT_TRUE(notifications.CheckNotifications( |
| 286 chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| 287 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 288 |
| 289 // Un-blacklist a. |
| 290 blacklist()->SetFromUpdater(vector_b, "2"); |
| 291 base::RunLoop().RunUntilIdle(); |
| 292 |
| 293 EXPECT_TRUE(IsSafe(extension_a)); |
| 294 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 295 EXPECT_TRUE(IsSafe(extension_c)); |
| 296 EXPECT_TRUE(notifications.CheckNotifications( |
| 297 chrome::NOTIFICATION_EXTENSION_LOADED)); |
| 298 |
| 299 // Blacklist a then switch with c. |
| 300 blacklist()->SetFromUpdater(vector_ab, "3"); |
| 301 base::RunLoop().RunUntilIdle(); |
| 302 |
| 303 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 304 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 305 EXPECT_TRUE(IsSafe(extension_c)); |
| 306 EXPECT_TRUE(notifications.CheckNotifications( |
| 307 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 308 |
| 309 blacklist()->SetFromUpdater(vector_bc, "4"); |
| 310 base::RunLoop().RunUntilIdle(); |
| 311 |
| 312 EXPECT_TRUE(IsSafe(extension_a)); |
| 313 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 314 EXPECT_TRUE(IsBlacklisted(extension_c)); |
| 315 EXPECT_TRUE(notifications.CheckNotifications( |
| 316 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 317 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 318 |
| 319 // Add a to blacklist. |
| 320 blacklist()->SetFromUpdater(vector_abc, "5"); |
| 321 base::RunLoop().RunUntilIdle(); |
| 322 |
| 323 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 324 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 325 EXPECT_TRUE(IsBlacklisted(extension_c)); |
| 326 EXPECT_TRUE(notifications.CheckNotifications( |
| 327 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 328 |
| 329 // Clear blacklist. |
| 330 blacklist()->SetFromUpdater(empty_vector, "6"); |
| 331 base::RunLoop().RunUntilIdle(); |
| 332 |
| 333 EXPECT_TRUE(IsSafe(extension_a)); |
| 334 EXPECT_TRUE(IsSafe(extension_b)); |
| 335 EXPECT_TRUE(IsSafe(extension_c)); |
| 336 EXPECT_TRUE(notifications.CheckNotifications( |
| 337 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 338 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 339 chrome::NOTIFICATION_EXTENSION_LOADED)); |
| 340 |
| 341 // Add a and b back again for the next test. |
| 342 blacklist()->SetFromUpdater(vector_ab, "7"); |
| 343 base::RunLoop().RunUntilIdle(); |
| 344 |
| 345 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 346 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 347 EXPECT_TRUE(IsSafe(extension_c)); |
| 348 EXPECT_TRUE(notifications.CheckNotifications( |
| 349 chrome::NOTIFICATION_EXTENSION_UNLOADED, |
| 350 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 351 } |
| 352 |
| 353 // Stage 2: blacklisting with extensions A and B having been installed, |
| 354 // with A actually in the blacklist. |
| 355 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest, Blacklist) { |
| 356 FilteringNotificationObserver notifications( |
| 357 content::Source<Profile>(profile()), GetTestExtensionIDs()); |
| 358 |
| 359 scoped_refptr<const Extension> extension_a = |
| 360 extension_service()->blacklisted_extensions()->GetByID(info_a_.id()); |
| 361 ASSERT_TRUE(extension_a); |
| 362 |
| 363 scoped_refptr<const Extension> extension_b = |
| 364 extension_service()->blacklisted_extensions()->GetByID(info_b_.id()); |
| 365 ASSERT_TRUE(extension_b); |
| 366 |
| 367 scoped_refptr<const Extension> extension_c = |
| 368 extension_service()->extensions()->GetByID(info_c_.id()); |
| 369 ASSERT_TRUE(extension_c); |
| 370 |
| 371 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 372 EXPECT_TRUE(IsBlacklisted(extension_b)); |
| 373 EXPECT_TRUE(IsSafe(extension_c)); |
| 374 |
| 375 // Make sure that we can still blacklist c and unblacklist b. |
| 376 std::vector<std::string> vector_ac(1, extension_a->id()); |
| 377 vector_ac.push_back(extension_c->id()); |
| 378 blacklist()->SetFromUpdater(vector_ac, "8"); |
| 379 base::RunLoop().RunUntilIdle(); |
| 380 |
| 381 EXPECT_TRUE(IsBlacklisted(extension_a)); |
| 382 EXPECT_TRUE(IsSafe(extension_b)); |
| 383 EXPECT_TRUE(IsBlacklisted(extension_c)); |
| 384 EXPECT_TRUE(notifications.CheckNotifications( |
| 385 chrome::NOTIFICATION_EXTENSION_LOADED, |
| 386 chrome::NOTIFICATION_EXTENSION_UNLOADED)); |
| 387 } |
| 388 |
| 389 } // namespace extensions |
OLD | NEW |