summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 17:53:31 +0000
committererikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-10-08 17:53:31 +0000
commit2c05d9130a944565f6cd34f9e2c2aca0312e9c13 (patch)
treea79718504bfbbd98db2ab450cc584b95f2689e7e
parent8f5d65329f6c7812805732a47d33cfd43becd619 (diff)
downloadchromium_src-2c05d9130a944565f6cd34f9e2c2aca0312e9c13.zip
chromium_src-2c05d9130a944565f6cd34f9e2c2aca0312e9c13.tar.gz
chromium_src-2c05d9130a944565f6cd34f9e2c2aca0312e9c13.tar.bz2
Implement install module verification metric.
BUG=297675 NOTRY=True Review URL: https://codereview.chromium.org/23513049 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@227542 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/browser_resources.grd1
-rw-r--r--chrome/browser/chrome_browser_main_win.cc14
-rw-r--r--chrome/browser/chrome_browser_main_win.h1
-rw-r--r--chrome/browser/install_module_verifier_unittest_win.cc156
-rw-r--r--chrome/browser/install_module_verifier_win.cc219
-rw-r--r--chrome/browser/install_module_verifier_win.h48
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
8 files changed, 442 insertions, 0 deletions
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 43eaafe..346fdb3 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -396,6 +396,7 @@
<include name="IDR_AUTOMATIC_PROFILE_RESET_RULES_DRY" file="internal\resources\profile_reset\automatic_profile_reset_rules_dry.dat" type="BINDATA" />
<include name="IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED" file="internal\resources\profile_reset\automatic_profile_reset_hash_seed.dat" type="BINDATA" />
<include name="IDR_AUTOMATIC_PROFILE_RESET_HASH_SEED_DRY" file="internal\resources\profile_reset\automatic_profile_reset_hash_seed_dry.dat" type="BINDATA" />
+ <include name="IDR_ADDITIONAL_MODULES_LIST" file="internal\resources\additional_modules_list.txt" type="BINDATA" />
</if>
<if expr="pp_ifdef('chromeos')">
<include name="IDR_SOUND_STARTUP_WAV" file="resources\chromeos\sounds\startup.wav" type="BINDATA" />
diff --git a/chrome/browser/chrome_browser_main_win.cc b/chrome/browser/chrome_browser_main_win.cc
index 16a6452..7be7e23 100644
--- a/chrome/browser/chrome_browser_main_win.cc
+++ b/chrome/browser/chrome_browser_main_win.cc
@@ -22,6 +22,7 @@
#include "base/win/windows_version.h"
#include "base/win/wrapped_window_proc.h"
#include "chrome/browser/browser_util_win.h"
+#include "chrome/browser/install_module_verifier_win.h"
#include "chrome/browser/profiles/profile_info_cache.h"
#include "chrome/browser/profiles/profile_shortcut_manager.h"
#include "chrome/browser/shell_integration.h"
@@ -38,6 +39,7 @@
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/l10n_string_util.h"
#include "chrome/installer/util/shell_util.h"
+#include "content/public/browser/browser_thread.h"
#include "content/public/common/main_function_params.h"
#include "grit/app_locale_settings.h"
#include "grit/chromium_strings.h"
@@ -224,6 +226,18 @@ void ChromeBrowserMainPartsWin::ShowMissingLocaleMessageBox() {
MB_OK | MB_ICONERROR | MB_TOPMOST);
}
+void ChromeBrowserMainPartsWin::PostBrowserStart() {
+ ChromeBrowserMainParts::PostBrowserStart();
+
+ // Set up a task to verify installed modules in the current process. Use a
+ // delay to reduce the impact on startup time.
+ content::BrowserThread::GetMessageLoopProxyForThread(
+ content::BrowserThread::UI)->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&BeginModuleVerification),
+ base::TimeDelta::FromSeconds(45));
+}
+
// static
void ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment(
const CommandLine& parsed_command_line) {
diff --git a/chrome/browser/chrome_browser_main_win.h b/chrome/browser/chrome_browser_main_win.h
index f3e7c78..170f92b 100644
--- a/chrome/browser/chrome_browser_main_win.h
+++ b/chrome/browser/chrome_browser_main_win.h
@@ -30,6 +30,7 @@ class ChromeBrowserMainPartsWin : public ChromeBrowserMainParts {
// ChromeBrowserMainParts overrides.
virtual void ShowMissingLocaleMessageBox() OVERRIDE;
+ virtual void PostBrowserStart() OVERRIDE;
// Prepares the localized strings that are going to be displayed to
// the user if the browser process dies. These strings are stored in the
diff --git a/chrome/browser/install_module_verifier_unittest_win.cc b/chrome/browser/install_module_verifier_unittest_win.cc
new file mode 100644
index 0000000..0e1f501
--- /dev/null
+++ b/chrome/browser/install_module_verifier_unittest_win.cc
@@ -0,0 +1,156 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/install_module_verifier_win.h"
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/md5.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/enumerate_modules_model_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+void AddModule(base::ListValue* list,
+ ModuleEnumerator::ModuleType type,
+ const std::string& name) {
+ scoped_ptr<base::DictionaryValue> data(new DictionaryValue());
+ data->SetInteger("type", type);
+ data->SetString("name", name);
+ list->Append(data.release());
+}
+
+} // namespace
+
+TEST(InstallModuleVerifierTest, ExtractLoadedModuleNameDigests) {
+ std::set<std::string> loaded_module_name_digests;
+ scoped_ptr<base::ListValue> list(new ListValue());
+ ExtractLoadedModuleNameDigests(*list, &loaded_module_name_digests);
+ ASSERT_TRUE(loaded_module_name_digests.empty());
+ // WinSock modules are ignored.
+ AddModule(list.get(),
+ ModuleEnumerator::SHELL_EXTENSION, "winsock_module.dll");
+ // Shell Extension modules are ignored.
+ AddModule(
+ list.get(),
+ ModuleEnumerator::WINSOCK_MODULE_REGISTRATION, "shell_extension.dll");
+ AddModule(list.get(), ModuleEnumerator::LOADED_MODULE, "fancy_pants.dll");
+ AddModule(list.get(), ModuleEnumerator::LOADED_MODULE, "chrome.dll");
+ ExtractLoadedModuleNameDigests(*list, &loaded_module_name_digests);
+ ASSERT_EQ(2, loaded_module_name_digests.size());
+ ASSERT_NE(loaded_module_name_digests.end(),
+ loaded_module_name_digests.find(base::MD5String("chrome.dll")));
+ ASSERT_NE(loaded_module_name_digests.end(),
+ loaded_module_name_digests.find(
+ base::MD5String("fancy_pants.dll")));
+}
+
+TEST(InstallModuleVerifierTest, VerifyModules) {
+ std::set<std::string> loaded_module_name_digests;
+ std::set<size_t> reported_module_ids;
+
+ VerifyModules(loaded_module_name_digests,
+ AdditionalModules(),
+ &reported_module_ids);
+ ASSERT_TRUE(reported_module_ids.empty());
+
+ // Expected, Loaded modules are reported.
+ loaded_module_name_digests.insert(base::MD5String("chrome.dll"));
+ // Unexpected modules are ignored.
+ loaded_module_name_digests.insert(base::MD5String("fancy_pants.dll"));
+ VerifyModules(loaded_module_name_digests,
+ AdditionalModules(),
+ &reported_module_ids);
+ ASSERT_EQ(1, reported_module_ids.size());
+ // chrome.dll
+ ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(1u));
+ reported_module_ids.clear();
+
+ // AdditionalModules can be used to detect other modules.
+ AdditionalModules additional_modules;
+ std::string fancy_pants_md5_digest(base::MD5String("fancy_pants.dll"));
+ additional_modules.push_back(
+ std::make_pair(123u, base::StringPiece(fancy_pants_md5_digest)));
+ VerifyModules(loaded_module_name_digests,
+ additional_modules,
+ &reported_module_ids);
+ ASSERT_EQ(2, reported_module_ids.size());
+ // chrome.dll
+ ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(1u));
+ // fancy_pants.dll
+ ASSERT_NE(reported_module_ids.end(), reported_module_ids.find(123u));
+ reported_module_ids.clear();
+
+ // TODO(erikwright): Verify case-insensitive.
+}
+
+TEST(InstallModuleVerifierTest, ParseAdditionalModules) {
+ std::vector<std::pair<size_t, base::StringPiece> > additional_modules;
+
+ ParseAdditionalModules(base::StringPiece(), &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+
+ // Invalid input.
+ ParseAdditionalModules("hello", &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+ ParseAdditionalModules("hello world", &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+ ParseAdditionalModules("hello world\nfoo bar", &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+ ParseAdditionalModules("123 world\nfoo bar", &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+
+ // A single valid line followed by an invalid line.
+ ParseAdditionalModules("hello 0123456789abcdef0123456789abcdef\nfoo bar",
+ &additional_modules);
+ ASSERT_EQ(0u, additional_modules.size());
+ ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\nfoo bar",
+ &additional_modules);
+ ASSERT_EQ(1u, additional_modules.size());
+ ASSERT_EQ(123u, additional_modules[0].first);
+ ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second);
+ additional_modules.clear();
+
+ // The same, but with \r\n.
+ ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\r\nfoo bar",
+ &additional_modules);
+ ASSERT_EQ(1u, additional_modules.size());
+ ASSERT_EQ(123u, additional_modules[0].first);
+ ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second);
+ additional_modules.clear();
+
+ // Several valid and invalid lines, with varying line terminations etc.
+ ParseAdditionalModules("123 0123456789abcdef0123456789abcdef\r\n"
+ "456 DEADBEEFDEADBEEF\r"
+ "789 DEADBEEFDEADBEEFDEADBEEFDEADBEEF\n"
+ "\n"
+ "\n"
+ "321 BAADCAFEBAADCAFEBAADCAFEBAADCAFE\n",
+ &additional_modules);
+ ASSERT_EQ(3u, additional_modules.size());
+ ASSERT_EQ(123u, additional_modules[0].first);
+ ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second);
+ ASSERT_EQ(789u, additional_modules[1].first);
+ ASSERT_EQ("DEADBEEFDEADBEEFDEADBEEFDEADBEEF", additional_modules[1].second);
+ ASSERT_EQ(321u, additional_modules[2].first);
+ ASSERT_EQ("BAADCAFEBAADCAFEBAADCAFEBAADCAFE", additional_modules[2].second);
+ additional_modules.clear();
+
+ // Leading empty lines, no termination on final line.
+ ParseAdditionalModules("\n"
+ "123 0123456789abcdef0123456789abcdef\r\n"
+ "321 BAADCAFEBAADCAFEBAADCAFEBAADCAFE",
+ &additional_modules);
+ ASSERT_EQ(2u, additional_modules.size());
+ ASSERT_EQ(123u, additional_modules[0].first);
+ ASSERT_EQ("0123456789abcdef0123456789abcdef", additional_modules[0].second);
+ ASSERT_EQ(321u, additional_modules[1].first);
+ ASSERT_EQ("BAADCAFEBAADCAFEBAADCAFEBAADCAFE", additional_modules[1].second);
+ additional_modules.clear();
+}
diff --git a/chrome/browser/install_module_verifier_win.cc b/chrome/browser/install_module_verifier_win.cc
new file mode 100644
index 0000000..bca42a5
--- /dev/null
+++ b/chrome/browser/install_module_verifier_win.cc
@@ -0,0 +1,219 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/install_module_verifier_win.h"
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/md5.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/metrics/sparse_histogram.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_tokenizer.h"
+#include "base/strings/string_util.h"
+#include "base/values.h"
+#include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/enumerate_modules_model_win.h"
+#include "content/public/browser/notification_observer.h"
+#include "content/public/browser/notification_registrar.h"
+#include "content/public/browser/notification_service.h"
+#include "content/public/browser/notification_source.h"
+#include "grit/browser_resources.h"
+#include "ui/base/resource/resource_bundle.h"
+
+namespace {
+
+struct { size_t id; const char* module_name_digest; }
+ kExpectedInstallModules[] = {
+ {1u, "c8cc47613e155f2129f480c6ced84549"}, // chrome.dll
+ {2u, "49b78a23b0d8d5d8fb60d4e472b22764"}, // chrome_child.dll
+ };
+
+// Helper to extract canonical loaded module names from the EnumerateModulesWin
+// output and then verify the results.
+void VerifyEnumeratedModules(const base::ListValue& module_list) {
+ std::set<std::string> module_name_digests;
+ ExtractLoadedModuleNameDigests(module_list, &module_name_digests);
+
+ AdditionalModules additional_modules;
+ ParseAdditionalModules(
+ ResourceBundle::GetSharedInstance().GetRawDataResource(
+ IDR_ADDITIONAL_MODULES_LIST),
+ &additional_modules);
+
+ std::set<size_t> installed_module_ids;
+ VerifyModules(module_name_digests, additional_modules, &installed_module_ids);
+ for (std::set<size_t>::iterator it = installed_module_ids.begin();
+ it != installed_module_ids.end();
+ ++it) {
+ UMA_HISTOGRAM_SPARSE_SLOWLY("InstallVerifier.ModuleMatch", *it);
+ }
+}
+
+// Waits for NOTIFICATION_MODULE_LIST_ENUMERATED, which indicates that
+// EnumerateModulesWin has completed its work. Retrieves the enumerated module
+// list and processes it.
+class InstallModuleVerifier : public content::NotificationObserver {
+ public:
+ // Creates an instance that will wait for module enumeration to complete,
+ // process the results, and then delete itself.
+ static void WaitForModuleList() {
+ // Will delete itself when scan results are available.
+ new InstallModuleVerifier();
+ }
+
+ private:
+ InstallModuleVerifier() {
+ notification_registrar_.Add(this,
+ chrome::NOTIFICATION_MODULE_LIST_ENUMERATED,
+ content::NotificationService::AllSources());
+ }
+
+ ~InstallModuleVerifier() {}
+
+ // content::NotificationObserver implementation.
+ virtual void Observe(int type,
+ const content::NotificationSource& source,
+ const content::NotificationDetails& details) OVERRIDE {
+ DCHECK_EQ(type, chrome::NOTIFICATION_MODULE_LIST_ENUMERATED);
+ EnumerateModulesModel* model =
+ content::Source<EnumerateModulesModel>(source).ptr();
+ scoped_ptr<base::ListValue> module_list(model->GetModuleList());
+
+ if (module_list.get())
+ VerifyEnumeratedModules(*module_list);
+
+ delete this;
+ }
+
+ content::NotificationRegistrar notification_registrar_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstallModuleVerifier);
+};
+
+} // namespace
+
+void BeginModuleVerification() {
+ scoped_ptr<base::ListValue> module_list(
+ EnumerateModulesModel::GetInstance()->GetModuleList());
+ if (module_list.get()) {
+ VerifyEnumeratedModules(*module_list);
+ } else {
+ InstallModuleVerifier::WaitForModuleList();
+ EnumerateModulesModel::GetInstance()->ScanNow();
+ }
+}
+
+void ExtractLoadedModuleNameDigests(
+ const base::ListValue& module_list,
+ std::set<std::string>* module_name_digests) {
+ DCHECK(module_name_digests);
+
+ // EnumerateModulesModel produces a list of dictionaries.
+ // Each dictionary corresponds to a module and exposes a number of properties.
+ // We care only about 'type' and 'name'.
+ for (size_t i = 0; i < module_list.GetSize(); ++i) {
+ const base::DictionaryValue* module_dictionary = NULL;
+ if (!module_list.GetDictionary(i, &module_dictionary))
+ continue;
+ ModuleEnumerator::ModuleType module_type =
+ ModuleEnumerator::LOADED_MODULE;
+ if (!module_dictionary->GetInteger(
+ "type", reinterpret_cast<int*>(&module_type)) ||
+ module_type != ModuleEnumerator::LOADED_MODULE) {
+ continue;
+ }
+ std::string module_name;
+ if (!module_dictionary->GetString("name", &module_name))
+ continue;
+ StringToLowerASCII(&module_name);
+ module_name_digests->insert(base::MD5String(module_name));
+ }
+}
+
+void VerifyModules(
+ const std::set<std::string>& module_name_digests,
+ const AdditionalModules& additional_modules,
+ std::set<size_t>* installed_module_ids) {
+ DCHECK(installed_module_ids);
+
+ for (size_t i = 0; i < arraysize(kExpectedInstallModules); ++i) {
+ if (module_name_digests.find(
+ kExpectedInstallModules[i].module_name_digest) !=
+ module_name_digests.end()) {
+ installed_module_ids->insert(kExpectedInstallModules[i].id);
+ }
+ }
+
+ for (AdditionalModules::const_iterator it = additional_modules.begin();
+ it != additional_modules.end(); ++it) {
+ std::string additional_module = it->second.as_string();
+ StringToLowerASCII(&additional_module);
+
+ if (module_name_digests.find(additional_module)
+ != module_name_digests.end()) {
+ installed_module_ids->insert(it->first);
+ }
+ }
+}
+
+namespace {
+
+// Parses a line consisting of a positive decimal number and a 32-digit
+// hexadecimal number, separated by a space. Inserts the output, if valid, into
+// |additional_modules|. Unexpected leading or trailing characters will cause
+// the line to be ignored, as will invalid decimal/hexadecimal characters.
+void ParseAdditionalModuleLine(
+ const base::StringPiece& line,
+ AdditionalModules* additional_modules) {
+ DCHECK(additional_modules);
+
+ base::CStringTokenizer line_tokenizer(line.begin(), line.end(), " ");
+
+ if (!line_tokenizer.GetNext())
+ return; // Empty string.
+ base::StringPiece id_piece(line_tokenizer.token_piece());
+
+ if (!line_tokenizer.GetNext())
+ return; // No delimiter (' ').
+ base::StringPiece digest_piece(line_tokenizer.token_piece());
+
+ if (line_tokenizer.GetNext())
+ return; // Unexpected trailing characters.
+
+ unsigned id = 0;
+ if (!StringToUint(id_piece, &id))
+ return; // First token was not decimal.
+
+ if (digest_piece.length() != 32)
+ return; // Second token is not the right length.
+
+ for (base::StringPiece::const_iterator it = digest_piece.begin();
+ it != digest_piece.end(); ++it) {
+ if (!IsHexDigit(*it))
+ return; // Second token has invalid characters.
+ }
+
+ // This is a valid line.
+ additional_modules->push_back(std::make_pair(id, digest_piece));
+}
+
+} // namespace
+
+void ParseAdditionalModules(
+ const base::StringPiece& raw_data,
+ AdditionalModules* additional_modules) {
+ DCHECK(additional_modules);
+
+ base::CStringTokenizer file_tokenizer(raw_data.begin(),
+ raw_data.end(),
+ "\r\n");
+ while (file_tokenizer.GetNext()) {
+ ParseAdditionalModuleLine(base::StringPiece(file_tokenizer.token_piece()),
+ additional_modules);
+ }
+}
diff --git a/chrome/browser/install_module_verifier_win.h b/chrome/browser/install_module_verifier_win.h
new file mode 100644
index 0000000..ab0683d
--- /dev/null
+++ b/chrome/browser/install_module_verifier_win.h
@@ -0,0 +1,48 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_
+#define CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_
+
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/strings/string_piece.h"
+
+namespace base {
+class ListValue;
+} // namespace base
+
+typedef std::vector<std::pair<size_t, base::StringPiece> > AdditionalModules;
+
+// Starts a background process to verify installed modules. Results are reported
+// via UMA.
+void BeginModuleVerification();
+
+// Converts a list of modules descriptions extracted from EnumerateModulesModel
+// into a list of loaded module name digests.
+void ExtractLoadedModuleNameDigests(
+ const base::ListValue& module_list,
+ std::set<std::string>* loaded_module_name_digests);
+
+// Verifies a list of installed module name digests and inserts installed module
+// IDs into the supplied set.
+void VerifyModules(const std::set<std::string>& module_name_digests,
+ const AdditionalModules& additional_modules,
+ std::set<size_t>* installed_module_ids);
+
+// Parses a list of additional modules to verify. The data format is a series of
+// lines. Each line starts with a decimal ID, then a module name digest,
+// separated by a space. Lines are terminated by \r and/or \n. The result is a
+// vector of pairs where the first value is an ID and the second value is the
+// corresponding module name digest. The StringPieces use the same underlying
+// storage as |raw_data|.
+//
+// Invalid lines are ignored.
+void ParseAdditionalModules(const base::StringPiece& raw_data,
+ AdditionalModules* additional_modules);
+
+#endif // CHROME_BROWSER_INSTALL_MODULE_VERIFIER_WIN_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index cf2aa24..bb4c335 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -904,6 +904,8 @@
'browser/infobars/insecure_content_infobar_delegate.h',
'browser/infobars/simple_alert_infobar_delegate.cc',
'browser/infobars/simple_alert_infobar_delegate.h',
+ 'browser/install_module_verifier_win.cc',
+ 'browser/install_module_verifier_win.h',
'browser/internal_auth.cc',
'browser/internal_auth.h',
'browser/intranet_redirect_detector.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index 0044fd4..9742249 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -954,6 +954,7 @@
'browser/importer/firefox_profile_lock_unittest.cc',
'browser/importer/profile_writer_unittest.cc',
'browser/internal_auth_unittest.cc',
+ 'browser/install_module_verifier_unittest_win.cc',
'browser/invalidation/invalidation_service_android_unittest.cc',
'browser/invalidation/invalidation_service_test_template.cc',
'browser/invalidation/invalidation_service_test_template.h',