Commit 98761d95 authored by Jeffrey Kardatzke's avatar Jeffrey Kardatzke Committed by Commit Bot

Expand URL whitelisting for anonymizer

This adds whitelisting to URL redaction for URLs of the format
chrome://*/crisper.js and also chrome-extension://<first-party-id>/*.js
as long as there are also no query parameters appended to those.

There is also a constant added for the list of 1st party extension IDs.
Extra plumbing of those values were required in order to keep with
allowed dependencies.

Bug: 979779
Test: component_unittests pass
Change-Id: I151ddb3d3791a57b6f394867bf244e945febc321
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1699699
Commit-Queue: Jeffrey Kardatzke <jkardatzke@google.com>
Reviewed-by: default avatarJ Kardatzke <jkardatzke@chromium.org>
Reviewed-by: default avatarBen Wells <benwells@chromium.org>
Reviewed-by: default avatarAhmed Fakhry <afakhry@chromium.org>
Reviewed-by: default avatarSergey Poromov <poromov@chromium.org>
Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarMay Lippert <maybelle@chromium.org>
Cr-Commit-Position: refs/heads/master@{#678328}
parent 20b1da2d
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/feedback/anonymizer_tool.h" #include "components/feedback/anonymizer_tool.h"
#include "components/policy/core/browser/browser_policy_connector.h" #include "components/policy/core/browser/browser_policy_connector.h"
#include "components/user_manager/user_manager.h" #include "components/user_manager/user_manager.h"
...@@ -130,7 +131,8 @@ std::string ReadAndAnonymizeLogFile(feedback::AnonymizerTool* anonymizer, ...@@ -130,7 +131,8 @@ std::string ReadAndAnonymizeLogFile(feedback::AnonymizerTool* anonymizer,
// as pairs (file name, data) and returns. Called on blocking thread. // as pairs (file name, data) and returns. Called on blocking thread.
std::unique_ptr<SystemLogUploader::SystemLogs> ReadFiles() { std::unique_ptr<SystemLogUploader::SystemLogs> ReadFiles() {
auto system_logs = std::make_unique<SystemLogUploader::SystemLogs>(); auto system_logs = std::make_unique<SystemLogUploader::SystemLogs>();
feedback::AnonymizerTool anonymizer; feedback::AnonymizerTool anonymizer(
extension_misc::kBuiltInFirstPartyExtensionIds);
for (const char* file_path : kSystemLogFileNames) { for (const char* file_path : kSystemLogFileNames) {
if (!base::PathExists(base::FilePath(file_path))) if (!base::PathExists(base::FilePath(file_path)))
continue; continue;
......
...@@ -200,8 +200,12 @@ void ChromeFeedbackPrivateDelegate::FetchExtraLogs( ...@@ -200,8 +200,12 @@ void ChromeFeedbackPrivateDelegate::FetchExtraLogs(
constexpr bool scrub = true; constexpr bool scrub = true;
if (system_logs::ContainsIwlwifiLogs(feedback_data->sys_info())) { if (system_logs::ContainsIwlwifiLogs(feedback_data->sys_info())) {
// TODO (jkardatzke): Modify this so that we are using the same instance of
// the anonymizer for the rest of the logs.
// We can pass null for the 1st party IDs since we are just anonymizing
// wifi data here.
system_logs::SystemLogsFetcher* fetcher = system_logs::SystemLogsFetcher* fetcher =
new system_logs::SystemLogsFetcher(scrub); new system_logs::SystemLogsFetcher(scrub, nullptr);
fetcher->AddSource(std::make_unique<system_logs::IwlwifiDumpLogSource>()); fetcher->AddSource(std::make_unique<system_logs::IwlwifiDumpLogSource>());
fetcher->Fetch(base::BindOnce(&OnFetchedExtraLogs, feedback_data, fetcher->Fetch(base::BindOnce(&OnFetchedExtraLogs, feedback_data,
std::move(callback))); std::move(callback)));
......
...@@ -21,7 +21,8 @@ namespace system_logs { ...@@ -21,7 +21,8 @@ namespace system_logs {
SystemLogsFetcher* BuildAboutSystemLogsFetcher() { SystemLogsFetcher* BuildAboutSystemLogsFetcher() {
const bool scrub_data = false; const bool scrub_data = false;
SystemLogsFetcher* fetcher = new SystemLogsFetcher(scrub_data); // We aren't anonymizing, so we can pass null for the 1st party IDs.
SystemLogsFetcher* fetcher = new SystemLogsFetcher(scrub_data, nullptr);
fetcher->AddSource(std::make_unique<ChromeInternalLogSource>()); fetcher->AddSource(std::make_unique<ChromeInternalLogSource>());
fetcher->AddSource(std::make_unique<MemoryDetailsLogSource>()); fetcher->AddSource(std::make_unique<MemoryDetailsLogSource>());
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h" #include "chrome/browser/feedback/system_logs/log_sources/chrome_internal_log_source.h"
#include "chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h" #include "chrome/browser/feedback/system_logs/log_sources/crash_ids_source.h"
#include "chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h" #include "chrome/browser/feedback/system_logs/log_sources/memory_details_log_source.h"
#include "chrome/common/extensions/extension_constants.h"
#include "components/feedback/system_logs/system_logs_fetcher.h" #include "components/feedback/system_logs/system_logs_fetcher.h"
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
...@@ -23,7 +24,8 @@ namespace system_logs { ...@@ -23,7 +24,8 @@ namespace system_logs {
SystemLogsFetcher* BuildChromeSystemLogsFetcher() { SystemLogsFetcher* BuildChromeSystemLogsFetcher() {
const bool scrub_data = true; const bool scrub_data = true;
SystemLogsFetcher* fetcher = new SystemLogsFetcher(scrub_data); SystemLogsFetcher* fetcher = new SystemLogsFetcher(
scrub_data, extension_misc::kBuiltInFirstPartyExtensionIds);
fetcher->AddSource(std::make_unique<ChromeInternalLogSource>()); fetcher->AddSource(std::make_unique<ChromeInternalLogSource>());
fetcher->AddSource(std::make_unique<CrashIdsSource>()); fetcher->AddSource(std::make_unique<CrashIdsSource>());
......
...@@ -45,6 +45,50 @@ const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda"; ...@@ -45,6 +45,50 @@ const char kInAppPaymentsSupportAppId[] = "nmmhkkegccagdldgiimedpiccmgmieda";
const char kMediaRouterStableExtensionId[] = "pkedcjkdefgpdelpbcmbmeomcjbeemfm"; const char kMediaRouterStableExtensionId[] = "pkedcjkdefgpdelpbcmbmeomcjbeemfm";
const char kCloudReportingExtensionId[] = "oempjldejiginopiohodkdoklcjklbaa"; const char kCloudReportingExtensionId[] = "oempjldejiginopiohodkdoklcjklbaa";
const char* const kBuiltInFirstPartyExtensionIds[] = {
kCalculatorAppId,
kCalendarAppId,
kChromeRemoteDesktopAppId,
kCloudPrintAppId,
kDataSaverExtensionId,
kDocsOfflineExtensionId,
kDriveHostedAppId,
kEnterpriseWebStoreAppId,
kGmailAppId,
kGoogleDocAppId,
kGoogleMapsAppId,
kGooglePhotosAppId,
kGooglePlayBooksAppId,
kGooglePlayMoviesAppId,
kGooglePlayMusicAppId,
kGooglePlusAppId,
kGoogleSheetsAppId,
kGoogleSlidesAppId,
kHTermAppId,
kHTermDevAppId,
kIdentityApiUiAppId,
kCroshBuiltinAppId,
kTextEditorAppId,
kInAppPaymentsSupportAppId,
kMediaRouterStableExtensionId,
kCloudReportingExtensionId,
#if defined(OS_CHROMEOS)
kAssessmentAssistantExtensionId,
kAutoclickExtensionId,
kSelectToSpeakExtensionId,
kSwitchAccessExtensionId,
kFirstRunDialogId,
kEspeakSpeechSynthesisExtensionId,
kGoogleSpeechSynthesisExtensionId,
kWallpaperManagerId,
kZipArchiverExtensionId,
kChromeCameraAppId,
kChromeCameraAppDevId,
kKioskNextHomeAppId,
#endif // defined(OS_CHROMEOS)
nullptr, // Null-terminated array.
};
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
const char kAssessmentAssistantExtensionId[] = const char kAssessmentAssistantExtensionId[] =
"gndmhdcefbhlchkhipcnnbkcmicncehk"; "gndmhdcefbhlchkhipcnnbkcmicncehk";
...@@ -76,7 +120,8 @@ const char kChromeCameraAppId[] = "hfhhnacclhffhdffklopdkcgdhifgngh"; ...@@ -76,7 +120,8 @@ const char kChromeCameraAppId[] = "hfhhnacclhffhdffklopdkcgdhifgngh";
const char kChromeCameraAppDevId[] = "flgnmkgjffmkephdokeeliiopbjaafpm"; const char kChromeCameraAppDevId[] = "flgnmkgjffmkephdokeeliiopbjaafpm";
const char kChromeCameraAppPath[] = "chromeos/camera"; const char kChromeCameraAppPath[] = "chromeos/camera";
const char kKioskNextHomeAppId[] = "nbaolgedfgoedkjbfmpediclncanmpbc"; const char kKioskNextHomeAppId[] = "nbaolgedfgoedkjbfmpediclncanmpbc";
#endif
#endif // defined(CHROME_OS)
const char kAppStateNotInstalled[] = "not_installed"; const char kAppStateNotInstalled[] = "not_installed";
const char kAppStateInstalled[] = "installed"; const char kAppStateInstalled[] = "installed";
......
...@@ -105,6 +105,9 @@ extern const char kMediaRouterStableExtensionId[]; ...@@ -105,6 +105,9 @@ extern const char kMediaRouterStableExtensionId[];
// The extension id of the Chrome Reporting extension. // The extension id of the Chrome Reporting extension.
extern const char kCloudReportingExtensionId[]; extern const char kCloudReportingExtensionId[];
// A list of all the first party extension IDs, last entry is null.
extern const char* const kBuiltInFirstPartyExtensionIds[];
// The buckets used for app launches. // The buckets used for app launches.
enum AppLaunchBucket { enum AppLaunchBucket {
// Launch from NTP apps section while maximized. // Launch from NTP apps section while maximized.
......
...@@ -353,8 +353,9 @@ bool FindAndConsumeAndGetSkipped(re2::StringPiece* input, ...@@ -353,8 +353,9 @@ bool FindAndConsumeAndGetSkipped(re2::StringPiece* input,
} // namespace } // namespace
AnonymizerTool::AnonymizerTool() AnonymizerTool::AnonymizerTool(const char* const* first_party_extension_ids)
: custom_patterns_with_context_(base::size(kCustomPatternsWithContext)), : first_party_extension_ids_(first_party_extension_ids),
custom_patterns_with_context_(base::size(kCustomPatternsWithContext)),
custom_patterns_without_context_( custom_patterns_without_context_(
base::size(kCustomPatternsWithoutContext)) { base::size(kCustomPatternsWithoutContext)) {
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
...@@ -477,11 +478,49 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithContext( ...@@ -477,11 +478,49 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithContext(
return result; return result;
} }
bool WhitelistMatchedId(re2::StringPiece matched_id) { // This takes a |url| argument and returns true if the URL is whitelisted and
bool is_safe_chrome_resource = // does NOT need to be redacted, returns false otherwise.
matched_id.starts_with("chrome://resources/") && bool IsUrlWhitelisted(re2::StringPiece url,
!matched_id.contains("?"); const char* const* first_party_extension_ids) {
return is_safe_chrome_resource; // We do not whitelist anything with a query parameter.
if (url.contains("?"))
return false;
// Check for whitelisting of chrome:// URLs.
if (url.starts_with("chrome://")) {
// We allow everything in chrome://resources/.
if (url.starts_with("chrome://resources/"))
return true;
// We allow chrome://*/crisper.js.
if (url.ends_with("/crisper.js"))
return true;
return false;
}
// If the whitelist is null, then don't check it.
if (!first_party_extension_ids)
return false;
// Whitelist URLs of the format chrome-extension://<first-party-id>/*.js
if (!url.starts_with("chrome-extension://"))
return false;
// These must end with a .js extension.
if (!url.ends_with(".js"))
return false;
int i = 0;
const char* test_id = first_party_extension_ids[i];
const re2::StringPiece url_sub =
url.substr(sizeof("chrome-extension://") - 1);
while (test_id) {
if (url_sub.starts_with(test_id))
return true;
test_id = first_party_extension_ids[++i];
}
return false;
} }
std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext( std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext(
...@@ -499,7 +538,7 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext( ...@@ -499,7 +538,7 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext(
re2::StringPiece skipped; re2::StringPiece skipped;
re2::StringPiece matched_id; re2::StringPiece matched_id;
while (FindAndConsumeAndGetSkipped(&text, *re, &skipped, &matched_id)) { while (FindAndConsumeAndGetSkipped(&text, *re, &skipped, &matched_id)) {
if (WhitelistMatchedId(matched_id)) { if (IsUrlWhitelisted(matched_id, first_party_extension_ids_)) {
skipped.AppendToString(&result); skipped.AppendToString(&result);
matched_id.AppendToString(&result); matched_id.AppendToString(&result);
continue; continue;
...@@ -527,8 +566,10 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext( ...@@ -527,8 +566,10 @@ std::string AnonymizerTool::AnonymizeCustomPatternWithoutContext(
} }
AnonymizerToolContainer::AnonymizerToolContainer( AnonymizerToolContainer::AnonymizerToolContainer(
scoped_refptr<base::SequencedTaskRunner> task_runner) scoped_refptr<base::SequencedTaskRunner> task_runner,
: anonymizer_(new AnonymizerTool), task_runner_(task_runner) {} const char* const* first_party_extension_ids)
: anonymizer_(new AnonymizerTool(first_party_extension_ids)),
task_runner_(task_runner) {}
AnonymizerToolContainer::~AnonymizerToolContainer() { AnonymizerToolContainer::~AnonymizerToolContainer() {
task_runner_->DeleteSoon(FROM_HERE, std::move(anonymizer_)); task_runner_->DeleteSoon(FROM_HERE, std::move(anonymizer_));
......
...@@ -32,7 +32,10 @@ struct CustomPatternWithoutContext { ...@@ -32,7 +32,10 @@ struct CustomPatternWithoutContext {
class AnonymizerTool { class AnonymizerTool {
public: public:
AnonymizerTool(); // |first_party_extension_ids| is a null terminated array of all the 1st
// party extension IDs whose URLs won't be redacted. It is OK to pass null for
// that value if it's OK to redact those URLs or they won't be present.
AnonymizerTool(const char* const* first_party_extension_ids);
~AnonymizerTool(); ~AnonymizerTool();
// Returns an anonymized version of |input|. PII-sensitive data (such as MAC // Returns an anonymized version of |input|. PII-sensitive data (such as MAC
...@@ -57,6 +60,10 @@ class AnonymizerTool { ...@@ -57,6 +60,10 @@ class AnonymizerTool {
const CustomPatternWithoutContext& pattern, const CustomPatternWithoutContext& pattern,
std::map<std::string, std::string>* identifier_space); std::map<std::string, std::string>* identifier_space);
// Null-terminated list of first party extension IDs. We need to have this
// passed into us because we can't refer to the code where these are defined.
const char* const* first_party_extension_ids_; // Not owned.
// Map of MAC addresses discovered in anonymized strings to anonymized // Map of MAC addresses discovered in anonymized strings to anonymized
// representations. 11:22:33:44:55:66 gets anonymized to 11:22:33:00:00:01, // representations. 11:22:33:44:55:66 gets anonymized to 11:22:33:00:00:01,
// where the first three bytes represent the manufacturer. The last three // where the first three bytes represent the manufacturer. The last three
...@@ -88,7 +95,8 @@ class AnonymizerToolContainer ...@@ -88,7 +95,8 @@ class AnonymizerToolContainer
: public base::RefCountedThreadSafe<AnonymizerToolContainer> { : public base::RefCountedThreadSafe<AnonymizerToolContainer> {
public: public:
explicit AnonymizerToolContainer( explicit AnonymizerToolContainer(
scoped_refptr<base::SequencedTaskRunner> task_runner); scoped_refptr<base::SequencedTaskRunner> task_runner,
const char* const* first_party_extension_ids);
// Returns a pointer to the instance of this anonymier. May only be called // Returns a pointer to the instance of this anonymier. May only be called
// on |task_runner_|. // on |task_runner_|.
......
...@@ -10,6 +10,9 @@ ...@@ -10,6 +10,9 @@
namespace feedback { namespace feedback {
const char kFakeFirstPartyID[] = "nkoccljplnhpfnfiajclkommnmllphnl";
const char* const kFakeFirstPartyExtensionIDs[] = {kFakeFirstPartyID, nullptr};
class AnonymizerToolTest : public testing::Test { class AnonymizerToolTest : public testing::Test {
protected: protected:
std::string AnonymizeMACAddresses(const std::string& input) { std::string AnonymizeMACAddresses(const std::string& input) {
...@@ -35,7 +38,7 @@ class AnonymizerToolTest : public testing::Test { ...@@ -35,7 +38,7 @@ class AnonymizerToolTest : public testing::Test {
space); space);
} }
AnonymizerTool anonymizer_; AnonymizerTool anonymizer_{kFakeFirstPartyExtensionIDs};
}; };
TEST_F(AnonymizerToolTest, Anonymize) { TEST_F(AnonymizerToolTest, Anonymize) {
...@@ -347,8 +350,16 @@ TEST_F(AnonymizerToolTest, AnonymizeChunk) { ...@@ -347,8 +350,16 @@ TEST_F(AnonymizerToolTest, AnonymizeChunk) {
"aa:aa:aa:00:00:01"}, "aa:aa:aa:00:00:01"},
{"chrome://resources/foo", // Secure chrome resource, whitelisted. {"chrome://resources/foo", // Secure chrome resource, whitelisted.
"chrome://resources/foo"}, "chrome://resources/foo"},
{"chrome://settings/crisper.js", // Whitelisted settings URLs.
"chrome://settings/crisper.js"},
// Whitelisted first party extension.
{"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js",
"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js"},
{"chrome://resources/f?user=bar", // Potentially PII in parameter. {"chrome://resources/f?user=bar", // Potentially PII in parameter.
"<URL: 2>"}}; "<URL: 2>"},
{"chrome-extension://nkoccljplnhpfnfiajclkommnmllphnl/foobar.js?bar=x",
"<URL: 3>"}, // Potentially PII in parameter.
};
std::string anon_input; std::string anon_input;
std::string anon_output; std::string anon_output;
for (const auto& s : data) { for (const auto& s : data) {
......
...@@ -46,7 +46,9 @@ void Anonymize(feedback::AnonymizerTool* anonymizer, ...@@ -46,7 +46,9 @@ void Anonymize(feedback::AnonymizerTool* anonymizer,
} // namespace } // namespace
SystemLogsFetcher::SystemLogsFetcher(bool scrub_data) SystemLogsFetcher::SystemLogsFetcher(
bool scrub_data,
const char* const first_party_extension_ids[])
: response_(std::make_unique<SystemLogsResponse>()), : response_(std::make_unique<SystemLogsResponse>()),
num_pending_requests_(0), num_pending_requests_(0),
task_runner_for_anonymizer_(base::CreateSequencedTaskRunnerWithTraits( task_runner_for_anonymizer_(base::CreateSequencedTaskRunnerWithTraits(
...@@ -55,7 +57,8 @@ SystemLogsFetcher::SystemLogsFetcher(bool scrub_data) ...@@ -55,7 +57,8 @@ SystemLogsFetcher::SystemLogsFetcher(bool scrub_data)
base::TaskPriority::USER_VISIBLE, base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) { base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})) {
if (scrub_data) if (scrub_data)
anonymizer_ = std::make_unique<feedback::AnonymizerTool>(); anonymizer_ =
std::make_unique<feedback::AnonymizerTool>(first_party_extension_ids);
} }
SystemLogsFetcher::~SystemLogsFetcher() { SystemLogsFetcher::~SystemLogsFetcher() {
......
...@@ -43,8 +43,12 @@ using SysLogsFetcherCallback = ...@@ -43,8 +43,12 @@ using SysLogsFetcherCallback =
// }; // };
class SystemLogsFetcher { class SystemLogsFetcher {
public: public:
// If scrub_data is true, logs will be anonymized. // If |scrub_data| is true, logs will be anonymized.
explicit SystemLogsFetcher(bool scrub_data); // |first_party_extension_ids| is a null terminated array of all the 1st
// party extension IDs whose URLs won't be redacted. It is OK to pass null for
// that value if it's OK to redact those URLs or they won't be present.
explicit SystemLogsFetcher(bool scrub_data,
const char* const first_party_extension_ids[]);
~SystemLogsFetcher(); ~SystemLogsFetcher();
// Adds a source to use when fetching. // Adds a source to use when fetching.
......
...@@ -81,7 +81,8 @@ LogSourceAccessManager::LogSourceAccessManager(content::BrowserContext* context) ...@@ -81,7 +81,8 @@ LogSourceAccessManager::LogSourceAccessManager(content::BrowserContext* context)
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
anonymizer_container_( anonymizer_container_(
base::MakeRefCounted<feedback::AnonymizerToolContainer>( base::MakeRefCounted<feedback::AnonymizerToolContainer>(
task_runner_for_anonymizer_)), task_runner_for_anonymizer_,
/* first_party_extension_ids= */ nullptr)),
weak_factory_(this) {} weak_factory_(this) {}
LogSourceAccessManager::~LogSourceAccessManager() {} LogSourceAccessManager::~LogSourceAccessManager() {}
......
...@@ -12,7 +12,9 @@ namespace system_logs { ...@@ -12,7 +12,9 @@ namespace system_logs {
SystemLogsFetcher* BuildShellSystemLogsFetcher( SystemLogsFetcher* BuildShellSystemLogsFetcher(
content::BrowserContext* browser_context) { content::BrowserContext* browser_context) {
// Deletes itself after Fetch() is completes. // Deletes itself after Fetch() is completes.
SystemLogsFetcher* fetcher = new SystemLogsFetcher(true /* scrub_data */); SystemLogsFetcher* fetcher =
new SystemLogsFetcher(/* scrub_data= */ true,
/* first_party_extension_ids= */ nullptr);
fetcher->AddSource(std::make_unique<BasicLogSource>(browser_context)); fetcher->AddSource(std::make_unique<BasicLogSource>(browser_context));
return fetcher; return fetcher;
} }
......
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