Commit 0e6404f5 authored by Joe Mason's avatar Joe Mason Committed by Commit Bot

Add chrome_cleaner/components dir

Fix ICWYU error in chrome_cleaner/os/rebooter_api.h

R=csharp

Bug: 830892
Change-Id: I3f311bdd32e21b2901594a835aead483ae8d823b
Reviewed-on: https://chromium-review.googlesource.com/1227683
Commit-Queue: Joe Mason <joenotcharles@chromium.org>
Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Reviewed-by: default avatarChris Sharp <csharp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#595101}
parent db709282
......@@ -33,6 +33,7 @@ test("chrome_cleaner_unittests") {
# Tests from sub-directories.
"//chrome/chrome_cleaner/chrome_utils:unittest_sources",
"//chrome/chrome_cleaner/components:unittest_sources",
"//chrome/chrome_cleaner/http:unittest_sources",
"//chrome/chrome_cleaner/interfaces/typemaps:unittest_sources",
"//chrome/chrome_cleaner/ipc:unittest_sources",
......
# 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.
source_set("crx_file") {
sources = [
"crx_file.cc",
"crx_file.h",
]
deps = [
"//base",
"//components/chrome_cleaner/public/constants:constants",
]
}
source_set("components") {
sources = [
"component_api.h",
"component_manager.cc",
"component_manager.h",
"component_unpacker.cc",
"component_unpacker.h",
"recovery_component.cc",
"recovery_component.h",
"system_report_component.cc",
"system_report_component.h",
"system_restore_point_component.cc",
"system_restore_point_component.h",
]
deps = [
":crx_file",
"//base:base",
"//chrome/chrome_cleaner/chrome_utils:chrome_util_lib",
"//chrome/chrome_cleaner/chrome_utils:extensions_util_lib",
"//chrome/chrome_cleaner/constants:common_strings",
"//chrome/chrome_cleaner/constants:uws_id",
"//chrome/chrome_cleaner/http:http",
"//chrome/chrome_cleaner/http:http_status_codes",
"//chrome/chrome_cleaner/json_parser",
"//chrome/chrome_cleaner/logging:common",
"//chrome/chrome_cleaner/logging:scoped_timed_task_logger",
"//chrome/chrome_cleaner/logging/proto:shared_data_proto",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/pup_data:pup_data_base",
"//components/chrome_cleaner/public/constants:constants",
"//crypto:crypto",
"//third_party/zlib/google:zip",
"//url:url",
]
}
source_set("unittest_sources") {
testonly = true
sources = [
"component_manager_unittest.cc",
"recovery_component_unittest.cc",
"system_report_component_unittest.cc",
"system_restore_point_component_unittest.cc",
]
deps = [
":components",
"//base",
"//base/test:test_support",
"//chrome/chrome_cleaner/chrome_utils:chrome_util_lib",
"//chrome/chrome_cleaner/constants:common_strings",
"//chrome/chrome_cleaner/constants:uws_id",
"//chrome/chrome_cleaner/http:mock_http_agent_factory",
"//chrome/chrome_cleaner/json_parser",
"//chrome/chrome_cleaner/logging:cleaner_logging",
"//chrome/chrome_cleaner/logging/proto:chrome_cleaner_report_proto",
"//chrome/chrome_cleaner/os:cleaner_os",
"//chrome/chrome_cleaner/os:common_os",
"//chrome/chrome_cleaner/test:test_branding_header",
"//chrome/chrome_cleaner/test:test_component",
"//chrome/chrome_cleaner/test:test_pup_data",
"//chrome/chrome_cleaner/test:test_util",
"//components/chrome_cleaner/public/constants",
"//testing/gtest",
]
}
include_rules = [
"+third_party/zlib/google",
]
// 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_COMPONENTS_COMPONENT_API_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_API_H_
#include <vector>
#include "chrome/chrome_cleaner/constants/uws_id.h"
#include "components/chrome_cleaner/public/constants/result_codes.h"
namespace chrome_cleaner {
class RebooterAPI;
// This class is used to register components that are to be executed by the main
// controller either before the scanner or after the cleaner.
class ComponentAPI {
public:
virtual ~ComponentAPI() {}
// Called before the scanning starts. Components may need to consult the
// command line to identify if this is a post-reboot scan.
virtual void PreScan() = 0;
// Called after scanning. |found_pups| contains the ids of the found pups.
virtual void PostScan(const std::vector<UwSId>& found_pups) = 0;
// Called before cleanup starts.
virtual void PreCleanup() = 0;
// Called after the final cleanup. |result_code| can be used to identify if
// a post-reboot removal will be needed, or other status that might be useful
// to the component. |rebooter| is specified in case the component needs to
// specify command line arguments for the post-reboot run so that the
// |PostValidation| call below can take some decisions based on it. |rebooter|
// can be null in tests.
virtual void PostCleanup(ResultCode result_code, RebooterAPI* rebooter) = 0;
// Called after the post reboot run scanner is done validating the cleanup.
virtual void PostValidation(ResultCode result_code) = 0;
// Called when the final dialog window is closed. As above, |result_code| can
// be used to tell the result of the run (i.e., whether a cleanup was needed
// or not). Since this happens after users had a chance to look at the logs to
// decide whether they want to opt-out or not, any logging done here won't be
// uploaded back to Google.
virtual void OnClose(ResultCode result_code) = 0;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_API_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/components/component_manager.h"
#include <memory>
#include <utility>
#include "base/run_loop.h"
#include "base/task/post_task.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/chrome_cleaner/components/component_api.h"
namespace chrome_cleaner {
namespace {
// A set of factory functions to create the calls to code to be executed in
// worker threads. Since the code to loop through the components is the same
// for all calls, it was extracted in the |PostComponentTasks| method. So the
// public API methods must specify which function to call on the components.
// Since the component pointer must be the first argument, and we can't pre-pend
// args to bound calls, the public API methods sends a bound version of these
// factory functions so that |PostComponentTasks| can call them to get the
// closure by passing the component pointer to them. The component pointer
// must be the last argument to these factory functions so that the other
// arguments are pre-bound by the public API methods when they call
// |PostComponentTasks| (e.g., |found_pups|).
base::OnceClosure BindPreScan(ComponentAPI* component) {
return base::BindOnce(&ComponentAPI::PreScan, base::Unretained(component));
}
base::OnceClosure BindPostScan(const std::vector<UwSId>& found_pups,
ComponentAPI* component) {
return base::BindOnce(&ComponentAPI::PostScan, base::Unretained(component),
found_pups);
}
base::OnceClosure BindPreCleanup(ComponentAPI* component) {
return base::BindOnce(&ComponentAPI::PreCleanup, base::Unretained(component));
}
base::OnceClosure BindPostCleanup(ResultCode result_code,
RebooterAPI* rebooter,
ComponentAPI* component) {
return base::BindOnce(&ComponentAPI::PostCleanup, base::Unretained(component),
result_code, rebooter);
}
} // namespace
ComponentManager::ComponentManager(ComponentManagerDelegate* delegate)
: delegate_(delegate) {
DCHECK(delegate_);
}
ComponentManager::~ComponentManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Make sure |CloseAllComponents| was called. IT can't be called from here
// because it needs the result code. The components will still be released by
// the scoped vector, but their OnClose won't be called if
// |CloseAllComponents| wasn't called.
DCHECK(!cancelable_task_tracker_.HasTrackedTasks());
DCHECK(components_.empty());
DCHECK(done_callback_.is_null());
}
void ComponentManager::AddComponent(std::unique_ptr<ComponentAPI> component) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Components can't be added while tasks are in flight.
DCHECK(!cancelable_task_tracker_.HasTrackedTasks());
DCHECK(done_callback_.is_null());
DCHECK_EQ(0UL, num_tasks_pending_);
components_.push_back(std::move(component));
}
void ComponentManager::PreScan() {
// No task should be pending when any method from the ComponentsAPI are
// called.
DCHECK(done_callback_.is_null());
done_callback_ = base::BindOnce(&ComponentManagerDelegate::PreScanDone,
base::Unretained(delegate_));
PostComponentTasks(base::BindRepeating(&BindPreScan), "PreScan");
}
void ComponentManager::PostScan(const std::vector<UwSId>& found_pups) {
DCHECK(done_callback_.is_null());
done_callback_ = base::BindOnce(&ComponentManagerDelegate::PostScanDone,
base::Unretained(delegate_));
PostComponentTasks(base::BindRepeating(&BindPostScan, found_pups),
"PostScan");
}
void ComponentManager::PreCleanup() {
DCHECK(done_callback_.is_null());
done_callback_ = base::BindOnce(&ComponentManagerDelegate::PreCleanupDone,
base::Unretained(delegate_));
PostComponentTasks(base::BindRepeating(&BindPreCleanup), "PreCleanup");
}
void ComponentManager::PostCleanup(ResultCode result_code,
RebooterAPI* rebooter) {
DCHECK(done_callback_.is_null());
done_callback_ = base::BindOnce(&ComponentManagerDelegate::PostCleanupDone,
base::Unretained(delegate_));
PostComponentTasks(
base::BindRepeating(&BindPostCleanup, result_code, rebooter),
"PostCleanup");
}
void ComponentManager::PostValidation(ResultCode result_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!cancelable_task_tracker_.HasTrackedTasks());
DCHECK_EQ(0UL, num_tasks_pending_);
for (auto& component : components_)
component->PostValidation(result_code);
}
void ComponentManager::CloseAllComponents(ResultCode result_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (num_tasks_pending_ > 0) {
cancelable_task_tracker_.TryCancelAll();
while (cancelable_task_tracker_.HasTrackedTasks())
base::RunLoop().RunUntilIdle();
done_callback_.Reset();
} else {
DCHECK(!cancelable_task_tracker_.HasTrackedTasks());
}
for (auto& component : components_)
component->OnClose(result_code);
components_.clear();
}
void ComponentManager::PostComponentTasks(
const base::RepeatingCallback<base::OnceClosure(ComponentAPI*)>
component_task,
const char* method_name) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!cancelable_task_tracker_.HasTrackedTasks());
DCHECK_EQ(0UL, num_tasks_pending_);
if (components_.empty()) {
DCHECK(!done_callback_.is_null());
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(done_callback_));
return;
}
// Allow blocking operations, such as file operations, in the component task.
// This is allowed since there is no UI for it to block.
auto task_runner = base::CreateTaskRunnerWithTraits(base::MayBlock());
for (auto& component : components_) {
if (cancelable_task_tracker_.PostTaskAndReply(
task_runner.get(), FROM_HERE,
component_task.Run(component.get()), // This returns a Closure.
base::BindOnce(&ComponentManager::TaskCompleted,
base::Unretained(this))) ==
base::CancelableTaskTracker::kBadTaskId) {
PLOG(ERROR) << "Failed to run component's method: " << method_name;
} else {
// The reply task is never called synchronously, so it's OK to increment
// the number of tasks pending after the task was posted.
++num_tasks_pending_;
}
}
}
void ComponentManager::TaskCompleted() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(num_tasks_pending_, 0UL);
if (--num_tasks_pending_ == 0) {
// The callback must be run asynchronously so that the task tracker is not
// on the call stack anymore in cases where the callback ends up calling
// CloseAllComponents.
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(done_callback_));
}
}
} // 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_COMPONENTS_COMPONENT_MANAGER_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_MANAGER_H_
#include <vector>
#include "base/sequence_checker.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/chrome_cleaner/components/component_api.h"
#include "chrome/chrome_cleaner/constants/uws_id.h"
#include "chrome/chrome_cleaner/os/rebooter_api.h"
#include "components/chrome_cleaner/public/constants/result_codes.h"
namespace chrome_cleaner {
// A delegate API to be called back when all requested tasks are completed.
class ComponentManagerDelegate {
public:
virtual ~ComponentManagerDelegate() {}
virtual void PreScanDone() = 0;
virtual void PostScanDone() = 0;
virtual void PreCleanupDone() = 0;
virtual void PostCleanupDone() = 0;
};
// This class is used to register components that are to be executed by the main
// controller either before the scanner or after the cleaner.
class ComponentManager {
public:
// |delegate| must outlive the ComponentManager.
explicit ComponentManager(ComponentManagerDelegate* delegate);
// |CloseAllComponents| must be called before deleting the component manager.
// Self cleanup is NOT supported!
~ComponentManager();
// Add a new component. The ComponentManager takes ownership of the component.
// This method can't be called while there is an active call to the
// ComponentsAPI methods below that has not been completed by a call to
// |delegate_| yet.
void AddComponent(std::unique_ptr<ComponentAPI> component);
// All ComponentsAPI methods are duplicated here. Each of these calls run
// asynchronously and call their respective counterpart on |delegate_|. There
// can only be one of these calls active at a time. Callers must wait for
// |delegate_| to be called back before attempting other calls on this API.
void PreScan();
void PostScan(const std::vector<UwSId>& found_pups);
void PreCleanup();
void PostCleanup(ResultCode result_code, RebooterAPI* rebooter);
// This call is synchronous so doesn't have an equivalent done call on the
// delegate. TODO(csharp): This is confusing, fix it! b/23372645.
void PostValidation(ResultCode result_code);
// Call OnClose on all components and then destroy them. Any pending component
// tasks will be canceled and any pending threads will be joined. |delegate_|
// won't be called, even if there was a pending task. This must absolutely
// be called before the object is destroyed.
void CloseAllComponents(ResultCode result_code);
// Return the number of pending tasks. Mainly used by tests.
size_t num_tasks_pending() const { return num_tasks_pending_; }
private:
// Common code to post tasks to the worker threads. |component_task| is called
// to create the closure that will run in the worker threads for each
// component. |method_name| is used for logging.
void PostComponentTasks(const base::RepeatingCallback<
base::OnceClosure(ComponentAPI*)> component_task,
const char* method_name);
// Called back on the main thread when a task is completed. When the last task
// completes, run |done_callback_| asynchronously to avoid re-entrance.
void TaskCompleted();
// The components that are to be called before / after the scan / cleanup.
std::vector<std::unique_ptr<ComponentAPI>> components_;
// The task tracker that can be used to cancel pending tasks.
base::CancelableTaskTracker cancelable_task_tracker_;
SEQUENCE_CHECKER(sequence_checker_);
// The delegate to be called when tasks are completed.
ComponentManagerDelegate* delegate_;
// The closure to callback once all tasks completed. This always contains a
// call to |delegate_|.
base::OnceClosure done_callback_;
// The number of pending tasks that have not called |TaskCompleted| yet.
size_t num_tasks_pending_ = 0;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_MANAGER_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/components/component_manager.h"
#include "base/run_loop.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/chrome_cleaner/components/component_api.h"
#include "chrome/chrome_cleaner/test/test_component.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
const UwSId kFakePupId = 42;
// This delegate validates that calls were made, and also interrupt the
// currently running message loop, waiting for the delegate methods to be
// called.
class TestComponentManagerDelegate : public ComponentManagerDelegate {
public:
struct Calls {
bool pre_scan = false;
bool post_scan = false;
bool pre_cleanup = false;
bool post_cleanup = false;
};
explicit TestComponentManagerDelegate(Calls* calls) : calls_(calls) {}
void set_quit_closure(base::OnceClosure quit_closure) {
quit_closure_ = std::move(quit_closure);
}
// ComponentManagerDelegate
void PreScanDone() override {
calls_->pre_scan = true;
if (quit_closure_)
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(quit_closure_));
}
void PostScanDone() override {
calls_->post_scan = true;
if (quit_closure_)
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(quit_closure_));
}
void PreCleanupDone() override {
calls_->pre_cleanup = true;
if (quit_closure_)
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(quit_closure_));
}
void PostCleanupDone() override {
calls_->post_cleanup = true;
if (quit_closure_)
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(quit_closure_));
}
private:
Calls* calls_;
base::OnceClosure quit_closure_;
};
} // namespace
TEST(ComponentManagerTest, Empty) {
base::test::ScopedTaskEnvironment scoped_task_environment;
TestComponentManagerDelegate::Calls calls;
TestComponentManagerDelegate delegate(&calls);
ComponentManager component_manager(&delegate);
base::RunLoop run_loop1;
delegate.set_quit_closure(run_loop1.QuitWhenIdleClosure());
component_manager.PreScan();
run_loop1.Run();
EXPECT_TRUE(calls.pre_scan);
calls.pre_scan = false;
EXPECT_FALSE(calls.post_scan);
EXPECT_FALSE(calls.pre_cleanup);
EXPECT_FALSE(calls.post_cleanup);
base::RunLoop run_loop2;
delegate.set_quit_closure(run_loop2.QuitWhenIdleClosure());
component_manager.PostScan(std::vector<UwSId>());
run_loop2.Run();
EXPECT_FALSE(calls.pre_scan);
EXPECT_TRUE(calls.post_scan);
calls.post_scan = false;
EXPECT_FALSE(calls.pre_cleanup);
EXPECT_FALSE(calls.post_cleanup);
base::RunLoop run_loop3;
delegate.set_quit_closure(run_loop3.QuitWhenIdleClosure());
component_manager.PreCleanup();
run_loop3.Run();
EXPECT_FALSE(calls.pre_scan);
EXPECT_FALSE(calls.post_scan);
EXPECT_TRUE(calls.pre_cleanup);
calls.pre_cleanup = false;
EXPECT_FALSE(calls.post_cleanup);
base::RunLoop run_loop4;
delegate.set_quit_closure(run_loop4.QuitWhenIdleClosure());
component_manager.PostCleanup(RESULT_CODE_SUCCESS, nullptr);
run_loop4.Run();
EXPECT_FALSE(calls.pre_scan);
EXPECT_FALSE(calls.post_scan);
EXPECT_FALSE(calls.pre_cleanup);
EXPECT_TRUE(calls.post_cleanup);
// Nothing to validate, just make sure it can be called.
component_manager.CloseAllComponents(RESULT_CODE_FAILED);
}
TEST(ComponentManagerTest, All) {
base::test::ScopedTaskEnvironment scoped_task_environment;
TestComponentManagerDelegate::Calls delegate_calls;
TestComponentManagerDelegate delegate(&delegate_calls);
ComponentManager component_manager(&delegate);
TestComponent::Calls component_calls;
component_manager.AddComponent(
std::make_unique<TestComponent>(&component_calls));
base::RunLoop run_loop1;
delegate.set_quit_closure(run_loop1.QuitWhenIdleClosure());
component_manager.PreScan();
run_loop1.Run();
EXPECT_TRUE(delegate_calls.pre_scan);
EXPECT_TRUE(component_calls.pre_scan);
base::RunLoop run_loop2;
delegate.set_quit_closure(run_loop2.QuitWhenIdleClosure());
std::vector<UwSId> pup_ids;
pup_ids.push_back(kFakePupId);
component_manager.PostScan(pup_ids);
run_loop2.Run();
EXPECT_TRUE(delegate_calls.post_scan);
EXPECT_TRUE(component_calls.post_scan);
ASSERT_EQ(1UL, component_calls.post_scan_found_pups.size());
EXPECT_EQ(kFakePupId, component_calls.post_scan_found_pups[0]);
base::RunLoop run_loop3;
delegate.set_quit_closure(run_loop3.QuitWhenIdleClosure());
component_manager.PreCleanup();
run_loop3.Run();
EXPECT_TRUE(delegate_calls.pre_cleanup);
EXPECT_TRUE(component_calls.pre_cleanup);
base::RunLoop run_loop4;
delegate.set_quit_closure(run_loop4.QuitWhenIdleClosure());
component_manager.PostCleanup(RESULT_CODE_SUCCESS, nullptr);
run_loop4.Run();
EXPECT_TRUE(delegate_calls.post_cleanup);
EXPECT_TRUE(component_calls.post_cleanup);
EXPECT_EQ(RESULT_CODE_SUCCESS, component_calls.result_code);
component_calls.result_code = RESULT_CODE_INVALID;
component_manager.PostValidation(RESULT_CODE_FAILED);
EXPECT_TRUE(component_calls.post_validation);
EXPECT_EQ(RESULT_CODE_FAILED, component_calls.result_code);
component_manager.CloseAllComponents(RESULT_CODE_CANCELED);
EXPECT_TRUE(component_calls.on_close);
EXPECT_TRUE(component_calls.destroyed);
EXPECT_EQ(RESULT_CODE_CANCELED, component_calls.result_code);
}
TEST(ComponentManagerTest, Interrupt) {
base::test::ScopedTaskEnvironment scoped_task_environment;
TestComponentManagerDelegate::Calls delegate_calls;
TestComponentManagerDelegate delegate(&delegate_calls);
ComponentManager component_manager(&delegate);
TestComponent::Calls component_calls;
for (size_t i = 0; i < 1000; ++i) {
component_manager.AddComponent(
std::make_unique<TestComponent>(&component_calls));
}
component_manager.PreScan();
EXPECT_FALSE(delegate_calls.pre_scan);
component_manager.CloseAllComponents(RESULT_CODE_INVALID);
EXPECT_FALSE(delegate_calls.pre_scan);
// We rely on test harness leak detector to make sure TestComponents were
// properly destroyed.
EXPECT_GT(component_manager.num_tasks_pending(), 0UL);
}
} // 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.
// Stolen from chrome/browser/component_updater/component_unpacker.cc
#include "chrome/chrome_cleaner/components/component_unpacker.h"
#include <memory>
#include <vector>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "chrome/chrome_cleaner/components/crx_file.h"
#include "chrome/chrome_cleaner/os/file_path_sanitization.h"
#include "crypto/secure_hash.h"
#include "crypto/signature_verifier.h"
#include "third_party/zlib/google/zip.h"
using crypto::SecureHash;
namespace chrome_cleaner {
namespace {
// This class makes sure that the CRX digital signature is valid and well
// formed.
class CRXValidator {
public:
explicit CRXValidator(FILE* crx_file) : valid_(false) {
CrxFile::Header header;
size_t len = fread(&header, 1, sizeof(header), crx_file);
if (len < sizeof(header))
return;
CrxFile::Error error;
std::unique_ptr<CrxFile> crx(CrxFile::Parse(header, &error));
if (!crx.get())
return;
DCHECK(!CrxFile::HeaderIsDelta(header));
std::vector<uint8_t> key(header.key_size);
len = fread(&key[0], sizeof(uint8_t), header.key_size, crx_file);
if (len < header.key_size)
return;
std::vector<uint8_t> signature(header.signature_size);
len =
fread(&signature[0], sizeof(uint8_t), header.signature_size, crx_file);
if (len < header.signature_size)
return;
crypto::SignatureVerifier verifier;
if (!verifier.VerifyInit(crypto::SignatureVerifier::RSA_PKCS1_SHA1,
signature, key)) {
// Signature verification initialization failed. This is most likely
// caused by a public key in the wrong format (should encode algorithm).
return;
}
const size_t kBufSize = 8 * 1024;
std::vector<uint8_t> buf(kBufSize);
while ((len = fread(buf.data(), 1, kBufSize, crx_file)) > 0)
verifier.VerifyUpdate(buf);
if (!verifier.VerifyFinal())
return;
public_key_.swap(key);
valid_ = true;
}
bool valid() const { return valid_; }
const std::vector<uint8_t>& public_key() const { return public_key_; }
private:
bool valid_;
std::vector<uint8_t> public_key_;
};
} // namespace
ComponentUnpacker::ComponentUnpacker(const std::vector<uint8_t>& pk_hash,
const base::FilePath& path)
: pk_hash_(pk_hash), path_(path) {}
bool ComponentUnpacker::Unpack(const base::FilePath& ouput_folder) {
return Verify() && zip::Unzip(path_, ouput_folder);
}
bool ComponentUnpacker::Verify() {
if (pk_hash_.empty() || path_.empty())
return false;
// First, validate the CRX header and signature. As of today this is SHA1 with
// RSA 1024.
base::ScopedFILE file(base::OpenFile(path_, "rb"));
if (!file.get())
return false;
CRXValidator validator(file.get());
file.reset();
if (!validator.valid())
return false;
// File is valid and the digital signature matches. Now make sure the public
// key hash matches the expected hash. If they do we fully trust this CRX.
uint8_t hash[32] = {};
std::unique_ptr<SecureHash> sha256(SecureHash::Create(SecureHash::SHA256));
sha256->Update(&(validator.public_key()[0]), validator.public_key().size());
sha256->Finish(hash, base::size(hash));
if (!std::equal(pk_hash_.begin(), pk_hash_.end(), hash)) {
LOG(WARNING) << "Hash mismatch: " << SanitizePath(path_);
return false;
}
return true;
}
ComponentUnpacker::~ComponentUnpacker() {}
} // 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.
// Stolen from chrome/browser/component_updater/component_unpacker.h
#ifndef CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_UNPACKER_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_UNPACKER_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/files/file_path.h"
namespace chrome_cleaner {
// In charge of unpacking the component CRX package and verifying that it is
// well formed and the cryptographic signature is correct.
//
// This class is inspired by and overlaps with code in the extension's
// SandboxedUnpacker.
// The main differences are:
// - The public key hash is full SHA256.
// - Does not use a sandboxed unpacker. A valid component is fully trusted.
class ComponentUnpacker {
public:
// Constructs an unpacker for a specific component unpacking operation.
// |pk_hash| is the expected public key SHA256 hash. |path| is the current
// location of the CRX.
ComponentUnpacker(const std::vector<uint8_t>& pk_hash,
const base::FilePath& path);
virtual ~ComponentUnpacker();
// Unpack the file to the provided folder. Return true on success.
bool Unpack(const base::FilePath& ouput_folder);
private:
// The first step of unpacking is to verify the file. Return false if an
// error is encountered, the file is malformed, or the file is incorrectly
// signed.
bool Verify();
std::vector<uint8_t> pk_hash_;
base::FilePath path_;
DISALLOW_COPY_AND_ASSIGN(ComponentUnpacker);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_COMPONENT_UNPACKER_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.
//
// Stolen from extensions/common/crx_file.cc
// TODO(crbug.com/889575): The above file no longer exists. It's been
// refactored and now lives in components/crx_file. Should pull in that
// component directly.
#include "chrome/chrome_cleaner/components/crx_file.h"
#include "base/memory/ptr_util.h"
namespace chrome_cleaner {
namespace {
// The current version of the crx format.
static const uint32_t kCurrentVersion = 2;
// The current version of the crx diff format.
static const uint32_t kCurrentDiffVersion = 0;
// The maximum size the crx parser will tolerate for a public key.
static const uint32_t kMaxPublicKeySize = 1 << 16;
// The maximum size the crx parser will tolerate for a signature.
static const uint32_t kMaxSignatureSize = 1 << 16;
} // namespace
// The magic string embedded in the header.
const char kCrxFileHeaderMagic[] = "Cr24";
const char kCrxDiffFileHeaderMagic[] = "CrOD";
std::unique_ptr<CrxFile> CrxFile::Parse(const CrxFile::Header& header,
CrxFile::Error* error) {
if (HeaderIsValid(header, error))
return base::WrapUnique<CrxFile>(new CrxFile(header));
return nullptr;
}
std::unique_ptr<CrxFile> CrxFile::Create(const uint32_t key_size,
const uint32_t signature_size,
CrxFile::Error* error) {
CrxFile::Header header;
memcpy(&header.magic, kCrxFileHeaderMagic, kCrxFileHeaderMagicSize);
header.version = kCurrentVersion;
header.key_size = key_size;
header.signature_size = signature_size;
if (HeaderIsValid(header, error))
return base::WrapUnique<CrxFile>(new CrxFile(header));
return nullptr;
}
CrxFile::CrxFile(const Header& header) : header_(header) {}
bool CrxFile::HeaderIsDelta(const CrxFile::Header& header) {
return !strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic));
}
bool CrxFile::HeaderIsValid(const CrxFile::Header& header,
CrxFile::Error* error) {
bool valid = false;
bool diffCrx = false;
if (!strncmp(kCrxDiffFileHeaderMagic, header.magic, sizeof(header.magic)))
diffCrx = true;
if (strncmp(kCrxFileHeaderMagic, header.magic, sizeof(header.magic)) &&
!diffCrx)
*error = kWrongMagic;
else if (header.version != kCurrentVersion &&
!(diffCrx && header.version == kCurrentDiffVersion))
*error = kInvalidVersion;
else if (header.key_size > kMaxPublicKeySize)
*error = kInvalidKeyTooLarge;
else if (header.key_size == 0)
*error = kInvalidKeyTooSmall;
else if (header.signature_size > kMaxSignatureSize)
*error = kInvalidSignatureTooLarge;
else if (header.signature_size == 0)
*error = kInvalidSignatureTooSmall;
else
valid = true;
return valid;
}
} // 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.
// Stolen from extensions/common/crx_file.h
#ifndef CHROME_CHROME_CLEANER_COMPONENTS_CRX_FILE_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_CRX_FILE_H_
#include <stdint.h>
#include <sys/types.h>
#include <memory>
namespace chrome_cleaner {
// CRX files have a header that includes a magic key, version number, and
// some signature sizing information. Use CrxFile object to validate whether
// the header is valid or not.
class CrxFile {
public:
// The size of the magic character sequence at the beginning of each crx
// file, in bytes. This should be a multiple of 4.
static const size_t kCrxFileHeaderMagicSize = 4;
// This header is the first data at the beginning of an extension. Its
// contents are purposely 32-bit aligned so that it can just be slurped into
// a struct without manual parsing.
struct Header {
char magic[kCrxFileHeaderMagicSize];
uint32_t version;
uint32_t key_size; // The size of the public key, in bytes.
uint32_t signature_size; // The size of the signature, in bytes.
// An ASN.1-encoded PublicKeyInfo structure follows.
// The signature follows.
};
enum Error {
kWrongMagic,
kInvalidVersion,
kInvalidKeyTooLarge,
kInvalidKeyTooSmall,
kInvalidSignatureTooLarge,
kInvalidSignatureTooSmall,
};
// Construct a new CRX file header object with bytes of a header
// read from a CRX file. If a null std::unique_ptr is returned, |error|
// contains an error code with additional information.
static std::unique_ptr<CrxFile> Parse(const Header& header, Error* error);
// Construct a new header for the given key and signature sizes.
// Returns a null std::unique_ptr if erroneous values of |key_size| and/or
// |signature_size| are provided. |error| contains an error code with
// additional information.
// Use this constructor and then .header() to obtain the Header
// for writing out to a CRX file.
static std::unique_ptr<CrxFile> Create(const uint32_t key_size,
const uint32_t signature_size,
Error* error);
// Returns the header structure for writing out to a CRX file.
const Header& header() const { return header_; }
// Checks a valid |header| to determine whether or not the CRX represents a
// differential CRX.
static bool HeaderIsDelta(const Header& header);
private:
Header header_;
// Constructor is private. Clients should use static factory methods above.
explicit CrxFile(const Header& header);
// Checks the |header| for validity and returns true if the values are valid.
// If false is returned, more detailed error code is returned in |error|.
static bool HeaderIsValid(const Header& header, Error* error);
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_CRX_FILE_H_
This diff is collapsed.
// 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_COMPONENTS_RECOVERY_COMPONENT_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_RECOVERY_COMPONENT_H_
#include <memory>
#include <vector>
#include "base/files/scoped_temp_dir.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "chrome/chrome_cleaner/components/component_api.h"
#include "url/gurl.h"
namespace chrome_cleaner {
class HttpAgentFactory;
// This class starts downloading the recovery component before the scanner
// starts, and it runs it after the cleaner completed, except if a reboot is
// pending.
class RecoveryComponent : public ComponentAPI {
public:
// Return true when the recovery component is available for the current build
// and command line flags combination.
static bool IsAvailable();
RecoveryComponent();
~RecoveryComponent() override = default;
// Replace the HttpAgent factory with a new factory. Passing in an empty
// factory (nullptr) will reset to the default factory. Exposed so tests can
// create mock HttpAgent objects. This method is not thread-safe.
static void SetHttpAgentFactoryForTesting(const HttpAgentFactory* factory);
// ComponentAPI methods.
void PreScan() override;
void PostScan(const std::vector<UwSId>& found_pups) override;
void PreCleanup() override;
void PostCleanup(ResultCode result_code, RebooterAPI* rebooter) override;
void PostValidation(ResultCode result_code) override;
void OnClose(ResultCode result_code) override;
// Exposed for testing. The size of the buffer used when reading data from the
// response.
static const size_t kReadDataFromResponseBufferSize = 8192;
protected:
// Try to run the recovery component if it's ready, or wait for it. Protected
// virtual to test the logic of the public methods.
virtual void Run();
// A protected abstraction of the unpacking so that tests can override it.
virtual void UnpackComponent(const base::FilePath& crx_file);
// Return when |FetchOnIOThread| is done. Mainly used by tests.
void WaitForDoneExpandingCrxForTest() { done_expanding_crx_.Wait(); }
private:
// The fetch must be done on the IO thread as it's synchronous.
void FetchOnIOThread();
// Thread on which the recovery component is fetched, and the CRX is unpacked.
base::Thread recovery_io_thread_;
// Where the downloaded CRX was unzipped.
base::ScopedTempDir component_path_;
// Signaled when the expansion of the crx is complete.
base::WaitableEvent done_expanding_crx_;
// To check whether the component already ran or not.
bool ran_ = false;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_RECOVERY_COMPONENT_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/components/recovery_component.h"
#include <memory>
#include <string>
#include <utility>
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop.h"
#include "base/test/test_simple_task_runner.h"
#include "chrome/chrome_cleaner/http/mock_http_agent_factory.h"
#include "chrome/chrome_cleaner/test/test_pup_data.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_cleaner {
namespace {
const UwSId kRemovableUwSId = 42;
const UwSId kReportOnlyUwSId = 21;
} // namespace
class TestRecoveryComponent : public RecoveryComponent {
public:
TestRecoveryComponent() = default;
using RecoveryComponent::WaitForDoneExpandingCrxForTest;
bool run_called() const { return run_called_; }
bool unpack_component_called() const { return unpack_component_called_; }
const std::string& crx_file_contents() const { return crx_file_contents_; }
protected:
// RecoveryComponent.
void Run() override { run_called_ = true; }
void UnpackComponent(const base::FilePath& crx_file) override {
unpack_component_called_ = true;
if (base::PathExists(crx_file)) {
ASSERT_TRUE(base::ReadFileToString(crx_file, &crx_file_contents_));
}
}
private:
bool run_called_{false};
bool unpack_component_called_{false};
std::string crx_file_contents_;
};
class RecoveryComponentTest : public testing::Test {
public:
void SetUp() override {
RecoveryComponent::SetHttpAgentFactoryForTesting(factory_.get());
}
void TearDown() override {
RecoveryComponent::SetHttpAgentFactoryForTesting(nullptr);
}
protected:
RecoveryComponentTest() : task_runner_(new base::TestSimpleTaskRunner) {}
// A message loop is needed for the current task runner to be available.
base::MessageLoopForUI ui_message_loop_;
// The recover component under test. This declaration must be after the
// |ui_message_loop_| because the |RecoveryComponent| constructor needs
// a reference to the current run loop.
TestRecoveryComponent recovery_component_;
// A task runner and a URL fetcher factory are necessary to test URL requests.
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
MockHttpAgentConfig config_;
std::unique_ptr<HttpAgentFactory> factory_{
std::make_unique<MockHttpAgentFactory>(&config_)};
};
TEST_F(RecoveryComponentTest, Success) {
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
config_.AddCalls(calls);
recovery_component_.PreScan();
recovery_component_.WaitForDoneExpandingCrxForTest();
EXPECT_TRUE(recovery_component_.unpack_component_called());
}
TEST_F(RecoveryComponentTest, Failure) {
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
calls.request_succeeds = false;
config_.AddCalls(calls);
recovery_component_.PreScan();
recovery_component_.WaitForDoneExpandingCrxForTest();
EXPECT_FALSE(recovery_component_.unpack_component_called());
}
TEST_F(RecoveryComponentTest, CrxDataSavedToDisk) {
const std::string test_data = "test data";
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
calls.read_data_result = test_data;
config_.AddCalls(calls);
recovery_component_.PreScan();
recovery_component_.WaitForDoneExpandingCrxForTest();
EXPECT_TRUE(recovery_component_.unpack_component_called());
EXPECT_EQ(test_data, recovery_component_.crx_file_contents());
}
TEST_F(RecoveryComponentTest, CrxDataPartiallySavedToDisk) {
// Ensure the buffer is large enough, so a second buffer needs to be read.
const std::string test_data =
std::string(RecoveryComponent::kReadDataFromResponseBufferSize + 1, 'x');
MockHttpAgentConfig::Calls calls(HttpStatus::kOk);
calls.read_data_result = test_data;
calls.read_data_success_sequence.push_back(true);
calls.read_data_success_sequence.push_back(false);
config_.AddCalls(calls);
recovery_component_.PreScan();
recovery_component_.WaitForDoneExpandingCrxForTest();
EXPECT_FALSE(recovery_component_.unpack_component_called());
}
TEST_F(RecoveryComponentTest, RunCalledForRemovablePUP) {
const UwSId kRemovableUwSId = 42;
std::vector<UwSId> found_pups;
TestPUPData test_pup_data;
// When a removable PUP was found, the post scan shouldn't run, it should
// wait for the post-cleanup.
test_pup_data.AddPUP(kRemovableUwSId, PUPData::FLAGS_ACTION_REMOVE, nullptr,
PUPData::kMaxFilesToRemoveSmallUwS);
found_pups.push_back(kRemovableUwSId);
recovery_component_.PostScan(found_pups);
EXPECT_FALSE(recovery_component_.run_called());
recovery_component_.PostCleanup(RESULT_CODE_SUCCESS, nullptr);
EXPECT_TRUE(recovery_component_.run_called());
}
TEST_F(RecoveryComponentTest, RunCalledForNoPUPs) {
std::vector<UwSId> found_pups;
TestPUPData test_pup_data;
// When no PUPs are found, the post scan should run, but not the post-cleanup.
found_pups.clear();
recovery_component_.PostScan(found_pups);
EXPECT_TRUE(recovery_component_.run_called());
}
TEST_F(RecoveryComponentTest, RunCalledForReportOnlyUwS) {
std::vector<UwSId> found_pups;
TestPUPData test_pup_data;
// When only report only PUPs are found, the post scan should run, but not the
// post-cleanup.
test_pup_data.AddPUP(kReportOnlyUwSId, PUPData::FLAGS_NONE, nullptr,
PUPData::kMaxFilesToRemoveSmallUwS);
found_pups.push_back(kReportOnlyUwSId);
recovery_component_.PostScan(found_pups);
EXPECT_TRUE(recovery_component_.run_called());
}
TEST_F(RecoveryComponentTest, RunNotCalledPreReboot) {
std::vector<UwSId> found_pups;
TestPUPData test_pup_data;
// And finally, when a reboot will be needed, none should call run.
test_pup_data.AddPUP(kRemovableUwSId, PUPData::FLAGS_ACTION_REMOVE, nullptr,
PUPData::kMaxFilesToRemoveSmallUwS);
found_pups.push_back(kRemovableUwSId);
recovery_component_.PostScan(found_pups);
EXPECT_FALSE(recovery_component_.run_called());
recovery_component_.PostCleanup(RESULT_CODE_PENDING_REBOOT, nullptr);
EXPECT_FALSE(recovery_component_.run_called());
}
} // namespace chrome_cleaner
This diff is collapsed.
// 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_COMPONENTS_SYSTEM_REPORT_COMPONENT_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_SYSTEM_REPORT_COMPONENT_H_
#include <vector>
#include "chrome/chrome_cleaner/components/component_api.h"
#include "chrome/chrome_cleaner/json_parser/json_parser_api.h"
namespace chrome_cleaner {
// This class manages the production of a system information report.
class SystemReportComponent : public ComponentAPI {
public:
explicit SystemReportComponent(JsonParserAPI* json_parser);
// ComponentAPI methods.
void PreScan() override;
void PostScan(const std::vector<UwSId>& found_pups) override;
void PreCleanup() override;
void PostCleanup(ResultCode result_code, RebooterAPI* rebooter) override;
void PostValidation(ResultCode result_code) override;
void OnClose(ResultCode result_code) override;
void CreateFullSystemReport();
// Only exposed for tests.
bool created_report() { return created_report_; }
private:
bool created_report_;
JsonParserAPI* json_parser_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_SYSTEM_REPORT_COMPONENT_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/components/system_restore_point_component.h"
#include <stdint.h>
#include <windows.h>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/win/registry.h"
#include "base/win/windows_version.h"
namespace {
const wchar_t* kSystemRestoreKey =
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\SystemRestore";
const wchar_t* kSystemRestoreFrequencyWin8 =
L"SystemRestorePointCreationFrequency";
const int64_t kInvalidSequenceNumber = -1;
// Name of the restore point library.
const wchar_t kRestorePointClientLibrary[] = L"srclient.dll";
} // namespace
namespace chrome_cleaner {
SystemRestorePointComponent::SystemRestorePointComponent(
const base::string16& product_fullname)
: set_restore_point_info_fn_(nullptr),
remove_restore_point_info_fn_(nullptr),
sequence_number_(kInvalidSequenceNumber),
product_fullname_(product_fullname) {
base::NativeLibraryLoadError error;
srclient_dll_ = base::LoadNativeLibrary(
base::FilePath(kRestorePointClientLibrary), &error);
if (!srclient_dll_) {
PLOG(ERROR) << "Failed to load the restore point library, error="
<< error.code;
} else {
// Force the DLL to stay loaded until program termination.
base::NativeLibrary module_handle = nullptr;
if (!::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
kRestorePointClientLibrary, &module_handle)) {
PLOG(ERROR) << "Failed to pin the restore point library.";
return;
}
DCHECK_EQ(srclient_dll_, module_handle);
set_restore_point_info_fn_ = reinterpret_cast<SetRestorePointInfoWFn>(
base::GetFunctionPointerFromNativeLibrary(srclient_dll_,
"SRSetRestorePointW"));
remove_restore_point_info_fn_ = reinterpret_cast<RemoveRestorePointFn>(
base::GetFunctionPointerFromNativeLibrary(srclient_dll_,
"SRRemoveRestorePoint"));
}
if (!set_restore_point_info_fn_)
LOG(ERROR) << "Unable to find System Restore Point library.";
}
void SystemRestorePointComponent::PreScan() {}
void SystemRestorePointComponent::PostScan(
const std::vector<UwSId>& found_pups) {}
void SystemRestorePointComponent::PreCleanup() {
// It is not ok to call SRSetRestorePoint recursively. Make sure this is the
// first call.
DCHECK_EQ(sequence_number_, kInvalidSequenceNumber);
if (!set_restore_point_info_fn_)
return;
// On Windows8, a registry value needs to be created in order for restore
// points to be deterministically created. Attempt to create this value, but
// continue with the restore point anyway even if doing so fails. See
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa378941.aspx for
// more information.
if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
base::win::RegKey system_restore_key(HKEY_LOCAL_MACHINE, kSystemRestoreKey,
KEY_SET_VALUE | KEY_QUERY_VALUE);
if (system_restore_key.Valid() &&
!system_restore_key.HasValue(kSystemRestoreFrequencyWin8)) {
system_restore_key.WriteValue(kSystemRestoreFrequencyWin8,
static_cast<DWORD>(0));
}
}
// Take a system restore point before doing anything else.
RESTOREPOINTINFO restore_point_spec = {};
STATEMGRSTATUS state_manager_status = {};
restore_point_spec.dwEventType = BEGIN_SYSTEM_CHANGE;
// MSDN documents few of the available values here. Use APPLICATION_INSTALL
// since that seems closest from the documented ones. The header file
// mentions a CHECKPOINT type which looks interesting, but let's stay on the
// beaten path for now.
restore_point_spec.dwRestorePtType = APPLICATION_INSTALL;
restore_point_spec.llSequenceNumber = 0;
wcsncpy(restore_point_spec.szDescription, product_fullname_.c_str(),
base::size(restore_point_spec.szDescription));
if (set_restore_point_info_fn_(&restore_point_spec, &state_manager_status)) {
sequence_number_ = state_manager_status.llSequenceNumber;
} else {
if (state_manager_status.nStatus == ERROR_SERVICE_DISABLED) {
LOG(WARNING) << "System Restore is disabled.";
} else {
LOG(ERROR) << "Failed to start System Restore service, error: "
<< state_manager_status.nStatus;
}
}
}
void SystemRestorePointComponent::PostCleanup(ResultCode result_code,
RebooterAPI* rebooter) {
if (!set_restore_point_info_fn_ || sequence_number_ == kInvalidSequenceNumber)
return;
RESTOREPOINTINFO restore_point_spec = {};
STATEMGRSTATUS state_manager_status = {};
restore_point_spec.dwEventType = END_SYSTEM_CHANGE;
restore_point_spec.llSequenceNumber = sequence_number_;
if (result_code == RESULT_CODE_SUCCESS ||
result_code == RESULT_CODE_PENDING_REBOOT ||
result_code == RESULT_CODE_POST_REBOOT_SUCCESS ||
result_code == RESULT_CODE_POST_REBOOT_ELEVATION_DENIED) {
// Success! For now... Commit the restore point.
restore_point_spec.dwRestorePtType = APPLICATION_INSTALL;
if (!SetRestorePointInfoWrapper(&restore_point_spec,
&state_manager_status)) {
LOG(ERROR) << "Failed to commit System Restore point, error: "
<< state_manager_status.nStatus;
}
} else {
// No mutations were made, either because we found nothing, the user
// canceled or an error occurred. Abort the restore point.
restore_point_spec.dwRestorePtType = CANCELLED_OPERATION;
if (!SetRestorePointInfoWrapper(&restore_point_spec,
&state_manager_status)) {
LOG(ERROR) << "Failed to cancel System Restore point, error: "
<< state_manager_status.nStatus;
}
// I have observed, at least on Win8, that cancelling the restore point
// still leaves it behind, so explicitly remove it as well.
if (remove_restore_point_info_fn_ &&
!RemoveRestorePointWrapper(sequence_number_)) {
LOG(ERROR) << "Failed to remove cancelled Restore point.";
}
}
}
void SystemRestorePointComponent::PostValidation(ResultCode result_code) {}
void SystemRestorePointComponent::OnClose(ResultCode result_code) {}
bool SystemRestorePointComponent::IsLoadedRestorePointLibrary() {
base::NativeLibrary module_handle = nullptr;
if (!::GetModuleHandleExW(0, kRestorePointClientLibrary, &module_handle)) {
PLOG(ERROR) << "Restore point library no longer present.";
return false;
}
DCHECK_EQ(srclient_dll_, module_handle);
return true;
}
bool SystemRestorePointComponent::SetRestorePointInfoWrapper(
PRESTOREPOINTINFOW info,
PSTATEMGRSTATUS status) {
if (!set_restore_point_info_fn_ ||
!SystemRestorePointComponent::IsLoadedRestorePointLibrary()) {
return false;
}
return set_restore_point_info_fn_(info, status) != FALSE;
}
bool SystemRestorePointComponent::RemoveRestorePointWrapper(DWORD sequence) {
if (!remove_restore_point_info_fn_ ||
!SystemRestorePointComponent::IsLoadedRestorePointLibrary()) {
return false;
}
return remove_restore_point_info_fn_(sequence) != 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_COMPONENTS_SYSTEM_RESTORE_POINT_COMPONENT_H_
#define CHROME_CHROME_CLEANER_COMPONENTS_SYSTEM_RESTORE_POINT_COMPONENT_H_
#include <windows.h>
#include <srrestoreptapi.h>
#include <stdint.h>
#include <vector>
#include "base/native_library.h"
#include "base/strings/string16.h"
#include "chrome/chrome_cleaner/components/component_api.h"
namespace chrome_cleaner {
// This class manages the setting and clearing of a system restore point.
class SystemRestorePointComponent : public ComponentAPI {
public:
explicit SystemRestorePointComponent(const base::string16& product_fullname);
// ComponentAPI methods.
void PreScan() override;
void PostScan(const std::vector<UwSId>& found_pups) override;
void PreCleanup() override;
void PostCleanup(ResultCode result_code, RebooterAPI* rebooter) override;
void PostValidation(ResultCode result_code) override;
void OnClose(ResultCode result_code) override;
protected:
// The below typedefs and members have protected visibility for testing.
typedef BOOL(WINAPI* SetRestorePointInfoWFn)(PRESTOREPOINTINFOW,
PSTATEMGRSTATUS);
typedef BOOL(WINAPI* RemoveRestorePointFn)(DWORD);
SetRestorePointInfoWFn set_restore_point_info_fn_;
RemoveRestorePointFn remove_restore_point_info_fn_;
private:
// Perform internal sanity checks to validate the presence of the restore
// point library.
bool IsLoadedRestorePointLibrary();
// Safe wrappers to dynamic calls to the restore point library.
bool SetRestorePointInfoWrapper(PRESTOREPOINTINFOW, PSTATEMGRSTATUS);
bool RemoveRestorePointWrapper(DWORD);
base::NativeLibrary srclient_dll_;
int64_t sequence_number_;
base::string16 product_fullname_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_COMPONENTS_SYSTEM_RESTORE_POINT_COMPONENT_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/components/system_restore_point_component.h"
#include "base/synchronization/lock.h"
#include "base/test/test_reg_util_win.h"
#include "chrome/chrome_cleaner/test/test_branding.h"
#include "testing/gtest/include/gtest/gtest.h"
// TODO(robertshield): Figure out how to test this. Near as I can tell the
// only way to enumerate RestorePoints is via WMI, which is 12 kinds of ugh.
namespace chrome_cleaner {
namespace {
constexpr DWORD kFakeSeqNumber = 42U;
bool g_set_called = false;
bool g_remove_called = false;
base::Lock* SharedLock() {
static base::Lock lock;
return &lock;
}
BOOL WINAPI FakeSetRestorePointInfoWFn(RESTOREPOINTINFOW* restore_point_info,
STATEMGRSTATUS* state_mgr_status) {
state_mgr_status->llSequenceNumber = kFakeSeqNumber;
state_mgr_status->nStatus = 0;
g_set_called = true;
return true;
}
BOOL WINAPI FakeRemoveRestorePointFn(DWORD sequence_number) {
EXPECT_EQ(kFakeSeqNumber, sequence_number);
g_remove_called = true;
return true;
}
BOOL WINAPI
FakeSetRestorePointInfoWFailFn(RESTOREPOINTINFOW* restore_point_info,
STATEMGRSTATUS* state_mgr_status) {
state_mgr_status->llSequenceNumber = kFakeSeqNumber;
state_mgr_status->nStatus = 0;
g_set_called = true;
return false;
}
BOOL WINAPI FakeRemoveRestorePointFailFn(DWORD sequence_number) {
EXPECT_EQ(kFakeSeqNumber, sequence_number);
g_remove_called = true;
return false;
}
} // namespace
class TestSystemRestorePointComponent : public SystemRestorePointComponent {
public:
TestSystemRestorePointComponent()
: SystemRestorePointComponent(TEST_PRODUCT_FULLNAME_STRING) {}
void OverrideSetRestorePointInfoWFn(
SystemRestorePointComponent::SetRestorePointInfoWFn func) {
set_restore_point_info_fn_ = func;
}
void OverrideRemoveRestorePointFn(
SystemRestorePointComponent::RemoveRestorePointFn func) {
remove_restore_point_info_fn_ = func;
}
};
class SystemRestorePointComponentTest : public testing::Test {
protected:
SystemRestorePointComponentTest() : auto_lock_(*SharedLock()) {}
void SetUp() override {
system_restore_point_component_.OverrideSetRestorePointInfoWFn(
&FakeSetRestorePointInfoWFn);
system_restore_point_component_.OverrideRemoveRestorePointFn(
&FakeRemoveRestorePointFn);
registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE);
g_set_called = false;
g_remove_called = false;
}
base::AutoLock auto_lock_;
TestSystemRestorePointComponent system_restore_point_component_;
registry_util::RegistryOverrideManager registry_override_;
};
TEST_F(SystemRestorePointComponentTest, CheckRestoreCallsSuccess) {
system_restore_point_component_.PreCleanup();
system_restore_point_component_.PostCleanup(RESULT_CODE_SUCCESS, nullptr);
EXPECT_TRUE(g_set_called);
EXPECT_FALSE(g_remove_called);
}
TEST_F(SystemRestorePointComponentTest, CheckRestoreCallsFailure) {
system_restore_point_component_.PreCleanup();
system_restore_point_component_.PostCleanup(RESULT_CODE_FAILED, nullptr);
EXPECT_TRUE(g_set_called);
EXPECT_TRUE(g_remove_called);
}
TEST_F(SystemRestorePointComponentTest, CheckRestoreCallsRestorePointFailure) {
system_restore_point_component_.OverrideSetRestorePointInfoWFn(
&FakeSetRestorePointInfoWFailFn);
system_restore_point_component_.PreCleanup();
system_restore_point_component_.PostCleanup(RESULT_CODE_SUCCESS, nullptr);
EXPECT_TRUE(g_set_called);
EXPECT_FALSE(g_remove_called);
}
TEST_F(SystemRestorePointComponentTest, CheckRestoreCommitRestorePointFailure) {
system_restore_point_component_.OverrideRemoveRestorePointFn(
&FakeRemoveRestorePointFailFn);
system_restore_point_component_.PreCleanup();
system_restore_point_component_.PostCleanup(RESULT_CODE_FAILED, nullptr);
EXPECT_TRUE(g_set_called);
EXPECT_TRUE(g_remove_called);
}
} // namespace chrome_cleaner
......@@ -7,6 +7,7 @@
#include <string>
#include "base/command_line.h"
#include "components/chrome_cleaner/public/constants/constants.h"
namespace chrome_cleaner {
......
......@@ -33,6 +33,20 @@ source_set("test_branding_header") {
]
}
source_set("test_component") {
testonly = true
sources = [
"test_component.cc",
"test_component.h",
]
deps = [
"//chrome/chrome_cleaner/components:components",
"//chrome/chrome_cleaner/constants:uws_id",
]
}
source_set("test_executables") {
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_component.h"
namespace chrome_cleaner {
TestComponent::Calls::Calls() = default;
TestComponent::Calls::~Calls() = default;
TestComponent::~TestComponent() {
calls_->destroyed = true;
}
void TestComponent::PreScan() {
calls_->pre_scan = true;
}
void TestComponent::PostScan(const std::vector<UwSId>& found_pups) {
calls_->post_scan = true;
calls_->post_scan_found_pups = found_pups;
}
void TestComponent::PreCleanup() {
calls_->pre_cleanup = true;
}
void TestComponent::PostCleanup(ResultCode result_code, RebooterAPI* rebooter) {
calls_->result_code = result_code;
calls_->post_cleanup = true;
}
void TestComponent::PostValidation(ResultCode result_code) {
calls_->result_code = result_code;
calls_->post_validation = true;
}
void TestComponent::OnClose(ResultCode result_code) {
calls_->result_code = result_code;
calls_->on_close = true;
}
} // 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_COMPONENT_H_
#define CHROME_CHROME_CLEANER_TEST_TEST_COMPONENT_H_
#include <vector>
#include "chrome/chrome_cleaner/components/component_api.h"
namespace chrome_cleaner {
class TestComponent : public ComponentAPI {
public:
struct Calls {
Calls();
~Calls();
bool pre_scan = false;
bool post_scan = false;
bool pre_cleanup = false;
bool post_cleanup = false;
bool post_validation = false;
bool on_close = false;
bool destroyed = false;
std::vector<UwSId> post_scan_found_pups;
ResultCode result_code = RESULT_CODE_INVALID;
};
explicit TestComponent(Calls* calls) : calls_(calls) {}
~TestComponent() override;
// ComponentAPI.
void PreScan() override;
void PostScan(const std::vector<UwSId>& found_pups) override;
void PreCleanup() override;
void PostCleanup(ResultCode result_code, RebooterAPI* rebooter) override;
void PostValidation(ResultCode result_code) override;
void OnClose(ResultCode result_code) override;
private:
Calls* calls_;
};
} // namespace chrome_cleaner
#endif // CHROME_CHROME_CLEANER_TEST_TEST_COMPONENT_H_
......@@ -90,6 +90,7 @@ Refer to README.md for content description and update process.
<item id="doodle_service" hash_code="41154842" type="0" deprecated="2017-08-28" content_hash_code="28273962" file_path=""/>
<item id="download_internals_webui_source" hash_code="38670228" type="0" content_hash_code="129391056" os_list="linux,windows" file_path="chrome/browser/ui/webui/download_internals/download_internals_ui_message_handler.cc"/>
<item id="download_manager_resume" hash_code="35380758" type="0" content_hash_code="41227674" os_list="linux,windows" file_path="components/download/internal/common/download_item_impl.cc"/>
<item id="download_recovery_component" hash_code="131711536" type="0" content_hash_code="110581751" os_list="windows" file_path="chrome/chrome_cleaner/components/recovery_component.cc"/>
<item id="download_web_contents_frame" hash_code="56351037" type="0" content_hash_code="3657889" os_list="linux,windows" file_path="content/browser/web_contents/web_contents_impl.cc"/>
<item id="downloads_api_run_async" hash_code="121068967" type="0" content_hash_code="87443585" os_list="linux,windows" file_path="chrome/browser/extensions/api/downloads/downloads_api.cc"/>
<item id="drag_download_file" hash_code="95910019" type="0" content_hash_code="126492858" os_list="linux,windows" file_path="content/browser/download/drag_download_file.cc"/>
......
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