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") {
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 @@ source_set("unittest_sources") {
"//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",
]
}
// 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_
......@@ -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 ExtensionPolicyRegistryEntry {
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 @@ void GetMasterPreferencesExtensions(JsonParserAPI* json_parser,
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_
// 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") {
]
}
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
......
// 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