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.
5 #include "base/run_loop.h"
6 #include "base/strings/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"
20 namespace extensions
{
24 // Records notifications, but only for extensions with specific IDs.
25 class FilteringNotificationObserver
: public content::NotificationObserver
{
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
);
36 // Checks then clears notifications for our extensions.
37 testing::AssertionResult
CheckNotifications(chrome::NotificationType type
) {
38 return CheckNotifications(std::vector
<chrome::NotificationType
>(1, type
));
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
;
47 return CheckNotifications(types
);
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
;
58 return CheckNotifications(types
);
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
;
75 return CheckNotifications(types
);
79 // content::NotificationObserver implementation.
80 virtual void Observe(int type
,
81 const content::NotificationSource
& source
,
82 const content::NotificationDetails
& details
) OVERRIDE
{
84 case chrome::NOTIFICATION_EXTENSION_INSTALLED
: {
85 const Extension
* extension
=
86 content::Details
<const InstalledExtensionInfo
>(details
)->extension
;
87 if (extension_ids_
.count(extension
->id()))
88 notifications_
.push_back(static_cast<chrome::NotificationType
>(type
));
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
));
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
107 EXPECT_EQ(extension_misc::UNLOAD_REASON_BLACKLIST
,
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();
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
) {
138 str
+= base::StringPrintf("%d", *it
);
143 const std::set
<std::string
> extension_ids_
;
145 std::vector
<chrome::NotificationType
> notifications_
;
147 content::NotificationRegistrar registrar_
;
150 // Stores the paths to CRX files of extensions, and the extension's ID.
151 // Use arbitrary extensions; we're just testing blacklisting behavior.
154 CrxInfo(const std::string
& path
, const std::string
& id
)
155 : path_(path
), id_(id
) {}
157 const std::string
& path() { return path_
; }
158 const std::string
& id() { return id_
; }
161 const std::string path_
;
162 const std::string id_
;
167 class ExtensionBlacklistBrowserTest
: public ExtensionBrowserTest
{
169 ExtensionBlacklistBrowserTest()
170 : info_a_("install/install.crx", "ogdbpbegnmindpdjfafpmpicikegejdj"),
171 info_b_("autoupdate/v1.crx", "ogjcoiohnmldgjemafoockdghcjciccf"),
172 info_c_("hosted_app.crx", "kbmnembihfiondgfjekmnmcbddelicoi"),
173 // Just disable the safe browsing altogether.
174 // TODO(kalman): a different approach will be needed when the blacklist
175 // comes entirely from safe browsing.
176 scoped_blacklist_database_manager_(
177 scoped_refptr
<SafeBrowsingDatabaseManager
>(NULL
)) {}
179 virtual ~ExtensionBlacklistBrowserTest() {}
182 // Returns whether |extension| is strictly safe: in one of ExtensionService's
183 // non-blacklisted extension sets, and not in its blacklisted extensions.
184 testing::AssertionResult
IsSafe(const Extension
* extension
) {
185 std::string id
= extension
->id();
186 int include_mask
= ExtensionService::INCLUDE_EVERYTHING
&
187 ~ExtensionService::INCLUDE_BLACKLISTED
;
188 if (!extension_service()->GetExtensionById(id
, include_mask
))
189 return testing::AssertionFailure() << id
<< " is safe";
190 return IsInValidState(extension
);
193 // Returns whether |extension| is strictly blacklisted: in ExtensionService's
194 // blacklist, and not in any of its other extension sets.
195 testing::AssertionResult
IsBlacklisted(const Extension
* extension
) {
196 std::string id
= extension
->id();
197 if (!extension_service()->blacklisted_extensions()->Contains(id
))
198 return testing::AssertionFailure() << id
<< " is not blacklisted";
199 return IsInValidState(extension
);
202 std::set
<std::string
> GetTestExtensionIDs() {
203 std::set
<std::string
> extension_ids
;
204 extension_ids
.insert(info_a_
.id());
205 extension_ids
.insert(info_b_
.id());
206 extension_ids
.insert(info_c_
.id());
207 return extension_ids
;
210 Blacklist
* blacklist() {
211 return ExtensionSystem::Get(profile())->blacklist();
219 // Returns whether |extension| is either installed or blacklisted, but
220 // neither both nor neither.
221 testing::AssertionResult
IsInValidState(const Extension
* extension
) {
222 std::string id
= extension
->id();
223 bool is_blacklisted
=
224 extension_service()->blacklisted_extensions()->Contains(id
);
225 int safe_mask
= ExtensionService::INCLUDE_EVERYTHING
&
226 ~ExtensionService::INCLUDE_BLACKLISTED
;
227 bool is_safe
= extension_service()->GetExtensionById(id
, safe_mask
) != NULL
;
228 if (is_blacklisted
&& is_safe
) {
229 return testing::AssertionFailure() <<
230 id
<< " is both safe and in blacklisted_extensions";
232 if (!is_blacklisted
&& !is_safe
) {
233 return testing::AssertionFailure() <<
234 id
<< " is neither safe nor in blacklisted_extensions";
236 return testing::AssertionSuccess();
239 Blacklist::ScopedDatabaseManagerForTest scoped_blacklist_database_manager_
;
242 // Stage 1: blacklisting when there weren't any extensions installed when the
244 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest
, PRE_Blacklist
) {
245 FilteringNotificationObserver
notifications(
246 content::NotificationService::AllSources(), GetTestExtensionIDs());
248 scoped_refptr
<const Extension
> extension_a
=
249 InstallExtension(test_data_dir_
.AppendASCII(info_a_
.path()), 1);
250 scoped_refptr
<const Extension
> extension_b
=
251 InstallExtension(test_data_dir_
.AppendASCII(info_b_
.path()), 1);
252 scoped_refptr
<const Extension
> extension_c
=
253 InstallExtension(test_data_dir_
.AppendASCII(info_c_
.path()), 1);
255 EXPECT_TRUE(notifications
.CheckNotifications(
256 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
257 chrome::NOTIFICATION_EXTENSION_LOADED
,
258 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
259 chrome::NOTIFICATION_EXTENSION_LOADED
,
260 chrome::NOTIFICATION_EXTENSION_INSTALLED
,
261 chrome::NOTIFICATION_EXTENSION_LOADED
));
263 ASSERT_TRUE(extension_a
.get());
264 ASSERT_TRUE(extension_b
.get());
265 ASSERT_EQ(info_a_
.id(), extension_a
->id());
266 ASSERT_EQ(info_b_
.id(), extension_b
->id());
267 ASSERT_EQ(info_c_
.id(), extension_c
->id());
269 std::vector
<std::string
> empty_vector
;
270 std::vector
<std::string
> vector_a(1, info_a_
.id());
271 std::vector
<std::string
> vector_b(1, info_b_
.id());
272 std::vector
<std::string
> vector_c(1, info_c_
.id());
273 std::vector
<std::string
> vector_ab(1, info_a_
.id());
274 vector_ab
.push_back(info_b_
.id());
275 std::vector
<std::string
> vector_bc(1, info_b_
.id());
276 vector_bc
.push_back(info_c_
.id());
277 std::vector
<std::string
> vector_abc(1, info_a_
.id());
278 vector_abc
.push_back(info_b_
.id());
279 vector_abc
.push_back(info_c_
.id());
281 EXPECT_TRUE(IsSafe(extension_a
.get()));
282 EXPECT_TRUE(IsSafe(extension_b
.get()));
283 EXPECT_TRUE(IsSafe(extension_c
.get()));
285 // Blacklist a and b.
286 blacklist()->SetFromUpdater(vector_ab
, "1");
287 base::RunLoop().RunUntilIdle();
289 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
290 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
291 EXPECT_TRUE(IsSafe(extension_c
.get()));
292 EXPECT_TRUE(notifications
.CheckNotifications(
293 chrome::NOTIFICATION_EXTENSION_UNLOADED
,
294 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
297 blacklist()->SetFromUpdater(vector_b
, "2");
298 base::RunLoop().RunUntilIdle();
300 EXPECT_TRUE(IsSafe(extension_a
.get()));
301 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
302 EXPECT_TRUE(IsSafe(extension_c
.get()));
304 notifications
.CheckNotifications(chrome::NOTIFICATION_EXTENSION_LOADED
));
306 // Blacklist a then switch with c.
307 blacklist()->SetFromUpdater(vector_ab
, "3");
308 base::RunLoop().RunUntilIdle();
310 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
311 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
312 EXPECT_TRUE(IsSafe(extension_c
.get()));
313 EXPECT_TRUE(notifications
.CheckNotifications(
314 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
316 blacklist()->SetFromUpdater(vector_bc
, "4");
317 base::RunLoop().RunUntilIdle();
319 EXPECT_TRUE(IsSafe(extension_a
.get()));
320 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
321 EXPECT_TRUE(IsBlacklisted(extension_c
.get()));
322 EXPECT_TRUE(notifications
.CheckNotifications(
323 chrome::NOTIFICATION_EXTENSION_LOADED
,
324 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
326 // Add a to blacklist.
327 blacklist()->SetFromUpdater(vector_abc
, "5");
328 base::RunLoop().RunUntilIdle();
330 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
331 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
332 EXPECT_TRUE(IsBlacklisted(extension_c
.get()));
333 EXPECT_TRUE(notifications
.CheckNotifications(
334 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
337 blacklist()->SetFromUpdater(empty_vector
, "6");
338 base::RunLoop().RunUntilIdle();
340 EXPECT_TRUE(IsSafe(extension_a
.get()));
341 EXPECT_TRUE(IsSafe(extension_b
.get()));
342 EXPECT_TRUE(IsSafe(extension_c
.get()));
344 notifications
.CheckNotifications(chrome::NOTIFICATION_EXTENSION_LOADED
,
345 chrome::NOTIFICATION_EXTENSION_LOADED
,
346 chrome::NOTIFICATION_EXTENSION_LOADED
));
348 // Add a and b back again for the next test.
349 blacklist()->SetFromUpdater(vector_ab
, "7");
350 base::RunLoop().RunUntilIdle();
352 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
353 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
354 EXPECT_TRUE(IsSafe(extension_c
.get()));
355 EXPECT_TRUE(notifications
.CheckNotifications(
356 chrome::NOTIFICATION_EXTENSION_UNLOADED
,
357 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
360 // Stage 2: blacklisting with extensions A and B having been installed,
361 // with A actually in the blacklist.
362 IN_PROC_BROWSER_TEST_F(ExtensionBlacklistBrowserTest
, Blacklist
) {
363 FilteringNotificationObserver
notifications(
364 content::Source
<Profile
>(profile()), GetTestExtensionIDs());
366 scoped_refptr
<const Extension
> extension_a
=
367 extension_service()->blacklisted_extensions()->GetByID(info_a_
.id());
368 ASSERT_TRUE(extension_a
.get());
370 scoped_refptr
<const Extension
> extension_b
=
371 extension_service()->blacklisted_extensions()->GetByID(info_b_
.id());
372 ASSERT_TRUE(extension_b
.get());
374 scoped_refptr
<const Extension
> extension_c
=
375 extension_service()->extensions()->GetByID(info_c_
.id());
376 ASSERT_TRUE(extension_c
.get());
378 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
379 EXPECT_TRUE(IsBlacklisted(extension_b
.get()));
380 EXPECT_TRUE(IsSafe(extension_c
.get()));
382 // Make sure that we can still blacklist c and unblacklist b.
383 std::vector
<std::string
> vector_ac(1, extension_a
->id());
384 vector_ac
.push_back(extension_c
->id());
385 blacklist()->SetFromUpdater(vector_ac
, "8");
386 base::RunLoop().RunUntilIdle();
388 EXPECT_TRUE(IsBlacklisted(extension_a
.get()));
389 EXPECT_TRUE(IsSafe(extension_b
.get()));
390 EXPECT_TRUE(IsBlacklisted(extension_c
.get()));
391 EXPECT_TRUE(notifications
.CheckNotifications(
392 chrome::NOTIFICATION_EXTENSION_LOADED
,
393 chrome::NOTIFICATION_EXTENSION_UNLOADED
));
396 } // namespace extensions