blob: 5e3c0e56c0bd3f5d2b751aefc2686be2c1402542 [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/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 --arg=value --flag ");
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_EQ(sanitized_cmd.find(exe_in_programfiles.value()),
base::string16::npos)
<< sanitized_cmd;
switches.AppendSwitchPath("path", exe_in_programfiles);
switches.AppendArgPath(exe_in_programfiles);
to_sanitize = base::CommandLine(switches);
sanitized_cmd = SanitizeCommandLine(to_sanitize);
EXPECT_NE(to_sanitize.GetCommandLineString(), sanitized_cmd);
EXPECT_EQ(sanitized_cmd.find(exe_in_programfiles.value()),
base::string16::npos)
<< sanitized_cmd;
}
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