Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: chrome/browser/prefs/tracked/pref_hash_browsertest.cc

Issue 431973002: Add integration browser tests for settings hardening. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/prefs/chrome_pref_service_factory.cc ('k') | chrome/chrome_tests.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <string>
6
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/files/file_path.h"
10 #include "base/json/json_file_value_serializer.h"
11 #include "base/metrics/histogram_base.h"
12 #include "base/metrics/histogram_samples.h"
13 #include "base/metrics/statistics_recorder.h"
14 #include "base/path_service.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/values.h"
19 #include "build/build_config.h"
20 #include "chrome/browser/extensions/extension_browsertest.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/prefs/chrome_pref_service_factory.h"
23 #include "chrome/browser/prefs/profile_pref_store_manager.h"
24 #include "chrome/browser/prefs/session_startup_pref.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/test/base/testing_profile.h"
31 #include "components/search_engines/default_search_manager.h"
32 #include "content/public/common/content_switches.h"
33
34 #if defined(OS_CHROMEOS)
35 #include "chromeos/chromeos_switches.h"
36 #endif
37
38 namespace {
39
40 // Extension ID of chrome/test/data/extensions/good.crx
41 const char kGoodCrxId[] = "ldnnhddmnhbkjipkidpdiheffobcpfmf";
42
43 // Returns the number of times |histogram_name| was reported so far; adding the
44 // results of the first 100 buckets (there are only ~18 reporting IDs as of this
45 // writing; varies depending on the platform). If |expect_zero| is true, this
46 // method will explicitly report IDs that are non-zero for ease of diagnosis.
47 int GetTrackedPrefHistogramCount(const char* histogram_name, bool expect_zero) {
48 const base::HistogramBase* histogram =
49 base::StatisticsRecorder::FindHistogram(histogram_name);
50 if (!histogram)
51 return 0;
52
53 scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
54 int sum = 0;
55 for (int i = 0; i < 18; ++i) {
56 int count_for_id = samples->GetCount(i);
57 sum += count_for_id;
58
59 //FIXME needed?!
60 // FIXME use size_t for return value here?
61 if (expect_zero)
62 EXPECT_EQ(0, count_for_id) << "Faulty reporting_id: " << i;
gab 2014/07/31 13:19:14 Please ignore this for now. I'm using it for debu
63 }
64 return sum;
65 }
66
67 base::DictionaryValue* ReadPrefsDictionary(
68 JSONFileValueSerializer* serializer) {
69 int error_code = JSONFileValueSerializer::JSON_NO_ERROR;
70 std::string error_str;
71 base::Value* prefs = serializer->Deserialize(&error_code, &error_str);
72 if (!prefs || error_code != JSONFileValueSerializer::JSON_NO_ERROR) {
73 ADD_FAILURE() << "Error #" << error_code << ": " << error_str;
74 return NULL;
75 }
76 if (!prefs->IsType(base::Value::TYPE_DICTIONARY)) {
77 ADD_FAILURE();
78 return NULL;
79 }
80 return static_cast<base::DictionaryValue*>(prefs);
81 }
82
83 #define PREF_HASH_BROWSER_TEST(fixture, test_name) \
84 IN_PROC_BROWSER_TEST_P(fixture, PRE_##test_name) { \
85 SetupPreferences(); \
86 } \
87 IN_PROC_BROWSER_TEST_P(fixture, test_name) { \
88 VerifyReactionToPrefAttack(); \
89 } \
90 INSTANTIATE_TEST_CASE_P( \
91 fixture##Instance, \
92 fixture, \
93 testing::Values( \
94 chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement, \
95 chrome_prefs::internals::kSettingsEnforcementGroupEnforceAlways, \
96 chrome_prefs::internals:: \
97 kSettingsEnforcementGroupEnforceAlwaysWithDSE, \
98 chrome_prefs::internals:: \
99 kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE));
100
101 // A base fixture designed such that implementations do two things:
102 // 1) Override all three pure-virtual methods below to setup, attack, and
103 // verify preferenes throughout the tests provided by this fixture.
104 // 2) Instantiate their test via the PREF_HASH_BROWSER_TEST macro above.
105 // Based on top of ExtensionBrowserTest to allow easy interaction with the
106 // ExtensionService.
107 class PrefHashBrowserTestBase
108 : public ExtensionBrowserTest,
109 public testing::WithParamInterface<std::string> {
110 public:
111 // List of potential protection levels for this test in strict increasing
112 // order of protection levels.
113 enum SettingsProtectionLevel {
114 PROTECTION_DISABLED_ON_PLATFORM,
115 PROTECTION_DISABLED_FOR_GROUP,
116 PROTECTION_ENABLED_BASIC,
117 PROTECTION_ENABLED_DSE,
118 PROTECTION_ENABLED_EXTENSIONS,
119 };
120
121 PrefHashBrowserTestBase()
122 : protection_level_(PROTECTION_DISABLED_ON_PLATFORM) {
123 if (ProfilePrefStoreManager::kPlatformSupportsPreferenceTracking) {
124 const std::string& experiment_group = GetParam();
125 if (experiment_group ==
126 chrome_prefs::internals::kSettingsEnforcementGroupNoEnforcement) {
127 protection_level_ = PROTECTION_DISABLED_FOR_GROUP;
128 } else if (experiment_group ==
129 chrome_prefs::internals::
130 kSettingsEnforcementGroupEnforceAlways) {
131 protection_level_ = PROTECTION_ENABLED_BASIC;
132 } else if (experiment_group ==
133 chrome_prefs::internals::
134 kSettingsEnforcementGroupEnforceAlwaysWithDSE) {
135 protection_level_ = PROTECTION_ENABLED_DSE;
136 } else if (experiment_group ==
137 chrome_prefs::internals::
138 kSettingsEnforcementGroupEnforceAlwaysWithExtensionsAndDSE) {
139 protection_level_ = PROTECTION_ENABLED_EXTENSIONS;
140 } else {
141 ADD_FAILURE();
142 }
143 }
144 }
145
146 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
147 ExtensionBrowserTest::SetUpCommandLine(command_line);
148 EXPECT_FALSE(command_line->HasSwitch(switches::kForceFieldTrials));
149 command_line->AppendSwitchASCII(
150 switches::kForceFieldTrials,
151 std::string(chrome_prefs::internals::kSettingsEnforcementTrialName) +
152 "/" + GetParam() + "/");
153 #if defined(OS_CHROMEOS)
154 command_line->AppendSwitch(
155 chromeos::switches::kIgnoreUserProfileMappingForTests);
156 #endif
157 }
158
159 virtual bool SetUpUserDataDirectory() OVERRIDE {
160 // Do the normal setup in the PRE test and attack preferences in the main
161 // test.
162 if (IsPRETest())
163 return ExtensionBrowserTest::SetUpUserDataDirectory();
164
165 base::FilePath profile_dir;
166 EXPECT_TRUE(PathService::Get(chrome::DIR_USER_DATA, &profile_dir));
167 profile_dir = profile_dir.AppendASCII(TestingProfile::kTestUserProfileDir);
168
169 AttackPreferencesOnDisk(profile_dir);
170
171 return true;
172 }
173
174 // In the PRE_ test, find the number of tracked preferences that were
175 // initialized and save it to a file to be read back in the main test and used
176 // as the total number of tracked preferences.
177 virtual void SetUpOnMainThread() OVERRIDE {
178 ExtensionBrowserTest::SetUpOnMainThread();
179
180 // File in which the PRE_ test will save the number of tracked preferences
181 // on this platform.
182 const char kNumTrackedPrefFilename[] = "NumTrackedPrefs";
183
184 base::FilePath num_tracked_prefs_file;
185 ASSERT_TRUE(
186 PathService::Get(chrome::DIR_USER_DATA, &num_tracked_prefs_file));
187 num_tracked_prefs_file =
188 num_tracked_prefs_file.AppendASCII(kNumTrackedPrefFilename);
189
190 if (IsPRETest()) {
191 num_tracked_prefs_ = GetTrackedPrefHistogramCount(
192 "Settings.TrackedPreferenceTrustedInitialized", false);
193 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM,
194 num_tracked_prefs_ > 0);
195
196 // Split tracked prefs are reported as Unchanged not as TrustedInitialized
197 // when an empty dictionary is encountered on first run.
198 int num_split_tracked_prefs = GetTrackedPrefHistogramCount(
199 "Settings.TrackedPreferenceUnchanged", false);
200 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM ? 1 : 0,
201 num_split_tracked_prefs);
202
203 num_tracked_prefs_ += num_split_tracked_prefs;
204
205 std::string num_tracked_prefs_str = base::IntToString(num_tracked_prefs_);
206 EXPECT_EQ(num_tracked_prefs_str.size(),
207 base::WriteFile(num_tracked_prefs_file,
208 num_tracked_prefs_str.c_str(),
209 num_tracked_prefs_str.size()));
210 } else {
211 std::string num_tracked_prefs_str;
212 EXPECT_TRUE(base::ReadFileToString(num_tracked_prefs_file,
213 &num_tracked_prefs_str));
214 EXPECT_TRUE(
215 base::StringToInt(num_tracked_prefs_str, &num_tracked_prefs_));
216 }
217 }
218
219 protected:
220 // Called from the PRE_ test's body. Overrides should use it to setup
221 // preferences through Chrome.
222 virtual void SetupPreferences() = 0;
223
224 // Called prior to the main test launching its browser. Overrides should use
225 // it to attack preferences in |profile_dir| on disk.
226 virtual void AttackPreferencesOnDisk(const base::FilePath& profile_dir) = 0;
227
228 // Called from the body of the main test. Overrides should use it to verify
229 // that the browser had the desired reaction when faced when the attack
230 // orchestrated in AttackPreferencesOnDisk().
231 virtual void VerifyReactionToPrefAttack() = 0;
232
233 int num_tracked_prefs_;
234
235 SettingsProtectionLevel protection_level_;
236
237 private:
238 // Returns true if this is the PRE_ phase of the test.
239 bool IsPRETest() {
240 return StartsWithASCII(
241 testing::UnitTest::GetInstance()->current_test_info()->name(),
242 "PRE_",
243 true /* case_sensitive */);
244 }
245 };
246
247 } // namespace
248
249 // Verifies that nothing is reset when nothing is tampered with.
250 // Also sanity checks that the expected preferences files are in place.
251 class PrefHashBrowserTestUnchangedDefault : public PrefHashBrowserTestBase {
252 public:
253 virtual void SetupPreferences() OVERRIDE {
254 // Default Chrome setup.
255 }
256
257 virtual void AttackPreferencesOnDisk(
258 const base::FilePath& profile_dir) OVERRIDE {
259 // No attack, just sanity check pref setup on disk.
260 EXPECT_TRUE(
261 base::PathExists(profile_dir.Append(chrome::kPreferencesFilename)));
262 EXPECT_FALSE(base::PathExists(
263 profile_dir.Append(chrome::kProtectedPreferencesFilenameDeprecated)));
264 EXPECT_TRUE(base::PathExists(
265 profile_dir.Append(chrome::kSecurePreferencesFilename)));
266 }
267
268 virtual void VerifyReactionToPrefAttack() OVERRIDE {
269 // Expect all prefs to be reported as Unchanged with no resets.
270 EXPECT_EQ(num_tracked_prefs_,
271 GetTrackedPrefHistogramCount(
272 "Settings.TrackedPreferenceUnchanged", false));
273 EXPECT_EQ(0,
274 GetTrackedPrefHistogramCount(
275 "Settings.TrackedPreferenceWantedReset", true));
276 EXPECT_EQ(
277 0,
278 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", true));
279
280 // Nothing else should have triggered.
281 EXPECT_EQ(0,
282 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged",
283 true));
284 EXPECT_EQ(0,
285 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared",
286 true));
287 EXPECT_EQ(0,
288 GetTrackedPrefHistogramCount(
289 "Settings.TrackedPreferenceInitialized", true));
290 EXPECT_EQ(0,
291 GetTrackedPrefHistogramCount(
292 "Settings.TrackedPreferenceTrustedInitialized", true));
293 EXPECT_EQ(0,
294 GetTrackedPrefHistogramCount(
295 "Settings.TrackedPreferenceMigratedLegacyDeviceId", true));
296 }
297 };
298
299 PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUnchangedDefault, UnchangedDefault);
300
301 // Augments PrefHashBrowserTestUnchangedDefault to confirm that nothing is reset
302 // when nothing is tampered with, even if Chrome itself wrote custom prefs in
303 // its last run.
304 class PrefHashBrowserTestUnchangedCustom
305 : public PrefHashBrowserTestUnchangedDefault {
306 public:
307 virtual void SetupPreferences() OVERRIDE {
308 profile()->GetPrefs()->SetString(prefs::kHomePage, "http://example.com");
309
310 InstallExtensionWithUIAutoConfirm(
311 test_data_dir_.AppendASCII("good.crx"), 1, browser());
312 }
313
314 virtual void VerifyReactionToPrefAttack() OVERRIDE {
315 // Make sure the settings written in the last run stuck.
316 EXPECT_EQ("http://example.com",
317 profile()->GetPrefs()->GetString(prefs::kHomePage));
318
319 EXPECT_TRUE(extension_service()->GetExtensionById(kGoodCrxId, false));
320
321 // Reaction should be identical to unattacked default prefs.
322 PrefHashBrowserTestUnchangedDefault::VerifyReactionToPrefAttack();
323 }
324 };
325
326 PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUnchangedCustom, UnchangedCustom);
327
328 // Verifies that cleared prefs are reported.
329 class PrefHashBrowserTestClearedAtomic : public PrefHashBrowserTestBase {
330 public:
331 virtual void SetupPreferences() OVERRIDE {
332 profile()->GetPrefs()->SetString(prefs::kHomePage, "http://example.com");
333 }
334
335 virtual void AttackPreferencesOnDisk(
336 const base::FilePath& profile_dir) OVERRIDE {
337 JSONFileValueSerializer serializer(
erikwright (departed) 2014/07/31 14:38:26 Presumably this stanza will appear frequently. Wh
gab 2014/07/31 19:14:16 Great idea, done (didn't use scoped_ptr*'s in the
338 profile_dir.Append(protection_level_ >= PROTECTION_ENABLED_BASIC
339 ? chrome::kSecurePreferencesFilename
340 : chrome::kPreferencesFilename));
341
342 base::DictionaryValue* prefs = ReadPrefsDictionary(&serializer);
343 EXPECT_TRUE(prefs->Remove(prefs::kHomePage, NULL));
344 EXPECT_TRUE(serializer.Serialize(*prefs));
345 }
346
347 virtual void VerifyReactionToPrefAttack() OVERRIDE {
348 // The clearance of homepage should have been noticed, but shouldn't have
349 // triggered a reset (as there is nothing we can do when the pref is already
350 // gone).
351 EXPECT_EQ(1,
352 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared",
353 false));
354 EXPECT_EQ(num_tracked_prefs_ - 1,
355 GetTrackedPrefHistogramCount(
356 "Settings.TrackedPreferenceUnchanged", false));
357 EXPECT_EQ(0,
358 GetTrackedPrefHistogramCount(
359 "Settings.TrackedPreferenceWantedReset", true));
360 EXPECT_EQ(
361 0,
362 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", true));
363
364 // Nothing else should have triggered.
365 EXPECT_EQ(0,
366 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged",
367 true));
368 EXPECT_EQ(0,
369 GetTrackedPrefHistogramCount(
370 "Settings.TrackedPreferenceInitialized", true));
371 EXPECT_EQ(0,
372 GetTrackedPrefHistogramCount(
373 "Settings.TrackedPreferenceTrustedInitialized", true));
374 EXPECT_EQ(0,
375 GetTrackedPrefHistogramCount(
376 "Settings.TrackedPreferenceMigratedLegacyDeviceId", true));
377 }
378 };
379
380 PREF_HASH_BROWSER_TEST(PrefHashBrowserTestClearedAtomic, ClearedAtomic);
381
382 // Verifies that clearing the MACs results in untrusted Initialized pings for
383 // non-null protected prefs.
384 class PrefHashBrowserTestUntrustedInitialized : public PrefHashBrowserTestBase {
385 public:
386 virtual void SetupPreferences() OVERRIDE {
387 // Explicitly set the DSE (it's otherwise NULL by default, preventing
388 // thorough testing of the PROTECTION_ENABLED_DSE level).
389 DefaultSearchManager default_search_manager(
390 profile()->GetPrefs(), DefaultSearchManager::ObserverCallback());
391 DefaultSearchManager::Source dse_source =
392 static_cast<DefaultSearchManager::Source>(-1);
393
394 const TemplateURLData* default_template_url_data =
395 default_search_manager.GetDefaultSearchEngine(&dse_source);
396 EXPECT_EQ(DefaultSearchManager::FROM_FALLBACK, dse_source);
397
398 default_search_manager.SetUserSelectedDefaultSearchEngine(
399 *default_template_url_data);
400
401 default_search_manager.GetDefaultSearchEngine(&dse_source);
402 EXPECT_EQ(DefaultSearchManager::FROM_USER, dse_source);
403
404 // Also explicitly set an atomic pref that falls under
405 // PROTECTION_ENABLED_BASIC.
406 profile()->GetPrefs()->SetInteger(prefs::kRestoreOnStartup,
407 SessionStartupPref::URLS);
408 }
409
410 virtual void AttackPreferencesOnDisk(
411 const base::FilePath& profile_dir) OVERRIDE {
412 static const base::FilePath::CharType* kPrefFiles[] = {
413 chrome::kPreferencesFilename, chrome::kSecurePreferencesFilename};
414 for (size_t i = 0; i < arraysize(kPrefFiles); ++i) {
415 JSONFileValueSerializer serializer(profile_dir.Append(kPrefFiles[i]));
416 base::DictionaryValue* prefs = ReadPrefsDictionary(&serializer);
417 EXPECT_TRUE(prefs->Remove("protection.macs", NULL));
418 EXPECT_TRUE(serializer.Serialize(*prefs));
419 }
420 }
421
422 virtual void VerifyReactionToPrefAttack() OVERRIDE {
423 // Preferences that are NULL by default will be TrustedInitialized.
424 int num_null_values = GetTrackedPrefHistogramCount(
425 "Settings.TrackedPreferenceTrustedInitialized", false);
426 EXPECT_EQ(protection_level_ > PROTECTION_DISABLED_ON_PLATFORM,
427 num_null_values > 0);
428 if (num_null_values > 0) {
429 // This test requires that at least 3 prefs be non-null (extensions, DSE,
430 // and 1 atomic pref explictly set for this test above).
431 EXPECT_LT(num_null_values, num_tracked_prefs_ - 3);
432 }
433
434 // Expect all non-null prefs to be reported as Initialized (with
435 // accompanying resets or wanted resets based on the current protection
436 // level).
437 EXPECT_EQ(num_tracked_prefs_ - num_null_values,
438 GetTrackedPrefHistogramCount(
439 "Settings.TrackedPreferenceInitialized", false));
440
441 int num_protected_prefs = 0;
442 // A switch statement falling through each protection level in decreasing
443 // levels of protection to add expectations for each level which augments
444 // the previous one.
445 switch (protection_level_) {
446 case PROTECTION_ENABLED_EXTENSIONS:
447 ++num_protected_prefs;
448 // Falls through.
449 case PROTECTION_ENABLED_DSE:
450 ++num_protected_prefs;
451 // Falls through.
452 case PROTECTION_ENABLED_BASIC:
453 num_protected_prefs += num_tracked_prefs_ - num_null_values - 2;
454 // Falls through.
455 case PROTECTION_DISABLED_FOR_GROUP:
456 // No protection. Falls through.
457 case PROTECTION_DISABLED_ON_PLATFORM:
458 // No protection.
459 break;
460 }
461
462 EXPECT_EQ(num_tracked_prefs_ - num_null_values - num_protected_prefs,
463 GetTrackedPrefHistogramCount(
464 "Settings.TrackedPreferenceWantedReset", false));
465 EXPECT_EQ(
466 num_protected_prefs,
467 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceReset", false));
468
469 DefaultSearchManager default_search_manager(
470 profile()->GetPrefs(), DefaultSearchManager::ObserverCallback());
471 DefaultSearchManager::Source dse_source =
472 static_cast<DefaultSearchManager::Source>(-1);
473 default_search_manager.GetDefaultSearchEngine(&dse_source);
474 EXPECT_EQ(protection_level_ < PROTECTION_ENABLED_DSE
475 ? DefaultSearchManager::FROM_USER
476 : DefaultSearchManager::FROM_FALLBACK,
477 dse_source);
478
479 EXPECT_EQ(protection_level_ < PROTECTION_ENABLED_BASIC,
480 profile()->GetPrefs()->GetInteger(prefs::kRestoreOnStartup) ==
481 SessionStartupPref::URLS);
482
483 // Nothing else should have triggered.
484 EXPECT_EQ(0,
485 GetTrackedPrefHistogramCount(
486 "Settings.TrackedPreferenceUnchanged", true));
487 EXPECT_EQ(0,
488 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceChanged",
489 true));
490 EXPECT_EQ(0,
491 GetTrackedPrefHistogramCount("Settings.TrackedPreferenceCleared",
492 true));
493 EXPECT_EQ(0,
494 GetTrackedPrefHistogramCount(
495 "Settings.TrackedPreferenceMigratedLegacyDeviceId", true));
496 }
497 };
498
499 PREF_HASH_BROWSER_TEST(PrefHashBrowserTestUntrustedInitialized,
500 UntrustedInitialized);
OLDNEW
« no previous file with comments | « chrome/browser/prefs/chrome_pref_service_factory.cc ('k') | chrome/chrome_tests.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698