blob: 3dc353b9fbaf940cc88ea033c72d63571213535d [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/scanner/matcher_util.h"
#include <shlobj.h>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/registry.h"
#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
#include "chrome/chrome_cleaner/scanner/signature_matcher.h"
#include "chrome/chrome_cleaner/test/resources/grit/test_resources.h"
#include "chrome/chrome_cleaner/test/test_file_util.h"
#include "chrome/chrome_cleaner/test/test_pup_data.h"
#include "chrome/chrome_cleaner/test/test_signature_matcher.h"
#include "chrome/chrome_cleaner/test/test_task_scheduler.h"
#include "chrome/chrome_cleaner/test/test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
constexpr base::char16 kFileName1[] = L"File1";
constexpr base::char16 kFileName2[] = L"File2";
constexpr base::char16 kFileName3[] = L"File3";
constexpr base::char16 kFileName4[] = L"File4";
constexpr char kFileContent1[] = "This is the file content.";
constexpr char kFileContent2[] = "Hi!";
constexpr char kFileContent3[] = "Hello World!";
constexpr char kFileContent4[] = "Ho!";
constexpr char kFileContent[] = "This is the file content.";
const char* const kKnownContentDigests[] = {
"00D2BB3E285BA62224888A9AD874AC2787D4CF681F30A2FD8EE2873859ECE1DC",
// Hash for content: |kFileContent|.
"BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371",
};
constexpr base::char16 kKnownOriginalFilename[] = L"uws.exe";
constexpr base::char16 kUnknownOriginalFilename[] = L"knowngood.exe";
const base::char16* const kKnownOriginalFilenames[] = {
L"dummy entry", L"uws.exe", L"zombie uws.exe",
};
constexpr base::char16 kKnownCompanyName[] = L"uws vendor inc";
constexpr base::char16 kUnknownCompanyName[] = L"paradise";
const base::char16* const kKnownCompanyNames[] = {
L"dummy entry", L"uws vendor inc", L"ACME",
};
constexpr FileDigestInfo kFileContentDigestInfos[] = {
{"02544E052F29BBA79C81243EC63B43B6CD85B185461928E65BFF501346C62A75", 33},
{"04614470DDF4939091F5EC4A13C92A9EAAACF07CA5C3F713E792E2D21CD24075", 21},
// Hash for content: |kFileContent2|.
{"82E0B92772BC0DA59AAB0B9231AA006FB37B4F99EC3E853C5A62786A1C7215BD", 4},
{"9000000000000000000000000000000000000000000000000000000000000009", 4},
{"94F7BDF53CDFDE7AA5E5C90BCDA6793B7377CE39E2591ABC758EBAE8072A275C", 12},
// Hash for content: |kFileContent1|.
{"BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371", 26},
};
// Messages are logged to a vector for testing.
class LoggingTest : public testing::Test {
public:
LoggingOverride logger_;
};
} // namespace
TEST(MatcherUtilTest, IsKnownFileByDigest) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path1(temp_dir.GetPath().Append(kFileName1));
base::FilePath file_path2(temp_dir.GetPath().Append(kFileName2));
base::FilePath file_path3(temp_dir.GetPath().Append(kFileName3));
CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
CreateFileWithRepeatedContent(file_path2, kFileContent, sizeof(kFileContent),
2);
std::unique_ptr<SignatureMatcher> signature_matcher =
std::make_unique<SignatureMatcher>();
ASSERT_TRUE(signature_matcher);
// Hash: BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371.
EXPECT_TRUE(IsKnownFileByDigest(file_path1, signature_matcher.get(),
kKnownContentDigests,
base::size(kKnownContentDigests)));
// Hash: not present.
EXPECT_FALSE(IsKnownFileByDigest(file_path2, signature_matcher.get(),
kKnownContentDigests,
base::size(kKnownContentDigests)));
// The file doesn't exist.
EXPECT_FALSE(IsKnownFileByDigest(file_path3, signature_matcher.get(),
kKnownContentDigests,
base::size(kKnownContentDigests)));
}
TEST(MatcherUtilTest, IsKnownFileByDigestInfo) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path1(temp_dir.GetPath().Append(kFileName1));
base::FilePath file_path2(temp_dir.GetPath().Append(kFileName2));
base::FilePath file_path3(temp_dir.GetPath().Append(kFileName3));
base::FilePath file_path4(temp_dir.GetPath().Append(kFileName4));
CreateFileWithContent(file_path1, kFileContent1, sizeof(kFileContent1));
CreateFileWithContent(file_path2, kFileContent2, sizeof(kFileContent2));
CreateFileWithContent(file_path3, kFileContent3, sizeof(kFileContent3));
std::unique_ptr<SignatureMatcher> signature_matcher =
std::make_unique<SignatureMatcher>();
// Search BD283E41A3672B6BDAA574F8BD7176F8BCA95BD81383CDE32AA6D78B1DB0E371.
EXPECT_TRUE(IsKnownFileByDigestInfo(file_path1, signature_matcher.get(),
kFileContentDigestInfos,
base::size(kFileContentDigestInfos)));
// Search 82E0B92772BC0DA59AAB0B9231AA006FB37B4F99EC3E853C5A62786A1C7215BD.
EXPECT_TRUE(IsKnownFileByDigestInfo(file_path2, signature_matcher.get(),
kFileContentDigestInfos,
base::size(kFileContentDigestInfos)));
// Replace the content of file_path2 with a content of the same size, it
// must no longer match.
ASSERT_EQ(sizeof(kFileContent2), sizeof(kFileContent4));
CreateFileWithContent(file_path2, kFileContent4, sizeof(kFileContent4));
EXPECT_FALSE(IsKnownFileByDigestInfo(file_path2, signature_matcher.get(),
kFileContentDigestInfos,
base::size(kFileContentDigestInfos)));
// The digest of |file_path3| is not in the array.
EXPECT_FALSE(IsKnownFileByDigestInfo(file_path3, signature_matcher.get(),
kFileContentDigestInfos,
base::size(kFileContentDigestInfos)));
// The |file_path4| doesn't exist.
EXPECT_FALSE(IsKnownFileByDigestInfo(file_path4, signature_matcher.get(),
kFileContentDigestInfos,
base::size(kFileContentDigestInfos)));
}
TEST(MatcherUtilTest, IsKnownFileByOriginalFilename) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
TestSignatureMatcher signature_matcher;
const base::FilePath normalized_temp_dir_path =
NormalizePath(temp_dir.GetPath());
// A non-existing file should not be recognized.
base::FilePath nonexistent_file_path(
normalized_temp_dir_path.Append(kFileName1));
EXPECT_FALSE(IsKnownFileByOriginalFilename(
nonexistent_file_path, &signature_matcher, kKnownOriginalFilenames,
base::size(kKnownOriginalFilenames)));
// An existing file without version information should not be recognized.
base::FilePath file_path2(normalized_temp_dir_path.Append(kFileName2));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
EXPECT_FALSE(IsKnownFileByOriginalFilename(
file_path2, &signature_matcher, kKnownOriginalFilenames,
base::size(kKnownOriginalFilenames)));
// A file with version information but not in the array should not be
// recognized.
base::FilePath file_path3(normalized_temp_dir_path.Append(kFileName3));
VersionInformation goodware_information = {};
goodware_information.original_filename = kUnknownOriginalFilename;
signature_matcher.MatchVersionInformation(file_path3, goodware_information);
CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
EXPECT_FALSE(IsKnownFileByOriginalFilename(
file_path3, &signature_matcher, kKnownOriginalFilenames,
base::size(kKnownOriginalFilenames)));
// A file with version information present in the array should be recognized.
base::FilePath file_path4(normalized_temp_dir_path.Append(kFileName4));
VersionInformation badware_information = {};
badware_information.original_filename = kKnownOriginalFilename;
signature_matcher.MatchVersionInformation(file_path4, badware_information);
CreateFileWithContent(file_path4, kFileContent, sizeof(kFileContent));
EXPECT_TRUE(IsKnownFileByOriginalFilename(
file_path4, &signature_matcher, kKnownOriginalFilenames,
base::size(kKnownOriginalFilenames)));
}
TEST(MatcherUtilTest, IsKnownFileByCompanyName) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath normalized_temp_dir_path =
NormalizePath(temp_dir.GetPath());
TestSignatureMatcher signature_matcher;
// A non-existing file should not be recognized.
base::FilePath nonexistent_file_path(
normalized_temp_dir_path.Append(kFileName1));
EXPECT_FALSE(IsKnownFileByCompanyName(nonexistent_file_path,
&signature_matcher, kKnownCompanyNames,
base::size(kKnownCompanyNames)));
// An existing file without version information should not be recognized.
base::FilePath file_path2(normalized_temp_dir_path.Append(kFileName2));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
EXPECT_FALSE(IsKnownFileByCompanyName(file_path2, &signature_matcher,
kKnownCompanyNames,
base::size(kKnownCompanyNames)));
// A file with version information but not in the array should not be
// recognized.
base::FilePath file_path3(normalized_temp_dir_path.Append(kFileName3));
VersionInformation goodware_information = {};
goodware_information.company_name = kUnknownCompanyName;
signature_matcher.MatchVersionInformation(file_path3, goodware_information);
CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
EXPECT_FALSE(IsKnownFileByCompanyName(file_path3, &signature_matcher,
kKnownCompanyNames,
base::size(kKnownCompanyNames)));
// A file with version information present in the array should be recognized.
base::FilePath file_path4(normalized_temp_dir_path.Append(kFileName4));
VersionInformation badware_information = {};
badware_information.company_name = kKnownCompanyName;
signature_matcher.MatchVersionInformation(file_path4, badware_information);
CreateFileWithContent(file_path4, kFileContent, sizeof(kFileContent));
EXPECT_TRUE(IsKnownFileByCompanyName(file_path4, &signature_matcher,
kKnownCompanyNames,
base::size(kKnownCompanyNames)));
}
TEST(MatcherUtilTest, MatchSingleFileWithPattern) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath match;
// Collect no path.
EXPECT_FALSE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"*", false, &match));
// Collect exactly one path matching the pattern.
base::FilePath file_path1(temp_dir.GetPath().Append(L"dummy.1.tar.gz"));
base::FilePath file_path2(temp_dir.GetPath().Append(L"uws-name.exe"));
CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
match.clear();
EXPECT_TRUE(MatchSingleFileWithPattern(temp_dir.GetPath(), L"*.*.*.*", false,
&match));
EXPECT_EQ(file_path1, match);
match.clear();
EXPECT_TRUE(MatchSingleFileWithPattern(temp_dir.GetPath(), L"uws*.exe", false,
&match));
EXPECT_EQ(file_path2, match);
match.clear();
EXPECT_TRUE(MatchSingleFileWithPattern(temp_dir.GetPath(), LR"(uws-????.exe)",
false, &match));
EXPECT_EQ(file_path2, match);
// Collecting multiple paths should fail.
base::FilePath file_path3(temp_dir.GetPath().Append(L"dummy.2.tar.gz"));
CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
EXPECT_FALSE(MatchSingleFileWithPattern(temp_dir.GetPath(), L"*.*.*.*", false,
&match));
}
TEST(MatcherUtilTest, MatchSingleFileWithPatternWithFolders) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath match;
// Collect no path.
EXPECT_FALSE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"*", true, &match));
// Add a folder to the main folder.
base::FilePath folder = temp_dir.GetPath().Append(L"folder");
ASSERT_TRUE(base::CreateDirectory(folder));
// Collect exactly one path matching the pattern.
base::FilePath file_path(temp_dir.GetPath().Append(L"dummy.1.tar.gz"));
CreateFileWithContent(file_path, kFileContent, sizeof(kFileContent));
match.clear();
EXPECT_TRUE(MatchSingleFileWithPattern(temp_dir.GetPath(), L"*.*.*.*", false,
&match));
EXPECT_EQ(file_path, match);
match.clear();
EXPECT_TRUE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"*.*.*.*", true, &match));
EXPECT_EQ(file_path, match);
// Collect with a wild-card matching everything.
match.clear();
EXPECT_TRUE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"*", false, &match));
EXPECT_EQ(file_path, match);
match.clear();
EXPECT_FALSE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"*", true, &match));
EXPECT_TRUE(match.empty());
// Collecting the folder.
EXPECT_FALSE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"fold?r", false, &match));
EXPECT_TRUE(match.empty());
EXPECT_TRUE(
MatchSingleFileWithPattern(temp_dir.GetPath(), L"fold?r", true, &match));
EXPECT_EQ(folder, match);
}
TEST(MatcherUtilTest, CollectPathRecursively) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
// Add a folder to the main folder.
base::FilePath folder = temp_dir.GetPath().Append(L"folder");
ASSERT_TRUE(base::CreateDirectory(folder));
base::FilePath subfolder = folder.Append(L"subfolder");
ASSERT_TRUE(base::CreateDirectory(subfolder));
base::FilePath file_path1(folder.Append(kFileName1));
CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
base::FilePath file_path2(subfolder.Append(kFileName2));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
base::FilePath nonexistent_path = temp_dir.GetPath().Append(L"nonexistent");
SimpleTestPUP pup;
CollectPathRecursively(nonexistent_path, &pup);
EXPECT_TRUE(pup.expanded_disk_footprints.empty());
CollectPathRecursively(folder, &pup);
EXPECT_FALSE(pup.expanded_disk_footprints.empty());
ExpectDiskFootprint(pup, folder);
ExpectDiskFootprint(pup, subfolder);
ExpectDiskFootprint(pup, file_path1);
ExpectDiskFootprint(pup, file_path2);
}
TEST(MatcherUtilTest, CollectDiskFootprintRecursively) {
base::FilePath local_appdata_path(
ExpandSpecialFolderPath(CSIDL_LOCAL_APPDATA, base::FilePath()));
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDirUnderPath(local_appdata_path));
// Add a folder to the main folder.
base::FilePath folder = temp_dir.GetPath().Append(L"folder");
ASSERT_TRUE(base::CreateDirectory(folder));
base::FilePath subfolder = folder.Append(L"subfolder");
ASSERT_TRUE(base::CreateDirectory(subfolder));
base::FilePath file_path1(folder.Append(kFileName1));
CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
base::FilePath file_path2(subfolder.Append(kFileName2));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
// Create the relative paths.
base::FilePath basename = temp_dir.GetPath().BaseName();
base::FilePath folder_path = basename.Append(L"Folder");
base::FilePath not_folder_path = basename.Append(L"not_Folder");
SimpleTestPUP pup;
// Collect in the wrong CSIDL.
CollectDiskFootprintRecursively(CSIDL_APPDATA, folder_path.value().c_str(),
&pup);
EXPECT_TRUE(pup.expanded_disk_footprints.empty());
// Collect in the good CSIDL but an inexisting folder.
CollectDiskFootprintRecursively(CSIDL_LOCAL_APPDATA,
not_folder_path.value().c_str(), &pup);
EXPECT_TRUE(pup.expanded_disk_footprints.empty());
// Collect the right files.
CollectDiskFootprintRecursively(CSIDL_LOCAL_APPDATA,
folder_path.value().c_str(), &pup);
EXPECT_FALSE(pup.expanded_disk_footprints.empty());
ExpectDiskFootprint(pup, folder);
ExpectDiskFootprint(pup, subfolder);
ExpectDiskFootprint(pup, file_path1);
ExpectDiskFootprint(pup, file_path2);
}
TEST_F(LoggingTest, LogFolderContent) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath temp_dir_path = temp_dir.GetPath();
base::FilePath file_path1(temp_dir_path.Append(kFileName1));
base::FilePath file_path2(temp_dir_path.Append(kFileName2));
CreateFileWithContent(file_path1, kFileContent, sizeof(kFileContent));
CreateFileWithContent(file_path2, kFileContent, sizeof(kFileContent));
// Add a folder to the main folder.
base::FilePath subfolder = temp_dir.GetPath().Append(L"folder");
ASSERT_TRUE(base::CreateDirectory(subfolder));
base::FilePath file_path3(subfolder.Append(kFileName3));
CreateFileWithContent(file_path3, kFileContent, sizeof(kFileContent));
LOG(INFO) << "Logging content of " << temp_dir.GetPath().value();
LogFolderContent(temp_dir.GetPath());
const std::string filename1 = base::WideToUTF8(kFileName1);
const std::string filename2 = base::WideToUTF8(kFileName2);
const std::string filename3 = base::WideToUTF8(kFileName3);
EXPECT_TRUE(logger_.LoggingMessagesContain(filename1));
EXPECT_TRUE(logger_.LoggingMessagesContain(filename2));
EXPECT_TRUE(logger_.LoggingMessagesContain(filename3));
// Add more files in the folder than maximum to be logged.
logger_.FlushMessages();
for (size_t count = 0; count < kMaxFilesInFolderToLog; ++count) {
base::FilePath file_path(temp_dir_path.Append(
base::StrCat({L"dummy", base::NumberToString16(count)})));
CreateFileWithContent(file_path, kFileContent, sizeof(kFileContent));
}
LOG(INFO) << "Logging content of " << temp_dir.GetPath().value();
LogFolderContent(temp_dir.GetPath());
EXPECT_TRUE(
logger_.LoggingMessagesContain("The folder contains too many files"));
}
} // namespace chrome_cleaner