Commit db528719 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

Update chrome_cleaner/chrome_utils to remove force-installed extensions.

Also includes changes to parsers/ and test/ that chrome_utils depends on.

R=csharp

Bug: 830892
Change-Id: Idaba2972a0fbc2c5e896c9403442c24c8b8c6a60
Reviewed-on: https://chromium-review.googlesource.com/c/1336047
Commit-Queue: Joe Mason <joenotcharles@google.com>
Reviewed-by: default avatarChris Sharp <csharp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608249}
parent 4e6a1098
...@@ -23,9 +23,25 @@ source_set("extensions_util_lib") { ...@@ -23,9 +23,25 @@ source_set("extensions_util_lib") {
deps = [ deps = [
":chrome_util_lib", ":chrome_util_lib",
":force_installed_extension",
"//base:base", "//base:base",
"//chrome/chrome_cleaner/logging/proto:shared_data_proto",
"//chrome/chrome_cleaner/os:common_os", "//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/parsers/json_parser", "//chrome/chrome_cleaner/parsers/json_parser",
"//chrome/chrome_cleaner/parsers/json_parser:json_splicer",
"//chrome/chrome_cleaner/parsers/parser_utils:parse_tasks_remaining_counter",
]
}
source_set("force_installed_extension") {
sources = [
"force_installed_extension.cc",
"force_installed_extension.h",
]
deps = [
":extension_id",
"//chrome/chrome_cleaner/logging/proto:shared_data_proto",
] ]
} }
...@@ -41,7 +57,19 @@ source_set("unittest_sources") { ...@@ -41,7 +57,19 @@ source_set("unittest_sources") {
"//base:base", "//base:base",
"//base/test:test_support", "//base/test:test_support",
"//chrome/chrome_cleaner/parsers/json_parser", "//chrome/chrome_cleaner/parsers/json_parser",
"//chrome/chrome_cleaner/test:test_extensions",
"//chrome/chrome_cleaner/test:test_util", "//chrome/chrome_cleaner/test:test_util",
"//testing/gtest", "//testing/gtest",
] ]
} }
source_set("extension_id") {
sources = [
"extension_id.cc",
"extension_id.h",
]
deps = [
"//base:base",
]
}
// 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/chrome_utils/extension_id.h"
#include <string>
#include "base/logging.h"
#include "base/optional.h"
namespace chrome_cleaner {
base::Optional<ExtensionID> ExtensionID::Create(const std::string& value) {
if (ExtensionID::IsValidID(value)) {
return {ExtensionID(value)};
}
return {};
}
ExtensionID::ExtensionID(const std::string& value) : value_(value) {}
bool ExtensionID::IsValidID(const std::string& value) {
if (value.length() < 32) {
return false;
}
for (const auto& character : value) {
if (character < 'a' || character > 'p') {
return false;
}
}
return true;
}
std::string ExtensionID::AsString() const {
return 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.
#ifndef CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSION_ID_H_
#define CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSION_ID_H_
#include <string>
#include "base/optional.h"
namespace chrome_cleaner {
// A wrapper around std::string which upholds the extension id invariants.
// An extension must be a unique identifier that is 32 characters long between
// 'a' - 'p' for each character.
class ExtensionID {
public:
// Creates an ExtensionID if the |value| is a valid extension id.
// If the extension id is invalid no ExtensionID is stored in the optional.
static base::Optional<ExtensionID> Create(const std::string& value);
// Determines if the |value| is a valid extension ID.
static bool IsValidID(const std::string& value);
std::string AsString() const;
bool operator==(const ExtensionID& other) const {
return value_ == other.value_;
}
bool operator<(const ExtensionID& other) const {
return value_ < other.value_;
}
bool operator>(const ExtensionID& other) const {
return value_ > other.value_;
}
private:
explicit ExtensionID(const std::string& value);
std::string value_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSION_ID_H_
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <string> #include <string>
#include <utility>
#include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
...@@ -28,6 +30,8 @@ ...@@ -28,6 +30,8 @@
#include "chrome/chrome_cleaner/os/registry.h" #include "chrome/chrome_cleaner/os/registry.h"
#include "chrome/chrome_cleaner/os/registry_util.h" #include "chrome/chrome_cleaner/os/registry_util.h"
#include "chrome/chrome_cleaner/os/system_util.h" #include "chrome/chrome_cleaner/os/system_util.h"
#include "chrome/chrome_cleaner/parsers/json_parser/json_splicer.h"
#include "chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h"
using base::WaitableEvent; using base::WaitableEvent;
...@@ -36,36 +40,7 @@ namespace { ...@@ -36,36 +40,7 @@ namespace {
const int kExtensionIdLength = 32; const int kExtensionIdLength = 32;
class ParseTasksRemainingCounter // TODO(joenotcharles): Use RegKeyPath instead.
: public base::RefCountedThreadSafe<ParseTasksRemainingCounter> {
public:
ParseTasksRemainingCounter(size_t count, WaitableEvent* done)
: count_(count), done_(done) {
DCHECK(count_ > 0) << "Must be constructed with a positive count.";
}
void Increment() {
DCHECK(count_ > 0)
<< "Once decremented to zero, Increment should never be called.";
count_++;
}
void Decrement() {
DCHECK(count_);
count_--;
if (count_ == 0) {
done_->Signal();
}
}
private:
friend class base::RefCountedThreadSafe<ParseTasksRemainingCounter>;
~ParseTasksRemainingCounter() = default;
size_t count_;
WaitableEvent* done_;
};
struct RegistryKey { struct RegistryKey {
HKEY hkey; HKEY hkey;
const wchar_t* path; const wchar_t* path;
...@@ -95,6 +70,21 @@ constexpr std::array<const base::char16*, 8> default_extension_whitelist = { ...@@ -95,6 +70,21 @@ constexpr std::array<const base::char16*, 8> default_extension_whitelist = {
const wchar_t kMasterPreferencesFileName[] = L"master_preferences"; const wchar_t kMasterPreferencesFileName[] = L"master_preferences";
// Removes the extension from the JSON. If the extension is not associated
// wih a valid file and JSON value then this function returns false and
// |json_result| is not modified.
bool RemoveExtensionFromJson(const ForceInstalledExtension& extension,
base::Value* json_result) {
DCHECK(json_result);
DCHECK(extension.policy_file);
if (!extension.policy_file->json) {
return false;
}
bool result = RemoveKeyFromDictionary(json_result, extension.id.AsString());
return result;
}
void GetForcelistPoliciesForAccessMask( void GetForcelistPoliciesForAccessMask(
REGSAM access_mask, REGSAM access_mask,
std::vector<ExtensionPolicyRegistryEntry>* policies) { std::vector<ExtensionPolicyRegistryEntry>* policies) {
...@@ -114,16 +104,50 @@ void GetForcelistPoliciesForAccessMask( ...@@ -114,16 +104,50 @@ void GetForcelistPoliciesForAccessMask(
policies->emplace_back(extension_id, extension_forcelist_keys[i].hkey, policies->emplace_back(extension_id, extension_forcelist_keys[i].hkey,
extension_forcelist_keys[i].path, extension_forcelist_keys[i].path,
forcelist_it.Name()); forcelist_it.Name(), forcelist_it.Type(),
nullptr);
} }
} }
} }
} }
bool RemoveForcelistPolicyExtensionForAccessMask(
REGSAM access_mask,
const ForceInstalledExtension& extension) {
for (size_t i = 0; i < base::size(extension_forcelist_keys); ++i) {
std::vector<base::string16> keys;
base::win::RegistryValueIterator forcelist_it(
extension_forcelist_keys[i].hkey, extension_forcelist_keys[i].path,
access_mask);
for (; forcelist_it.Valid(); ++forcelist_it) {
base::string16 entry;
GetRegistryValueAsString(forcelist_it.Value(), forcelist_it.ValueSize(),
forcelist_it.Type(), &entry);
if (base::UTF16ToUTF8(entry.substr(0, kExtensionIdLength)) ==
extension.id.AsString()) {
keys.push_back(forcelist_it.Name());
}
}
base::win::RegKey key;
key.Open(extension_forcelist_keys[i].hkey, extension_forcelist_keys[i].path,
access_mask | KEY_WRITE);
for (base::string16& key_name : keys) {
LONG result = key.DeleteValue(key_name.c_str());
if (result != ERROR_SUCCESS) {
LOG(WARNING) << "Could not delete value at key " << key_name
<< ", error code: " << result;
return false;
}
}
}
return true;
}
void GetExtensionSettingsPoliciesFromParsedJson( void GetExtensionSettingsPoliciesFromParsedJson(
const RegistryKey& registry_key, const RegistryKey& registry_key,
std::vector<ExtensionPolicyRegistryEntry>* policies, std::vector<ExtensionPolicyRegistryEntry>* policies,
scoped_refptr<ParseTasksRemainingCounter> counter, scoped_refptr<ParseTasksRemainingCounter> counter,
ContentType type,
base::Optional<base::Value> json, base::Optional<base::Value> json,
const base::Optional<std::string>& error) { const base::Optional<std::string>& error) {
base::ScopedClosureRunner closure( base::ScopedClosureRunner closure(
...@@ -140,6 +164,8 @@ void GetExtensionSettingsPoliciesFromParsedJson( ...@@ -140,6 +164,8 @@ void GetExtensionSettingsPoliciesFromParsedJson(
return; return;
} }
scoped_refptr<RefValue> saved_json =
base::WrapRefCounted(new RefValue(json->Clone()));
for (const auto& entry : *extension_settings) { for (const auto& entry : *extension_settings) {
const base::string16& extension_id = base::UTF8ToUTF16(entry.first); const base::string16& extension_id = base::UTF8ToUTF16(entry.first);
const std::unique_ptr<base::Value>& settings_value = entry.second; const std::unique_ptr<base::Value>& settings_value = entry.second;
...@@ -150,9 +176,9 @@ void GetExtensionSettingsPoliciesFromParsedJson( ...@@ -150,9 +176,9 @@ void GetExtensionSettingsPoliciesFromParsedJson(
if (installation_mode != nullptr && if (installation_mode != nullptr &&
installation_mode->GetString() == installation_mode->GetString() ==
kExtensionSettingsForceInstalledValue) { kExtensionSettingsForceInstalledValue) {
policies->emplace_back(extension_id, registry_key.hkey, policies->emplace_back(
registry_key.path, extension_id, registry_key.hkey, registry_key.path,
kExtensionSettingsRegistryEntryName); kExtensionSettingsRegistryEntryName, type, saved_json);
} }
} }
} }
...@@ -168,7 +194,7 @@ void GetExtensionSettingsPoliciesForAccessMask( ...@@ -168,7 +194,7 @@ void GetExtensionSettingsPoliciesForAccessMask(
extension_settings_keys[i].path, access_mask); extension_settings_keys[i].path, access_mask);
base::string16 extension_settings; base::string16 extension_settings;
RegistryError error; RegistryError error;
uint32_t type; ContentType type;
ReadRegistryValue(key, kExtensionSettingsRegistryEntryName, ReadRegistryValue(key, kExtensionSettingsRegistryEntryName,
&extension_settings, &type, &error); &extension_settings, &type, &error);
...@@ -185,7 +211,7 @@ void GetExtensionSettingsPoliciesForAccessMask( ...@@ -185,7 +211,7 @@ void GetExtensionSettingsPoliciesForAccessMask(
json_parser->Parse( json_parser->Parse(
base::UTF16ToUTF8(extension_settings), base::UTF16ToUTF8(extension_settings),
base::BindOnce(&GetExtensionSettingsPoliciesFromParsedJson, base::BindOnce(&GetExtensionSettingsPoliciesFromParsedJson,
extension_settings_keys[i], policies, counter)); extension_settings_keys[i], policies, counter, type));
} }
} }
...@@ -208,10 +234,13 @@ void GetDefaultExtensionsFromParsedJson( ...@@ -208,10 +234,13 @@ void GetDefaultExtensionsFromParsedJson(
return; return;
} }
scoped_refptr<RefValue> saved_json =
base::WrapRefCounted(new RefValue(json->Clone()));
for (const auto& entry : *default_extensions) { for (const auto& entry : *default_extensions) {
base::string16 extension_id = base::UTF8ToUTF16(entry.first); base::string16 extension_id = base::UTF8ToUTF16(entry.first);
if (!base::ContainsValue(default_extension_whitelist, extension_id)) if (!base::ContainsValue(default_extension_whitelist, extension_id)) {
policies->emplace_back(extension_id, extensions_file); policies->emplace_back(extension_id, extensions_file, saved_json);
}
} }
} }
...@@ -241,9 +270,11 @@ void GetMasterPreferencesExtensionsFromParsedJson( ...@@ -241,9 +270,11 @@ void GetMasterPreferencesExtensionsFromParsedJson(
base::DictionaryValue* extension_settings_dictionary; base::DictionaryValue* extension_settings_dictionary;
extension_settings->GetAsDictionary(&extension_settings_dictionary); extension_settings->GetAsDictionary(&extension_settings_dictionary);
scoped_refptr<RefValue> saved_json =
base::WrapRefCounted(new RefValue(json->Clone()));
for (const auto& entry : *extension_settings_dictionary) { for (const auto& entry : *extension_settings_dictionary) {
base::string16 extension_id = base::UTF8ToUTF16(entry.first); base::string16 extension_id = base::UTF8ToUTF16(entry.first);
policies->emplace_back(extension_id, extensions_file); policies->emplace_back(extension_id, extensions_file, saved_json);
} }
} }
...@@ -253,21 +284,33 @@ ExtensionPolicyRegistryEntry::ExtensionPolicyRegistryEntry( ...@@ -253,21 +284,33 @@ ExtensionPolicyRegistryEntry::ExtensionPolicyRegistryEntry(
const base::string16& extension_id, const base::string16& extension_id,
HKEY hkey, HKEY hkey,
const base::string16& path, const base::string16& path,
const base::string16& name) const base::string16& name,
: extension_id(extension_id), hkey(hkey), path(path), name(name) {} ContentType content_type,
scoped_refptr<RefValue> json)
: extension_id(extension_id),
hkey(hkey),
path(path),
name(name),
content_type(content_type),
json(std::move(json)) {}
ExtensionPolicyRegistryEntry::ExtensionPolicyRegistryEntry( ExtensionPolicyRegistryEntry::ExtensionPolicyRegistryEntry(
ExtensionPolicyRegistryEntry&&) = default; ExtensionPolicyRegistryEntry&&) = default;
ExtensionPolicyRegistryEntry::~ExtensionPolicyRegistryEntry() = default;
ExtensionPolicyRegistryEntry& ExtensionPolicyRegistryEntry::operator=( ExtensionPolicyRegistryEntry& ExtensionPolicyRegistryEntry::operator=(
ExtensionPolicyRegistryEntry&&) = default; ExtensionPolicyRegistryEntry&&) = default;
ExtensionPolicyFile::ExtensionPolicyFile(const base::string16& extension_id, ExtensionPolicyFile::ExtensionPolicyFile(const base::string16& extension_id,
const base::FilePath& path) const base::FilePath& path,
: extension_id(extension_id), path(path) {} scoped_refptr<RefValue> json)
: extension_id(extension_id), path(path), json(std::move(json)) {}
ExtensionPolicyFile::ExtensionPolicyFile(ExtensionPolicyFile&&) = default; ExtensionPolicyFile::ExtensionPolicyFile(ExtensionPolicyFile&&) = default;
ExtensionPolicyFile::~ExtensionPolicyFile() = default;
ExtensionPolicyFile& ExtensionPolicyFile::operator=(ExtensionPolicyFile&&) = ExtensionPolicyFile& ExtensionPolicyFile::operator=(ExtensionPolicyFile&&) =
default; default;
...@@ -278,6 +321,19 @@ void GetExtensionForcelistRegistryPolicies( ...@@ -278,6 +321,19 @@ void GetExtensionForcelistRegistryPolicies(
GetForcelistPoliciesForAccessMask(KEY_WOW64_64KEY, policies); GetForcelistPoliciesForAccessMask(KEY_WOW64_64KEY, policies);
} }
bool RemoveForcelistPolicyExtension(const ForceInstalledExtension& extension) {
DCHECK(extension.install_method == POLICY_EXTENSION_FORCELIST);
// No need to check for policy_registry_entry, as it's not used in deletion.
bool result =
RemoveForcelistPolicyExtensionForAccessMask(KEY_WOW64_32KEY, extension);
if (IsX64Architecture() && result) {
result =
RemoveForcelistPolicyExtensionForAccessMask(KEY_WOW64_64KEY, extension);
}
return result;
}
void GetNonWhitelistedDefaultExtensions( void GetNonWhitelistedDefaultExtensions(
JsonParserAPI* json_parser, JsonParserAPI* json_parser,
std::vector<ExtensionPolicyFile>* policies, std::vector<ExtensionPolicyFile>* policies,
...@@ -317,6 +373,12 @@ void GetNonWhitelistedDefaultExtensions( ...@@ -317,6 +373,12 @@ void GetNonWhitelistedDefaultExtensions(
} }
} }
bool RemoveDefaultExtension(const ForceInstalledExtension& extension,
base::Value* json_result) {
DCHECK(extension.install_method == DEFAULT_APPS_EXTENSION);
return RemoveExtensionFromJson(extension, json_result);
}
void GetExtensionSettingsForceInstalledExtensions( void GetExtensionSettingsForceInstalledExtensions(
JsonParserAPI* json_parser, JsonParserAPI* json_parser,
std::vector<ExtensionPolicyRegistryEntry>* policies, std::vector<ExtensionPolicyRegistryEntry>* policies,
...@@ -335,6 +397,19 @@ void GetExtensionSettingsForceInstalledExtensions( ...@@ -335,6 +397,19 @@ void GetExtensionSettingsForceInstalledExtensions(
counter->Decrement(); counter->Decrement();
} }
bool RemoveExtensionSettingsPoliciesExtension(
const ForceInstalledExtension& extension,
base::Value* json_result) {
DCHECK(extension.install_method == POLICY_EXTENSION_SETTINGS);
DCHECK(extension.policy_registry_entry);
DCHECK(json_result);
if (!extension.policy_registry_entry->json.get()) {
return false;
}
return RemoveKeyFromDictionary(json_result, extension.id.AsString());
}
void GetMasterPreferencesExtensions(JsonParserAPI* json_parser, void GetMasterPreferencesExtensions(JsonParserAPI* json_parser,
std::vector<ExtensionPolicyFile>* policies, std::vector<ExtensionPolicyFile>* policies,
base::WaitableEvent* done) { base::WaitableEvent* done) {
...@@ -373,4 +448,15 @@ void GetMasterPreferencesExtensions(JsonParserAPI* json_parser, ...@@ -373,4 +448,15 @@ void GetMasterPreferencesExtensions(JsonParserAPI* json_parser,
} }
} }
bool RemoveMasterPreferencesExtension(const ForceInstalledExtension& extension,
base::Value* json_result) {
DCHECK(extension.install_method == POLICY_MASTER_PREFERENCES);
DCHECK(json_result);
DCHECK(json_result->is_dict());
// The extensions are stored in ["extensions"]["settings"]
base::Value* sub_dictionary =
json_result->FindPath({"extensions", "settings"});
return RemoveExtensionFromJson(extension, sub_dictionary);
}
} // namespace chrome_cleaner } // namespace chrome_cleaner
...@@ -9,25 +9,39 @@ ...@@ -9,25 +9,39 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
#include "chrome/chrome_cleaner/chrome_utils/force_installed_extension.h"
#include "chrome/chrome_cleaner/os/registry_util.h" #include "chrome/chrome_cleaner/os/registry_util.h"
#include "chrome/chrome_cleaner/parsers/json_parser/json_parser_api.h" #include "chrome/chrome_cleaner/parsers/json_parser/json_parser_api.h"
namespace chrome_cleaner { namespace chrome_cleaner {
typedef base::RefCountedData<base::Value> RefValue;
typedef uint32_t ContentType;
constexpr int64_t kParseAttemptTimeoutMilliseconds = 10000;
// A registry key that holds some form of policy for |extension_id|. // A registry key that holds some form of policy for |extension_id|.
struct ExtensionPolicyRegistryEntry { struct ExtensionPolicyRegistryEntry {
base::string16 extension_id; base::string16 extension_id;
HKEY hkey; HKEY hkey;
base::string16 path; base::string16 path;
base::string16 name; base::string16 name;
ContentType content_type;
scoped_refptr<RefValue> json;
ExtensionPolicyRegistryEntry(const base::string16& extension_id, ExtensionPolicyRegistryEntry(const base::string16& extension_id,
HKEY hkey, HKEY hkey,
const base::string16& path, const base::string16& path,
const base::string16& name); const base::string16& name,
ContentType content_type,
scoped_refptr<RefValue>);
ExtensionPolicyRegistryEntry(ExtensionPolicyRegistryEntry&&); ExtensionPolicyRegistryEntry(ExtensionPolicyRegistryEntry&&);
~ExtensionPolicyRegistryEntry();
ExtensionPolicyRegistryEntry& operator=(ExtensionPolicyRegistryEntry&&); ExtensionPolicyRegistryEntry& operator=(ExtensionPolicyRegistryEntry&&);
DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyRegistryEntry); DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyRegistryEntry);
...@@ -37,10 +51,13 @@ struct ExtensionPolicyRegistryEntry { ...@@ -37,10 +51,13 @@ struct ExtensionPolicyRegistryEntry {
struct ExtensionPolicyFile { struct ExtensionPolicyFile {
base::string16 extension_id; base::string16 extension_id;
base::FilePath path; base::FilePath path;
scoped_refptr<RefValue> json;
ExtensionPolicyFile(const base::string16& extension_id, ExtensionPolicyFile(const base::string16& extension_id,
const base::FilePath& path); const base::FilePath& path,
scoped_refptr<RefValue> json);
ExtensionPolicyFile(ExtensionPolicyFile&&); ExtensionPolicyFile(ExtensionPolicyFile&&);
~ExtensionPolicyFile();
ExtensionPolicyFile& operator=(ExtensionPolicyFile&&); ExtensionPolicyFile& operator=(ExtensionPolicyFile&&);
DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyFile); DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyFile);
...@@ -75,6 +92,32 @@ void GetMasterPreferencesExtensions(JsonParserAPI* json_parser, ...@@ -75,6 +92,32 @@ void GetMasterPreferencesExtensions(JsonParserAPI* json_parser,
std::vector<ExtensionPolicyFile>* policies, std::vector<ExtensionPolicyFile>* policies,
base::WaitableEvent* done); base::WaitableEvent* done);
// Attempts to remove an |extension| installed through the whitelist.
// The extension id will be removed from the |json_result| passed in, so that
// the caller can build up the new JSON value before writing it to the disk.
// On failure returns false and doesn't modify the |json_result|.
bool RemoveDefaultExtension(const ForceInstalledExtension& extension,
base::Value* json_result);
// Attempts to remove an extension installed through the forcelist.
// Return True on success.
bool RemoveForcelistPolicyExtension(const ForceInstalledExtension& extension);
// Attempts to remove an extension installed from the policy settings
// The extension id will be removed from the |json_result| passed in so that
// the caller can build up a new JSON value before writing it to the registry.
// On failure returns false and does not modify the |json_result|.
bool RemoveExtensionSettingsPoliciesExtension(
const ForceInstalledExtension& extension,
base::Value* json_result);
// Attempts to remove an extension installed through the master preferences.
// The extension id will be removed from the |json_result| passed in so that the
// caller can build up the a new JSON value before writing it to the disk.
// On failure returns false and does not modify the |json_result|.
bool RemoveMasterPreferencesExtension(const ForceInstalledExtension& extension,
base::Value* json_result);
} // namespace chrome_cleaner } // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSIONS_UTIL_H_ #endif // CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSIONS_UTIL_H_
...@@ -4,12 +4,18 @@ ...@@ -4,12 +4,18 @@
#include "chrome/chrome_cleaner/chrome_utils/extensions_util.h" #include "chrome/chrome_cleaner/chrome_utils/extensions_util.h"
#include <memory>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector> #include <vector>
#include "base/base_paths_win.h" #include "base/base_paths_win.h"
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/json/json_string_value_serializer.h"
#include "base/path_service.h" #include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h" #include "base/synchronization/waitable_event.h"
...@@ -18,6 +24,7 @@ ...@@ -18,6 +24,7 @@
#include "base/test/test_timeouts.h" #include "base/test/test_timeouts.h"
#include "base/win/registry.h" #include "base/win/registry.h"
#include "chrome/chrome_cleaner/parsers/json_parser/test_json_parser.h" #include "chrome/chrome_cleaner/parsers/json_parser/test_json_parser.h"
#include "chrome/chrome_cleaner/test/test_extensions.h"
#include "chrome/chrome_cleaner/test/test_file_util.h" #include "chrome/chrome_cleaner/test/test_file_util.h"
#include "testing/gmock/include/gmock/gmock.h" #include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -29,91 +36,18 @@ namespace { ...@@ -29,91 +36,18 @@ namespace {
const int kExtensionIdLength = 32; const int kExtensionIdLength = 32;
struct TestRegistryEntry { struct ExtensionIDHash {
HKEY hkey; size_t operator()(const ForceInstalledExtension& extension) const {
const base::string16 path; return std::hash<std::string>{}(extension.id.AsString());
const base::string16 name; }
const base::string16 value;
}; };
const TestRegistryEntry extension_forcelist_entries[] = { struct ExtensionIDEqual {
{HKEY_LOCAL_MACHINE, kChromePoliciesForcelistKeyPath, L"test1", bool operator()(const ForceInstalledExtension& lhs,
L"ababababcdcdcdcdefefefefghghghgh;https://test.test/crx" const ForceInstalledExtension& rhs) const {
L"update2/crx"}, return lhs.id == rhs.id;
{HKEY_CURRENT_USER, kChromePoliciesForcelistKeyPath, L"test2", }
L"aaaabbbbccccddddeeeeffffgggghhhh;https://test.test/crx" };
L"update2/crx"}};
const wchar_t kFakeChromeFolder[] = L"google\\chrome\\application\\42.12.34.56";
const wchar_t kTestExtensionId1[] = L"ababababcdcdcdcdefefefefghghghgh";
const wchar_t kTestExtensionId2[] = L"aaaabbbbccccddddeeeeffffgggghhhh";
const char kDefaultExtensionsJson[] =
R"(
{
"ababababcdcdcdcdefefefefghghghgh" : {
"external_update_url":"https://test.test/crx"
},
"aaaabbbbccccddddeeeeffffgggghhhh" : {
"external_update_url":"https://test.test/crx"
},
// Google Sheets
"aapocclcgogkmnckokdopfmhonfmgoek" : {
"external_update_url":"https://test.test/crx"
},
})";
const char kInvalidDefaultExtensionsJson[] = "{ json: invalid }";
// ExtensionSettings that has two force_installed extensions and two not.
const wchar_t kExtensionSettingsJson[] =
LR"(
{
"ababababcdcdcdcdefefefefghghghgh": {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
},
"aaaabbbbccccddddeeeeffffgggghhhh": {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
},
"extensionwithinstallmodeblockeda": {
"installation_mode": "blocked",
"update_url":"https://test.test/crx"
},
"extensionwithnosettingsabcdefghi": {}
})";
const TestRegistryEntry extension_settings_entry = {
HKEY_LOCAL_MACHINE, L"software\\policies\\google\\chrome",
L"ExtensionSettings", kExtensionSettingsJson};
const wchar_t kChromeExePath[] = L"google\\chrome\\application";
const wchar_t kMasterPreferencesFileName[] = L"master_preferences";
const char kMasterPreferencesJson[] =
R"(
{
"homepage": "http://dev.chromium.org/",
"extensions": {
"settings": {
"ababababcdcdcdcdefefefefghghghgh": {
"location": 1,
"manifest": {
"name": "Test extension"
}
},
"aaaabbbbccccddddeeeeffffgggghhhh": {
"location": 1,
"manifest": {
"name": "Another one"
}
}
}
}
})";
const char kMasterPreferencesJsonNoExtensions[] =
R"(
{
"homepage": "http://dev.chromium.org/"
})";
bool ExtensionPolicyRegistryEntryFound( bool ExtensionPolicyRegistryEntryFound(
TestRegistryEntry test_entry, TestRegistryEntry test_entry,
...@@ -135,11 +69,11 @@ TEST(ExtensionsUtilTest, GetExtensionForcelistRegistryPolicies) { ...@@ -135,11 +69,11 @@ TEST(ExtensionsUtilTest, GetExtensionForcelistRegistryPolicies) {
registry_util::RegistryOverrideManager registry_override; registry_util::RegistryOverrideManager registry_override;
registry_override.OverrideRegistry(HKEY_CURRENT_USER); registry_override.OverrideRegistry(HKEY_CURRENT_USER);
registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE); registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
for (const TestRegistryEntry& policy : extension_forcelist_entries) { for (const TestRegistryEntry& policy : kExtensionForcelistEntries) {
base::win::RegKey policy_key; base::win::RegKey policy_key;
ASSERT_EQ(ERROR_SUCCESS, policy_key.Create(policy.hkey, policy.path.c_str(), ASSERT_EQ(ERROR_SUCCESS, policy_key.Create(policy.hkey, policy.path.c_str(),
KEY_ALL_ACCESS)); KEY_ALL_ACCESS));
DCHECK(policy_key.Valid()); ASSERT_TRUE(policy_key.Valid());
ASSERT_EQ(ERROR_SUCCESS, ASSERT_EQ(ERROR_SUCCESS,
policy_key.WriteValue(policy.name.c_str(), policy.value.c_str())); policy_key.WriteValue(policy.name.c_str(), policy.value.c_str()));
} }
...@@ -147,11 +81,52 @@ TEST(ExtensionsUtilTest, GetExtensionForcelistRegistryPolicies) { ...@@ -147,11 +81,52 @@ TEST(ExtensionsUtilTest, GetExtensionForcelistRegistryPolicies) {
std::vector<ExtensionPolicyRegistryEntry> policies; std::vector<ExtensionPolicyRegistryEntry> policies;
GetExtensionForcelistRegistryPolicies(&policies); GetExtensionForcelistRegistryPolicies(&policies);
for (const TestRegistryEntry& expected_result : extension_forcelist_entries) { for (const TestRegistryEntry& expected_result : kExtensionForcelistEntries) {
EXPECT_TRUE(ExtensionPolicyRegistryEntryFound(expected_result, policies)); EXPECT_TRUE(ExtensionPolicyRegistryEntryFound(expected_result, policies));
} }
} }
TEST(ExtensionsUtilTest, RemoveForcelistPolicyExtensions) {
registry_util::RegistryOverrideManager registry_override;
registry_override.OverrideRegistry(HKEY_CURRENT_USER);
registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
for (const TestRegistryEntry& policy : kExtensionForcelistEntries) {
base::win::RegKey policy_key;
ASSERT_EQ(ERROR_SUCCESS, policy_key.Create(policy.hkey, policy.path.c_str(),
KEY_ALL_ACCESS));
DCHECK(policy_key.Valid());
ASSERT_EQ(ERROR_SUCCESS,
policy_key.WriteValue(policy.name.c_str(), policy.value.c_str()));
base::string16 value;
policy_key.ReadValue(policy.name.c_str(), &value);
ASSERT_EQ(value, policy.value);
}
std::vector<ForceInstalledExtension> extensions;
std::vector<ExtensionPolicyRegistryEntry> policies;
GetExtensionForcelistRegistryPolicies(&policies);
for (ExtensionPolicyRegistryEntry& policy : policies) {
ForceInstalledExtension extension(
ExtensionID::Create(base::UTF16ToUTF8(policy.extension_id)).value(),
POLICY_EXTENSION_FORCELIST, "", "");
extension.policy_registry_entry =
std::make_shared<ExtensionPolicyRegistryEntry>(std::move(policy));
extensions.push_back(extension);
}
for (ForceInstalledExtension& extension : extensions) {
base::win::RegKey policy_key;
ASSERT_TRUE(RemoveForcelistPolicyExtension(extension));
ASSERT_EQ(ERROR_SUCCESS,
policy_key.Open(extension.policy_registry_entry->hkey,
extension.policy_registry_entry->path.c_str(),
KEY_READ));
base::string16 value;
policy_key.ReadValue(extension.policy_registry_entry->name.c_str(), &value);
ASSERT_EQ(value, L"");
}
}
TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensions) { TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensions) {
// Set up a fake default extensions JSON file. // Set up a fake default extensions JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES); base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
...@@ -200,6 +175,69 @@ TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensions) { ...@@ -200,6 +175,69 @@ TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensions) {
::testing::UnorderedElementsAreArray(found_extension_ids)); ::testing::UnorderedElementsAreArray(found_extension_ids));
} }
TEST(ExtensionsUtilTest, RemoveNonWhitelistedDefaultExtensions) {
// Set up a fake default extensions JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
base::FilePath program_files_dir;
ASSERT_TRUE(
base::PathService::Get(base::DIR_PROGRAM_FILES, &program_files_dir));
base::FilePath fake_apps_dir(
program_files_dir.Append(kFakeChromeFolder).Append(L"default_apps"));
ASSERT_TRUE(base::CreateDirectoryAndGetError(fake_apps_dir, nullptr));
base::FilePath default_extensions_file =
fake_apps_dir.Append(L"external_extensions.json");
CreateFileWithContent(default_extensions_file, kDefaultExtensionsJson,
sizeof(kDefaultExtensionsJson) - 1);
ASSERT_TRUE(base::PathExists(default_extensions_file));
// Set up an invalid default extensions JSON file
base::ScopedPathOverride program_files_x86_override(
base::DIR_PROGRAM_FILESX86);
ASSERT_TRUE(
base::PathService::Get(base::DIR_PROGRAM_FILESX86, &program_files_dir));
fake_apps_dir =
program_files_dir.Append(kFakeChromeFolder).Append(L"default_apps");
ASSERT_TRUE(base::CreateDirectoryAndGetError(fake_apps_dir, nullptr));
default_extensions_file = fake_apps_dir.Append(L"external_extensions.json");
CreateFileWithContent(default_extensions_file, kInvalidDefaultExtensionsJson,
sizeof(kInvalidDefaultExtensionsJson) - 1);
ASSERT_TRUE(base::PathExists(default_extensions_file));
TestJsonParser json_parser;
std::vector<ExtensionPolicyFile> policies;
base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
GetNonWhitelistedDefaultExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
std::vector<ForceInstalledExtension> extensions;
for (ExtensionPolicyFile& policy : policies) {
ForceInstalledExtension extension(
ExtensionID::Create(base::UTF16ToUTF8(policy.extension_id)).value(),
DEFAULT_APPS_EXTENSION, "", "");
extension.policy_file =
std::make_shared<ExtensionPolicyFile>(std::move(policy));
extensions.push_back(extension);
}
base::Value json_result = extensions[0].policy_file->json->data.Clone();
for (ForceInstalledExtension& extension : extensions) {
ASSERT_TRUE(RemoveDefaultExtension(extension, &json_result));
}
std::string result;
JSONStringValueSerializer serializer(&result);
ASSERT_TRUE(serializer.Serialize(json_result));
ASSERT_EQ(result, kValidDefaultExtensionsJson);
base::Value original = json_result.Clone();
for (ForceInstalledExtension& extension : extensions) {
ASSERT_FALSE(RemoveDefaultExtension(extension, &json_result));
ASSERT_EQ(original, json_result);
}
}
TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensionsNoFilesFound) { TEST(ExtensionsUtilTest, GetNonWhitelistedDefaultExtensionsNoFilesFound) {
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES); base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
base::ScopedPathOverride program_files_x86_override( base::ScopedPathOverride program_files_x86_override(
...@@ -223,13 +261,12 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) { ...@@ -223,13 +261,12 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) {
base::win::RegKey settings_key; base::win::RegKey settings_key;
ASSERT_EQ(ERROR_SUCCESS, ASSERT_EQ(ERROR_SUCCESS,
settings_key.Create(extension_settings_entry.hkey, settings_key.Create(HKEY_LOCAL_MACHINE,
extension_settings_entry.path.c_str(), kExtensionSettingsPolicyPath, KEY_ALL_ACCESS));
KEY_ALL_ACCESS)); ASSERT_TRUE(settings_key.Valid());
DCHECK(settings_key.Valid());
ASSERT_EQ(ERROR_SUCCESS, ASSERT_EQ(ERROR_SUCCESS,
settings_key.WriteValue(extension_settings_entry.name.c_str(), settings_key.WriteValue(kExtensionSettingsName,
extension_settings_entry.value.c_str())); kExtensionSettingsJsonOnlyForced));
TestJsonParser json_parser; TestJsonParser json_parser;
std::vector<ExtensionPolicyRegistryEntry> policies; std::vector<ExtensionPolicyRegistryEntry> policies;
...@@ -239,8 +276,8 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) { ...@@ -239,8 +276,8 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) {
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout())); ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
// Check that only the two force installed extensions were found // Check that only the two force installed extensions were found
const base::string16 expected_extension_ids[] = {kTestExtensionId1, const base::string16 expected_extension_ids[] = {kTestExtensionId4,
kTestExtensionId2}; kTestExtensionId5};
const base::string16 found_extension_ids[] = {policies[0].extension_id, const base::string16 found_extension_ids[] = {policies[0].extension_id,
policies[1].extension_id}; policies[1].extension_id};
EXPECT_THAT(expected_extension_ids, EXPECT_THAT(expected_extension_ids,
...@@ -249,9 +286,106 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) { ...@@ -249,9 +286,106 @@ TEST(ExtensionsUtilTest, GetExtensionSettingsForceInstalledExtensions) {
// Also check that the collected registry entries match the values in the // Also check that the collected registry entries match the values in the
// registry. // registry.
for (const ExtensionPolicyRegistryEntry& policy : policies) { for (const ExtensionPolicyRegistryEntry& policy : policies) {
EXPECT_EQ(policy.hkey, extension_settings_entry.hkey); EXPECT_EQ(policy.hkey, HKEY_LOCAL_MACHINE);
EXPECT_EQ(policy.path, extension_settings_entry.path); EXPECT_EQ(policy.path, kExtensionSettingsPolicyPath);
EXPECT_EQ(policy.name, extension_settings_entry.name); EXPECT_EQ(policy.name, kExtensionSettingsName);
}
}
TEST(ExtensionsUtilTest, RemoveExtensionSettingsForceInstalledExtensions) {
registry_util::RegistryOverrideManager registry_override;
registry_override.OverrideRegistry(HKEY_CURRENT_USER);
registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
base::win::RegKey settings_key;
ASSERT_EQ(ERROR_SUCCESS,
settings_key.Create(HKEY_LOCAL_MACHINE,
kExtensionSettingsPolicyPath, KEY_ALL_ACCESS));
DCHECK(settings_key.Valid());
ASSERT_EQ(ERROR_SUCCESS,
settings_key.WriteValue(kExtensionSettingsName,
kExtensionSettingsJsonOnlyForced));
TestJsonParser json_parser;
std::vector<ExtensionPolicyRegistryEntry> policies;
base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
GetExtensionSettingsForceInstalledExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
std::unordered_set<ForceInstalledExtension, ExtensionIDHash, ExtensionIDEqual>
extensions;
base::Value json_result = policies[0].json->data.Clone();
for (ExtensionPolicyRegistryEntry& policy : policies) {
ForceInstalledExtension extension(
ExtensionID::Create(base::UTF16ToUTF8(policy.extension_id)).value(),
POLICY_EXTENSION_SETTINGS, "", "");
extension.policy_registry_entry =
std::make_shared<ExtensionPolicyRegistryEntry>(std::move(policy));
extensions.insert(extension);
}
for (const ForceInstalledExtension& extension : extensions) {
ASSERT_TRUE(
RemoveExtensionSettingsPoliciesExtension(extension, &json_result));
}
std::string result;
JSONStringValueSerializer serializer(&result);
ASSERT_TRUE(serializer.Serialize(json_result));
ASSERT_EQ(result, "{}");
base::Value original = json_result.Clone();
for (const ForceInstalledExtension& extension : extensions) {
ASSERT_FALSE(
RemoveExtensionSettingsPoliciesExtension(extension, &json_result));
ASSERT_EQ(original, json_result);
}
}
TEST(ExtensionsUtilTest, RemoveSomeExtensionSettingsForceInstalledExtensions) {
registry_util::RegistryOverrideManager registry_override;
registry_override.OverrideRegistry(HKEY_CURRENT_USER);
registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
base::win::RegKey settings_key;
ASSERT_EQ(ERROR_SUCCESS,
settings_key.Create(HKEY_LOCAL_MACHINE,
kExtensionSettingsPolicyPath, KEY_ALL_ACCESS));
DCHECK(settings_key.Valid());
ASSERT_EQ(ERROR_SUCCESS, settings_key.WriteValue(kExtensionSettingsName,
kExtensionSettingsJson));
TestJsonParser json_parser;
std::vector<ExtensionPolicyRegistryEntry> policies;
base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
GetExtensionSettingsForceInstalledExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
std::unordered_set<ForceInstalledExtension, ExtensionIDHash, ExtensionIDEqual>
extensions;
base::Value json_result = policies[0].json->data.Clone();
for (ExtensionPolicyRegistryEntry& policy : policies) {
ForceInstalledExtension extension(
ExtensionID::Create(base::UTF16ToUTF8(policy.extension_id)).value(),
POLICY_EXTENSION_SETTINGS, "", "");
extension.policy_registry_entry =
std::make_shared<ExtensionPolicyRegistryEntry>(std::move(policy));
extensions.insert(extension);
}
for (const ForceInstalledExtension& extension : extensions) {
ASSERT_TRUE(
RemoveExtensionSettingsPoliciesExtension(extension, &json_result));
}
std::string result;
JSONStringValueSerializer serializer(&result);
ASSERT_TRUE(serializer.Serialize(json_result));
ASSERT_EQ(result, kValidExtensionSettingsJson);
base::Value original = json_result.Clone();
for (const ForceInstalledExtension& extension : extensions) {
ASSERT_FALSE(
RemoveExtensionSettingsPoliciesExtension(extension, &json_result));
ASSERT_EQ(original, json_result);
} }
} }
...@@ -295,8 +429,8 @@ TEST(ExtensionsUtilTest, GetMasterPreferencesExtensions) { ...@@ -295,8 +429,8 @@ TEST(ExtensionsUtilTest, GetMasterPreferencesExtensions) {
GetMasterPreferencesExtensions(&json_parser, &policies, &done); GetMasterPreferencesExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout())); ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
const base::string16 expected_extension_ids[] = {kTestExtensionId1, const base::string16 expected_extension_ids[] = {kTestExtensionId6,
kTestExtensionId2}; kTestExtensionId7};
ASSERT_EQ(base::size(expected_extension_ids), policies.size()); ASSERT_EQ(base::size(expected_extension_ids), policies.size());
const base::string16 found_extension_ids[] = {policies[0].extension_id, const base::string16 found_extension_ids[] = {policies[0].extension_id,
policies[1].extension_id}; policies[1].extension_id};
...@@ -304,6 +438,48 @@ TEST(ExtensionsUtilTest, GetMasterPreferencesExtensions) { ...@@ -304,6 +438,48 @@ TEST(ExtensionsUtilTest, GetMasterPreferencesExtensions) {
::testing::UnorderedElementsAreArray(found_extension_ids)); ::testing::UnorderedElementsAreArray(found_extension_ids));
} }
TEST(ExtensionsUtilTest, RemoveMasterPreferencesExtensionsNoneFound) {
// Set up a fake master preferences JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
base::FilePath program_files_dir;
ASSERT_TRUE(
base::PathService::Get(base::DIR_PROGRAM_FILES, &program_files_dir));
base::FilePath chrome_dir(program_files_dir.Append(kChromeExePath));
ASSERT_TRUE(base::CreateDirectoryAndGetError(chrome_dir, nullptr));
base::FilePath master_preferences =
chrome_dir.Append(kMasterPreferencesFileName);
CreateFileWithContent(master_preferences, kMasterPreferencesJson,
sizeof(kMasterPreferencesJson) - 1);
ASSERT_TRUE(base::PathExists(master_preferences));
TestJsonParser json_parser;
std::vector<ExtensionPolicyFile> policies;
base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL,
WaitableEvent::InitialState::NOT_SIGNALED);
GetMasterPreferencesExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
std::vector<ForceInstalledExtension> extensions;
for (ExtensionPolicyFile& policy : policies) {
ForceInstalledExtension extension(
ExtensionID::Create(base::UTF16ToUTF8(policy.extension_id)).value(),
POLICY_MASTER_PREFERENCES, "", "");
extension.policy_file =
std::make_shared<ExtensionPolicyFile>(std::move(policy));
extensions.push_back(extension);
}
base::Value json_result = extensions[0].policy_file->json->data.Clone();
for (ForceInstalledExtension& extension : extensions) {
ASSERT_TRUE(RemoveMasterPreferencesExtension(extension, &json_result));
}
std::string result;
JSONStringValueSerializer serializer(&result);
ASSERT_TRUE(serializer.Serialize(json_result));
ASSERT_EQ(result, kValidMasterPreferencesJson);
}
TEST(ExtensionsUtilTest, GetMasterPreferencesExtensionsNoneFound) { TEST(ExtensionsUtilTest, GetMasterPreferencesExtensionsNoneFound) {
// Set up a fake master preferences JSON file. // Set up a fake master preferences JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES); base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
......
// 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/chrome_utils/force_installed_extension.h"
#include <string>
#include <utility>
namespace chrome_cleaner {
ForceInstalledExtension::ForceInstalledExtension(
const ExtensionID& id,
ExtensionInstallMethod install_method,
const std::string& update_url,
const std::string& manifest_permissions)
: id(std::move(id)),
install_method(std::move(install_method)),
update_url(std::move(update_url)),
manifest_permissions(std::move(manifest_permissions)) {}
ForceInstalledExtension& ForceInstalledExtension::operator=(
const ForceInstalledExtension& other) = default;
ForceInstalledExtension& ForceInstalledExtension::operator=(
ForceInstalledExtension&& other) = default;
ForceInstalledExtension::ForceInstalledExtension(
ExtensionID id,
ExtensionInstallMethod install_method)
: id(std::move(id)), install_method(std::move(install_method)) {}
ForceInstalledExtension::ForceInstalledExtension(
const ForceInstalledExtension& extension) = default;
ForceInstalledExtension::ForceInstalledExtension(
ForceInstalledExtension&& extension) = default;
ForceInstalledExtension::~ForceInstalledExtension() = default;
bool ForceInstalledExtension::operator==(
const ForceInstalledExtension& other) const {
// Don't include policy pointers in comparison because that metadata
// is only used when writing out the results of transforming the values.
return id == other.id && install_method == other.install_method &&
update_url == other.update_url &&
manifest_permissions == other.manifest_permissions;
}
bool ForceInstalledExtension::operator<(
const ForceInstalledExtension& other) const {
if (id < other.id) {
return true;
} else if (id > other.id) {
return false;
} else if (install_method < other.install_method) {
return true;
} else if (install_method > other.install_method) {
return false;
} else if (update_url < other.update_url) {
return true;
} else if (update_url > other.update_url) {
return false;
} else if (manifest_permissions < other.manifest_permissions) {
return true;
} else if (manifest_permissions > other.manifest_permissions) {
return false;
}
return false;
}
} // 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_CHROME_UTILS_FORCE_INSTALLED_EXTENSION_H_
#define CHROME_CHROME_CLEANER_CHROME_UTILS_FORCE_INSTALLED_EXTENSION_H_
#include <memory>
#include <string>
#include "chrome/chrome_cleaner/chrome_utils/extension_id.h"
#include "chrome/chrome_cleaner/logging/proto/shared_data.pb.h"
namespace chrome_cleaner {
// Forward declare to avoid dependency cycle.
struct ExtensionPolicyRegistryEntry;
struct ExtensionPolicyFile;
// An extension that has been force installed.
struct ForceInstalledExtension {
ForceInstalledExtension(const ExtensionID& id,
ExtensionInstallMethod install_method,
const std::string& update_url,
const std::string& manifest_permissions);
ForceInstalledExtension(ExtensionID id,
ExtensionInstallMethod install_method);
ForceInstalledExtension(const ForceInstalledExtension& extension);
ForceInstalledExtension(ForceInstalledExtension&& extension);
ForceInstalledExtension& operator=(const ForceInstalledExtension& other);
ForceInstalledExtension& operator=(ForceInstalledExtension&& other);
~ForceInstalledExtension();
bool operator==(const ForceInstalledExtension& other) const;
bool operator<(const ForceInstalledExtension& other) const;
ExtensionID id;
ExtensionInstallMethod install_method;
std::string update_url;
std::string manifest_permissions;
std::shared_ptr<ExtensionPolicyRegistryEntry> policy_registry_entry;
std::shared_ptr<ExtensionPolicyFile> policy_file;
};
// Overload to compare by Extension IDs, useful when constructing sets
struct ExtensionIDCompare {
bool operator()(const ForceInstalledExtension& lhs,
const ForceInstalledExtension& rhs) const {
return lhs.id < rhs.id;
}
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_CHROME_UTILS_FORCE_INSTALLED_EXTENSION_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.
static_library("parse_tasks_remaining_counter") {
sources = [
"parse_tasks_remaining_counter.cc",
"parse_tasks_remaining_counter.h",
]
deps = [
"//base:base",
]
}
// 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/parsers/parser_utils/parse_tasks_remaining_counter.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
namespace chrome_cleaner {
ParseTasksRemainingCounter::ParseTasksRemainingCounter(
size_t count,
base::WaitableEvent* done)
: count_(count), done_(done) {
DCHECK(count_ > 0) << "Must be constructed with a positive count.";
}
void ParseTasksRemainingCounter::Increment() {
DCHECK(count_ > 0)
<< "Once decremented to zero, Increment should never be called.";
count_++;
}
void ParseTasksRemainingCounter::Decrement() {
DCHECK(count_);
count_--;
if (count_ == 0) {
done_->Signal();
}
}
} // 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_PARSERS_PARSER_UTILS_PARSE_TASKS_REMAINING_COUNTER_H_
#define CHROME_CHROME_CLEANER_PARSERS_PARSER_UTILS_PARSE_TASKS_REMAINING_COUNTER_H_
#include "base/memory/ref_counted.h"
#include "base/synchronization/waitable_event.h"
namespace chrome_cleaner {
class ParseTasksRemainingCounter
: public base::RefCountedThreadSafe<ParseTasksRemainingCounter> {
public:
ParseTasksRemainingCounter(size_t count, base::WaitableEvent* done);
void Increment();
void Decrement();
private:
friend class base::RefCountedThreadSafe<ParseTasksRemainingCounter>;
~ParseTasksRemainingCounter() = default;
size_t count_;
base::WaitableEvent* done_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_PARSERS_PARSER_UTILS_PARSE_TASKS_REMAINING_COUNTER_H_
...@@ -71,6 +71,18 @@ source_set("test_executables") { ...@@ -71,6 +71,18 @@ source_set("test_executables") {
] ]
} }
source_set("test_extensions") {
sources = [
"test_extensions.cc",
"test_extensions.h",
]
deps = [
"//base:base",
"//chrome/chrome_cleaner/os:common_os",
]
}
source_set("test_pup_data") { source_set("test_pup_data") {
testonly = true testonly = true
......
// 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_extensions.h"
namespace chrome_cleaner {
TestRegistryEntry::TestRegistryEntry(HKEY hkey,
const base::string16& path,
const base::string16& name,
const base::string16& value)
: hkey(hkey), path(path), name(name), value(value) {}
TestRegistryEntry::TestRegistryEntry(const TestRegistryEntry& other) = default;
TestRegistryEntry& TestRegistryEntry::operator=(
const TestRegistryEntry& other) = default;
} // 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_EXTENSIONS_H_
#define CHROME_CHROME_CLEANER_TEST_TEST_EXTENSIONS_H_
#include "base/win/registry.h"
#include "chrome/chrome_cleaner/os/registry_util.h"
namespace chrome_cleaner {
struct TestRegistryEntry {
HKEY hkey;
base::string16 path;
base::string16 name;
base::string16 value;
TestRegistryEntry(HKEY hkey,
const base::string16& path,
const base::string16& name,
const base::string16& value);
TestRegistryEntry(const TestRegistryEntry& other);
TestRegistryEntry& operator=(const TestRegistryEntry& other);
};
const wchar_t kChromeExePath[] = L"google\\chrome\\application";
const wchar_t kFakeChromeFolder[] = L"google\\chrome\\application\\42.12.34.56";
const wchar_t kExtensionSettingsPolicyPath[] =
L"software\\policies\\google\\chrome";
const wchar_t kExtensionSettingsName[] = L"ExtensionSettings";
const wchar_t kMasterPreferencesFileName[] = L"master_preferences";
const wchar_t kTestExtensionId1[] = L"ababababcdcdcdcdefefefefghghghgh";
const wchar_t kTestExtensionId2[] = L"aaaabbbbccccddddeeeeffffgggghhhh";
const wchar_t kTestExtensionId3[] = L"hhhheeeeebbbbbccccddddaaaaabcdef";
const wchar_t kTestExtensionId4[] = L"abcdefghgfedcbabcdefghgfedcbaced";
const wchar_t kTestExtensionId5[] = L"dcbdcbdcbdcbdcbdcbdcbdcbdcbdcbcd";
const wchar_t kTestExtensionId6[] = L"egegegegegegegegegegegegegegegeg";
const wchar_t kTestExtensionId7[] = L"nopnopnopnopnopnopnopnopnopnopno";
// Test force installed extension settings
const TestRegistryEntry kExtensionForcelistEntries[] = {
{HKEY_LOCAL_MACHINE, kChromePoliciesForcelistKeyPath, L"test1",
base::string16(kTestExtensionId3) + L"https://test.test/crxupdate2/crx"}};
const char kValidExtensionSettingsJson[] =
"{\"extensionwithinstallmodeblockeda\":{\"installation_mode\":\"blocked\","
"\"update_url\":"
"\"https://test.test/crx\"},\"extensionwithnosettingsabcdefghi\":{}}";
const wchar_t kExtensionSettingsJson[] =
LR"(
{
"dcbdcbdcbdcbdcbdcbdcbdcbdcbdcbcd": {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
},
"abcdefghgfedcbabcdefghgfedcbaced" : {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
},
"extensionwithinstallmodeblockeda": {
"installation_mode": "blocked",
"update_url":"https://test.test/crx"
},
"extensionwithnosettingsabcdefghi": {}
})";
const wchar_t kExtensionSettingsJsonOnlyForced[] =
LR"(
{
"dcbdcbdcbdcbdcbdcbdcbdcbdcbdcbcd": {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
},
"abcdefghgfedcbabcdefghgfedcbaced": {
"installation_mode": "force_installed",
"update_url":"https://test.test/crx"
}
})";
// Test force installed default extensions
const char kInvalidDefaultExtensionsJson[] = "{ json: invalid }";
const char kDefaultExtensionsJson[] =
R"(
{
"ababababcdcdcdcdefefefefghghghgh" : {
"external_update_url":"https://test.test/crx"
},
"aaaabbbbccccddddeeeeffffgggghhhh" : {
"external_update_url":"https://test.test/crx"
},
// Google Sheets
"aapocclcgogkmnckokdopfmhonfmgoek" : {
"external_update_url":"https://test.test/crx"
},
})";
const char kValidDefaultExtensionsJson[] =
"{\"aapocclcgogkmnckokdopfmhonfmgoek\":{"
"\"external_update_url\":\"https://test.test/crx\"}}";
// Test force installed master preferences
const char kMasterPreferencesJson[] =
R"(
{
"homepage": "http://dev.chromium.org/",
"extensions": {
"settings": {
"egegegegegegegegegegegegegegegeg": {
"location": 1,
"manifest": {
"name": "Test extension"
}
},
"nopnopnopnopnopnopnopnopnopnopno": {
"location": 1,
"manifest": {
"name": "Another one"
}
}
}
}
})";
const char kValidMasterPreferencesJson[] =
"{\"extensions\":{\"settings\":{}},\"homepage\":\"http://dev.chromium.org/"
"\"}";
const char kMasterPreferencesJsonNoExtensions[] =
R"(
{
"homepage": "http://dev.chromium.org/"
})";
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_TEST_EXTENSIONS_H_
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