Commit c3c5fb3d authored by Victor Hugo Vianna Silva's avatar Victor Hugo Vianna Silva Committed by Commit Bot

Sync: Support omitting sensitive info in ConstructAboutInformation()

ConstructAboutInformation() gathers debugging information about the sync
state. It's employed both to power the sync-internals page - a debugging
page only accessible locally - as well as to compose logs sent to Google
in case of crashes (provided the user has agreed to this). For the
latter use case, the caller used to manually filter a section containing
PII, for privacy. Delegating this responsibility to the caller was bad
since the return value is quite "weakly typed" (base::DictionaryValue).
This meant that one might easily add new sensitive information to the
return value while forgetting to update the filter in the caller.

In this CL we transfer this responsibility to the method, by exposing
an include_sensitive_data parameter. Additionally, we now force the
constructor of a "section" to specify whether it is sensitive or not.
This last part makes the code more privacy-aware, which should make it
less likely to accidentally leak any data (even if this solution isn't
bullet-proof).

Bug: 973820
Change-Id: I99138a741c26755e138ac0a60dd08815aa90a10c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2467939
Commit-Queue: Victor Vianna <victorvianna@google.com>
Reviewed-by: default avatarMarc Treib <treib@chromium.org>
Cr-Commit-Position: refs/heads/master@{#816616}
parent 722d50ff
...@@ -393,35 +393,15 @@ void ChromeInternalLogSource::PopulateSyncLogs(SystemLogsResponse* response) { ...@@ -393,35 +393,15 @@ void ChromeInternalLogSource::PopulateSyncLogs(SystemLogsResponse* response) {
if (!profile || !ProfileSyncServiceFactory::HasSyncService(profile)) if (!profile || !ProfileSyncServiceFactory::HasSyncService(profile))
return; return;
syncer::SyncService* service = // Add sync logs to |response|.
ProfileSyncServiceFactory::GetForProfile(profile); std::unique_ptr<base::DictionaryValue> sync_logs =
std::unique_ptr<base::DictionaryValue> sync_logs( syncer::sync_ui_util::ConstructAboutInformation(
syncer::sync_ui_util::ConstructAboutInformation(service, syncer::sync_ui_util::IncludeSensitiveData(false),
chrome::GetChannel())); ProfileSyncServiceFactory::GetForProfile(profile),
chrome::GetChannel());
// Remove identity section. std::string serialized_sync_logs;
base::ListValue* details = NULL; JSONStringValueSerializer(&serialized_sync_logs).Serialize(*sync_logs);
sync_logs->GetList(syncer::sync_ui_util::kDetailsKey, &details); response->emplace(kSyncDataKey, serialized_sync_logs);
if (!details)
return;
for (auto it = details->begin(); it != details->end(); ++it) {
base::DictionaryValue* dict = NULL;
if (it->GetAsDictionary(&dict)) {
std::string title;
dict->GetString("title", &title);
if (title == syncer::sync_ui_util::kIdentityTitle) {
details->Erase(it, NULL);
break;
}
}
}
// Add sync logs to logs.
std::string sync_logs_string;
JSONStringValueSerializer serializer(&sync_logs_string);
serializer.Serialize(*sync_logs.get());
response->emplace(kSyncDataKey, sync_logs_string);
} }
void ChromeInternalLogSource::PopulateExtensionInfoLogs( void ChromeInternalLogSource::PopulateExtensionInfoLogs(
......
...@@ -606,8 +606,11 @@ SyncCycleSnapshot ProfileSyncServiceHarness::GetLastCycleSnapshot() const { ...@@ -606,8 +606,11 @@ SyncCycleSnapshot ProfileSyncServiceHarness::GetLastCycleSnapshot() const {
} }
std::string ProfileSyncServiceHarness::GetServiceStatus() { std::string ProfileSyncServiceHarness::GetServiceStatus() {
// This method is only used in test code for debugging purposes, so it's fine
// to include sensitive data in ConstructAboutInformation().
std::unique_ptr<base::DictionaryValue> value( std::unique_ptr<base::DictionaryValue> value(
syncer::sync_ui_util::ConstructAboutInformation(service(), syncer::sync_ui_util::ConstructAboutInformation(
syncer::sync_ui_util::IncludeSensitiveData(true), service(),
chrome::GetChannel())); chrome::GetChannel()));
std::string service_status; std::string service_status;
base::JSONWriter::WriteWithOptions( base::JSONWriter::WriteWithOptions(
......
...@@ -65,7 +65,11 @@ bool GetIncludeSpecificsInitialState() { ...@@ -65,7 +65,11 @@ bool GetIncludeSpecificsInitialState() {
SyncInternalsMessageHandler::SyncInternalsMessageHandler() SyncInternalsMessageHandler::SyncInternalsMessageHandler()
: SyncInternalsMessageHandler(base::BindRepeating( : SyncInternalsMessageHandler(base::BindRepeating(
&syncer::sync_ui_util::ConstructAboutInformation)) {} &syncer::sync_ui_util::ConstructAboutInformation,
syncer::sync_ui_util::IncludeSensitiveData(true))) {
// This class serves to display debug information to the user, so it's fine to
// include sensitive data in ConstructAboutInformation() above.
}
SyncInternalsMessageHandler::SyncInternalsMessageHandler( SyncInternalsMessageHandler::SyncInternalsMessageHandler(
AboutSyncDataDelegate about_sync_data_delegate) AboutSyncDataDelegate about_sync_data_delegate)
......
...@@ -120,9 +120,8 @@ class Stat : public StatBase { ...@@ -120,9 +120,8 @@ class Stat : public StatBase {
// fields. // fields.
class Section { class Section {
public: public:
explicit Section(const std::string& title) : title_(title) {} Section(const std::string& title, bool is_sensitive)
: title_(title), is_sensitive_(is_sensitive) {}
void MarkSensitive() { is_sensitive_ = true; }
Stat<bool>* AddBoolStat(const std::string& key) { Stat<bool>* AddBoolStat(const std::string& key) {
return AddStat(key, false); return AddStat(key, false);
...@@ -143,6 +142,8 @@ class Section { ...@@ -143,6 +142,8 @@ class Section {
return result; return result;
} }
bool is_sensitive() { return is_sensitive_; }
private: private:
template <typename T> template <typename T>
Stat<T>* AddStat(const std::string& key, const T& default_value) { Stat<T>* AddStat(const std::string& key, const T& default_value) {
...@@ -161,15 +162,22 @@ class SectionList { ...@@ -161,15 +162,22 @@ class SectionList {
public: public:
SectionList() = default; SectionList() = default;
Section* AddSection(const std::string& title) { // WARNING: If this section includes any Personally Identifiable Information,
sections_.push_back(std::make_unique<Section>(title)); // |is_sensitive| should be set to true.
Section* AddSection(const std::string& title, bool is_sensitive) {
sections_.push_back(std::make_unique<Section>(title, is_sensitive));
return sections_.back().get(); return sections_.back().get();
} }
base::Value ToValue() const { // If |include_sensitive_data| is true, returns all added sections. Otherwise,
// omits those added with |is_sensitive| set to true.
base::Value ToValue(IncludeSensitiveData include_sensitive_data) const {
base::Value result(base::Value::Type::LIST); base::Value result(base::Value::Type::LIST);
for (const std::unique_ptr<Section>& section : sections_) for (const std::unique_ptr<Section>& section : sections_) {
if (include_sensitive_data || !section->is_sensitive()) {
result.Append(section->ToValue()); result.Append(section->ToValue());
}
}
return result; return result;
} }
...@@ -297,13 +305,15 @@ std::string GetConnectionStatus(const SyncTokenStatus& status) { ...@@ -297,13 +305,15 @@ std::string GetConnectionStatus(const SyncTokenStatus& status) {
// which are grouped into sections and populated with the help of the SyncStat // which are grouped into sections and populated with the help of the SyncStat
// classes defined above. // classes defined above.
std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
IncludeSensitiveData include_sensitive_data,
SyncService* service, SyncService* service,
version_info::Channel channel) { version_info::Channel channel) {
auto about_info = std::make_unique<base::DictionaryValue>(); auto about_info = std::make_unique<base::DictionaryValue>();
SectionList section_list; SectionList section_list;
Section* section_summary = section_list.AddSection("Summary"); Section* section_summary =
section_list.AddSection("Summary", /*is_sensitive=*/false);
Stat<std::string>* transport_state = Stat<std::string>* transport_state =
section_summary->AddStringStat("Transport State"); section_summary->AddStringStat("Transport State");
Stat<std::string>* disable_reasons = Stat<std::string>* disable_reasons =
...@@ -318,13 +328,14 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -318,13 +328,14 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
section_summary->AddBoolStat("Setup In Progress"); section_summary->AddBoolStat("Setup In Progress");
Stat<std::string>* auth_error = section_summary->AddStringStat("Auth Error"); Stat<std::string>* auth_error = section_summary->AddStringStat("Auth Error");
Section* section_version = section_list.AddSection("Version Info"); Section* section_version =
section_list.AddSection("Version Info", /*is_sensitive=*/false);
Stat<std::string>* client_version = Stat<std::string>* client_version =
section_version->AddStringStat("Client Version"); section_version->AddStringStat("Client Version");
Stat<std::string>* server_url = section_version->AddStringStat("Server URL"); Stat<std::string>* server_url = section_version->AddStringStat("Server URL");
Section* section_identity = section_list.AddSection(kIdentityTitle); Section* section_identity =
section_identity->MarkSensitive(); section_list.AddSection(kIdentityTitle, /*is_sensitive=*/true);
Stat<std::string>* sync_client_id = Stat<std::string>* sync_client_id =
section_identity->AddStringStat("Sync Client ID"); section_identity->AddStringStat("Sync Client ID");
Stat<std::string>* invalidator_id = Stat<std::string>* invalidator_id =
...@@ -332,7 +343,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -332,7 +343,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<std::string>* username = section_identity->AddStringStat("Username"); Stat<std::string>* username = section_identity->AddStringStat("Username");
Stat<bool>* user_is_primary = section_identity->AddBoolStat("Is Primary"); Stat<bool>* user_is_primary = section_identity->AddBoolStat("Is Primary");
Section* section_credentials = section_list.AddSection("Credentials"); Section* section_credentials =
section_list.AddSection("Credentials", /*is_sensitive=*/false);
Stat<std::string>* token_request_time = Stat<std::string>* token_request_time =
section_credentials->AddStringStat("Requested Token"); section_credentials->AddStringStat("Requested Token");
Stat<std::string>* token_response_time = Stat<std::string>* token_response_time =
...@@ -343,7 +355,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -343,7 +355,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<std::string>* next_token_request = Stat<std::string>* next_token_request =
section_credentials->AddStringStat("Next Token Request"); section_credentials->AddStringStat("Next Token Request");
Section* section_local = section_list.AddSection("Local State"); Section* section_local =
section_list.AddSection("Local State", /*is_sensitive=*/false);
Stat<std::string>* server_connection = Stat<std::string>* server_connection =
section_local->AddStringStat("Server Connection"); section_local->AddStringStat("Server Connection");
Stat<std::string>* last_synced = section_local->AddStringStat("Last Synced"); Stat<std::string>* last_synced = section_local->AddStringStat("Last Synced");
...@@ -355,14 +368,16 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -355,14 +368,16 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<std::string>* local_backend_path = Stat<std::string>* local_backend_path =
section_local->AddStringStat("Local Backend Path"); section_local->AddStringStat("Local Backend Path");
Section* section_network = section_list.AddSection("Network"); Section* section_network =
section_list.AddSection("Network", /*is_sensitive=*/false);
Stat<bool>* is_any_throttled_or_backoff = Stat<bool>* is_any_throttled_or_backoff =
section_network->AddBoolStat("Throttled or Backoff"); section_network->AddBoolStat("Throttled or Backoff");
Stat<std::string>* retry_time = section_network->AddStringStat("Retry Time"); Stat<std::string>* retry_time = section_network->AddStringStat("Retry Time");
Stat<bool>* are_notifications_enabled = Stat<bool>* are_notifications_enabled =
section_network->AddBoolStat("Notifications Enabled"); section_network->AddBoolStat("Notifications Enabled");
Section* section_encryption = section_list.AddSection("Encryption"); Section* section_encryption =
section_list.AddSection("Encryption", /*is_sensitive=*/false);
Stat<bool>* is_using_explicit_passphrase = Stat<bool>* is_using_explicit_passphrase =
section_encryption->AddBoolStat("Explicit Passphrase"); section_encryption->AddBoolStat("Explicit Passphrase");
Stat<bool>* is_passphrase_required = Stat<bool>* is_passphrase_required =
...@@ -382,8 +397,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -382,8 +397,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<std::string>* passphrase_time = Stat<std::string>* passphrase_time =
section_encryption->AddStringStat("Passphrase Time"); section_encryption->AddStringStat("Passphrase Time");
Section* section_last_session = Section* section_last_session = section_list.AddSection(
section_list.AddSection("Status from Last Completed Session"); "Status from Last Completed Session", /*is_sensitive=*/false);
Stat<std::string>* session_source = Stat<std::string>* session_source =
section_last_session->AddStringStat("Sync Source"); section_last_session->AddStringStat("Sync Source");
Stat<std::string>* get_key_result = Stat<std::string>* get_key_result =
...@@ -393,7 +408,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -393,7 +408,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<std::string>* commit_result = Stat<std::string>* commit_result =
section_last_session->AddStringStat("Commit Step Result"); section_last_session->AddStringStat("Commit Step Result");
Section* section_counters = section_list.AddSection("Running Totals"); Section* section_counters =
section_list.AddSection("Running Totals", /*is_sensitive=*/false);
Stat<int>* notifications_received = Stat<int>* notifications_received =
section_counters->AddIntStat("Notifications Received"); section_counters->AddIntStat("Notifications Received");
Stat<int>* updates_received = Stat<int>* updates_received =
...@@ -409,8 +425,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -409,8 +425,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
Stat<int>* conflicts_resolved_server_wins = Stat<int>* conflicts_resolved_server_wins =
section_counters->AddIntStat("Conflicts Resolved: Server Wins"); section_counters->AddIntStat("Conflicts Resolved: Server Wins");
Section* section_this_cycle = Section* section_this_cycle = section_list.AddSection(
section_list.AddSection("Transient Counters (this cycle)"); "Transient Counters (this cycle)", /*is_sensitive=*/false);
Stat<int>* encryption_conflicts = Stat<int>* encryption_conflicts =
section_this_cycle->AddIntStat("Encryption Conflicts"); section_this_cycle->AddIntStat("Encryption Conflicts");
Stat<int>* hierarchy_conflicts = Stat<int>* hierarchy_conflicts =
...@@ -421,7 +437,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -421,7 +437,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
section_this_cycle->AddIntStat("Committed Items"); section_this_cycle->AddIntStat("Committed Items");
Section* section_that_cycle = section_list.AddSection( Section* section_that_cycle = section_list.AddSection(
"Transient Counters (last cycle of last completed session)"); "Transient Counters (last cycle of last completed session)",
/*is_sensitive=*/false);
Stat<int>* updates_downloaded = Stat<int>* updates_downloaded =
section_that_cycle->AddIntStat("Updates Downloaded"); section_that_cycle->AddIntStat("Updates Downloaded");
Stat<int>* committed_count = Stat<int>* committed_count =
...@@ -433,7 +450,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -433,7 +450,8 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
if (!service) { if (!service) {
transport_state->Set("Sync service does not exist"); transport_state->Set("Sync service does not exist");
about_info->SetKey(kDetailsKey, section_list.ToValue()); about_info->SetKey(kDetailsKey,
section_list.ToValue(include_sensitive_data));
return about_info; return about_info;
} }
...@@ -571,7 +589,7 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( ...@@ -571,7 +589,7 @@ std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
// This list of sections belongs in the 'details' field of the returned // This list of sections belongs in the 'details' field of the returned
// message. // message.
about_info->SetKey(kDetailsKey, section_list.ToValue()); about_info->SetKey(kDetailsKey, section_list.ToValue(include_sensitive_data));
// The values set from this point onwards do not belong in the // The values set from this point onwards do not belong in the
// details list. // details list.
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include "base/util/type_safety/strong_alias.h"
#include "components/signin/public/identity_manager/account_info.h" #include "components/signin/public/identity_manager/account_info.h"
#include "components/version_info/version_info.h" #include "components/version_info/version_info.h"
...@@ -65,10 +66,16 @@ extern const char kOnProtocolEvent[]; ...@@ -65,10 +66,16 @@ extern const char kOnProtocolEvent[];
extern const char kOnReceivedIncludeSpecificsInitialState[]; extern const char kOnReceivedIncludeSpecificsInitialState[];
extern const char kOnReceivedListOfTypes[]; extern const char kOnReceivedListOfTypes[];
extern const char kTypes[]; extern const char kTypes[];
using IncludeSensitiveData =
util::StrongAlias<class IncludeSensitiveDataTag, bool>;
// This function returns a DictionaryValue which contains all the information // This function returns a DictionaryValue which contains all the information
// required to populate the 'About' tab of about:sync. // required to populate the 'About' tab of about:sync.
// Note that |service| may be null. // Note that |service| may be null.
// If |include_sensitive_data| is false, Personally Identifiable Information
// won't be included in the return value.
std::unique_ptr<base::DictionaryValue> ConstructAboutInformation( std::unique_ptr<base::DictionaryValue> ConstructAboutInformation(
IncludeSensitiveData include_sensitive_data,
SyncService* service, SyncService* service,
version_info::Channel channel); version_info::Channel channel);
......
...@@ -18,8 +18,8 @@ TEST(SyncUIUtilTestAbout, ConstructAboutInformationWithUnrecoverableErrorTest) { ...@@ -18,8 +18,8 @@ TEST(SyncUIUtilTestAbout, ConstructAboutInformationWithUnrecoverableErrorTest) {
service.SetDisableReasons( service.SetDisableReasons(
syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR); syncer::SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR);
std::unique_ptr<base::DictionaryValue> strings( std::unique_ptr<base::DictionaryValue> strings(ConstructAboutInformation(
ConstructAboutInformation(&service, version_info::Channel::UNKNOWN)); IncludeSensitiveData(true), &service, version_info::Channel::UNKNOWN));
EXPECT_TRUE(strings->HasKey("unrecoverable_error_detected")); EXPECT_TRUE(strings->HasKey("unrecoverable_error_detected"));
} }
......
...@@ -267,9 +267,11 @@ void SyncInternalsMessageHandler::HandleJsEvent( ...@@ -267,9 +267,11 @@ void SyncInternalsMessageHandler::HandleJsEvent(
} }
void SyncInternalsMessageHandler::SendAboutInfo() { void SyncInternalsMessageHandler::SendAboutInfo() {
syncer::SyncService* sync_service = GetSyncService(); // This class serves to display debug information to the user, so it's fine to
// include sensitive data in ConstructAboutInformation().
std::unique_ptr<base::DictionaryValue> value = std::unique_ptr<base::DictionaryValue> value =
syncer::sync_ui_util::ConstructAboutInformation(sync_service, syncer::sync_ui_util::ConstructAboutInformation(
syncer::sync_ui_util::IncludeSensitiveData(true), GetSyncService(),
web_ui::GetChannel()); web_ui::GetChannel());
DispatchEvent(syncer::sync_ui_util::kOnAboutInfoUpdated, *value); DispatchEvent(syncer::sync_ui_util::kOnAboutInfoUpdated, *value);
} }
......
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