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 "chrome/browser/metrics/extensions_metrics_provider.h"
6
7 #include <stdint.h>
8
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12
13 #include "base/bind.h"
14 #include "base/strings/string16.h"
15 #include "base/test/task_environment.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/extensions/extension_service_test_base.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "chrome/test/base/testing_profile_manager.h"
21 #include "components/metrics/client_info.h"
22 #include "components/metrics/metrics_service.h"
23 #include "components/metrics/metrics_state_manager.h"
24 #include "components/metrics/test/test_enabled_state_provider.h"
25 #include "components/prefs/testing_pref_service.h"
26 #include "extensions/browser/disable_reason.h"
27 #include "extensions/browser/extension_prefs.h"
28 #include "extensions/common/extension.h"
29 #include "extensions/common/extension_builder.h"
30 #include "extensions/common/extension_set.h"
31 #include "extensions/common/scoped_worker_based_extensions_channel.h"
32 #include "extensions/common/value_builder.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "third_party/metrics_proto/extension_install.pb.h"
35 #include "third_party/metrics_proto/system_profile.pb.h"
36
37 using metrics::ExtensionInstallProto;
38 using extensions::Extension;
39 using extensions::ExtensionBuilder;
40 using extensions::Manifest;
41 using extensions::DictionaryBuilder;
42
43 namespace {
44
StoreNoClientInfoBackup(const metrics::ClientInfo &)45 void StoreNoClientInfoBackup(const metrics::ClientInfo& /* client_info */) {
46 }
47
ReturnNoBackup()48 std::unique_ptr<metrics::ClientInfo> ReturnNoBackup() {
49 return std::unique_ptr<metrics::ClientInfo>();
50 }
51
52 class TestExtensionsMetricsProvider : public ExtensionsMetricsProvider {
53 public:
TestExtensionsMetricsProvider(metrics::MetricsStateManager * metrics_state_manager)54 explicit TestExtensionsMetricsProvider(
55 metrics::MetricsStateManager* metrics_state_manager)
56 : ExtensionsMetricsProvider(metrics_state_manager) {}
57
58 // Makes the protected HashExtension method available to testing code.
59 using ExtensionsMetricsProvider::HashExtension;
60
61 protected:
62 // Override the GetInstalledExtensions method to return a set of extensions
63 // for tests.
GetInstalledExtensions(Profile * profile)64 std::unique_ptr<extensions::ExtensionSet> GetInstalledExtensions(
65 Profile* profile) override {
66 std::unique_ptr<extensions::ExtensionSet> extensions(
67 new extensions::ExtensionSet());
68 scoped_refptr<const extensions::Extension> extension;
69 extension = extensions::ExtensionBuilder()
70 .SetManifest(extensions::DictionaryBuilder()
71 .Set("name", "Test extension")
72 .Set("version", "1.0.0")
73 .Set("manifest_version", 2)
74 .Build())
75 .SetID("ahfgeienlihckogmohjhadlkjgocpleb")
76 .Build();
77 extensions->Insert(extension);
78 extension = extensions::ExtensionBuilder()
79 .SetManifest(extensions::DictionaryBuilder()
80 .Set("name", "Test extension 2")
81 .Set("version", "1.0.0")
82 .Set("manifest_version", 2)
83 .Build())
84 .SetID("pknkgggnfecklokoggaggchhaebkajji")
85 .Build();
86 extensions->Insert(extension);
87 extension = extensions::ExtensionBuilder()
88 .SetManifest(extensions::DictionaryBuilder()
89 .Set("name", "Colliding Extension")
90 .Set("version", "1.0.0")
91 .Set("manifest_version", 2)
92 .Build())
93 .SetID("mdhofdjgenpkhlmddfaegdjddcecipmo")
94 .Build();
95 extensions->Insert(extension);
96 return extensions;
97 }
98
99 // Override GetClientID() to return a specific value on which test
100 // expectations are based.
GetClientID() const101 uint64_t GetClientID() const override { return 0x3f1bfee9; }
102 };
103
104 } // namespace
105
106 // Checks that the hash function used to hide precise extension IDs produces
107 // the expected values.
TEST(ExtensionsMetricsProvider,HashExtension)108 TEST(ExtensionsMetricsProvider, HashExtension) {
109 EXPECT_EQ(978,
110 TestExtensionsMetricsProvider::HashExtension(
111 "ahfgeienlihckogmohjhadlkjgocpleb", 0));
112 EXPECT_EQ(10,
113 TestExtensionsMetricsProvider::HashExtension(
114 "ahfgeienlihckogmohjhadlkjgocpleb", 3817));
115 EXPECT_EQ(1007,
116 TestExtensionsMetricsProvider::HashExtension(
117 "pknkgggnfecklokoggaggchhaebkajji", 3817));
118 EXPECT_EQ(10,
119 TestExtensionsMetricsProvider::HashExtension(
120 "mdhofdjgenpkhlmddfaegdjddcecipmo", 3817));
121 }
122
123 // Checks that the fake set of extensions provided by
124 // TestExtensionsMetricsProvider is encoded properly.
TEST(ExtensionsMetricsProvider,SystemProtoEncoding)125 TEST(ExtensionsMetricsProvider, SystemProtoEncoding) {
126 metrics::SystemProfileProto system_profile;
127 base::test::TaskEnvironment task_environment;
128 TestingProfileManager testing_profile_manager(
129 TestingBrowserProcess::GetGlobal());
130 ASSERT_TRUE(testing_profile_manager.SetUp());
131 TestingPrefServiceSimple local_state;
132 metrics::TestEnabledStateProvider enabled_state_provider(true, true);
133 metrics::MetricsService::RegisterPrefs(local_state.registry());
134 std::unique_ptr<metrics::MetricsStateManager> metrics_state_manager(
135 metrics::MetricsStateManager::Create(
136 &local_state, &enabled_state_provider, base::string16(),
137 base::BindRepeating(&StoreNoClientInfoBackup),
138 base::BindRepeating(&ReturnNoBackup)));
139 TestExtensionsMetricsProvider extension_metrics(metrics_state_manager.get());
140 extension_metrics.ProvideSystemProfileMetrics(&system_profile);
141 ASSERT_EQ(2, system_profile.occupied_extension_bucket_size());
142 EXPECT_EQ(10, system_profile.occupied_extension_bucket(0));
143 EXPECT_EQ(1007, system_profile.occupied_extension_bucket(1));
144 }
145
146 class ExtensionMetricsProviderInstallsTest
147 : public extensions::ExtensionServiceTestBase {
148 public:
ExtensionMetricsProviderInstallsTest()149 ExtensionMetricsProviderInstallsTest() {}
~ExtensionMetricsProviderInstallsTest()150 ~ExtensionMetricsProviderInstallsTest() override {}
151
SetUp()152 void SetUp() override {
153 ExtensionServiceTestBase::SetUp();
154 InitializeEmptyExtensionService();
155 prefs_ = extensions::ExtensionPrefs::Get(profile());
156
157 last_sample_time_ = base::Time::Now() - base::TimeDelta::FromMinutes(30);
158 }
159
ConstructProto(const Extension & extension)160 ExtensionInstallProto ConstructProto(const Extension& extension) {
161 return ExtensionsMetricsProvider::ConstructInstallProtoForTesting(
162 extension, prefs_, last_sample_time_);
163 }
GetInstallsForProfile()164 std::vector<ExtensionInstallProto> GetInstallsForProfile() {
165 return ExtensionsMetricsProvider::GetInstallsForProfileForTesting(
166 profile(), last_sample_time_);
167 }
168
prefs()169 extensions::ExtensionPrefs* prefs() { return prefs_; }
set_last_sample_time(base::Time last_sample_time)170 void set_last_sample_time(base::Time last_sample_time) {
171 last_sample_time_ = last_sample_time;
172 }
173
174 private:
175 extensions::ExtensionPrefs* prefs_ = nullptr;
176 base::Time last_sample_time_;
177
178 DISALLOW_COPY_AND_ASSIGN(ExtensionMetricsProviderInstallsTest);
179 };
180
181 // Tests the various aspects of constructing a relevant proto for a given
182 // extension installation.
TEST_F(ExtensionMetricsProviderInstallsTest,TestProtoConstruction)183 TEST_F(ExtensionMetricsProviderInstallsTest, TestProtoConstruction) {
184 auto add_extension = [this](const Extension* extension) {
185 prefs()->OnExtensionInstalled(extension, Extension::ENABLED,
186 syncer::StringOrdinal(), std::string());
187 };
188
189 {
190 // Test basic prototype construction. All fields should be present, except
191 // disable reasons (which should be empty).
192 scoped_refptr<const Extension> extension =
193 ExtensionBuilder("test").SetLocation(Manifest::INTERNAL).Build();
194 add_extension(extension.get());
195 ExtensionInstallProto install = ConstructProto(*extension);
196 EXPECT_TRUE(install.has_type());
197 EXPECT_EQ(ExtensionInstallProto::EXTENSION, install.type());
198
199 EXPECT_TRUE(install.has_install_location());
200 EXPECT_EQ(ExtensionInstallProto::INTERNAL, install.install_location());
201
202 EXPECT_TRUE(install.has_manifest_version());
203 EXPECT_EQ(2, install.manifest_version());
204
205 EXPECT_TRUE(install.has_action_type());
206 EXPECT_EQ(ExtensionInstallProto::NO_ACTION, install.action_type());
207
208 EXPECT_TRUE(install.has_has_file_access());
209 EXPECT_FALSE(install.has_file_access());
210
211 EXPECT_TRUE(install.has_has_incognito_access());
212 EXPECT_FALSE(install.has_incognito_access());
213
214 EXPECT_TRUE(install.has_updates_from_store());
215 EXPECT_FALSE(install.updates_from_store());
216
217 EXPECT_TRUE(install.has_is_from_bookmark());
218 EXPECT_FALSE(install.is_from_bookmark());
219
220 EXPECT_TRUE(install.has_is_converted_from_user_script());
221 EXPECT_FALSE(install.is_converted_from_user_script());
222
223 EXPECT_TRUE(install.has_is_default_installed());
224 EXPECT_FALSE(install.is_default_installed());
225
226 EXPECT_TRUE(install.has_is_oem_installed());
227 EXPECT_FALSE(install.is_oem_installed());
228
229 EXPECT_TRUE(install.has_background_script_type());
230 EXPECT_EQ(ExtensionInstallProto::NO_BACKGROUND_SCRIPT,
231 install.background_script_type());
232
233 EXPECT_EQ(0, install.disable_reasons_size());
234
235 EXPECT_TRUE(install.has_blacklist_state());
236 EXPECT_EQ(ExtensionInstallProto::NOT_BLACKLISTED,
237 install.blacklist_state());
238
239 EXPECT_TRUE(install.has_installed_in_this_sample_period());
240 EXPECT_TRUE(install.installed_in_this_sample_period());
241 }
242
243 // It's not helpful to exhaustively test each possible variation of each
244 // field in the proto (since in many cases the test code would then be
245 // re-writing the original code), but we test a few of the more interesting
246 // cases.
247
248 {
249 // Test the type() field; extensions of different types should be reported
250 // as such.
251 scoped_refptr<const Extension> extension =
252 ExtensionBuilder("app", ExtensionBuilder::Type::PLATFORM_APP)
253 .SetLocation(Manifest::INTERNAL)
254 .Build();
255 add_extension(extension.get());
256 ExtensionInstallProto install = ConstructProto(*extension);
257 EXPECT_EQ(ExtensionInstallProto::PLATFORM_APP, install.type());
258 }
259
260 {
261 // Test the install location.
262 scoped_refptr<const Extension> extension =
263 ExtensionBuilder("unpacked").SetLocation(Manifest::UNPACKED).Build();
264 add_extension(extension.get());
265 ExtensionInstallProto install = ConstructProto(*extension);
266 EXPECT_EQ(ExtensionInstallProto::UNPACKED, install.install_location());
267 }
268
269 {
270 // Test the extension action as a browser action.
271 scoped_refptr<const Extension> extension =
272 ExtensionBuilder("browser_action")
273 .SetLocation(Manifest::INTERNAL)
274 .SetAction(ExtensionBuilder::ActionType::BROWSER_ACTION)
275 .Build();
276 add_extension(extension.get());
277 ExtensionInstallProto install = ConstructProto(*extension);
278 EXPECT_EQ(ExtensionInstallProto::BROWSER_ACTION, install.action_type());
279 }
280
281 {
282 // Test the extension action as a page action.
283 scoped_refptr<const Extension> extension =
284 ExtensionBuilder("page_action")
285 .SetLocation(Manifest::INTERNAL)
286 .SetAction(ExtensionBuilder::ActionType::PAGE_ACTION)
287 .Build();
288 add_extension(extension.get());
289 ExtensionInstallProto install = ConstructProto(*extension);
290 EXPECT_EQ(ExtensionInstallProto::PAGE_ACTION, install.action_type());
291 }
292
293 {
294 // Test the disable reasons field.
295 scoped_refptr<const Extension> extension =
296 ExtensionBuilder("disable_reasons")
297 .SetLocation(Manifest::INTERNAL)
298 .Build();
299 add_extension(extension.get());
300 prefs()->SetExtensionDisabled(
301 extension->id(), extensions::disable_reason::DISABLE_USER_ACTION);
302 {
303 ExtensionInstallProto install = ConstructProto(*extension);
304 ASSERT_EQ(1, install.disable_reasons_size());
305 EXPECT_EQ(ExtensionInstallProto::USER_ACTION,
306 install.disable_reasons().Get(0));
307 }
308 // Adding additional disable reasons should result in all reasons being
309 // reported.
310 prefs()->AddDisableReason(extension->id(),
311 extensions::disable_reason::DISABLE_CORRUPTED);
312 {
313 ExtensionInstallProto install = ConstructProto(*extension);
314 ASSERT_EQ(2, install.disable_reasons_size());
315 EXPECT_EQ(ExtensionInstallProto::USER_ACTION,
316 install.disable_reasons().Get(0));
317 EXPECT_EQ(ExtensionInstallProto::CORRUPTED,
318 install.disable_reasons().Get(1));
319 }
320 // Adding additional disable reasons should result in all reasons being
321 // reported.
322 prefs()->AddDisableReason(
323 extension->id(),
324 extensions::disable_reason::DISABLE_REMOTELY_FOR_MALWARE);
325 {
326 ExtensionInstallProto install = ConstructProto(*extension);
327 ASSERT_EQ(3, install.disable_reasons_size());
328 EXPECT_EQ(ExtensionInstallProto::DISABLE_REMOTELY_FOR_MALWARE,
329 install.disable_reasons().Get(2));
330 }
331 }
332
333 {
334 // Test that event pages are reported correctly.
335 DictionaryBuilder background;
336 background.Set("persistent", false)
337 .Set("scripts", extensions::ListBuilder().Append("script.js").Build());
338 scoped_refptr<const Extension> extension =
339 ExtensionBuilder("event_page")
340 .SetLocation(Manifest::INTERNAL)
341 .MergeManifest(DictionaryBuilder()
342 .Set("background", background.Build())
343 .Build())
344 .Build();
345 add_extension(extension.get());
346 ExtensionInstallProto install = ConstructProto(*extension);
347 EXPECT_EQ(ExtensionInstallProto::EVENT_PAGE,
348 install.background_script_type());
349 }
350
351 {
352 // Test that persistent background pages are reported correctly.
353 DictionaryBuilder background;
354 background.Set("persistent", true)
355 .Set("scripts", extensions::ListBuilder().Append("script.js").Build());
356 scoped_refptr<const Extension> extension =
357 ExtensionBuilder("persisent_background")
358 .SetLocation(Manifest::INTERNAL)
359 .MergeManifest(DictionaryBuilder()
360 .Set("background", background.Build())
361 .Build())
362 .Build();
363 add_extension(extension.get());
364 ExtensionInstallProto install = ConstructProto(*extension);
365 EXPECT_EQ(ExtensionInstallProto::PERSISTENT_BACKGROUND_PAGE,
366 install.background_script_type());
367 }
368 {
369 // Test that service worker scripts are reported correctly.
370 extensions::ScopedWorkerBasedExtensionsChannel worker_channel_override;
371 scoped_refptr<const Extension> extension =
372 ExtensionBuilder("service worker")
373 .SetBackgroundContext(
374 ExtensionBuilder::BackgroundContext::SERVICE_WORKER)
375 .Build();
376 add_extension(extension.get());
377 ExtensionInstallProto install = ConstructProto(*extension);
378 EXPECT_EQ(ExtensionInstallProto::SERVICE_WORKER,
379 install.background_script_type());
380 }
381
382 {
383 // Test changing the blacklist state.
384 scoped_refptr<const Extension> extension =
385 ExtensionBuilder("blacklist").SetLocation(Manifest::INTERNAL).Build();
386 add_extension(extension.get());
387 prefs()->SetExtensionBlocklistState(
388 extension->id(), extensions::BLOCKLISTED_SECURITY_VULNERABILITY);
389 ExtensionInstallProto install = ConstructProto(*extension);
390 EXPECT_EQ(ExtensionInstallProto::BLACKLISTED_SECURITY_VULNERABILITY,
391 install.blacklist_state());
392 }
393
394 {
395 // Test that the installed_in_this_sample_period boolean is correctly
396 // reported.
397 scoped_refptr<const Extension> extension =
398 ExtensionBuilder("installtime").SetLocation(Manifest::INTERNAL).Build();
399 add_extension(extension.get());
400 set_last_sample_time(base::Time::Now() + base::TimeDelta::FromMinutes(60));
401 ExtensionInstallProto install = ConstructProto(*extension);
402 EXPECT_FALSE(install.installed_in_this_sample_period());
403 }
404 }
405
406 // Tests that we retrieve all extensions associated with a given profile.
TEST_F(ExtensionMetricsProviderInstallsTest,TestGettingAllExtensionsInProfile)407 TEST_F(ExtensionMetricsProviderInstallsTest,
408 TestGettingAllExtensionsInProfile) {
409 scoped_refptr<const Extension> extension =
410 ExtensionBuilder("extension").Build();
411 service()->AddExtension(extension.get());
412 scoped_refptr<const Extension> app =
413 ExtensionBuilder("app", ExtensionBuilder::Type::PLATFORM_APP).Build();
414 service()->AddExtension(app.get());
415 service()->DisableExtension(app->id(),
416 extensions::disable_reason::DISABLE_USER_ACTION);
417
418 std::vector<ExtensionInstallProto> installs = GetInstallsForProfile();
419 // There should be two installs total.
420 ASSERT_EQ(2u, installs.size());
421 // One should be the extension, and the other should be the app. We don't
422 // check the specifics of the proto, since that's tested above.
423 EXPECT_TRUE(std::any_of(installs.begin(), installs.end(),
424 [](const ExtensionInstallProto& install) {
425 return install.type() ==
426 ExtensionInstallProto::EXTENSION;
427 }));
428 EXPECT_TRUE(std::any_of(installs.begin(), installs.end(),
429 [](const ExtensionInstallProto& install) {
430 return install.type() ==
431 ExtensionInstallProto::PLATFORM_APP;
432 }));
433 }
434