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