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: Chris Sharp <csharp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608249}
diff --git a/chrome/chrome_cleaner/chrome_utils/BUILD.gn b/chrome/chrome_cleaner/chrome_utils/BUILD.gn
index d9fc74c..f7a99c0 100644
--- a/chrome/chrome_cleaner/chrome_utils/BUILD.gn
+++ b/chrome/chrome_cleaner/chrome_utils/BUILD.gn
@@ -23,9 +23,25 @@
deps = [
":chrome_util_lib",
+ ":force_installed_extension",
"//base:base",
+ "//chrome/chrome_cleaner/logging/proto:shared_data_proto",
"//chrome/chrome_cleaner/os:common_os",
"//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 @@
"//base:base",
"//base/test:test_support",
"//chrome/chrome_cleaner/parsers/json_parser",
+ "//chrome/chrome_cleaner/test:test_extensions",
"//chrome/chrome_cleaner/test:test_util",
"//testing/gtest",
]
}
+
+source_set("extension_id") {
+ sources = [
+ "extension_id.cc",
+ "extension_id.h",
+ ]
+
+ deps = [
+ "//base:base",
+ ]
+}
diff --git a/chrome/chrome_cleaner/chrome_utils/extension_id.cc b/chrome/chrome_cleaner/chrome_utils/extension_id.cc
new file mode 100644
index 0000000..91d4444
--- /dev/null
+++ b/chrome/chrome_cleaner/chrome_utils/extension_id.cc
@@ -0,0 +1,39 @@
+// 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
diff --git a/chrome/chrome_cleaner/chrome_utils/extension_id.h b/chrome/chrome_cleaner/chrome_utils/extension_id.h
new file mode 100644
index 0000000..03e4753d
--- /dev/null
+++ b/chrome/chrome_cleaner/chrome_utils/extension_id.h
@@ -0,0 +1,47 @@
+// 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_
diff --git a/chrome/chrome_cleaner/chrome_utils/extensions_util.cc b/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
index ac79c873..35d0874 100644
--- a/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
+++ b/chrome/chrome_cleaner/chrome_utils/extensions_util.cc
@@ -10,6 +10,8 @@
#include <memory>
#include <set>
#include <string>
+#include <utility>
+#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
@@ -28,6 +30,8 @@
#include "chrome/chrome_cleaner/os/registry.h"
#include "chrome/chrome_cleaner/os/registry_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;
@@ -36,36 +40,7 @@
const int kExtensionIdLength = 32;
-class ParseTasksRemainingCounter
- : 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_;
-};
-
+// TODO(joenotcharles): Use RegKeyPath instead.
struct RegistryKey {
HKEY hkey;
const wchar_t* path;
@@ -95,6 +70,21 @@
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(
REGSAM access_mask,
std::vector<ExtensionPolicyRegistryEntry>* policies) {
@@ -114,16 +104,50 @@
policies->emplace_back(extension_id, extension_forcelist_keys[i].hkey,
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(
const RegistryKey& registry_key,
std::vector<ExtensionPolicyRegistryEntry>* policies,
scoped_refptr<ParseTasksRemainingCounter> counter,
+ ContentType type,
base::Optional<base::Value> json,
const base::Optional<std::string>& error) {
base::ScopedClosureRunner closure(
@@ -140,6 +164,8 @@
return;
}
+ scoped_refptr<RefValue> saved_json =
+ base::WrapRefCounted(new RefValue(json->Clone()));
for (const auto& entry : *extension_settings) {
const base::string16& extension_id = base::UTF8ToUTF16(entry.first);
const std::unique_ptr<base::Value>& settings_value = entry.second;
@@ -150,9 +176,9 @@
if (installation_mode != nullptr &&
installation_mode->GetString() ==
kExtensionSettingsForceInstalledValue) {
- policies->emplace_back(extension_id, registry_key.hkey,
- registry_key.path,
- kExtensionSettingsRegistryEntryName);
+ policies->emplace_back(
+ extension_id, registry_key.hkey, registry_key.path,
+ kExtensionSettingsRegistryEntryName, type, saved_json);
}
}
}
@@ -168,7 +194,7 @@
extension_settings_keys[i].path, access_mask);
base::string16 extension_settings;
RegistryError error;
- uint32_t type;
+ ContentType type;
ReadRegistryValue(key, kExtensionSettingsRegistryEntryName,
&extension_settings, &type, &error);
@@ -185,7 +211,7 @@
json_parser->Parse(
base::UTF16ToUTF8(extension_settings),
base::BindOnce(&GetExtensionSettingsPoliciesFromParsedJson,
- extension_settings_keys[i], policies, counter));
+ extension_settings_keys[i], policies, counter, type));
}
}
@@ -208,10 +234,13 @@
return;
}
+ scoped_refptr<RefValue> saved_json =
+ base::WrapRefCounted(new RefValue(json->Clone()));
for (const auto& entry : *default_extensions) {
base::string16 extension_id = base::UTF8ToUTF16(entry.first);
- if (!base::ContainsValue(default_extension_whitelist, extension_id))
- policies->emplace_back(extension_id, extensions_file);
+ if (!base::ContainsValue(default_extension_whitelist, extension_id)) {
+ policies->emplace_back(extension_id, extensions_file, saved_json);
+ }
}
}
@@ -241,9 +270,11 @@
base::DictionaryValue* 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) {
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 @@
const base::string16& extension_id,
HKEY hkey,
const base::string16& path,
- const base::string16& name)
- : extension_id(extension_id), hkey(hkey), path(path), name(name) {}
+ const base::string16& 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&&) = default;
+ExtensionPolicyRegistryEntry::~ExtensionPolicyRegistryEntry() = default;
+
ExtensionPolicyRegistryEntry& ExtensionPolicyRegistryEntry::operator=(
ExtensionPolicyRegistryEntry&&) = default;
ExtensionPolicyFile::ExtensionPolicyFile(const base::string16& extension_id,
- const base::FilePath& path)
- : extension_id(extension_id), path(path) {}
+ const base::FilePath& path,
+ scoped_refptr<RefValue> json)
+ : extension_id(extension_id), path(path), json(std::move(json)) {}
ExtensionPolicyFile::ExtensionPolicyFile(ExtensionPolicyFile&&) = default;
+ExtensionPolicyFile::~ExtensionPolicyFile() = default;
+
ExtensionPolicyFile& ExtensionPolicyFile::operator=(ExtensionPolicyFile&&) =
default;
@@ -278,6 +321,19 @@
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(
JsonParserAPI* json_parser,
std::vector<ExtensionPolicyFile>* policies,
@@ -317,6 +373,12 @@
}
}
+bool RemoveDefaultExtension(const ForceInstalledExtension& extension,
+ base::Value* json_result) {
+ DCHECK(extension.install_method == DEFAULT_APPS_EXTENSION);
+ return RemoveExtensionFromJson(extension, json_result);
+}
+
void GetExtensionSettingsForceInstalledExtensions(
JsonParserAPI* json_parser,
std::vector<ExtensionPolicyRegistryEntry>* policies,
@@ -335,6 +397,19 @@
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,
std::vector<ExtensionPolicyFile>* policies,
base::WaitableEvent* done) {
@@ -373,4 +448,15 @@
}
}
+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
diff --git a/chrome/chrome_cleaner/chrome_utils/extensions_util.h b/chrome/chrome_cleaner/chrome_utils/extensions_util.h
index 37a3efc..f29628a 100644
--- a/chrome/chrome_cleaner/chrome_utils/extensions_util.h
+++ b/chrome/chrome_cleaner/chrome_utils/extensions_util.h
@@ -9,25 +9,39 @@
#include "base/files/file_path.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/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/parsers/json_parser/json_parser_api.h"
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|.
struct ExtensionPolicyRegistryEntry {
base::string16 extension_id;
HKEY hkey;
base::string16 path;
base::string16 name;
+ ContentType content_type;
+ scoped_refptr<RefValue> json;
ExtensionPolicyRegistryEntry(const base::string16& extension_id,
HKEY hkey,
const base::string16& path,
- const base::string16& name);
+ const base::string16& name,
+ ContentType content_type,
+ scoped_refptr<RefValue>);
ExtensionPolicyRegistryEntry(ExtensionPolicyRegistryEntry&&);
+ ~ExtensionPolicyRegistryEntry();
ExtensionPolicyRegistryEntry& operator=(ExtensionPolicyRegistryEntry&&);
DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyRegistryEntry);
@@ -37,10 +51,13 @@
struct ExtensionPolicyFile {
base::string16 extension_id;
base::FilePath path;
+ scoped_refptr<RefValue> json;
ExtensionPolicyFile(const base::string16& extension_id,
- const base::FilePath& path);
+ const base::FilePath& path,
+ scoped_refptr<RefValue> json);
ExtensionPolicyFile(ExtensionPolicyFile&&);
+ ~ExtensionPolicyFile();
ExtensionPolicyFile& operator=(ExtensionPolicyFile&&);
DISALLOW_COPY_AND_ASSIGN(ExtensionPolicyFile);
@@ -75,6 +92,32 @@
std::vector<ExtensionPolicyFile>* policies,
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
#endif // CHROME_CHROME_CLEANER_CHROME_UTILS_EXTENSIONS_UTIL_H_
diff --git a/chrome/chrome_cleaner/chrome_utils/extensions_util_unittest.cc b/chrome/chrome_cleaner/chrome_utils/extensions_util_unittest.cc
index 1160219..c658152e7 100644
--- a/chrome/chrome_cleaner/chrome_utils/extensions_util_unittest.cc
+++ b/chrome/chrome_cleaner/chrome_utils/extensions_util_unittest.cc
@@ -4,12 +4,18 @@
#include "chrome/chrome_cleaner/chrome_utils/extensions_util.h"
+#include <memory>
+#include <string>
+#include <unordered_set>
+#include <utility>
#include <vector>
#include "base/base_paths_win.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
+#include "base/json/json_string_value_serializer.h"
#include "base/path_service.h"
+#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
@@ -18,6 +24,7 @@
#include "base/test/test_timeouts.h"
#include "base/win/registry.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 "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -29,91 +36,18 @@
const int kExtensionIdLength = 32;
-struct TestRegistryEntry {
- HKEY hkey;
- const base::string16 path;
- const base::string16 name;
- const base::string16 value;
+struct ExtensionIDHash {
+ size_t operator()(const ForceInstalledExtension& extension) const {
+ return std::hash<std::string>{}(extension.id.AsString());
+ }
};
-const TestRegistryEntry extension_forcelist_entries[] = {
- {HKEY_LOCAL_MACHINE, kChromePoliciesForcelistKeyPath, L"test1",
- L"ababababcdcdcdcdefefefefghghghgh;https://test.test/crx"
- L"update2/crx"},
- {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/"
- })";
+struct ExtensionIDEqual {
+ bool operator()(const ForceInstalledExtension& lhs,
+ const ForceInstalledExtension& rhs) const {
+ return lhs.id == rhs.id;
+ }
+};
bool ExtensionPolicyRegistryEntryFound(
TestRegistryEntry test_entry,
@@ -135,11 +69,11 @@
registry_util::RegistryOverrideManager registry_override;
registry_override.OverrideRegistry(HKEY_CURRENT_USER);
registry_override.OverrideRegistry(HKEY_LOCAL_MACHINE);
- for (const TestRegistryEntry& policy : extension_forcelist_entries) {
+ 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_TRUE(policy_key.Valid());
ASSERT_EQ(ERROR_SUCCESS,
policy_key.WriteValue(policy.name.c_str(), policy.value.c_str()));
}
@@ -147,11 +81,52 @@
std::vector<ExtensionPolicyRegistryEntry> policies;
GetExtensionForcelistRegistryPolicies(&policies);
- for (const TestRegistryEntry& expected_result : extension_forcelist_entries) {
+ for (const TestRegistryEntry& expected_result : kExtensionForcelistEntries) {
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) {
// Set up a fake default extensions JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
@@ -200,6 +175,69 @@
::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) {
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
base::ScopedPathOverride program_files_x86_override(
@@ -223,13 +261,12 @@
base::win::RegKey settings_key;
ASSERT_EQ(ERROR_SUCCESS,
- settings_key.Create(extension_settings_entry.hkey,
- extension_settings_entry.path.c_str(),
- KEY_ALL_ACCESS));
- DCHECK(settings_key.Valid());
+ settings_key.Create(HKEY_LOCAL_MACHINE,
+ kExtensionSettingsPolicyPath, KEY_ALL_ACCESS));
+ ASSERT_TRUE(settings_key.Valid());
ASSERT_EQ(ERROR_SUCCESS,
- settings_key.WriteValue(extension_settings_entry.name.c_str(),
- extension_settings_entry.value.c_str()));
+ settings_key.WriteValue(kExtensionSettingsName,
+ kExtensionSettingsJsonOnlyForced));
TestJsonParser json_parser;
std::vector<ExtensionPolicyRegistryEntry> policies;
@@ -239,8 +276,8 @@
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
// Check that only the two force installed extensions were found
- const base::string16 expected_extension_ids[] = {kTestExtensionId1,
- kTestExtensionId2};
+ const base::string16 expected_extension_ids[] = {kTestExtensionId4,
+ kTestExtensionId5};
const base::string16 found_extension_ids[] = {policies[0].extension_id,
policies[1].extension_id};
EXPECT_THAT(expected_extension_ids,
@@ -249,9 +286,106 @@
// Also check that the collected registry entries match the values in the
// registry.
for (const ExtensionPolicyRegistryEntry& policy : policies) {
- EXPECT_EQ(policy.hkey, extension_settings_entry.hkey);
- EXPECT_EQ(policy.path, extension_settings_entry.path);
- EXPECT_EQ(policy.name, extension_settings_entry.name);
+ EXPECT_EQ(policy.hkey, HKEY_LOCAL_MACHINE);
+ EXPECT_EQ(policy.path, kExtensionSettingsPolicyPath);
+ 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 @@
GetMasterPreferencesExtensions(&json_parser, &policies, &done);
ASSERT_TRUE(done.TimedWait(TestTimeouts::action_timeout()));
- const base::string16 expected_extension_ids[] = {kTestExtensionId1,
- kTestExtensionId2};
+ const base::string16 expected_extension_ids[] = {kTestExtensionId6,
+ kTestExtensionId7};
ASSERT_EQ(base::size(expected_extension_ids), policies.size());
const base::string16 found_extension_ids[] = {policies[0].extension_id,
policies[1].extension_id};
@@ -304,6 +438,48 @@
::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) {
// Set up a fake master preferences JSON file.
base::ScopedPathOverride program_files_override(base::DIR_PROGRAM_FILES);
diff --git a/chrome/chrome_cleaner/chrome_utils/force_installed_extension.cc b/chrome/chrome_cleaner/chrome_utils/force_installed_extension.cc
new file mode 100644
index 0000000..08c6750
--- /dev/null
+++ b/chrome/chrome_cleaner/chrome_utils/force_installed_extension.cc
@@ -0,0 +1,69 @@
+// 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
diff --git a/chrome/chrome_cleaner/chrome_utils/force_installed_extension.h b/chrome/chrome_cleaner/chrome_utils/force_installed_extension.h
new file mode 100644
index 0000000..b21fd769
--- /dev/null
+++ b/chrome/chrome_cleaner/chrome_utils/force_installed_extension.h
@@ -0,0 +1,55 @@
+// 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_
diff --git a/chrome/chrome_cleaner/parsers/parser_utils/BUILD.gn b/chrome/chrome_cleaner/parsers/parser_utils/BUILD.gn
new file mode 100644
index 0000000..b9025c8
--- /dev/null
+++ b/chrome/chrome_cleaner/parsers/parser_utils/BUILD.gn
@@ -0,0 +1,14 @@
+# 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",
+ ]
+}
diff --git a/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.cc b/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.cc
new file mode 100644
index 0000000..d218678
--- /dev/null
+++ b/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.cc
@@ -0,0 +1,33 @@
+// 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
diff --git a/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h b/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h
new file mode 100644
index 0000000..3070580
--- /dev/null
+++ b/chrome/chrome_cleaner/parsers/parser_utils/parse_tasks_remaining_counter.h
@@ -0,0 +1,30 @@
+// 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_
diff --git a/chrome/chrome_cleaner/test/BUILD.gn b/chrome/chrome_cleaner/test/BUILD.gn
index 37fc2f3..a4be344 100644
--- a/chrome/chrome_cleaner/test/BUILD.gn
+++ b/chrome/chrome_cleaner/test/BUILD.gn
@@ -71,6 +71,18 @@
]
}
+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") {
testonly = true
diff --git a/chrome/chrome_cleaner/test/test_extensions.cc b/chrome/chrome_cleaner/test/test_extensions.cc
new file mode 100644
index 0000000..83375fa
--- /dev/null
+++ b/chrome/chrome_cleaner/test/test_extensions.cc
@@ -0,0 +1,18 @@
+// 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
diff --git a/chrome/chrome_cleaner/test/test_extensions.h b/chrome/chrome_cleaner/test/test_extensions.h
new file mode 100644
index 0000000..03e54c8
--- /dev/null
+++ b/chrome/chrome_cleaner/test/test_extensions.h
@@ -0,0 +1,132 @@
+// 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_