blob: bfbd9dfdd7589c3d469204a675c9a8ae9db9a34e [file] [log] [blame]
// 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/logging/cleaner_logging_service.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_path_override.h"
#include "base/test/scoped_task_environment.h"
#include "base/test/test_reg_util_win.h"
#include "chrome/chrome_cleaner/constants/chrome_cleaner_switches.h"
#include "chrome/chrome_cleaner/constants/uws_id.h"
#include "chrome/chrome_cleaner/http/http_agent_factory.h"
#include "chrome/chrome_cleaner/http/mock_http_agent_factory.h"
#include "chrome/chrome_cleaner/logging/pending_logs_service.h"
#include "chrome/chrome_cleaner/logging/proto/chrome_cleaner_report.pb.h"
#include "chrome/chrome_cleaner/logging/proto/removal_status.pb.h"
#include "chrome/chrome_cleaner/logging/registry_logger.h"
#include "chrome/chrome_cleaner/logging/safe_browsing_reporter.h"
#include "chrome/chrome_cleaner/logging/test_utils.h"
#include "chrome/chrome_cleaner/os/disk_util.h"
#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
#include "chrome/chrome_cleaner/os/file_removal_status_updater.h"
#include "chrome/chrome_cleaner/os/registry_util.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 "chrome/chrome_cleaner/pup_data/test_uws.h"
#include "chrome/chrome_cleaner/test/test_file_util.h"
#include "chrome/chrome_cleaner/test/test_settings_util.h"
#include "chrome/chrome_cleaner/test/test_task_scheduler.h"
#include "components/chrome_cleaner/public/constants/constants.h"
#include "components/chrome_cleaner/test/test_name_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
namespace chrome_cleaner {
namespace {
using testing::_;
using testing::Return;
const internal::FileInformation kFileInformation1(L"some/path/something.tmp",
"3/1/2016",
"3/3/2016",
"somedigest1234",
9876,
L"Company Name",
L"CNShort",
L"Product Name",
L"PNShort",
L"Internal Name",
L"Something_Original.tmp",
L"Very descriptive",
L"42.1.2");
const internal::FileInformation kFileInformation2(
L"some/path/somethingelse.tmp",
"2/1/2016",
"2/3/2016",
"someotherdigest1234",
6543,
L"Company Name 2",
L"CN2Short",
L"Product Name2",
L"PN2Short",
L"Internal Name2",
L"Something_Original2.tmp",
L"Very descriptive2",
L"43.2.3");
const wchar_t kMatchedFileToStringExpectedString[] =
L"path = 'some/path/something.tmp', file_creation_date = "
L"'3/1/2016', file_last_modified_date = '3/3/2016', digest = "
L"'somedigest1234', size = '9876', company_name = 'Company Name', "
L"company_short_name = 'CNShort', product_name = 'Product Name', "
L"product_short_name = 'PNShort', internal_name = 'Internal Name', "
L"original_filename = 'Something_Original.tmp', file_description = 'Very "
L"descriptive', file_version = '42.1.2', active_file = '0', removal_status "
L"= 0, quarantine_status = 0";
const wchar_t kMatchedRegistryEntryKey[] = L"123";
const wchar_t kMatchedRegistryEntryValueName[] = L"Value Name";
const wchar_t kMatchedRegistryEntryValueSubstring[] = L"Value Substring";
const wchar_t kMatchedRegistryEntryToStringExpectedString[] =
L"123\\Value Name Value Substring";
const internal::RegistryValue kRegistryValue1 = {
L"HKCU\\something\\that\\exists", L"ValueName", L"Some content"};
const internal::RegistryValue kRegistryValue2 = {
L"HKCU\\something\\else", L"ValueName2", L"Some other content"};
const wchar_t kSystemProxySettingsConfig[] = L"http://someconfigurl.com/hello";
const wchar_t kSystemProxySettingsBypass[] = L"http://somebypassurl.com/hello";
const char kFileContent1[] = "This is the file content.";
constexpr PUPData::UwSSignature kMatchedUwSSignature{
kGoogleTestAUwSID, PUPData::FLAGS_NONE, "Observed/matched_uws"};
constexpr PUPData::UwSSignature kRemovedUwSSignature{
kGoogleTestBUwSID, PUPData::FLAGS_ACTION_REMOVE, "Removed/removed_uws"};
constexpr PUPData::UwSSignature kMatchedUwSSlowSignature{
kGoogleTestCUwSID, PUPData::FLAGS_NONE, "Observed/matched_uws_slow_"};
void CompareRegistryEntries(
const std::vector<PUPData::RegistryFootprint>& expanded_registry_footprints,
const ::google::protobuf::RepeatedPtrField<MatchedRegistryEntry>&
registry_entries) {
ASSERT_EQ(expanded_registry_footprints.size(),
static_cast<size_t>(registry_entries.size()));
for (unsigned int i = 0; i < expanded_registry_footprints.size(); ++i) {
EXPECT_EQ(
base::WideToUTF8(expanded_registry_footprints[i].key_path.FullPath()),
registry_entries.Get(i).key_path());
EXPECT_EQ(base::WideToUTF8(expanded_registry_footprints[i].value_name),
registry_entries.Get(i).value_name());
EXPECT_EQ(base::WideToUTF8(expanded_registry_footprints[i].value_substring),
registry_entries.Get(i).value_substring());
}
}
void CompareUwSData(const PUPData::PUP& uws, const UwS& log_uws) {
EXPECT_EQ(uws.signature().name, log_uws.name());
EXPECT_EQ(uws.signature().id, log_uws.id());
ASSERT_EQ(uws.expanded_disk_footprints.size(),
static_cast<size_t>(log_uws.files_size() + log_uws.folders_size()));
int files_index = 0;
int folders_index = 0;
for (const auto& file_path : uws.expanded_disk_footprints.file_paths()) {
// The path will be sanitized in the logs.
std::string sanitized_path = base::UTF16ToUTF8(SanitizePath(file_path));
if (base::DirectoryExists(file_path)) {
ASSERT_TRUE(folders_index < log_uws.folders_size());
EXPECT_EQ(sanitized_path,
log_uws.folders(folders_index).folder_information().path());
++folders_index;
} else {
ASSERT_TRUE(files_index < log_uws.files_size());
EXPECT_EQ(sanitized_path,
log_uws.files(files_index).file_information().path());
++files_index;
}
}
EXPECT_EQ(log_uws.folders_size(), folders_index);
EXPECT_EQ(log_uws.files_size(), files_index);
CompareRegistryEntries(uws.expanded_registry_footprints,
log_uws.registry_entries());
ASSERT_EQ(uws.expanded_scheduled_tasks.size(),
static_cast<size_t>(log_uws.scheduled_tasks_size()));
for (unsigned int i = 0; i < uws.expanded_scheduled_tasks.size(); ++i) {
EXPECT_EQ(base::WideToUTF8(uws.expanded_scheduled_tasks[i]),
log_uws.scheduled_tasks(i).scheduled_task().name());
}
}
void ExpectFileInformationEqualToProtoObj(
const internal::FileInformation& file_information,
const FileInformation& proto_file_information) {
EXPECT_EQ(proto_file_information.path(),
base::UTF16ToUTF8(file_information.path));
EXPECT_EQ(proto_file_information.creation_date(),
file_information.creation_date);
EXPECT_EQ(proto_file_information.last_modified_date(),
file_information.last_modified_date);
EXPECT_EQ(proto_file_information.sha256(), file_information.sha256);
EXPECT_EQ(proto_file_information.size(), file_information.size);
EXPECT_EQ(proto_file_information.company_name(),
base::UTF16ToUTF8(file_information.company_name));
EXPECT_EQ(proto_file_information.company_short_name(),
base::UTF16ToUTF8(file_information.company_short_name));
EXPECT_EQ(proto_file_information.product_name(),
base::UTF16ToUTF8(file_information.product_name));
EXPECT_EQ(proto_file_information.product_short_name(),
base::UTF16ToUTF8(file_information.product_short_name));
EXPECT_EQ(proto_file_information.internal_name(),
base::UTF16ToUTF8(file_information.internal_name));
EXPECT_EQ(proto_file_information.original_filename(),
base::UTF16ToUTF8(file_information.original_filename));
EXPECT_EQ(proto_file_information.file_description(),
base::UTF16ToUTF8(file_information.file_description));
EXPECT_EQ(proto_file_information.file_version(),
base::UTF16ToUTF8(file_information.file_version));
EXPECT_EQ(proto_file_information.active_file(), file_information.active_file);
}
void ExpectRegistryValueEqualToProtoObj(
const internal::RegistryValue& registry_value,
const RegistryValue& proto_registry_value) {
EXPECT_EQ(proto_registry_value.key_path(),
base::UTF16ToUTF8(registry_value.key_path));
EXPECT_EQ(proto_registry_value.value_name(),
base::UTF16ToUTF8(registry_value.value_name));
EXPECT_EQ(proto_registry_value.data(),
base::UTF16ToUTF8(registry_value.data));
}
void NoSleep(base::TimeDelta) {}
} // namespace
// Test parametrized with the execution mode.
class CleanerLoggingServiceTest : public testing::TestWithParam<ExecutionMode> {
public:
// LoggingServiceAPI::UploadResultCallback implementation.
void LoggingServiceDone(base::OnceClosure run_loop_quit, bool success) {
done_callback_called_ = true;
upload_success_ = success;
// A RunLoop will be waiting for this callback to run before proceeding
// with the test. Now that the callback has run, we can quit the RunLoop.
std::move(run_loop_quit).Run();
}
void SetUp() override {
overriden_settings_ =
std::make_unique<SettingsWithExecutionModeOverride>(GetParam());
Settings::SetInstanceForTesting(overriden_settings_.get());
registry_override_manager_.OverrideRegistry(HKEY_CURRENT_USER);
// The registry logger must be created after calling OverrideRegistry.
registry_logger_.reset(new RegistryLogger(RegistryLogger::Mode::REMOVER));
// By default, tests use the NoOpLoggingService, so individual tests that
// need logging need to enable it.
logging_service_ = CleanerLoggingService::GetInstance();
logging_service_->Initialize(registry_logger_.get());
ASSERT_TRUE(uws_dir_.CreateUniqueTempDir());
// Add a folder for each UwS.
base::FilePath matched_uws_folder;
base::CreateTemporaryDirInDir(uws_dir_.GetPath(), L"matched_uws",
&matched_uws_folder);
base::FilePath removed_uws_folder;
base::CreateTemporaryDirInDir(uws_dir_.GetPath(), L"removed_uws",
&removed_uws_folder);
base::FilePath matched_uws_slow_folder;
base::CreateTemporaryDirInDir(uws_dir_.GetPath(), L"matched_uws_slow",
&matched_uws_slow_folder);
matched_uws_.AddDiskFootprint(matched_uws_folder);
removed_uws_.AddDiskFootprint(removed_uws_folder);
matched_uws_slow_.AddDiskFootprint(matched_uws_slow_folder);
// Add a file for each UwS.
matched_uws_file_ = matched_uws_folder.Append(L"matched_uws.tmp");
matched_uws_.AddDiskFootprint(matched_uws_file_);
removed_uws_file_ = removed_uws_folder.Append(L"removed_uws.tmp");
removed_uws_.AddDiskFootprint(removed_uws_file_);
matched_uws_slow_file_ =
matched_uws_slow_folder.Append(L"matched_uws_slow_.tmp");
matched_uws_slow_.AddDiskFootprint(matched_uws_slow_file_);
// Add a registry entry for each UwS.
RegKeyPath reg_path(HKEY_LOCAL_MACHINE, L"software\\test\\uws");
PUPData::RegistryFootprint reg(reg_path, L"matched_uws", L"1",
REGISTRY_VALUE_MATCH_KEY);
matched_uws_.expanded_registry_footprints.push_back(reg);
reg = PUPData::RegistryFootprint(reg_path, L"removed_uws", L"2",
REGISTRY_VALUE_MATCH_KEY);
removed_uws_.expanded_registry_footprints.push_back(reg);
reg = PUPData::RegistryFootprint(reg_path, L"matched_uws_slow", L"3",
REGISTRY_VALUE_MATCH_KEY);
matched_uws_slow_.expanded_registry_footprints.push_back(reg);
// Add dummy expanded scheduled tasks to one UwS.
matched_uws_.expanded_scheduled_tasks.push_back(L"ScheduledTask1");
matched_uws_.expanded_scheduled_tasks.push_back(L"ScheduledTask2");
// Use a mock HttpAgent instead of making real network requests.
SafeBrowsingReporter::SetHttpAgentFactoryForTesting(
http_agent_factory_.get());
SafeBrowsingReporter::SetSleepCallbackForTesting(
base::BindRepeating(&NoSleep));
SafeBrowsingReporter::SetNetworkCheckerForTesting(&network_checker_);
}
void TearDown() override {
// Disable logs uploading to delete any pending logs data.
logging_service_->EnableUploads(false, registry_logger_.get());
SafeBrowsingReporter::SetNetworkCheckerForTesting(nullptr);
SafeBrowsingReporter::SetHttpAgentFactoryForTesting(nullptr);
logging_service_->Terminate();
registry_logger_.reset();
Settings::SetInstanceForTesting(nullptr);
}
protected:
// Sends logs to Safe Browsing, and waits for LoggingServiceDone to be called
// before returning.
void DoSendLogsToSafeBrowsing() {
base::RunLoop run_loop;
logging_service_->SendLogsToSafeBrowsing(
base::BindRepeating(&CleanerLoggingServiceTest::LoggingServiceDone,
base::Unretained(this), run_loop.QuitClosure()),
registry_logger_.get());
run_loop.Run();
}
CleanerLoggingServiceTest()
: logging_service_(nullptr),
scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI),
done_callback_called_(false),
upload_success_(false),
matched_uws_(&kMatchedUwSSignature),
removed_uws_(&kRemovedUwSSignature),
matched_uws_slow_(&kMatchedUwSSlowSignature) {}
// This is a pointer to the singleton object which we don't own.
CleanerLoggingService* logging_service_;
registry_util::RegistryOverrideManager registry_override_manager_;
std::unique_ptr<RegistryLogger> registry_logger_;
// Needed for the current task runner to be available.
base::test::ScopedTaskEnvironment scoped_task_environment_;
// |done_callback_called_| is set to true in |LoggingServiceDone| to confirm
// it was called appropriately.
bool done_callback_called_;
// Set with the value given to the done callback. Default to false.
bool upload_success_;
// UwS data for various UwS that should be logged.
PUPData::PUP matched_uws_;
PUPData::PUP removed_uws_;
PUPData::PUP matched_uws_slow_;
base::FilePath matched_uws_file_;
base::FilePath removed_uws_file_;
base::FilePath matched_uws_slow_file_;
// A temporary dir to store some temp UwS files.
base::ScopedTempDir uws_dir_;
// Mocked TaskScheduler.
TestTaskScheduler task_scheduler_;
// Overridden settings with the ExecutionMode test param.
std::unique_ptr<SettingsWithExecutionModeOverride> overriden_settings_;
MockHttpAgentConfig http_agent_config_;
std::unique_ptr<HttpAgentFactory> http_agent_factory_{
std::make_unique<MockHttpAgentFactory>(&http_agent_config_)};
MockNetworkChecker network_checker_;
};
TEST(LoggingServiceUtilTest, GetCleanerStartupFromCommandLine) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
EXPECT_EQ(ChromeCleanerReport::CLEANER_STARTUP_NOT_PROMPTED,
GetCleanerStartupFromCommandLine(&command_line));
// Check that cleaner_startup is set to "unknown" when the chrome prompt
// switch is set, but has no value,
command_line.AppendSwitch(kChromePromptSwitch);
EXPECT_EQ(ChromeCleanerReport::CLEANER_STARTUP_UNKNOWN,
GetCleanerStartupFromCommandLine(&command_line));
// Check that cleaner_startup is set to "unknown" when the chrome prompt
// switch has an invalid non-integer value.
command_line.InitFromArgv(command_line.argv());
command_line.AppendSwitchASCII(kChromePromptSwitch, "blah");
EXPECT_EQ(ChromeCleanerReport::CLEANER_STARTUP_UNKNOWN,
GetCleanerStartupFromCommandLine(&command_line));
// Check that cleaner_startup is set according to the value of the chrome
// prompt switch when the integer is a valid CleanerStartup value.
for (int flag_value = 0;
flag_value <= ChromeCleanerReport::CleanerStartup_MAX; ++flag_value) {
command_line.InitFromArgv(command_line.argv());
command_line.AppendSwitchASCII(kChromePromptSwitch,
base::NumberToString(flag_value));
if (flag_value == ChromeCleanerReport::CLEANER_STARTUP_UNSPECIFIED ||
flag_value == ChromeCleanerReport::CLEANER_STARTUP_NOT_PROMPTED) {
EXPECT_EQ(ChromeCleanerReport::CLEANER_STARTUP_UNKNOWN,
GetCleanerStartupFromCommandLine(&command_line));
} else {
EXPECT_EQ(static_cast<ChromeCleanerReport::CleanerStartup>(flag_value),
GetCleanerStartupFromCommandLine(&command_line));
}
}
}
TEST_P(CleanerLoggingServiceTest, Disabled) {
logging_service_->EnableUploads(false, registry_logger_.get());
DoSendLogsToSafeBrowsing();
// No URLRequest should have been made when not enabled.
EXPECT_EQ(0UL, http_agent_config_.num_request_data());
// But the done callback should have been called.
EXPECT_TRUE(done_callback_called_);
// With a failure.
EXPECT_FALSE(upload_success_);
// That should NOT have registered a retry.
task_scheduler_.ExpectRegisterTaskCalled(false);
}
TEST_P(CleanerLoggingServiceTest, Empty) {
logging_service_->EnableUploads(true, registry_logger_.get());
DoSendLogsToSafeBrowsing();
// No URLRequest should have been made when there is nothing to report.
EXPECT_EQ(0UL, http_agent_config_.num_request_data());
// But the done callback should have been called.
EXPECT_TRUE(done_callback_called_);
// With a failure.
EXPECT_FALSE(upload_success_);
// That should NOT have registered a retry.
task_scheduler_.ExpectRegisterTaskCalled(false);
}
TEST_P(CleanerLoggingServiceTest, OnlyUwS) {
CreateFileWithContent(matched_uws_file_, kFileContent1,
sizeof(kFileContent1));
logging_service_->EnableUploads(true, registry_logger_.get());
logging_service_->AddFoundUwS(matched_uws_.signature().name);
logging_service_->AddDetectedUwS(&matched_uws_, kUwSDetectedFlagsNone);
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
http_agent_config_.AddCalls(calls);
DoSendLogsToSafeBrowsing();
// A URLRequest should have been made even if there's just UwS to report.
ASSERT_NE(0UL, http_agent_config_.num_request_data());
std::string upload_data(http_agent_config_.request_data(0).body);
ChromeCleanerReport uploaded_report;
ASSERT_TRUE(uploaded_report.ParseFromString(upload_data));
ASSERT_TRUE(uploaded_report.intermediate_log());
ASSERT_EQ(1, uploaded_report.found_uws_size());
EXPECT_EQ(matched_uws_.signature().name, uploaded_report.found_uws(0));
ASSERT_EQ(1, uploaded_report.detected_uws_size());
CompareUwSData(matched_uws_, uploaded_report.detected_uws(0));
EXPECT_FALSE(
uploaded_report.detected_uws(0).detail_level().only_one_footprint());
EXPECT_TRUE(done_callback_called_);
EXPECT_TRUE(upload_success_);
// A safety task should have been registered.
task_scheduler_.ExpectRegisterTaskCalled(true);
// And then deleted.
task_scheduler_.ExpectDeleteTaskCalled(true);
// So none should be left.
task_scheduler_.ExpectRegisteredTasksSize(0U);
}
TEST_P(CleanerLoggingServiceTest, FullContent) {
CreateFileWithContent(matched_uws_file_, kFileContent1,
sizeof(kFileContent1));
CreateFileWithContent(removed_uws_file_, kFileContent1,
sizeof(kFileContent1));
CreateFileWithContent(matched_uws_slow_file_, kFileContent1,
sizeof(kFileContent1));
logging_service_->EnableUploads(true, registry_logger_.get());
std::string log_line1("A log line");
std::string log_line2("Another log line");
LOG(INFO) << log_line1;
LOG(WARNING) << log_line2;
logging_service_->SetExitCode(RESULT_CODE_NO_PUPS_FOUND);
logging_service_->AddFoundUwS(matched_uws_.signature().name);
logging_service_->AddDetectedUwS(&matched_uws_, kUwSDetectedFlagsNone);
logging_service_->AddFoundUwS(removed_uws_.signature().name);
logging_service_->AddDetectedUwS(&removed_uws_,
kUwSDetectedFlagsOnlyOneFootprint);
logging_service_->AddFoundUwS(matched_uws_slow_.signature().name);
logging_service_->AddDetectedUwS(&matched_uws_slow_,
kUwSDetectedFlagsOnlyOneFootprint);
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
ChromeFoilResponse response;
response.SerializeToString(&calls.read_data_result);
http_agent_config_.AddCalls(calls);
DoSendLogsToSafeBrowsing();
std::string upload_data(http_agent_config_.request_data(0).body);
ChromeCleanerReport uploaded_report;
ASSERT_TRUE(uploaded_report.ParseFromString(upload_data));
EXPECT_EQ(RESULT_CODE_NO_PUPS_FOUND, static_cast<chrome_cleaner::ResultCode>(
uploaded_report.exit_code()));
ASSERT_EQ(3, uploaded_report.found_uws_size());
EXPECT_EQ(matched_uws_.signature().name, uploaded_report.found_uws(0));
EXPECT_EQ(removed_uws_.signature().name, uploaded_report.found_uws(1));
EXPECT_EQ(matched_uws_slow_.signature().name, uploaded_report.found_uws(2));
ASSERT_EQ(3, uploaded_report.detected_uws_size());
CompareUwSData(matched_uws_, uploaded_report.detected_uws(0));
EXPECT_FALSE(
uploaded_report.detected_uws(0).detail_level().only_one_footprint());
EXPECT_EQ(UwS::REPORT_ONLY, uploaded_report.detected_uws(0).state());
CompareUwSData(removed_uws_, uploaded_report.detected_uws(1));
EXPECT_TRUE(
uploaded_report.detected_uws(1).detail_level().only_one_footprint());
EXPECT_EQ(UwS::REMOVABLE, uploaded_report.detected_uws(1).state());
CompareUwSData(matched_uws_slow_, uploaded_report.detected_uws(2));
EXPECT_TRUE(
uploaded_report.detected_uws(2).detail_level().only_one_footprint());
EXPECT_EQ(UwS::REPORT_ONLY, uploaded_report.detected_uws(2).state());
ASSERT_EQ(2, uploaded_report.raw_log_line_size());
// Log lines include the \n char.
log_line1 += "\n";
log_line2 += "\n";
EXPECT_TRUE(base::EndsWith(uploaded_report.raw_log_line(0), log_line1,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(base::EndsWith(uploaded_report.raw_log_line(1), log_line2,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(done_callback_called_);
EXPECT_TRUE(upload_success_);
// A safety task should have been registered.
task_scheduler_.ExpectRegisterTaskCalled(true);
// And then deleted.
task_scheduler_.ExpectDeleteTaskCalled(true);
// So none should be left.
task_scheduler_.ExpectRegisteredTasksSize(0U);
}
TEST_P(CleanerLoggingServiceTest, EnableDisableChanges) {
// Start enable, and then disable, and then enable again.
logging_service_->EnableUploads(true, registry_logger_.get());
std::string log_line1("Visible log line");
LOG(INFO) << log_line1;
logging_service_->EnableUploads(false, registry_logger_.get());
std::string log_line2("Visible log line with uploads off");
LOG(INFO) << log_line2;
// Logs shouldn't be sent since uploads are disabled.
DoSendLogsToSafeBrowsing();
EXPECT_TRUE(done_callback_called_);
EXPECT_FALSE(upload_success_);
logging_service_->EnableUploads(true, registry_logger_.get());
std::string log_line3("Another visible log line");
LOG(INFO) << log_line3;
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
ChromeFoilResponse response;
response.SerializeToString(&calls.read_data_result);
http_agent_config_.AddCalls(calls);
// Reset done_callback before attempting to upload again.
done_callback_called_ = false;
DoSendLogsToSafeBrowsing();
std::string upload_data(http_agent_config_.request_data(0).body);
ChromeCleanerReport uploaded_report;
ASSERT_TRUE(uploaded_report.ParseFromString(upload_data));
ASSERT_EQ(3, uploaded_report.raw_log_line_size());
// Log lines include the \n char.
log_line1 += "\n";
log_line2 += "\n";
log_line3 += "\n";
EXPECT_TRUE(base::EndsWith(uploaded_report.raw_log_line(0), log_line1,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(base::EndsWith(uploaded_report.raw_log_line(1), log_line2,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(base::EndsWith(uploaded_report.raw_log_line(2), log_line3,
base::CompareCase::SENSITIVE));
EXPECT_TRUE(done_callback_called_);
EXPECT_TRUE(upload_success_);
// A safety task should have been registered.
task_scheduler_.ExpectRegisterTaskCalled(true);
// And then deleted.
task_scheduler_.ExpectDeleteTaskCalled(true);
// So none should be left.
task_scheduler_.ExpectRegisteredTasksSize(0U);
}
TEST_P(CleanerLoggingServiceTest, FailedAndRetry) {
logging_service_->EnableUploads(true, registry_logger_.get());
// First request will fail.
MockHttpAgentConfig::Calls calls(HttpStatus::kBadRequest);
http_agent_config_.AddCalls(calls);
// Second request will succeed.
calls.get_status_code_result = HttpStatus::kOk;
http_agent_config_.AddCalls(calls);
logging_service_->SetExitCode(RESULT_CODE_NO_PUPS_FOUND);
DoSendLogsToSafeBrowsing();
// Now the done callback should have been called.
EXPECT_TRUE(done_callback_called_);
EXPECT_TRUE(upload_success_);
// A safety task should have been registered.
task_scheduler_.ExpectRegisterTaskCalled(true);
// And then deleted.
task_scheduler_.ExpectDeleteTaskCalled(true);
// So none should be left.
task_scheduler_.ExpectRegisteredTasksSize(0U);
// And no more retries should be done.
EXPECT_EQ(2UL, http_agent_config_.num_request_data());
}
TEST_P(CleanerLoggingServiceTest, CompleteFailure) {
logging_service_->EnableUploads(true, registry_logger_.get());
MockHttpAgentConfig::Calls calls(HttpStatus::kBadRequest);
http_agent_config_.AddCalls(calls);
http_agent_config_.AddCalls(calls);
http_agent_config_.AddCalls(calls);
logging_service_->SetExitCode(RESULT_CODE_NO_PUPS_FOUND);
DoSendLogsToSafeBrowsing();
// Now the done callback should have been called.
EXPECT_TRUE(done_callback_called_);
EXPECT_FALSE(upload_success_);
// A safety task should have been registered.
task_scheduler_.ExpectRegisterTaskCalled(true);
// The safety task should not have been deleted this time.
task_scheduler_.ExpectDeleteTaskCalled(false);
// So task should still be registered.
task_scheduler_.ExpectRegisteredTasksSize(1U);
// And no more retries should be done.
EXPECT_EQ(3UL, http_agent_config_.num_request_data());
base::FilePath log_file;
// Now forget about the scheduled logs upload retry.
registry_logger_->GetNextLogFilePath(&log_file);
ASSERT_FALSE(log_file.empty());
bool success = base::DeleteFile(log_file, false);
EXPECT_TRUE(success) << "Failed to delete " << log_file.value();
bool more_log_files = registry_logger_->RemoveLogFilePath(log_file);
EXPECT_FALSE(more_log_files);
task_scheduler_.DeleteTask(L"");
}
TEST_P(CleanerLoggingServiceTest, RawReportContent) {
ChromeCleanerReport raw_report;
// Start with the current state of the LoggingService which adds stuff
// initially like the Environment object.
ASSERT_TRUE(raw_report.ParseFromString(logging_service_->RawReportContent()));
logging_service_->SetExitCode(RESULT_CODE_NO_PUPS_FOUND);
raw_report.set_exit_code(RESULT_CODE_NO_PUPS_FOUND);
raw_report.set_intermediate_log(false);
logging_service_->AddFoundUwS(matched_uws_.signature().name);
raw_report.add_found_uws(matched_uws_.signature().name);
logging_service_->AddFoundUwS(removed_uws_.signature().name);
raw_report.add_found_uws(removed_uws_.signature().name);
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
EXPECT_EQ(raw_report.SerializeAsString(), report.SerializeAsString());
}
TEST_P(CleanerLoggingServiceTest, ReadContentFromFile) {
ChromeCleanerReport raw_report;
raw_report.set_exit_code(RESULT_CODE_NO_PUPS_FOUND);
raw_report.add_found_uws("UwS-42");
raw_report.add_found_uws("UwS-trio");
std::string raw_report_string;
ASSERT_TRUE(raw_report.SerializeToString(&raw_report_string));
base::FilePath test_file;
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::CreateTemporaryFileInDir(test_dir.GetPath(), &test_file);
ASSERT_TRUE(base::PathExists(test_file));
ASSERT_GT(base::WriteFile(test_file, raw_report_string.c_str(),
raw_report_string.size()),
0);
EXPECT_TRUE(logging_service_->ReadContentFromFile(test_file));
std::string report_string(logging_service_->RawReportContent());
EXPECT_EQ(raw_report_string, report_string);
}
TEST_P(CleanerLoggingServiceTest, ReadContentFromNonexistentFile) {
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
// Only the folder was actually created, so Bla is always nonexistent.
base::FilePath nonexistent_file(test_dir.GetPath().Append(L"Bla"));
ASSERT_FALSE(base::PathExists(nonexistent_file));
EXPECT_FALSE(logging_service_->ReadContentFromFile(nonexistent_file));
}
TEST_P(CleanerLoggingServiceTest, ReadContentFromEmptyFile) {
base::FilePath test_file;
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::CreateTemporaryFileInDir(test_dir.GetPath(), &test_file);
ASSERT_TRUE(base::PathExists(test_file));
EXPECT_FALSE(logging_service_->ReadContentFromFile(test_file));
}
TEST_P(CleanerLoggingServiceTest, ReadContentFromInvalidFile) {
base::FilePath test_file;
base::ScopedTempDir test_dir;
ASSERT_TRUE(test_dir.CreateUniqueTempDir());
base::CreateTemporaryFileInDir(test_dir.GetPath(), &test_file);
ASSERT_TRUE(base::PathExists(test_file));
ASSERT_GT(base::WriteFile(test_file, "bla", 3), 0);
EXPECT_FALSE(logging_service_->ReadContentFromFile(test_file));
}
TEST_P(CleanerLoggingServiceTest, ScheduleFallbackLogsUpload) {
// Shouldn't schedule a task if uploads are not enabled.
logging_service_->ScheduleFallbackLogsUpload(registry_logger_.get(),
RESULT_CODE_SUCCESS);
task_scheduler_.ExpectRegisterTaskCalled(false);
logging_service_->EnableUploads(true, registry_logger_.get());
logging_service_->ScheduleFallbackLogsUpload(registry_logger_.get(),
RESULT_CODE_SUCCESS);
task_scheduler_.ExpectRegisterTaskCalled(true);
// The safety task should not have been deleted.
task_scheduler_.ExpectDeleteTaskCalled(false);
// A single task should still be registered.
task_scheduler_.ExpectRegisteredTasksSize(1U);
// Also make sure that opting out of logs upload will clear any pending logs
// upload.
logging_service_->EnableUploads(false, registry_logger_.get());
task_scheduler_.ExpectDeleteTaskCalled(true);
// No task should be registered anymore.
task_scheduler_.ExpectRegisteredTasksSize(0U);
}
TEST_P(CleanerLoggingServiceTest, AppendMatchedFile) {
MatchedFile matched_file;
FileInformation* proto_file_information =
matched_file.mutable_file_information();
proto_file_information->set_path(base::UTF16ToUTF8(kFileInformation1.path));
proto_file_information->set_creation_date(kFileInformation1.creation_date);
proto_file_information->set_last_modified_date(
kFileInformation1.last_modified_date);
proto_file_information->set_sha256(kFileInformation1.sha256);
proto_file_information->set_size(kFileInformation1.size);
proto_file_information->set_company_name(
base::UTF16ToUTF8(kFileInformation1.company_name));
proto_file_information->set_company_short_name(
base::UTF16ToUTF8(kFileInformation1.company_short_name));
proto_file_information->set_product_name(
base::UTF16ToUTF8(kFileInformation1.product_name));
proto_file_information->set_product_short_name(
base::UTF16ToUTF8(kFileInformation1.product_short_name));
proto_file_information->set_internal_name(
base::UTF16ToUTF8(kFileInformation1.internal_name));
proto_file_information->set_original_filename(
base::UTF16ToUTF8(kFileInformation1.original_filename));
proto_file_information->set_file_description(
base::UTF16ToUTF8(kFileInformation1.file_description));
proto_file_information->set_file_version(
base::UTF16ToUTF8(kFileInformation1.file_version));
proto_file_information->set_active_file(kFileInformation1.active_file);
MessageBuilder builder;
AppendMatchedFile(matched_file, &builder);
EXPECT_EQ(kMatchedFileToStringExpectedString, builder.content());
}
TEST_P(CleanerLoggingServiceTest, AppendFolderInformation) {
constexpr char kFolderPath[] = "some/path/something";
constexpr char kCreationDate[] = "3/1/2016";
constexpr char kLastModifiedDate[] = "3/3/2016";
constexpr wchar_t kExpectedFolderString[] =
L"path = 'some/path/something', folder_creation_date = "
L"'3/1/2016', folder_last_modified_date = '3/3/2016'";
FolderInformation folder_information;
MessageBuilder builder;
AppendFolderInformation(folder_information, &builder);
EXPECT_TRUE(builder.content().empty());
folder_information.set_path(kFolderPath);
folder_information.set_creation_date(kCreationDate);
folder_information.set_last_modified_date(kLastModifiedDate);
AppendFolderInformation(folder_information, &builder);
EXPECT_EQ(kExpectedFolderString, builder.content());
}
TEST_P(CleanerLoggingServiceTest, AppendMatchedRegistryEntry) {
MatchedRegistryEntry matched_registry_entry;
matched_registry_entry.set_key_path(
base::UTF16ToUTF8(kMatchedRegistryEntryKey));
matched_registry_entry.set_value_name(
base::UTF16ToUTF8(kMatchedRegistryEntryValueName));
matched_registry_entry.set_value_substring(
base::UTF16ToUTF8(kMatchedRegistryEntryValueSubstring));
MessageBuilder builder;
AppendMatchedRegistryEntry(matched_registry_entry, &builder);
EXPECT_EQ(kMatchedRegistryEntryToStringExpectedString, builder.content());
}
TEST_P(CleanerLoggingServiceTest, AddLoadedModule) {
static const wchar_t kLoadedModuleName1[] = L"SomeLoadedModuleName1";
static const wchar_t kLoadedModuleName2[] = L"SomeLoadedModuleName2";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
EXPECT_EQ(report.system_report().loaded_modules_size(), 0);
logging_service_->AddLoadedModule(kLoadedModuleName1, ModuleHost::CHROME,
kFileInformation1);
logging_service_->AddLoadedModule(
kLoadedModuleName2, ModuleHost::CHROME_CLEANUP_TOOL, kFileInformation2);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().loaded_modules_size(), 2);
ChromeCleanerReport_SystemReport_LoadedModule loaded_module_chrome =
report.system_report().loaded_modules(0);
ExpectFileInformationEqualToProtoObj(kFileInformation1,
loaded_module_chrome.file_information());
EXPECT_EQ(base::WideToUTF8(kLoadedModuleName1), loaded_module_chrome.name());
EXPECT_EQ(ModuleHost::CHROME, loaded_module_chrome.host());
ChromeCleanerReport_SystemReport_LoadedModule loaded_module_cct =
report.system_report().loaded_modules(1);
ExpectFileInformationEqualToProtoObj(kFileInformation2,
loaded_module_cct.file_information());
EXPECT_EQ(base::WideToUTF8(kLoadedModuleName2), loaded_module_cct.name());
EXPECT_EQ(ModuleHost::CHROME_CLEANUP_TOOL, loaded_module_cct.host());
}
TEST_P(CleanerLoggingServiceTest, AddService) {
static const wchar_t kServiceDisplayName[] = L"ServiceDisplayName";
static const wchar_t kServiceName[] = L"ServiceName";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().services_size(), 0);
logging_service_->AddService(kServiceDisplayName, kServiceName,
kFileInformation1);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().services_size(), 1);
ChromeCleanerReport_SystemReport_Service added_service =
report.system_report().services(0);
ExpectFileInformationEqualToProtoObj(kFileInformation1,
added_service.file_information());
EXPECT_EQ(base::WideToUTF8(kServiceDisplayName),
added_service.display_name());
EXPECT_EQ(base::WideToUTF8(kServiceName), added_service.service_name());
}
TEST_P(CleanerLoggingServiceTest, AddInstalledProgram) {
constexpr wchar_t kInstalledProgramName[] = L"some_installed_program";
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath installed_program_path;
ASSERT_TRUE(base::CreateTemporaryDirInDir(
temp_dir.GetPath(), kInstalledProgramName, &installed_program_path));
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().installed_programs_size(), 0);
logging_service_->AddInstalledProgram(installed_program_path);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().installed_programs_size(), 1);
FolderInformation folder_information =
report.system_report().installed_programs(0).folder_information();
const base::string16 sanitized_path = SanitizePath(installed_program_path);
EXPECT_EQ(base::UTF16ToUTF8(sanitized_path), folder_information.path());
EXPECT_FALSE(folder_information.creation_date().empty());
EXPECT_FALSE(folder_information.last_modified_date().empty());
}
TEST_P(CleanerLoggingServiceTest, AddProcess) {
static const wchar_t kProcessName[] = L"SomeProcessName";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
EXPECT_EQ(report.system_report().processes_size(), 0);
logging_service_->AddProcess(kProcessName, kFileInformation1);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().processes_size(), 1);
ChromeCleanerReport_SystemReport_Process added_process =
report.system_report().processes(0);
ExpectFileInformationEqualToProtoObj(kFileInformation1,
added_process.file_information());
EXPECT_EQ(base::WideToUTF8(kProcessName), added_process.name());
}
TEST_P(CleanerLoggingServiceTest, AddRegistryValue) {
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
EXPECT_EQ(report.system_report().registry_values_size(), 0);
// Add a registry value with no FileInformation.
std::vector<internal::FileInformation> empty_file_informations;
logging_service_->AddRegistryValue(kRegistryValue1, empty_file_informations);
// Add a registry value with many FileInformation.
std::vector<internal::FileInformation> multiple_file_informations;
multiple_file_informations.push_back(kFileInformation1);
multiple_file_informations.push_back(kFileInformation2);
logging_service_->AddRegistryValue(kRegistryValue2,
multiple_file_informations);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().registry_values_size(), 2);
RegistryValue registry_value1 = report.system_report().registry_values(0);
RegistryValue registry_value2 = report.system_report().registry_values(1);
ExpectRegistryValueEqualToProtoObj(kRegistryValue1, registry_value1);
EXPECT_EQ(registry_value1.file_informations_size(), 0);
ExpectRegistryValueEqualToProtoObj(kRegistryValue2, registry_value2);
ASSERT_EQ(registry_value2.file_informations_size(), 2);
ExpectFileInformationEqualToProtoObj(kFileInformation1,
registry_value2.file_informations(0));
ExpectFileInformationEqualToProtoObj(kFileInformation2,
registry_value2.file_informations(1));
}
TEST_P(CleanerLoggingServiceTest, AddLayeredServiceProvider) {
static const wchar_t kGuid1[] = L"Guid1";
static const wchar_t kGuid2[] = L"Guid2";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(0, report.system_report().layered_service_providers_size());
std::vector<base::string16> guids;
guids.push_back(kGuid1);
guids.push_back(kGuid2);
logging_service_->AddLayeredServiceProvider(guids, kFileInformation1);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(1, report.system_report().layered_service_providers_size());
ChromeCleanerReport_SystemReport_LayeredServiceProvider
layered_service_provider =
report.system_report().layered_service_providers(0);
ASSERT_EQ(2, layered_service_provider.guids_size());
EXPECT_EQ(base::UTF16ToUTF8(guids.at(0)), layered_service_provider.guids(0));
EXPECT_EQ(base::UTF16ToUTF8(guids.at(1)), layered_service_provider.guids(1));
ExpectFileInformationEqualToProtoObj(
kFileInformation1, layered_service_provider.file_information());
}
TEST_P(CleanerLoggingServiceTest, SetWinInetProxySettings) {
static const wchar_t kSystemProxySettingsAutoConfigURL[] =
L"https://somewhere.test/proxy.pac";
const bool kAutodetect = true;
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_FALSE(report.system_report().has_win_inet_proxy_settings());
logging_service_->SetWinInetProxySettings(
kSystemProxySettingsConfig, kSystemProxySettingsBypass,
kSystemProxySettingsAutoConfigURL, kAutodetect);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_TRUE(report.system_report().has_win_inet_proxy_settings());
ChromeCleanerReport_SystemReport_SystemProxySettings win_inet_proxy_settings =
report.system_report().win_inet_proxy_settings();
EXPECT_EQ(base::WideToUTF8(kSystemProxySettingsConfig),
win_inet_proxy_settings.config());
EXPECT_EQ(base::WideToUTF8(kSystemProxySettingsBypass),
win_inet_proxy_settings.bypass());
EXPECT_EQ(base::WideToUTF8(kSystemProxySettingsAutoConfigURL),
win_inet_proxy_settings.auto_config_url());
EXPECT_EQ(kAutodetect, win_inet_proxy_settings.autodetect());
}
TEST_P(CleanerLoggingServiceTest, SetWinHttpProxySettings) {
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_FALSE(report.system_report().has_win_http_proxy_settings());
logging_service_->SetWinHttpProxySettings(kSystemProxySettingsConfig,
kSystemProxySettingsBypass);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_TRUE(report.system_report().has_win_http_proxy_settings());
ChromeCleanerReport_SystemReport_SystemProxySettings win_http_proxy_settings =
report.system_report().win_http_proxy_settings();
EXPECT_EQ(base::WideToUTF8(kSystemProxySettingsConfig),
win_http_proxy_settings.config());
EXPECT_EQ(base::WideToUTF8(kSystemProxySettingsBypass),
win_http_proxy_settings.bypass());
}
TEST_P(CleanerLoggingServiceTest, AddInstalledExtension) {
static const wchar_t kExtensionId[] = L"ababababcdcdcdcdefefefefghghghgh";
static const wchar_t kExtensionId2[] = L"aaaabbbbccccddddeeeeffffgggghhhh";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().installed_extensions_size(), 0);
internal::FileInformation file1, file2;
const base::string16 kFilePath1 = L"path/file1";
const base::string16 kFilePath2 = L"path/file2";
file1.path = kFilePath1;
file2.path = kFilePath2;
logging_service_->AddInstalledExtension(
kExtensionId, ExtensionInstallMethod::POLICY_EXTENSION_FORCELIST,
{file1});
logging_service_->AddInstalledExtension(
kExtensionId2, ExtensionInstallMethod::POLICY_MASTER_PREFERENCES,
{file1, file2});
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().installed_extensions_size(), 2);
ChromeCleanerReport_SystemReport_InstalledExtension installed_extension =
report.system_report().installed_extensions(0);
EXPECT_EQ(base::WideToUTF8(kExtensionId), installed_extension.extension_id());
EXPECT_EQ(ExtensionInstallMethod::POLICY_EXTENSION_FORCELIST,
installed_extension.install_method());
ASSERT_EQ(installed_extension.extension_files().size(), 1);
EXPECT_EQ(installed_extension.extension_files(0).path(),
base::UTF16ToUTF8(kFilePath1));
installed_extension = report.system_report().installed_extensions(1);
EXPECT_EQ(base::WideToUTF8(kExtensionId2),
installed_extension.extension_id());
EXPECT_EQ(ExtensionInstallMethod::POLICY_MASTER_PREFERENCES,
installed_extension.install_method());
ASSERT_EQ(installed_extension.extension_files().size(), 2);
std::vector<std::string> reported_files = {
installed_extension.extension_files(0).path(),
installed_extension.extension_files(1).path()};
EXPECT_THAT(reported_files, testing::UnorderedElementsAreArray(
{base::UTF16ToUTF8(kFilePath1),
base::UTF16ToUTF8(kFilePath2)}));
}
TEST_P(CleanerLoggingServiceTest, AddScheduledTask) {
static const wchar_t kScheduledTaskName[] = L"SomeTaskName";
static const wchar_t kScheduledTaskDescription[] = L"Description";
logging_service_->EnableUploads(true, registry_logger_.get());
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(0, report.system_report().scheduled_tasks_size());
std::vector<internal::FileInformation> actions;
actions.push_back(kFileInformation1);
actions.push_back(kFileInformation2);
logging_service_->AddScheduledTask(kScheduledTaskName,
kScheduledTaskDescription, actions);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(1, report.system_report().scheduled_tasks_size());
ScheduledTask added_scheduled_task =
report.system_report().scheduled_tasks(0);
ASSERT_EQ(2, added_scheduled_task.actions_size());
ExpectFileInformationEqualToProtoObj(
kFileInformation1, added_scheduled_task.actions(0).file_information());
ExpectFileInformationEqualToProtoObj(
kFileInformation2, added_scheduled_task.actions(1).file_information());
EXPECT_EQ(base::WideToUTF8(kScheduledTaskName), added_scheduled_task.name());
EXPECT_EQ(base::WideToUTF8(kScheduledTaskDescription),
added_scheduled_task.description());
}
TEST_P(CleanerLoggingServiceTest, LogProcessInformation) {
base::IoCounters io_counters;
io_counters.ReadOperationCount = 1;
io_counters.WriteOperationCount = 2;
io_counters.OtherOperationCount = 3;
io_counters.ReadTransferCount = 4;
io_counters.WriteTransferCount = 5;
io_counters.OtherTransferCount = 6;
SystemResourceUsage usage = {io_counters, base::TimeDelta::FromSeconds(10),
base::TimeDelta::FromSeconds(20), 123456};
logging_service_->EnableUploads(true, registry_logger_.get());
logging_service_->LogProcessInformation(SandboxType::kNonSandboxed, usage);
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(1, report.process_information_size());
EXPECT_EQ(ProcessInformation::MAIN, report.process_information(0).process());
ProcessInformation::SystemResourceUsage usage_msg =
report.process_information(0).resource_usage();
EXPECT_TRUE(IoCountersEqual(io_counters, usage_msg));
EXPECT_EQ(10U, usage_msg.user_time());
EXPECT_EQ(20U, usage_msg.kernel_time());
EXPECT_EQ(123456U, usage_msg.peak_working_set_size());
}
namespace {
// Add a matched file entry to |uws| with path given by SanitizePath(|path|)
// and removal status "matched only".
void AddFileToUwS(const base::FilePath& path, UwS* uws) {
MatchedFile* file = uws->add_files();
file->mutable_file_information()->set_path(
base::UTF16ToUTF8(SanitizePath(path)));
file->mutable_file_information()->set_active_file(
PathHasActiveExtension(path));
file->set_removal_status(REMOVAL_STATUS_MATCHED_ONLY);
file->set_quarantine_status(QUARANTINE_STATUS_UNSPECIFIED);
}
// Add a matched folder entry to |uws| with path given by SanitizePath(|path|)
// and removal status "matched only".
void AddFolderToUwS(const base::FilePath& path, UwS* uws) {
MatchedFolder* folder = uws->add_folders();
folder->mutable_folder_information()->set_path(
base::UTF16ToUTF8(SanitizePath(path)));
folder->set_removal_status(REMOVAL_STATUS_MATCHED_ONLY);
}
// Checks that all files and folders for all UwS entries in |report| have
// removal status |expected_status|.
void ExpectRemovalStatus(const ChromeCleanerReport& report,
RemovalStatus expected_status) {
for (const UwS& uws : report.detected_uws()) {
for (const MatchedFile& file : uws.files())
EXPECT_EQ(expected_status, file.removal_status());
for (const MatchedFolder& folder : uws.folders())
EXPECT_EQ(expected_status, folder.removal_status());
}
}
// Checks that all files for all UwS entries in |report| have quarantine status
// |expected_status|.
void ExpectQuarantineStatus(const ChromeCleanerReport& report,
QuarantineStatus expected_status) {
for (const UwS& uws : report.detected_uws()) {
for (const MatchedFile& file : uws.files())
EXPECT_EQ(expected_status, file.quarantine_status());
// We should not quarantine any folder.
EXPECT_TRUE(uws.folders().empty());
}
}
// Checks that the given path appears in the unknown UwS field with the expected
// removal status.
void ExpectUnknownRemovalStatus(const ChromeCleanerReport& report,
const base::FilePath& path,
RemovalStatus expected_status) {
bool found = false;
std::string normalized_path = base::UTF16ToUTF8(SanitizePath(path));
for (const auto& file : report.unknown_uws().files()) {
if (file.file_information().path() == normalized_path) {
EXPECT_EQ(file.removal_status(), expected_status);
EXPECT_FALSE(found)
<< normalized_path
<< " appeared in the unknown UwS files more than once";
found = true;
}
}
EXPECT_TRUE(found) << normalized_path
<< " didn't appear in the unknown UwS files";
}
} // namespace
TEST_P(CleanerLoggingServiceTest, InitializeResetsRemovalStatus) {
EXPECT_TRUE(
FileRemovalStatusUpdater::GetInstance()->GetAllRemovalStatuses().empty());
}
TEST_P(CleanerLoggingServiceTest, UpdateRemovalStatus) {
constexpr wchar_t kFile1[] = L"C:\\Program Files\\My Dear UwS\\File1.exe";
constexpr wchar_t kFile2[] =
L"C:\\Program Files\\My least favority UwS\\File2.exe";
constexpr wchar_t kFolder1[] = L"C:\\Program Files\\My Dear UwS";
constexpr wchar_t kFolder2[] = L"C:\\Program Files\\Nasty UwS";
const std::vector<base::FilePath> paths{
base::FilePath(kFile1), base::FilePath(kFile2), base::FilePath(kFolder1),
base::FilePath(kFolder2),
};
// Creates a vector of all RemovalStatus enum values to improve readability
// of loops in this test. Loops start from RemovalStatus_MIN + 1 to avoid
// testing default value REMOVAL_STATUS_UNSPECIFIED, that is not written
// by the cleaner code. Also, ensures that all RemovalStatus enumerators are
// checked.
std::vector<RemovalStatus> all_removal_status;
for (int i = RemovalStatus_MIN + 1; i <= RemovalStatus_MAX; ++i) {
if (RemovalStatus_IsValid(i))
all_removal_status.push_back(static_cast<RemovalStatus>(i));
}
FileRemovalStatusUpdater* removal_status_updater =
FileRemovalStatusUpdater::GetInstance();
for (RemovalStatus removal_status : all_removal_status) {
for (RemovalStatus new_removal_status : all_removal_status) {
SCOPED_TRACE(::testing::Message()
<< "removal_status " << removal_status
<< ", new_removal_status " << new_removal_status);
logging_service_->EnableUploads(true, registry_logger_.get());
// Add two UwS to the logged proto with a shared file and a shared folder
// to ensure that removal status is set for all occurrences of the same
// file/folder.
UwS uws;
uws.set_id(1);
AddFileToUwS(base::FilePath(kFile1), &uws);
AddFolderToUwS(base::FilePath(kFolder1), &uws);
logging_service_->AddDetectedUwS(uws);
uws.set_id(2); // kFile1 and kFolder1 are in both UwS.
AddFileToUwS(base::FilePath(kFile2), &uws);
AddFolderToUwS(base::FilePath(kFolder2), &uws);
logging_service_->AddDetectedUwS(uws);
// Initially, files and folders have removal status "matched only", which
// was given in the proto passed to AddDetectedUwS().
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectRemovalStatus(report, REMOVAL_STATUS_MATCHED_ONLY);
// Any removal status can override "matched only".
for (const base::FilePath& path : paths)
removal_status_updater->UpdateRemovalStatus(path, removal_status);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectRemovalStatus(report, removal_status);
// Tests if attempts to override removal status obey the rules specified
// by GetRemovalStatusOverridePermissionMap().
for (const base::FilePath& path : paths)
removal_status_updater->UpdateRemovalStatus(path, new_removal_status);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
const internal::RemovalStatusOverridePermissionMap& decisions_map =
internal::GetRemovalStatusOverridePermissionMap();
const bool can_override = decisions_map.find(removal_status)
->second.find(new_removal_status)
->second == internal::kOkToOverride;
ExpectRemovalStatus(report,
can_override ? new_removal_status : removal_status);
// Reset the logging service, so one the current test doesn't interfere
// with the next one.
logging_service_->Terminate();
logging_service_->Initialize(registry_logger_.get());
}
}
}
TEST_P(CleanerLoggingServiceTest, UpdateQuarantineStatus) {
const base::FilePath kFile1(L"C:\\Program Files\\My Dear UwS\\File1.exe");
// Creates a vector of all QuarantineStatus enum values to improve readability
// of loops in this test and ensure that all QuarantineStatus enumerators are
// checked.
std::vector<QuarantineStatus> all_quarantine_status;
for (int i = QuarantineStatus_MIN; i <= QuarantineStatus_MAX; ++i) {
// Status cannot be set to QUARANTINE_STATUS_UNSPECIFIED - this is guarded
// by an assert.
QuarantineStatus status = static_cast<QuarantineStatus>(i);
if (QuarantineStatus_IsValid(status) &&
status != QUARANTINE_STATUS_UNSPECIFIED)
all_quarantine_status.push_back(status);
}
FileRemovalStatusUpdater* removal_status_updater =
FileRemovalStatusUpdater::GetInstance();
for (QuarantineStatus status : all_quarantine_status) {
logging_service_->EnableUploads(true, registry_logger_.get());
UwS uws;
uws.set_id(1);
AddFileToUwS(kFile1, &uws);
logging_service_->AddDetectedUwS(uws);
ChromeCleanerReport report;
// The default quarantine status should be |QUARANTINE_STATUS_UNSPECIFIED|.
EXPECT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectQuarantineStatus(report, QUARANTINE_STATUS_UNSPECIFIED);
// Removal status has to be updated with a valid status.
removal_status_updater->UpdateRemovalStatus(kFile1,
REMOVAL_STATUS_MATCHED_ONLY);
removal_status_updater->UpdateQuarantineStatus(kFile1, status);
// It should always succeed to override |QUARANTINE_STATUS_UNSPECIFIED|.
EXPECT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectQuarantineStatus(report, status);
// Reset the logging service, so one the current test doesn't interfere with
// the next one.
logging_service_->Terminate();
logging_service_->Initialize(registry_logger_.get());
}
}
TEST_P(CleanerLoggingServiceTest, UpdateRemovalStatus_UwSAdded) {
constexpr wchar_t kFile1[] = L"C:\\Program Files\\My Dear UwS\\File1.exe";
logging_service_->EnableUploads(true, registry_logger_.get());
FileRemovalStatusUpdater* removal_status_updater =
FileRemovalStatusUpdater::GetInstance();
// Create an UwS and update the removal status of one of its files.
UwS uws;
uws.set_id(1);
AddFileToUwS(base::FilePath(kFile1), &uws);
logging_service_->AddDetectedUwS(uws);
removal_status_updater->UpdateRemovalStatus(
base::FilePath(kFile1), REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectRemovalStatus(report, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
// Create another UwS with the same file. The file should already be
// scheduled for removal.
UwS uws2;
uws2.set_id(2);
AddFileToUwS(base::FilePath(kFile1), &uws2);
logging_service_->AddDetectedUwS(uws2);
ChromeCleanerReport report2;
ASSERT_TRUE(report2.ParseFromString(logging_service_->RawReportContent()));
ExpectRemovalStatus(report2, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
}
TEST_P(CleanerLoggingServiceTest, UpdateRemovalStatus_UnknownUwS) {
logging_service_->EnableUploads(true, registry_logger_.get());
FileRemovalStatusUpdater* removal_status_updater =
FileRemovalStatusUpdater::GetInstance();
// Update the removal status of a real file.
base::FilePath real_file = uws_dir_.GetPath().Append(L"real_file.exe");
EXPECT_TRUE(CreateEmptyFile(real_file));
removal_status_updater->UpdateRemovalStatus(real_file,
REMOVAL_STATUS_MATCHED_ONLY);
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectUnknownRemovalStatus(report, real_file, REMOVAL_STATUS_MATCHED_ONLY);
removal_status_updater->UpdateRemovalStatus(
real_file, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectUnknownRemovalStatus(report, real_file,
REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
// Update the removeal status of a non-existant file and ensure it is still
// recorded.
base::FilePath fake_file = uws_dir_.GetPath().Append(L"fake_file.exe");
removal_status_updater->UpdateRemovalStatus(fake_file,
REMOVAL_STATUS_MATCHED_ONLY);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectUnknownRemovalStatus(report, fake_file, REMOVAL_STATUS_MATCHED_ONLY);
removal_status_updater->UpdateRemovalStatus(
fake_file, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ExpectUnknownRemovalStatus(report, fake_file,
REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
}
TEST_P(CleanerLoggingServiceTest, AllExpectedRemovalsConfirmed) {
const base::FilePath kFile1(L"C:\\Program Files\\uws.exe");
const base::FilePath kFile2(L"C:\\Program Files\\persistant.exe");
const base::FilePath kFile3(L"C:\\Program Files\\another_uws.exe");
const base::FilePath kFile4(L"C:\\Program Files\\inactive.txt");
logging_service_->EnableUploads(true, registry_logger_.get());
FileRemovalStatusUpdater* removal_status_updater =
FileRemovalStatusUpdater::GetInstance();
// No detected UwS -> nothing to remove.
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
UwS uws1;
uws1.set_id(1);
uws1.set_state(UwS::REMOVABLE);
AddFileToUwS(kFile1, &uws1);
logging_service_->AddDetectedUwS(uws1);
EXPECT_FALSE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(kFile1,
REMOVAL_STATUS_MATCHED_ONLY);
EXPECT_FALSE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(kFile1,
REMOVAL_STATUS_FAILED_TO_REMOVE);
EXPECT_FALSE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(
kFile1, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL_FALLBACK);
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(
kFile1, REMOVAL_STATUS_SCHEDULED_FOR_REMOVAL);
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
UwS uws2;
uws2.set_id(2);
uws2.set_state(UwS::REPORT_ONLY);
AddFileToUwS(kFile2, &uws2);
logging_service_->AddDetectedUwS(uws2);
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
UwS uws3;
uws3.set_id(3);
uws3.set_state(UwS::REMOVABLE);
AddFileToUwS(kFile2, &uws3);
AddFileToUwS(kFile3, &uws3);
// Since this is inactive, it is not expected to be removed.
AddFileToUwS(kFile4, &uws3);
logging_service_->AddDetectedUwS(uws3);
EXPECT_FALSE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(kFile2, REMOVAL_STATUS_REMOVED);
EXPECT_FALSE(logging_service_->AllExpectedRemovalsConfirmed());
removal_status_updater->UpdateRemovalStatus(kFile3, REMOVAL_STATUS_NOT_FOUND);
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
// When another UwS is found with all files already scheduled for removal,
// the removals should already be confirmed.
UwS uws4;
uws4.set_id(4);
uws4.set_state(UwS::REMOVABLE);
AddFileToUwS(kFile1, &uws4);
logging_service_->AddDetectedUwS(uws4);
EXPECT_TRUE(logging_service_->AllExpectedRemovalsConfirmed());
}
TEST_P(CleanerLoggingServiceTest, AddShortcutData) {
const base::string16 kLnkPath = L"C:\\Users\\SomeUser";
const base::string16 kExecutablePath1 =
L"C:\\executable_path\\executable.exe";
const base::string16 kExecutablePath2 = L"C:\\executable_path\\bad_file.exe";
const std::string kHash = "HASHSTRING";
const std::vector<base::string16> kCommandLineArguments = {
L"some-argument", L"-ha", L"-ha", L"-ha"};
logging_service_->AddShortcutData(kLnkPath, kExecutablePath1, kHash, {});
logging_service_->AddShortcutData(kLnkPath, kExecutablePath2, kHash,
kCommandLineArguments);
ChromeCleanerReport report;
ASSERT_TRUE(report.ParseFromString(logging_service_->RawReportContent()));
ASSERT_EQ(report.system_report().shortcut_data().size(), 2);
ChromeCleanerReport_SystemReport_ShortcutData shortcut1, shortcut2;
shortcut1 = report.system_report().shortcut_data(0);
shortcut2 = report.system_report().shortcut_data(1);
EXPECT_EQ(shortcut1.lnk_path(), base::UTF16ToUTF8(kLnkPath));
EXPECT_EQ(shortcut2.lnk_path(), base::UTF16ToUTF8(kLnkPath));
EXPECT_EQ(shortcut1.executable_path(), base::UTF16ToUTF8(kExecutablePath1));
EXPECT_EQ(shortcut2.executable_path(), base::UTF16ToUTF8(kExecutablePath2));
EXPECT_EQ(shortcut1.executable_hash(), kHash);
EXPECT_EQ(shortcut2.executable_hash(), kHash);
EXPECT_EQ(shortcut1.command_line_arguments().size(), 0);
EXPECT_EQ(shortcut2.command_line_arguments().size(), 4);
}
// TODO(csharp) add multi-thread tests.
INSTANTIATE_TEST_SUITE_P(All,
CleanerLoggingServiceTest,
testing::Values(ExecutionMode::kScanning,
ExecutionMode::kCleanup),
GetParamNameForTest());
} // namespace chrome_cleaner