Commit 47a44d77 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

chrome_cleaner/os unit tests, part 1: a-f

Also add test_util add extra setup that uses it to the test harness/

Bug: 830892
Change-Id: Iae1bb4323f6a0b25339d13e702135c2a3625c99d
Reviewed-on: https://chromium-review.googlesource.com/1141146
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarChris Sharp <csharp@chromium.org>
Reviewed-by: default avatarPenny MacNeil <pennymac@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577240}
parent 8bb19a10
......@@ -22,10 +22,14 @@ test("chrome_cleaner_unittests") {
"//chrome/chrome_cleaner/pup_data:unittest_sources",
"//chrome/chrome_cleaner/settings:unittest_sources",
"//chrome/chrome_cleaner/strings:unittest_sources",
"//chrome/chrome_cleaner/test:unittest_sources",
# Dependencies of the test harness.
"//base",
"//base/test:test_support",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/test:test_util",
"//sandbox/win:sandbox",
"//testing/gtest",
]
......
include_rules = [
"+sandbox/win/src",
"+testing/gtest",
]
......@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
static_library("common_os") {
source_set("common_os") {
sources = [
"digest_verifier.cc",
"digest_verifier.h",
......@@ -74,7 +74,6 @@ static_library("common_os") {
"ntdll.lib", # For NtQueryKey
"secur32.lib", # For GetUserNameEx
"taskschd.lib", # For CLSID_TaskScheduler
"wintrust.lib", # For WinVerifyTrust
]
defines = [ "SECURITY_WIN32" ] # For secur32.lib
......@@ -91,7 +90,7 @@ source_set("file_remover_api") {
]
}
static_library("cleaner_os") {
source_set("cleaner_os") {
sources = [
"file_removal_status_updater.cc",
"file_removal_status_updater.h",
......@@ -124,11 +123,27 @@ source_set("unittest_sources") {
sources = [
# TODO(joenotcharles): Copy unit tests from the internal repo.
"digest_verifier_unittest.cc",
"disk_util_unittest.cc",
"file_path_sanitization_unittest.cc",
"file_path_set_unittest.cc",
"file_removal_status_updater_unittest.cc",
"file_remover_unittest.cc",
]
deps = [
":cleaner_os",
":common_os",
":file_remover_api",
"//base",
"//base/test:test_config",
"//base/test:test_support",
"//chrome/chrome_cleaner/logging/proto:removal_status_proto",
"//chrome/chrome_cleaner/strings",
"//chrome/chrome_cleaner/test:test_executables",
"//chrome/chrome_cleaner/test:test_strings",
"//chrome/chrome_cleaner/test:test_util",
"//chrome/chrome_cleaner/test/resources:test_resources",
"//testing/gtest",
]
}
// Copyright 2018 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/chrome_cleaner/os/digest_verifier.h"
#include "base/base_paths.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "chrome/chrome_cleaner/os/pre_fetched_paths.h"
#include "chrome/chrome_cleaner/os/system_util.h"
#include "chrome/chrome_cleaner/test/resources/grit/test_resources.h"
#include "chrome/chrome_cleaner/test/test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
TEST(DigestVerifier, KnownFile) {
std::shared_ptr<DigestVerifier> digest_verifier =
DigestVerifier::CreateFromResource(IDS_TEST_SAMPLE_DLL_DIGEST);
ASSERT_TRUE(digest_verifier);
base::FilePath dll_path = GetSampleDLLPath();
ASSERT_TRUE(base::PathExists(dll_path)) << dll_path.value();
EXPECT_TRUE(digest_verifier->IsKnownFile(dll_path));
}
TEST(DigestVerifier, UnknownFile) {
std::shared_ptr<DigestVerifier> digest_verifier =
DigestVerifier::CreateFromResource(IDS_TEST_SAMPLE_DLL_DIGEST);
ASSERT_TRUE(digest_verifier);
const base::FilePath self_path =
PreFetchedPaths::GetInstance()->GetExecutablePath();
EXPECT_FALSE(digest_verifier->IsKnownFile(self_path));
}
TEST(DigestVerifier, InexistentFile) {
std::shared_ptr<DigestVerifier> digest_verifier =
DigestVerifier::CreateFromResource(IDS_TEST_SAMPLE_DLL_DIGEST);
ASSERT_TRUE(digest_verifier);
base::FilePath invalid_path(L"this_file_should_not_exist.dll");
ASSERT_FALSE(base::PathExists(invalid_path));
EXPECT_FALSE(digest_verifier->IsKnownFile(invalid_path));
}
} // namespace chrome_cleaner
......@@ -4,7 +4,6 @@
#include "chrome/chrome_cleaner/os/disk_util.h"
#include <softpub.h>
#include <stdint.h>
#include <algorithm>
......@@ -135,22 +134,6 @@ void CollectMatchingPathsRecursive(
}
}
// Return whether an executable is whitelisted. On success, |file_information|
// receives the file version information. |file_information| is not modified by
// this function but cannot be const because of the accessors prototype in base.
bool IsExecutableWhiteListed(FileVersionInfo* file_information) {
DCHECK(file_information);
bool white_listed = false;
base::string16 company_name = file_information->company_name();
for (const base::string16& white_listed_name : company_white_list) {
if (company_name.compare(white_listed_name) == 0) {
white_listed = true;
break;
}
}
return white_listed;
}
void AppendFileInformationField(const wchar_t* field_name,
const base::string16& field,
base::string16* information) {
......@@ -520,10 +503,28 @@ base::string16 FileInformationToString(
return content;
}
bool IsExecutableOnDefaultReportingWhiteList(const base::FilePath& file_path) {
std::unique_ptr<FileVersionInfo> file_information(
FileVersionInfo::CreateFileVersionInfo(file_path));
if (!file_information)
return false;
bool white_listed = false;
base::string16 company_name = file_information->company_name();
for (const base::string16& white_listed_name : company_white_list) {
if (company_name.compare(white_listed_name) == 0) {
white_listed = true;
break;
}
}
return white_listed;
}
bool RetrieveDetailedFileInformation(
const base::FilePath& file_path,
internal::FileInformation* file_information,
bool* white_listed) {
bool* white_listed,
ReportingWhiteListCallback white_list_callback) {
DCHECK(file_information);
DCHECK(white_listed);
......@@ -531,9 +532,7 @@ bool RetrieveDetailedFileInformation(
if (!TryToExpandPath(file_path, &expanded_path))
return false;
std::unique_ptr<FileVersionInfo> version(
FileVersionInfo::CreateFileVersionInfo(expanded_path));
if (version.get() && IsExecutableWhiteListed(version.get())) {
if (std::move(white_list_callback).Run(file_path)) {
*white_listed = true;
return false;
}
......@@ -550,7 +549,9 @@ bool RetrieveDetailedFileInformation(
}
// Set the executable version information, when available.
if (version.get()) {
std::unique_ptr<FileVersionInfo> version(
FileVersionInfo::CreateFileVersionInfo(expanded_path));
if (version) {
file_information->company_name = version->company_name();
file_information->company_short_name = version->company_short_name();
file_information->product_name = version->product_name();
......
......@@ -14,6 +14,8 @@
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback.h"
#include "base/strings/string16.h"
#include "base/win/scoped_handle.h"
#include "chrome/chrome_cleaner/os/disk_util_types.h"
......@@ -27,6 +29,9 @@ namespace chrome_cleaner {
class LayeredServiceProviderAPI;
typedef base::OnceCallback<bool(const base::FilePath&)>
ReportingWhiteListCallback;
// Return the full path of the relative path |input_path| when expanded to the
// 64 bits program files path. Return an empty path when not running on 64 bits
// OS.
......@@ -92,13 +97,20 @@ void ExpandWow64Path(const base::FilePath& path, base::FilePath* expanded_path);
base::string16 FileInformationToString(
const internal::FileInformation& file_information);
// Returns true if the given |path| refers to an executable which is
// whitelisted so that its details should not be reported.
bool IsExecutableOnDefaultReportingWhiteList(const base::FilePath& file_path);
// Retrieve the detailed information for the executable |file_path| and append
// the fields to |file_information|. If the executable is |white_listed|,
// |file_information| stay unchanged.
// the fields to |file_information|. If the executable is |white_listed|
// according to the given |white_list_callback|, |file_information| stays
// unchanged.
bool RetrieveDetailedFileInformation(
const base::FilePath& file_path,
internal::FileInformation* file_information,
bool* white_listed);
bool* white_listed,
ReportingWhiteListCallback white_list_callback =
base::BindOnce(&IsExecutableOnDefaultReportingWhiteList));
// Retrieve the file information path, dates and size into |file_information|.
bool RetrieveBasicFileInformation(const base::FilePath& file_path,
......
This diff is collapsed.
// Copyright 2018 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/chrome_cleaner/os/file_path_sanitization.h"
#include <shlobj.h>
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/utf_string_conversions.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
base::string16 FirstComponent(const base::string16& original) {
return original.substr(0, original.find(L"\\"));
}
TEST(FilePathSanitizationTests, NormalizePath) {
base::FilePath expected_path =
base::FilePath(L"c:\\program files\\desktop.ini");
EXPECT_EQ(NormalizePath(base::FilePath(L"C:\\PROGRA~1\\DESKTOP.INI")),
expected_path);
EXPECT_EQ(NormalizePath(base::FilePath(L"c:\\pRoGrAm FiLeS\\desktop.INI")),
expected_path);
base::FilePath empty_path;
EXPECT_EQ(NormalizePath(empty_path), empty_path);
}
TEST(FilePathSanitizationTests, NormalizePathUnicode) {
EXPECT_EQ(
NormalizePath(
base::FilePath(L"C:\\\u03b1\u03c1\u03c7\u03b5\u03b9\u03b1 "
L"\u03c0\u03c1\u03bf\u03b3\u03c1"
L"\u03b1\u03bc\u03bc\u03b1\u03c4\u03bf\u03c2\\u03b5"
L"\u03c0\u03b9\u03c6"
L"\u03ac\u03bd\u03b5\u03b9\u03b1 "
L"\u03b5\u03c1\u03b3\u03b1\u03c3\u03af"
L"\u03b1\u03c2.iNi"))
.value(),
L"c:\\\u03b1\u03c1\u03c7\u03b5\u03b9\u03b1 \u03c0\u03c1\u03bf\u03b3\u03c1"
L"\u03b1\u03bc\u03bc\u03b1\u03c4\u03bf\u03c2\\u03b5\u03c0\u03b9\u03c6"
L"\u03ac\u03bd\u03b5\u03b9\u03b1 \u03b5\u03c1\u03b3\u03b1\u03c3\u03af"
L"\u03b1\u03c2.ini");
}
TEST(FilePathSanitizationTests, SanitizePath) {
base::FilePath programfiles_folder;
ASSERT_TRUE(base::PathService::Get(CsidlToPathServiceKey(CSIDL_PROGRAM_FILES),
&programfiles_folder));
base::FilePath absolute(L"C:\\Dummy\\Dummy.exe");
base::string16 result = SanitizePath(absolute);
EXPECT_EQ(NormalizePath(absolute).value(), result);
base::FilePath relative(programfiles_folder.Append(L"Dummy\\Dummy.exe"));
base::string16 sanitized_relative = SanitizePath(relative);
EXPECT_NE(sanitized_relative, relative.value());
EXPECT_EQ(L"CSIDL_PROGRAM_FILES\\dummy\\dummy.exe", sanitized_relative);
base::FilePath empty_path;
EXPECT_EQ(L"", SanitizePath(empty_path));
}
TEST(FilePathSanitizationTests, SanitizePathConsistency) {
// Loop over all the rewrite rules used by sanitize path to make sure all the
// rules work correctly. In particular this test verifies each rule is not
// masked by another.
base::FilePath arbitrary_path = NormalizePath(base::FilePath(L"Desktop.ini"));
for (auto* rule = sanitization_internal::rewrite_rules; rule->path != nullptr;
++rule) {
base::FilePath expanded_path;
base::PathService::Get(rule->id, &expanded_path);
expanded_path = expanded_path.Append(arbitrary_path);
const auto sanitized_path = chrome_cleaner::SanitizePath(expanded_path);
// The FirstComponent here is the label string used to sanitize the path. It
// is extracted to verify the correct label string is being used.
//
// For example:
// C:\Program Files (x86)\Common Files\Desktop.ini
// maps to
// CSIDL_PROGRAM_FILES_COMMON (CSIDL_PROGRAM_FILES_COMMON\Desktop.ini)
// and shouldn't map to
// CSIDL_PROGRAM_FILES (CSIDL_PROGRAM_FILES\Common Files\Desktop.ini)
// or it will clash with
// C:\Program Files (x86)\Desktop.ini
// which maps to
// CSIDL_PROGRAM_FILES (CSIDL_PROGRAM_FILES\desktop.ini)
const auto first_dir = FirstComponent(sanitized_path);
if (first_dir != rule->path) {
ADD_FAILURE() << base::WideToUTF8(expanded_path.value())
<< " is being Sanitized to "
<< base::WideToUTF8(sanitized_path) << " instead of using "
<< rule->path;
}
}
}
TEST(FilePathSanitizationTests, SanitizeCommandLine) {
base::CommandLine switches =
base::CommandLine::FromString(L"dummy.exe --flag --arg=value");
base::CommandLine already_sanitized(switches);
already_sanitized.SetProgram(base::FilePath(L"c:\\dummy\\dummy.exe"));
base::string16 result = SanitizeCommandLine(already_sanitized);
EXPECT_EQ(already_sanitized.GetCommandLineString(), result);
base::FilePath programfiles_folder;
ASSERT_TRUE(base::PathService::Get(CsidlToPathServiceKey(CSIDL_PROGRAM_FILES),
&programfiles_folder));
base::FilePath exe_in_programfiles =
programfiles_folder.Append(L"dummy\\dummy.exe");
base::CommandLine to_sanitize(switches);
to_sanitize.SetProgram(exe_in_programfiles);
base::string16 sanitized_cmd = SanitizeCommandLine(to_sanitize);
EXPECT_NE(to_sanitize.GetCommandLineString(), sanitized_cmd);
EXPECT_NE(sanitized_cmd.find(SanitizePath(exe_in_programfiles)),
base::string16::npos);
}
TEST(FilePathSanitizationTests, ExpandSpecialFolderPath) {
base::FilePath arbitrary_path(L"Desktop.ini");
for (auto* rule = sanitization_internal::rewrite_rules; rule->path != nullptr;
++rule) {
// Skip non-CSIDL entries.
if (rule->id < sanitization_internal::PATH_CSIDL_START ||
rule->id >= sanitization_internal::PATH_CSIDL_END) {
continue;
}
// Fetch and validate expected path.
base::FilePath expected_path;
ASSERT_TRUE(base::PathService::Get(rule->id, &expected_path));
ASSERT_FALSE(expected_path.empty());
expected_path = expected_path.Append(arbitrary_path);
int csidl = rule->id - sanitization_internal::PATH_CSIDL_START;
base::FilePath expanded_path =
ExpandSpecialFolderPath(csidl, arbitrary_path);
EXPECT_EQ(expected_path, expanded_path)
<< "Failed special folder path expansion. Got: \""
<< base::WideToUTF8(expanded_path.value())
<< "\", but expected: " << base::WideToUTF8(expected_path.value());
}
}
} // namespace
} // namespace chrome_cleaner
// Copyright 2018 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/chrome_cleaner/os/file_path_set.h"
#include "base/files/scoped_temp_dir.h"
#include "base/strings/string_util.h"
#include "chrome/chrome_cleaner/test/test_file_util.h"
#include "chrome/chrome_cleaner/test/test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
const wchar_t kFileNameFull[] = L"C:\\Filename";
const wchar_t kLongFileName[] = L"Long File Name.bla";
const wchar_t kLongFileNameFull[] = L"C:\\Long File Name.bla";
} // namespace
TEST(FilePathSetTests, Empty) {
FilePathSet file_paths;
// Start empty.
EXPECT_TRUE(file_paths.empty());
// Clear should be callable on empty sets.
file_paths.clear();
}
TEST(FilePathSetTests, InsertedOnce) {
FilePathSet file_paths;
// Same path should be inserted only once.
base::FilePath file_path1(kFileNameFull);
EXPECT_TRUE(file_paths.Insert(file_path1));
EXPECT_FALSE(file_paths.Insert(file_path1));
EXPECT_EQ(1UL, file_paths.size());
// But still should be found.
EXPECT_TRUE(file_paths.Contains(file_path1));
}
TEST(FilePathSetTests, EqualOperator) {
FilePathSet file_paths1;
base::FilePath file_path(kFileNameFull);
EXPECT_TRUE(file_paths1.Insert(file_path));
EXPECT_EQ(file_paths1, file_paths1);
FilePathSet file_paths2;
EXPECT_TRUE(file_paths2.Insert(file_path));
EXPECT_EQ(file_paths1, file_paths2);
base::FilePath long_file_path(kLongFileNameFull);
EXPECT_TRUE(file_paths1.Insert(long_file_path));
EXPECT_TRUE(file_paths2.Insert(long_file_path));
EXPECT_EQ(file_paths1, file_paths2);
base::FilePath other_path(L"C:\\other_path.txt");
EXPECT_TRUE(file_paths2.Insert(other_path));
// To avoid implementing operator!=();
EXPECT_FALSE(file_paths1 == file_paths2);
}
TEST(FilePathSetTests, LongName) {
FilePathSet file_paths;
// Long paths should also be found, even by their short version.
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath short_name_path;
base::FilePath long_name_path(
scoped_temp_dir.GetPath().Append(kLongFileName));
CreateFileAndGetShortName(long_name_path, &short_name_path);
EXPECT_TRUE(file_paths.Insert(long_name_path));
EXPECT_FALSE(file_paths.Insert(long_name_path));
EXPECT_FALSE(file_paths.Insert(short_name_path));
base::FilePath long_name_path_upper(
base::ToUpperASCII(long_name_path.value()));
EXPECT_FALSE(file_paths.Insert(long_name_path_upper));
// And they should be found-able in all its different forms.
EXPECT_TRUE(file_paths.Contains(long_name_path));
EXPECT_TRUE(file_paths.Contains(short_name_path));
EXPECT_TRUE(file_paths.Contains(long_name_path_upper));
}
TEST(FilePathSetTests, ShortName) {
FilePathSet file_paths;
// Short paths should also be found, even by their long version.
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath short_name_path;
base::FilePath long_name_path(
scoped_temp_dir.GetPath().Append(kLongFileName));
CreateFileAndGetShortName(long_name_path, &short_name_path);
EXPECT_TRUE(file_paths.Insert(short_name_path));
EXPECT_FALSE(file_paths.Insert(short_name_path));
EXPECT_FALSE(file_paths.Insert(long_name_path));
base::FilePath long_name_path_upper(
base::ToUpperASCII(long_name_path.value()));
EXPECT_FALSE(file_paths.Insert(long_name_path_upper));
// And they should be found-able in all its different forms.
EXPECT_TRUE(file_paths.Contains(long_name_path));
EXPECT_TRUE(file_paths.Contains(short_name_path));
EXPECT_TRUE(file_paths.Contains(long_name_path_upper));
}
TEST(FilePathSetTests, ProperOrder) {
// Ensure that all the items in a folder are listed before the folder.
const base::FilePath folder(L"C:\\folder");
base::FilePath file = folder.Append(L"file.exe");
base::FilePath sub_folder = folder.Append(L"sub_folder");
base::FilePath sub_file = sub_folder.Append(L"sub_file.txt");
FilePathSet file_paths;
EXPECT_TRUE(file_paths.Insert(folder));
EXPECT_TRUE(file_paths.Insert(file));
EXPECT_TRUE(file_paths.Insert(sub_folder));
EXPECT_TRUE(file_paths.Insert(sub_file));
const auto sorted_files = file_paths.ReverseSorted();
ASSERT_EQ(4UL, sorted_files.size());
EXPECT_EQ(sub_file, sorted_files[0]);
EXPECT_EQ(sub_folder, sorted_files[1]);
EXPECT_EQ(file, sorted_files[2]);
EXPECT_EQ(folder, sorted_files[3]);
}
TEST(FilePathMap, Find) {
// Long paths should also be found, even by their short version.
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath short_name_path;
base::FilePath long_name_path(
scoped_temp_dir.GetPath().Append(kLongFileName));
CreateFileAndGetShortName(long_name_path, &short_name_path);
FilePathMap<int> map;
EXPECT_TRUE(map.Insert(short_name_path, 42));
const int* found_value = nullptr;
ASSERT_NE(nullptr, found_value = map.Find(short_name_path));
EXPECT_EQ(42, *found_value);
ASSERT_NE(nullptr, found_value = map.Find(long_name_path));
EXPECT_EQ(42, *found_value);
EXPECT_EQ(nullptr, map.Find(base::FilePath(kFileNameFull)));
}
TEST(FilePathMap, DoNotInsertDuplicates) {
base::ScopedTempDir scoped_temp_dir;
ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDir());
base::FilePath short_name_path;
base::FilePath long_name_path(
scoped_temp_dir.GetPath().Append(kLongFileName));
CreateFileAndGetShortName(long_name_path, &short_name_path);
FilePathMap<int> map;
EXPECT_TRUE(map.Insert(long_name_path, 42));
EXPECT_FALSE(map.Insert(long_name_path, 43));
EXPECT_FALSE(map.Insert(short_name_path, 44));
EXPECT_EQ(1u, map.map().size());
const int* found_value = nullptr;
EXPECT_NE(nullptr, found_value = map.Find(short_name_path));
EXPECT_EQ(42, *found_value);
}
} // namespace chrome_cleaner
// Copyright 2018 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/chrome_cleaner/os/file_removal_status_updater.h"
#include "base/base_paths.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
constexpr wchar_t kFile1[] = L"file_one";
constexpr wchar_t kFile2[] = L"file_two";
class FileRemovalStatusUpdaterTest : public ::testing::Test {
protected:
FileRemovalStatusUpdaterTest() {
// Start each test with an empty map.
FileRemovalStatusUpdater::GetInstance()->Clear();
}
// Convenience accessors
FileRemovalStatusUpdater* instance_ = FileRemovalStatusUpdater::GetInstance();
const base::FilePath file_1_ = base::FilePath(kFile1);
const base::FilePath file_2_ = base::FilePath(kFile2);
};
} // namespace
TEST_F(FileRemovalStatusUpdaterTest, Clear) {
// Map should start empty.
EXPECT_TRUE(instance_->GetAllRemovalStatuses().empty());
EXPECT_EQ(REMOVAL_STATUS_UNSPECIFIED, instance_->GetRemovalStatus(file_1_));
EXPECT_EQ(REMOVAL_STATUS_UNSPECIFIED, instance_->GetRemovalStatus(file_2_));
instance_->UpdateRemovalStatus(file_1_, REMOVAL_STATUS_MATCHED_ONLY);
// Only file_1_ should be in the map.
EXPECT_EQ(1U, instance_->GetAllRemovalStatuses().size());
EXPECT_EQ(REMOVAL_STATUS_MATCHED_ONLY, instance_->GetRemovalStatus(file_1_));
EXPECT_EQ(REMOVAL_STATUS_UNSPECIFIED, instance_->GetRemovalStatus(file_2_));
instance_->Clear();
// Map should be empty again.
EXPECT_TRUE(instance_->GetAllRemovalStatuses().empty());
EXPECT_EQ(REMOVAL_STATUS_UNSPECIFIED, instance_->GetRemovalStatus(file_1_));
EXPECT_EQ(REMOVAL_STATUS_UNSPECIFIED, instance_->GetRemovalStatus(file_2_));
}
TEST_F(FileRemovalStatusUpdaterTest, UpdateRemovalStatus) {
// This function uses GetRemovalStatusOfSanitizedPath to cut down on the
// number of times it calls SanitizePath on the same paths, which is slow.
const base::string16 file_1_sanitized = SanitizePath(file_1_);
const base::string16 file_2_sanitized = SanitizePath(file_2_);
// Creates a vector of all RemovalStatus enum values to improve readability
// of loops in this test and ensure that all RemovalStatus enumerators are
// checked.
std::vector<RemovalStatus> all_removal_status;
for (int i = RemovalStatus_MIN; i <= RemovalStatus_MAX; ++i) {
// Status cannot be set to REMOVAL_STATUS_UNSPECIFIED - this is guarded by
// an assert.
RemovalStatus status = static_cast<RemovalStatus>(i);
if (status != REMOVAL_STATUS_UNSPECIFIED)
all_removal_status.push_back(status);
}
for (RemovalStatus removal_status : all_removal_status) {
// Repeatedly update the removal status of file_2_. file_1_'s status should
// not be touched.
for (RemovalStatus new_removal_status : all_removal_status) {
SCOPED_TRACE(::testing::Message()
<< "removal_status " << removal_status
<< ", new_removal_status " << new_removal_status);
// Initially, files should have removal status "unspecified".
ASSERT_TRUE(instance_->GetAllRemovalStatuses().empty());
ASSERT_EQ(REMOVAL_STATUS_UNSPECIFIED,
instance_->GetRemovalStatusOfSanitizedPath(file_1_sanitized));
ASSERT_EQ(REMOVAL_STATUS_UNSPECIFIED,
instance_->GetRemovalStatusOfSanitizedPath(file_2_sanitized));
// Any removal status can override "unspecified".
instance_->UpdateRemovalStatus(file_1_, removal_status);
instance_->UpdateRemovalStatus(file_2_, removal_status);
EXPECT_EQ(removal_status,
instance_->GetRemovalStatusOfSanitizedPath(file_1_sanitized));
EXPECT_EQ(removal_status,
instance_->GetRemovalStatusOfSanitizedPath(file_2_sanitized));
// Tests if attempts to override removal status obey the rules specified
// by GetRemovalStatusOfSanitizedPathOverridePermissionMap().
instance_->UpdateRemovalStatus(file_2_, new_removal_status);
const internal::RemovalStatusOverridePermissionMap& decisions_map =
internal::GetRemovalStatusOverridePermissionMap();
const bool can_override = decisions_map.find(removal_status)
->second.find(new_removal_status)
->second == internal::kOkToOverride;
EXPECT_EQ(can_override ? new_removal_status : removal_status,
instance_->GetRemovalStatusOfSanitizedPath(file_2_sanitized));
// Updating file_2_ should not have touched file_1_.
EXPECT_EQ(removal_status,
instance_->GetRemovalStatusOfSanitizedPath(file_1_sanitized));
// GetAllRemovalStatuses should agree with GetRemovalStatusOfSanitizedPath
// for all files.
FileRemovalStatusUpdater::SanitizedPathToRemovalStatusMap all_statuses =
instance_->GetAllRemovalStatuses();
EXPECT_EQ(2U, all_statuses.size());
for (const auto& path_and_status : all_statuses) {
base::string16 sanitized_path = path_and_status.first;
FileRemovalStatusUpdater::FileRemovalStatus status =
path_and_status.second;
EXPECT_EQ(instance_->GetRemovalStatusOfSanitizedPath(sanitized_path),
status.removal_status);
}
// Empty the map for the next loop.
instance_->Clear();
}
}
}
TEST_F(FileRemovalStatusUpdaterTest, PathSanitization) {
base::FilePath home_dir;
ASSERT_TRUE(base::PathService::Get(base::DIR_HOME, &home_dir));
base::FilePath path = home_dir.Append(L"UPPER_CASE_FILENAME");
instance_->UpdateRemovalStatus(path, REMOVAL_STATUS_REMOVED);
// Path should be accessible with any capitalization, sanitized or
// unsanitized.
base::string16 lowercase_path = base::ToLowerASCII(path.value());
EXPECT_EQ(REMOVAL_STATUS_REMOVED, instance_->GetRemovalStatus(path));
EXPECT_EQ(REMOVAL_STATUS_REMOVED,
instance_->GetRemovalStatus(base::FilePath(lowercase_path)));
base::string16 sanitized_path = SanitizePath(path);
EXPECT_EQ(REMOVAL_STATUS_REMOVED,
instance_->GetRemovalStatusOfSanitizedPath(sanitized_path));
FileRemovalStatusUpdater::SanitizedPathToRemovalStatusMap all_statuses =
instance_->GetAllRemovalStatuses();
EXPECT_EQ(1U, all_statuses.size());
EXPECT_EQ(sanitized_path, all_statuses.begin()->first);
EXPECT_EQ(path, all_statuses.begin()->second.path);
EXPECT_EQ(REMOVAL_STATUS_REMOVED,
all_statuses.begin()->second.removal_status);
}
} // namespace chrome_cleaner
This diff is collapsed.
......@@ -8,7 +8,7 @@ source_set("settings_types") {
]
}
static_library("settings") {
source_set("settings") {
sources = [
"settings.cc",
"settings.h",
......@@ -28,14 +28,14 @@ static_library("settings") {
]
}
static_library("matching_options") {
source_set("matching_options") {
sources = [
"matching_options.cc",
"matching_options.h",
]
}
static_library("default_matching_options") {
source_set("default_matching_options") {
sources = [
"default_matching_options.cc",
"default_matching_options.h",
......
......@@ -28,23 +28,69 @@ source_set("test_strings") {
]
}
static_library("test_util") {
source_set("test_executables") {
testonly = true
sources = [
"test_executables.cc",
"test_executables.h",
]
# Assume that every target that uses the test_executables header will run one
# of the executables, so make sure they are built.
data_deps = [
":test_process",
":test_service",
]
deps = [
":test_strings",
":test_util",
"//base:base",
"//chrome/chrome_cleaner/constants:common_strings",
]
}
source_set("test_util") {
testonly = true
sources = [
"reboot_deletion_helper.cc",
"reboot_deletion_helper.h",
"test_file_util.cc",
"test_file_util.h",
"test_layered_service_provider.cc",
"test_layered_service_provider.h",
"test_name_helper.cc",
"test_name_helper.h",
"test_registry_util.cc",
"test_registry_util.h",
"test_util.cc",
"test_util.h",
]
deps = [
":test_pup_data",
":test_strings",
"//base:base",
"//chrome/chrome_cleaner/logging/proto:shared_data_proto",
"//base/test:test_support",
"//chrome/chrome_cleaner/constants:common_strings",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/proto:shared_pup_enums_proto",
"//chrome/chrome_cleaner/pup_data:pup_data_base",
"//chrome/chrome_cleaner/strings",
"//components/chrome_cleaner/public/constants:constants",
"//sandbox/win:sandbox",
]
data_deps = [
":empty_dll",
"//chrome/chrome_cleaner/test/resources:signed_empty_dll",
]
public_deps = [
"//chrome/chrome_cleaner/logging/proto:shared_data_proto",
"//chrome/chrome_cleaner/proto:shared_pup_enums_proto",
"//testing/gmock",
"//testing/gtest",
]
......@@ -80,3 +126,52 @@ source_set("test_pup_data") {
"//testing/gtest",
]
}
source_set("unittest_sources") {
testonly = true
sources = [
"reboot_deletion_helper_unittest.cc",
"test_util_unittest.cc",
]
deps = [
":test_util",
"//base",
"//base/test:test_support",
"//chrome/chrome_cleaner/os:common_os",
"//testing/gtest",
]
}
executable("test_process") {
testonly = true
sources = [
"test_process_main.cc",
]
deps = [
":test_strings",
":test_util",
"//base:base",
"//base/test:test_support",
"//build/win:default_exe_manifest",
"//chrome/chrome_cleaner/os:common_os",
]
}
executable("test_service") {
testonly = true
sources = [
"test_service_main.cc",
]
deps = [
":test_strings",
"//base:base",
"//base/test:test_support",
"//build/win:default_exe_manifest",
]
}
This diff is collapsed.
// Copyright 2018 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_CHROME_CLEANER_TEST_REBOOT_DELETION_HELPER_H_
#define CHROME_CHROME_CLEANER_TEST_REBOOT_DELETION_HELPER_H_
#include <utility>
#include <vector>
#include "chrome/chrome_cleaner/os/file_path_set.h"
namespace chrome_cleaner {
// Holds the (source, destination) paths of a pending move. An empty
// destination string means deletion instead of move.
typedef std::pair<base::string16, base::string16> PendingMove;
typedef std::vector<PendingMove> PendingMoveVector;
// Returns true if the given file is registered to be deleted on the next
// reboot.
bool IsFileRegisteredForPostRebootRemoval(const base::FilePath& file_path);
// Unregister all of the given files from being registered to be deleted after
// the next reboot.
bool UnregisterPostRebootRemovals(const FilePathSet& paths);
// Get the list of all the pending post reboot moves.
bool GetPendingMoves(PendingMoveVector* pending_moves);
// Set all the pending moves for the nest reboot.
bool SetPendingMoves(const PendingMoveVector& pending_moves);
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_REBOOT_DELETION_HELPER_H_
This diff is collapsed.
......@@ -53,3 +53,13 @@ grit("test_resources") {
":compute_sample_dll_digest",
]
}
copy("signed_empty_dll") {
sources = [
"signed_dll/signed_empty_dll.dll",
]
outputs = [
"$root_out_dir/signed_empty_dll.dll",
]
}
### Signed test files
This directory contains a self-signed test certificate and its private key.
1. cleaner_test_cert.crt and cleaner_test_key.key were generated with:
```
openssl req -new -newkey rsa:4096 -x509 -sha256 -days 1095 -nodes -out cleaner_test_cert.crt -keyout cleaner_test_key.key
```
Leave every entry blank except CN set to "Cleaner Test Cert".
openssl is bundled with Windows Git. A copy can be found in
depot_tools/win_tools-2_7_6_bin/git.
1. cleaner_test_cert.pfx was generated with:
```
openssl pkcs12 -in cleaner_test_cert.crt -inkey cleaner_test_key.key -export -out cleaner_test_cert.pfx
```
Under Windows Git's bash shell, you need to use the "winpty" wrapper because
it asks for user input. Leave the password blank.
1. signed_empty_dll.dll was generated using the
//chrome/chrome_cleaner/test:empty_dll target, and then signed with
```
signtool.exe sign /fd sha256 /f cleaner_test_cert.pfx signed_empty_dll.dll
```
A copy of signtool.exe can be found in depot_tools/win_toolchain/vs_files.
Under Windows Git's bash shell, don't forget to escape the arguments (use
"//fd" and "//f"). The error message if you don't escape them is cryptic.
-----BEGIN CERTIFICATE-----
MIIFCzCCAvOgAwIBAgIJANsfwJbhowz8MA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNV
BAMMEUNsZWFuZXIgVGVzdCBDZXJ0MB4XDTE4MDcyMDE1MjcxN1oXDTIxMDcxOTE1
MjcxN1owHDEaMBgGA1UEAwwRQ2xlYW5lciBUZXN0IENlcnQwggIiMA0GCSqGSIb3
DQEBAQUAA4ICDwAwggIKAoICAQDDJXXzVyMjVU9IaufYZnbcfm+iDuKdIIStGIkP
KVlT22cnUtSVDKykJy+SAmnzFN0tsMWyYPzTj3onfPQ6y+qAW+AhDpKCASHeMUJC
VmPAa2mW06hEJpSRLLsBkUFV8jQgvgA0EZlBzIE9tTJXj3kTGry41Fb+Yh/RHRuV
VXCVK5aYRtbZznLJOSkGri8k8dIqiVBOzI3Z83JD1RmfGisRSgvtg7SoQ8l+ZfQz
bycw1wP4XJO+/NysksBW10Q5bB4XjwuPFURk+OGVlm/7WoaR6nFoQ0G9Z1YYh+pH
FGlIYuoFngCznrwxXEAFtOVTXWVrumWhOKnHMEYkdiNPujzYekeu7ch8si/ThTH4
m8Aphw8QAFQ2lKr9gAM/6fxSXhFUfo4juXtJF3Lz7CeLe7fY76e2NPaEwY7MyEuo
vx6KXvemHFDJtqix2HCQCH/03TrAZNYavxWxAaH6DYpAuQRw9yUWdALFoSYVykQV
vZ715yMtLRfT7Fi7UBK73G9G65rVbXPlDuB5uxwK48QA42PHT+u7QUY7BSKiJJBF
6+C8BLnzVgH0ew0hFtGIUba2AYhV94RDkfTRUUq3ZuK1OmKtyC2XuHv7UXvrurlm
BqhKl0yoACZnl3Jf3Fjouiq+Wh3IgzKvWCglYQUKQMvEHFnVk5Yu4Oh1s9xD5Mep
PQ2JWQIDAQABo1AwTjAdBgNVHQ4EFgQUiRfCjDp9ApRzG2WbBPG/1z0jiIwwHwYD
VR0jBBgwFoAUiRfCjDp9ApRzG2WbBPG/1z0jiIwwDAYDVR0TBAUwAwEB/zANBgkq
hkiG9w0BAQsFAAOCAgEARRS1G7qAzz5wN8X6CSKFsBf3XXlw/De4fm8Z2Hs13GdP
TMq8ASUgX3v+rlg/rKIiRov7sbPiaS3BpCwPfV/cCl5QVr3NWr/uqa5TPwYfb/y9
vIWsHffD3HMp9wE7lxJLlbWQ1gX7CGYPbSJ4z+WnXg4f3KYu9Cjk1hUktpANT8gj
p9vR7/kL9pEH70LWwTk8zDm6ibqkvSfEHP6wJpQBT6CiBfEB8x29F0klqQJ7jkh5
wp4gFHQOTGmAdARvQOb788L1EWBIjuRgG0AmB2CoS+VwXl72xTMEWtMkitKtx0oP
yvSXaGpW3/DcnxuWS7t0mtvNZMKmGj9rASeQcbqzGkpUmZkOHtB2msAsHVBakugb
JsDnuwrYX5C7D8fIdDzHQPXXrZUjqb6qjOy58EVqYtqINcYzA9j766Cx4SJujmbR
KrHU/9MAH39hcGrU/NW6mE8IYQ1SFpl8v2g7KdG0Er+ToTiCkOPrVJIst53G+0TG
K6l81JfQ58J7ilFlcF0GWKaTZY+7d7889PgAvlfazxsUuioM48vQYUlA/ucbbyBN
4nb/0q9kpY+tCiTZlFxCrmLNlspJHUvWUwbelUVtp+pjppbniMXXNwbOKvZltcoC
K/Ownd8Z+5hsqDJcJdZmqO1bFNEI1RD8Oz4kFOso5LU8nItB78/lz3WnNLKHeS4=
-----END CERTIFICATE-----
-----BEGIN PRIVATE KEY-----
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
wU4oaRK0RLzuQ79pYtXPcXBbIBRm7Zo=
-----END PRIVATE KEY-----
// Copyright 2018 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/chrome_cleaner/test/test_executables.h"
#include "base/base_paths.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process/launch.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/win_util.h"
#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
#include "chrome/chrome_cleaner/test/test_strings.h"
#include "chrome/chrome_cleaner/test/test_util.h"
namespace chrome_cleaner {
// If you add another test executable here, also add it to the data_deps in
// the "test_executables" target of chrome_cleaner/test/BUILD.gn.
const base::char16 kTestServiceExecutableName[] = L"test_service.exe";
const base::char16 kTestProcessExecutableName[] = L"test_process.exe";
base::Process LongRunningProcess(base::CommandLine* cmd) {
base::FilePath exe_dir;
if (!base::PathService::Get(base::DIR_EXE, &exe_dir)) {
LOG(ERROR) << "Failed to get the executable path, unable to create always "
"running process";
return base::Process();
}
base::FilePath exe_path =
exe_dir.Append(chrome_cleaner::kTestProcessExecutableName);
base::CommandLine command_line(exe_path);
// This will ensure this new process will run for one minute before dying.
command_line.AppendSwitchASCII(chrome_cleaner::kTestSleepMinutesSwitch, "1");
std::unique_ptr<base::WaitableEvent> init_done_event =
chrome_cleaner::CreateInheritableEvent(
base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED);
command_line.AppendSwitchNative(
chrome_cleaner::kInitDoneNotifierSwitch,
base::UintToString16(
base::win::HandleToUint32(init_done_event->handle())));
if (cmd)
*cmd = command_line;
base::LaunchOptions launch_options;
launch_options.handles_to_inherit.push_back(init_done_event->handle());
base::Process result = base::LaunchProcess(command_line, launch_options);
if (!init_done_event->TimedWait(base::TimeDelta::FromSeconds(10))) {
LOG(ERROR) << "Process did not signal";
result.Terminate(/*exit_code=*/1, /*wait=*/false);
return base::Process();
}
return result;
}
} // namespace chrome_cleaner
// Copyright 2018 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_CHROME_CLEANER_TEST_TEST_EXECUTABLES_H_
#define CHROME_CHROME_CLEANER_TEST_TEST_EXECUTABLES_H_
#include "base/command_line.h"
#include "base/process/process.h"
#include "base/strings/string16.h"
namespace chrome_cleaner {
// The name of the service executable used for tests.
extern const base::char16 kTestServiceExecutableName[];
// The name of the executable used for tests.
extern const base::char16 kTestProcessExecutableName[];
// Creates a process that will run for a minute, which is long enough to be
// killed by a reasonably fast unit or integration test.
// Populates |command_line| with the used command line if it is not nullptr.
base::Process LongRunningProcess(base::CommandLine* command_line);
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_TEST_EXECUTABLES_H_
// Copyright 2018 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/chrome_cleaner/test/test_layered_service_provider.h"
namespace chrome_cleaner {
TestLayeredServiceProvider::TestLayeredServiceProvider() = default;
TestLayeredServiceProvider::~TestLayeredServiceProvider() = default;
int TestLayeredServiceProvider::EnumProtocols(int* protocols,
WSAPROTOCOL_INFOW* protocol_info,
DWORD* nb_protocol_info,
int* error) const {
// We don't use the protocols argument yet.
DCHECK(!protocols);
DCHECK(nb_protocol_info);
DCHECK(error);
size_t bytes_needed = protocol_info_.size() * sizeof(WSAPROTOCOL_INFOW);
if (*nb_protocol_info < bytes_needed) {
*nb_protocol_info = bytes_needed;
*error = WSAENOBUFS;
return SOCKET_ERROR;
}
*nb_protocol_info = bytes_needed;
std::map<GUID, base::FilePath, GUIDLess>::const_iterator iter =
protocol_info_.begin();
for (size_t i = 0; iter != protocol_info_.end(); ++iter, ++i) {
protocol_info[i].ProviderId = iter->first;
}
return protocol_info_.size();
}
int TestLayeredServiceProvider::GetProviderPath(GUID* provider_id,
wchar_t* provider_dll_path,
int* provider_dll_path_len,
int* error) const {
DCHECK(provider_id);
DCHECK(provider_dll_path);
DCHECK(provider_dll_path_len);
DCHECK(error);
std::map<GUID, base::FilePath, GUIDLess>::const_iterator iter =
protocol_info_.find(*provider_id);
if (iter == protocol_info_.end()) {
*error = WSAEINVAL;
return SOCKET_ERROR;
}
if (*provider_dll_path_len <
static_cast<int>(iter->second.value().size() + 1)) {
*error = WSAEFAULT;
return SOCKET_ERROR;
}
::wcsncpy(provider_dll_path, iter->second.value().c_str(),
iter->second.value().size());
provider_dll_path[iter->second.value().size()] = L'\0';
return 0;
}
void TestLayeredServiceProvider::AddProvider(const GUID& provider_id,
const base::FilePath& path) {
protocol_info_[provider_id] = path;
}
} // namespace chrome_cleaner
// Copyright 2018 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_CHROME_CLEANER_TEST_TEST_LAYERED_SERVICE_PROVIDER_H_
#define CHROME_CHROME_CLEANER_TEST_TEST_LAYERED_SERVICE_PROVIDER_H_
#include <map>
#include "base/files/file_path.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/layered_service_provider_api.h"
namespace chrome_cleaner {
class TestLayeredServiceProvider : public LayeredServiceProviderAPI {
public:
TestLayeredServiceProvider();
~TestLayeredServiceProvider() override;
int EnumProtocols(int* protocols,
WSAPROTOCOL_INFOW* protocol_info,
DWORD* nb_protocol_info,
int* error) const override;
int GetProviderPath(GUID* provider_id,
wchar_t* provider_dll_path,
int* provider_dll_path_len,
int* error) const override;
void AddProvider(const GUID& provider_id, const base::FilePath& path);
private:
std::map<GUID, base::FilePath, GUIDLess> protocol_info_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_TEST_LAYERED_SERVICE_PROVIDER_H_
......@@ -2,22 +2,76 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/test/launcher/unit_test_launcher.h"
#include "base/test/test_suite.h"
#include "chrome/chrome_cleaner/os/initializer.h"
#include "chrome/chrome_cleaner/pup_data/pup_data.h"
#include "chrome/chrome_cleaner/test/test_uws_catalog.h"
#include "base/win/scoped_com_initializer.h"
#include "chrome/chrome_cleaner/os/rebooter.h"
#include "chrome/chrome_cleaner/os/secure_dll_loading.h"
#include "chrome/chrome_cleaner/os/system_util_cleaner.h"
#include "chrome/chrome_cleaner/os/task_scheduler.h"
#include "chrome/chrome_cleaner/test/test_util.h"
#include "sandbox/win/src/sandbox_factory.h"
namespace {
bool IsSandboxedProcess() {
static const bool is_sandboxed_process =
(sandbox::SandboxFactory::GetTargetServices() != nullptr);
return is_sandboxed_process;
}
} // namespace
int main(int argc, char** argv) {
// This must be executed as soon as possible to reduce the number of dlls that
// the code might try to load before we can lock things down.
//
// We enable secure DLL loading in the test suite to be sure that it doesn't
// affect the behaviour of functionality that's tested.
chrome_cleaner::EnableSecureDllLoading();
base::TestSuite test_suite(argc, argv);
chrome_cleaner::PUPData::InitializePUPData(
{&chrome_cleaner::TestUwSCatalog::GetInstance()});
chrome_cleaner::InitializeOSUtils();
if (!chrome_cleaner::SetupTestConfigs())
return 1;
if (!chrome_cleaner::CheckTestPrivileges())
return 1;
return base::LaunchUnitTests(
// Make sure tests will not end up in an infinite reboot loop.
if (chrome_cleaner::Rebooter::IsPostReboot())
return 0;
// ScopedCOMInitializer keeps COM initialized in a specific scope. We don't
// want to initialize it for sandboxed processes, so manage its lifetime with
// a unique_ptr, which will call ScopedCOMInitializer's destructor when it
// goes out of scope below.
std::unique_ptr<base::win::ScopedCOMInitializer> scoped_com_initializer;
if (!IsSandboxedProcess()) {
scoped_com_initializer = std::make_unique<base::win::ScopedCOMInitializer>(
base::win::ScopedCOMInitializer::kMTA);
bool success = chrome_cleaner::InitializeCOMSecurity();
DCHECK(success) << "InitializeCOMSecurity() failed.";
success = chrome_cleaner::TaskScheduler::Initialize();
DCHECK(success) << "TaskScheduler::Initialize() failed.";
}
// Some tests will fail if two tests try to launch test_process.exe
// simultaneously, so run the tests serially. This will still shard them and
// distribute the shards to different swarming bots, but tests will run
// serially on each bot.
const int result = base::LaunchUnitTestsSerially(
argc, argv,
base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite)));
if (!IsSandboxedProcess())
chrome_cleaner::TaskScheduler::Terminate();
return result;
}
// Copyright 2018 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 <windows.h>
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/time/time.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/initializer.h"
#include "chrome/chrome_cleaner/os/pre_fetched_paths.h"
#include "chrome/chrome_cleaner/os/secure_dll_loading.h"
#include "chrome/chrome_cleaner/test/test_strings.h"
#include "chrome/chrome_cleaner/test/test_util.h"
namespace {
constexpr base::char16 kLogFileExtension[] = L"log";
} // namespace
int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) {
// This must be executed as soon as possible to reduce the number of dlls that
// the code might try to load before we can lock things down.
chrome_cleaner::EnableSecureDllLoading();
base::AtExitManager at_exit;
bool success = base::CommandLine::Init(0, nullptr);
DCHECK(success);
if (!chrome_cleaner::SetupTestConfigs())
return 1;
// Initialize the logging settings to set a specific log file name.
base::FilePath exe_file_path =
chrome_cleaner::PreFetchedPaths::GetInstance()->GetExecutablePath();
base::FilePath log_file_path(
exe_file_path.ReplaceExtension(kLogFileExtension));
logging::LoggingSettings logging_settings;
logging_settings.logging_dest = logging::LOG_TO_FILE;
logging_settings.log_file = log_file_path.value().c_str();
success = logging::InitLogging(logging_settings);
DCHECK(success);
LOG(INFO) << "Process Started.";
chrome_cleaner::NotifyInitializationDoneForTesting();
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(
chrome_cleaner::kTestForceOverwriteZoneIdentifier)) {
LOG(INFO) << "Process is overwriting the zone identifier.";
chrome_cleaner::OverwriteZoneIdentifier(exe_file_path);
}
if (command_line->HasSwitch(chrome_cleaner::kTestSleepMinutesSwitch)) {
std::string value = command_line->GetSwitchValueASCII(
chrome_cleaner::kTestSleepMinutesSwitch);
int sleep_minutes = 0;
if (base::StringToInt(value, &sleep_minutes) && sleep_minutes > 0) {
LOG(INFO) << "Process is sleeping for " << sleep_minutes << " minutes";
::Sleep(base::TimeDelta::FromMinutes(sleep_minutes).InMilliseconds());
} else {
LOG(ERROR) << "Invalid sleep delay value " << value;
}
NOTREACHED();
return 1;
}
if (command_line->HasSwitch(chrome_cleaner::kTestEventToSignal)) {
LOG(INFO) << "Process is signaling event '"
<< chrome_cleaner::kTestEventToSignal << "'";
base::string16 event_name =
command_line->GetSwitchValueNative(chrome_cleaner::kTestEventToSignal);
base::win::ScopedHandle handle(
::OpenEvent(EVENT_ALL_ACCESS, TRUE, event_name.c_str()));
PLOG_IF(ERROR, !handle.IsValid())
<< "Cannot create event '" << chrome_cleaner::kTestEventToSignal << "'";
base::WaitableEvent event(std::move(handle));
event.Signal();
}
// TODO(pmbureau): Add more behavior to test processes termination.
LOG(INFO) << "Process ended.";
return 0;
}
// Copyright 2018 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 <windows.h>
#include <string>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/strings/string16.h"
#include "base/test/test_timeouts.h"
#include "base/threading/thread_checker.h"
#include "chrome/chrome_cleaner/test/test_strings.h"
namespace {
class TestService {
public:
TestService() {
service_status_.dwCurrentState = SERVICE_START_PENDING;
service_status_.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
service_status_.dwWaitHint =
TestTimeouts::action_max_timeout().InMilliseconds();
service_stop_event_ = ::CreateEvent(nullptr, TRUE, FALSE, nullptr);
}
void SignalStopEvent() { ::SetEvent(service_stop_event_); }
void WaitForStopEvent() {
::WaitForSingleObject(service_stop_event_, INFINITE);
}
void SetControlsAccepted(DWORD control) {
DCHECK_CALLED_ON_VALID_THREAD(service_status_thread_checker_);
service_status_.dwControlsAccepted = control;
}
void SetStatusHandle(SERVICE_STATUS_HANDLE status_handle) {
CHECK(status_handle);
status_handle_ = status_handle;
}
DWORD GetServiceStatusState() {
DCHECK_CALLED_ON_VALID_THREAD(service_status_thread_checker_);
return service_status_.dwCurrentState;
}
void SetServiceStatusState(DWORD state) {
DCHECK_CALLED_ON_VALID_THREAD(service_status_thread_checker_);
DCHECK(status_handle_);
service_status_.dwCurrentState = state;
if (::SetServiceStatus(status_handle_, &service_status_) == FALSE)
LOG(ERROR) << "Cannot set service status state.";
}
private:
SERVICE_STATUS_HANDLE status_handle_ = 0;
SERVICE_STATUS service_status_ = {};
HANDLE service_stop_event_ = INVALID_HANDLE_VALUE;
THREAD_CHECKER(service_status_thread_checker_);
};
DWORD WINAPI ServiceCtrlHandler(DWORD control,
DWORD /* event_type */,
LPVOID /* event_data */,
LPVOID context) {
TestService* service = reinterpret_cast<TestService*>(context);
DCHECK(service);
if (control == SERVICE_CONTROL_STOP)
service->SignalStopEvent();
return 0;
}
void WINAPI ServiceMain(DWORD argc, LPTSTR* argv) {
TestService service;
// Registers a function to handle extended service control requests.
SERVICE_STATUS_HANDLE status_handle =
RegisterServiceCtrlHandlerEx(L"", ServiceCtrlHandler, &service);
service.SetStatusHandle(status_handle);
// Tell the service controller the current service has started.
service.SetServiceStatusState(SERVICE_START_PENDING);
LOG(INFO) << "Service status is 'start pending'.";
// Tell the service controller the current service is running.
service.SetControlsAccepted(SERVICE_ACCEPT_STOP);
service.SetServiceStatusState(SERVICE_RUNNING);
LOG(INFO) << "Service status is 'running'.";
// The body of the service wait until a stop signal is received.
LOG(INFO) << "Service is waiting to be stopped.";
service.WaitForStopEvent();
// TODO(pmbureau): Add more kind of worker that may protect against service
// termination and validate service termination.
// Tell the service controller the current service is stopped.
service.SetServiceStatusState(SERVICE_STOP_PENDING);
LOG(INFO) << "Service status is 'stop pending'.";
service.SetServiceStatusState(SERVICE_STOPPED);
// Do not attempt to perform any additional work after calling
// SetServiceStatus with SERVICE_STOPPED, because the service process can be
// terminated at any time.
}
constexpr base::char16 kLogFileExtension[] = L"log";
} // namespace
int main(int argc, char** argv) {
base::AtExitManager at_exit;
bool success = base::CommandLine::Init(0, nullptr);
DCHECK(success);
TestTimeouts::Initialize();
// Initialize the logging settings to set a specific log file name.
base::FilePath exe_file_path;
success = base::PathService::Get(base::FILE_EXE, &exe_file_path);
DCHECK(success);
base::FilePath log_file_path(
exe_file_path.ReplaceExtension(kLogFileExtension));
logging::LoggingSettings logging_settings;
logging_settings.logging_dest = logging::LOG_TO_FILE;
logging_settings.log_file = log_file_path.value().c_str();
success = logging::InitLogging(logging_settings);
DCHECK(success);
LOG(INFO) << "Service Started.";
// Service main.
// SERVICE_TABLE_ENTRY::lpServiceName takes a non-const string.
base::char16 empty_string[] = L"";
SERVICE_TABLE_ENTRY dispatch_table[] = {{empty_string, ServiceMain},
{nullptr, nullptr}};
if (::StartServiceCtrlDispatcher(dispatch_table) == FALSE) {
LOG(ERROR) << "StartServiceCtrlDispatcher failed.";
return 1;
}
LOG(INFO) << "Service ended.";
return 0;
}
This diff is collapsed.
// Copyright 2018 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_CHROME_CLEANER_TEST_TEST_UTIL_H_
#define CHROME_CHROME_CLEANER_TEST_TEST_UTIL_H_
#include <stdint.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "base/files/scoped_temp_dir.h"
#include "base/process/process.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_command_line.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "chrome/chrome_cleaner/os/task_scheduler.h"
#include "chrome/chrome_cleaner/proto/shared_pup_enums.pb.h"
#include "chrome/chrome_cleaner/pup_data/pup_data.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
class FilePath;
} // namespace base
namespace chrome_cleaner {
// Setup all configs required by tests (like disabling path caching). This
// should be done in the main function of the test binary, not by individual
// tests.
//
// Returns false if setup fails. Tests shouldn't be run if the setup fails.
bool SetupTestConfigs();
// Sets up all test configs, as SetupTestConfigs, using the given list of
// |catalogs| instead of TestUwSCatalog.
bool SetupTestConfigsWithCatalogs(const PUPData::UwSCatalogs& catalogs);
// While this class is in scope, Rebooter::IsPostReboot will return true.
class ScopedIsPostReboot {
public:
ScopedIsPostReboot();
private:
base::test::ScopedCommandLine scoped_command_line_;
};
class LoggingOverride {
public:
LoggingOverride();
~LoggingOverride();
// Intercepts all log messages.
static bool LogMessageHandler(int severity,
const char* file,
int line,
size_t message_start,
const std::string& str) {
DCHECK(active_logging_messages_);
active_logging_messages_->push_back(str);
return false;
}
// Returns true if one of the messages contains |sub_string|.
bool LoggingMessagesContain(const std::string& sub_string);
// Returns true if one of the messages contains both |sub_string| and
// |sub_string2|.
bool LoggingMessagesContain(const std::string& sub_string1,
const std::string& sub_string2);
// Remove all the log messages.
void FlushMessages() { logging_messages_.clear(); }
std::vector<std::string> logging_messages_;
static std::vector<std::string>* active_logging_messages_;
};
// Validate that the run once on restart registry value contains the given
// |sub_string|.
bool RunOnceCommandLineContains(const base::string16& product_shortname,
const wchar_t* sub_string);
// Validate that the run once on restart switch-containing registry value
// contains the given |sub_string|.
bool RunOnceOverrideCommandLineContains(const std::string& cleanup_id,
const wchar_t* sub_string);
// Register a task with the given task scheduler. If the task is successfully
// added, |task_info| will contain all the information about the task.
// Callers are responsible for deleting the test task.
bool RegisterTestTask(TaskScheduler* task_scheduler,
TaskScheduler::TaskInfo* task_info);
// Append switches to the command line that is used to run cleaner or reporter
// in tests. Switches will disable logs upload, profile reset and other side
// effects.
void AppendTestSwitches(base::CommandLine* command_line);
// Expect the |expected_path| to be found in expanded disk footprint of |pup|.
void ExpectDiskFootprint(const PUPData::PUP& pup,
const base::FilePath& expected_path);
// Expect the scheduled task footprint to be found in |pup|.
void ExpectScheduledTaskFootprint(const PUPData::PUP& pup,
const wchar_t* task_name);
// This function is the 8 bits version of String16ContainsCaseInsensitive in
// chrome_cleaner/string_util. Since it's only used in tests, we decided not to
// move it to the main lib.
bool StringContainsCaseInsensitive(const std::string& value,
const std::string& substring);
// Expect two FilePathSets to be equal. Log files that are matched in excess
// or expected files that are missing.
void ExpectEqualFilePathSets(const FilePathSet& matched_files,
const FilePathSet& expected_files);
// Returns the path that base::DIR_SYSTEM is transparently redirected to under
// Wow64. Note that on 32-bit Windows this will always return the empty string
// because base::DIR_SYSTEM is not redirected.
base::FilePath GetWow64RedirectedSystemPath();
// Returns the path to a sample DLL file that can be used in tests.
base::FilePath GetSampleDLLPath();
// Returns the path to a signed sample DLL file that can be used in tests.
base::FilePath GetSignedSampleDLLPath();
// A ScopedTempDir with the ability to create and delete subdirs of
// c:\windows\system32, which are Wow64-redirected by default. This turns off
// Wow64 redirection so the subdir is created in the real c:\windows\system32
// even in binaries that would normally be redirected to c:\windows\SysWOW64.
class ScopedTempDirNoWow64 : protected base::ScopedTempDir {
public:
ScopedTempDirNoWow64();
~ScopedTempDirNoWow64();
// Creates a unique subdirectory under system32, bypassing Wow64 redirection,
// and takes ownership of it.
bool CreateUniqueSystem32TempDir() WARN_UNUSED_RESULT;
// Convenience function to call CreateUniqueSystem32TempDir and create an
// empty file with the given |file_name| in the resulting directory.
bool CreateEmptyFileInUniqueSystem32TempDir(const base::string16& file_name)
WARN_UNUSED_RESULT;
using base::ScopedTempDir::Delete;
using base::ScopedTempDir::GetPath;
using base::ScopedTempDir::IsValid;
using base::ScopedTempDir::Take;
// Do not give access to CreateUniqueTempDir, CreateUniqueTempDirUnderPath,
// or Set because they can set the temp dir path to a non-Wow64-redirected
// directory.
};
std::unique_ptr<base::WaitableEvent> CreateInheritableEvent(
base::WaitableEvent::ResetPolicy reset_policy,
base::WaitableEvent::InitialState initial_state);
// Check that the test has administrator privileges, but not debug privileges.
// This function drops unneeded privileges if possible, but won't try to raise
// privileges. Returns false if the privileges could not be made correct.
bool CheckTestPrivileges();
// Accepts PUPData::PUP parameters with id equals to |expected_id|.
MATCHER_P(PupHasId, expected_id, "") {
return arg->signature().id == expected_id;
}
// Accepts PUPData::PUP parameters with |size| expanded disk footprints.
MATCHER_P(PupHasFileListSize, size, "") {
return arg->expanded_disk_footprints.size() == static_cast<size_t>(size);
}
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_TEST_UTIL_H_
// Copyright 2018 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/chrome_cleaner/test/test_util.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "testing/gtest/include/gtest/gtest.h"
// The following include is needed to use EXPECT_NONFATAL_FAILURE.
#include "testing/gtest/include/gtest/gtest-spi.h"
namespace chrome_cleaner {
namespace {
const wchar_t kFileName1[] = L"test_path1";
const wchar_t kFileName2[] = L"test_path2";
} // namespace
TEST(TestUtilTest, ExpectEqualFilePathSets) {
// Messages are logged to a vector for testing.
LoggingOverride logger;
FilePathSet matched_files;
FilePathSet expected_files;
matched_files.Insert(base::FilePath(kFileName1));
EXPECT_NONFATAL_FAILURE(
ExpectEqualFilePathSets(matched_files, expected_files), "");
EXPECT_TRUE(logger.LoggingMessagesContain(
"Unexpected file in matched footprints: 'test_path1'"));
logger.FlushMessages();
expected_files.Insert(base::FilePath(kFileName1));
ExpectEqualFilePathSets(matched_files, expected_files);
logger.FlushMessages();
expected_files.Insert(base::FilePath(kFileName2));
EXPECT_NONFATAL_FAILURE(
ExpectEqualFilePathSets(matched_files, expected_files), "");
EXPECT_TRUE(logger.LoggingMessagesContain(
"Missing expected footprint: 'test_path2'"));
}
} // namespace chrome_cleaner
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment