| // Copyright 2022 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/browser/support_tool/support_packet_metadata.h" |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/callback_forward.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/guid.h" |
| #include "base/i18n/time_formatting.h" |
| #include "base/location.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/task/task_traits.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/policy/policy_ui_utils.h" |
| #include "chrome/browser/support_tool/data_collector.h" |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "chromeos/system/statistics_provider.h" |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| #include "components/feedback/pii_types.h" |
| #include "components/policy/core/browser/webui/json_generation.h" |
| |
| using feedback::PIIType; |
| |
| namespace { |
| |
| const char kTimestampKey[] = "Timestamp"; |
| const char kIssueDescriptionKey[] = "Issue Description"; |
| const char kSupportCaseIdKey[] = "Support Case ID"; |
| const char kEmailAddressKey[] = "Email Address"; |
| const char kSupportPacketGUIDKey[] = "Support Packet ID"; |
| const char kDataCollectorListKey[] = "Data Collectors Included"; |
| const char kPlatformKey[] = "Platform"; |
| const char kOSKey[] = "OS"; |
| const char kChromeVersionKey[] = "Chrome Version"; |
| const char kChromeRevisionKey[] = "Chrome Revision"; |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| const char kSerialNumberKey[] = "Serial Number"; |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| const char kErrorMessagesKey[] = "Support Tool Errors"; |
| |
| const std::pair<const char* const, feedback::PIIType> kMetadataKeys[] = { |
| {kTimestampKey, PIIType::kNone}, |
| {kIssueDescriptionKey, PIIType::kNone}, |
| {kSupportCaseIdKey, PIIType::kNone}, |
| {kEmailAddressKey, PIIType::kEmail}, |
| {kSupportPacketGUIDKey, PIIType::kNone}, |
| {kDataCollectorListKey, PIIType::kNone}, |
| {kPlatformKey, PIIType::kNone}, |
| {kOSKey, PIIType::kNone}, |
| {kChromeVersionKey, PIIType::kNone}, |
| {kChromeRevisionKey, PIIType::kNone}, |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| {kSerialNumberKey, PIIType::kSerial}, |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| {kErrorMessagesKey, PIIType::kNone}}; |
| |
| void WriteContentsOnFile(base::FilePath metadata_file, |
| std::set<feedback::PIIType> pii_to_keep, |
| std::map<std::string, std::string> metadata_contents) { |
| // Create the file with write permissions. |
| base::WriteFile(metadata_file, ""); |
| for (const auto& metadata_key : kMetadataKeys) { |
| // If the metadata key is considered a PII-sensitive data and the it's not |
| // listed in `pii_to_keep`, we redact it. |
| bool redact = (metadata_key.second != PIIType::kNone && |
| pii_to_keep.find(metadata_key.second) == pii_to_keep.end()); |
| base::AppendToFile( |
| metadata_file, |
| base::StringPrintf( |
| "%s:\n%s\n", metadata_key.first, |
| redact ? "<REDACTED>" |
| : metadata_contents[metadata_key.first].c_str())); |
| } |
| } |
| } // namespace |
| |
| SupportPacketMetadata::SupportPacketMetadata(std::string case_id, |
| std::string email_address, |
| std::string issue_description) { |
| metadata_[kSupportCaseIdKey] = case_id; |
| metadata_[kIssueDescriptionKey] = issue_description; |
| if (!email_address.empty()) { |
| metadata_[kEmailAddressKey] = email_address; |
| pii_[PIIType::kEmail].insert(email_address); |
| } |
| metadata_[kSupportPacketGUIDKey] = GetGUIDForSupportPacket(); |
| SetChromeMetadataFields(); |
| } |
| |
| SupportPacketMetadata::~SupportPacketMetadata() = default; |
| |
| void SupportPacketMetadata::SetChromeMetadataFields() { |
| base::Value::Dict chrome_metadata = policy::GetChromeMetadataValue( |
| policy::GetChromeMetadataParams(/*application_name=*/"Support Tool")); |
| |
| FindStringAndSetInSupportPacketMetadata( |
| chrome_metadata, policy::kChromeMetadataPlatformKey, kPlatformKey); |
| FindStringAndSetInSupportPacketMetadata(chrome_metadata, |
| policy::kChromeMetadataOSKey, kOSKey); |
| FindStringAndSetInSupportPacketMetadata( |
| chrome_metadata, policy::kChromeMetadataVersionKey, kChromeVersionKey); |
| FindStringAndSetInSupportPacketMetadata( |
| chrome_metadata, policy::kChromeMetadataRevisionKey, kChromeRevisionKey); |
| } |
| |
| void SupportPacketMetadata::FindStringAndSetInSupportPacketMetadata( |
| const base::Value::Dict& chrome_metadata, |
| const char* chrome_metadata_key, |
| const char* support_packet_key) { |
| const std::string* value = chrome_metadata.FindString(chrome_metadata_key); |
| if (value) { |
| metadata_[support_packet_key] = *value; |
| } |
| } |
| |
| void SupportPacketMetadata::PopulateMetadataContents( |
| const base::Time& timestamp, |
| const std::vector<std::unique_ptr<DataCollector>>& data_collectors_included, |
| base::OnceClosure on_metadata_contents_populated) { |
| metadata_[kDataCollectorListKey] = |
| GetDataCollectorsListString(data_collectors_included); |
| metadata_[kTimestampKey] = GetTimestampString(timestamp); |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| chromeos::system::StatisticsProvider::GetInstance() |
| ->ScheduleOnMachineStatisticsLoaded( |
| base::BindOnce(&SupportPacketMetadata::OnMachineStatisticsLoaded, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(on_metadata_contents_populated))); |
| #else |
| std::move(on_metadata_contents_populated).Run(); |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| } |
| |
| #if BUILDFLAG(IS_CHROMEOS_ASH) |
| void SupportPacketMetadata::OnMachineStatisticsLoaded( |
| base::OnceClosure on_metadata_contents_populated) { |
| std::string machine_serial = |
| chromeos::system::StatisticsProvider::GetInstance() |
| ->GetEnterpriseMachineID(); |
| if (!machine_serial.empty()) { |
| pii_[PIIType::kSerial].insert(machine_serial); |
| metadata_[kSerialNumberKey] = machine_serial; |
| } |
| std::move(on_metadata_contents_populated).Run(); |
| } |
| #endif // BUILDFLAG(IS_CHROMEOS_ASH) |
| |
| const std::string& SupportPacketMetadata::GetCaseId() { |
| return metadata_[kSupportCaseIdKey]; |
| } |
| |
| const PIIMap& SupportPacketMetadata::GetPII() { |
| return pii_; |
| } |
| |
| void SupportPacketMetadata::InsertErrors( |
| const std::set<SupportToolError>& errors) { |
| error_messages_.reserve(error_messages_.size() + errors.size()); |
| for (const auto& error : errors) { |
| error_messages_.push_back(error.error_message); |
| } |
| } |
| |
| std::string SupportPacketMetadata::GetTimestampString(const base::Time& time) { |
| return base::UTF16ToUTF8(base::TimeFormatShortDateAndTimeWithTimeZone(time)); |
| } |
| |
| std::string SupportPacketMetadata::GetDataCollectorsListString( |
| const std::vector<std::unique_ptr<DataCollector>>& |
| data_collectors_included) { |
| std::vector<std::string> names; |
| for (const auto& data_collector : data_collectors_included) { |
| names.push_back(data_collector->GetName()); |
| } |
| return base::JoinString(names, "\n"); |
| } |
| |
| std::string SupportPacketMetadata::GetGUIDForSupportPacket() { |
| return base::GUID::GenerateRandomV4().AsLowercaseString(); |
| } |
| |
| void SupportPacketMetadata::AddErrorMessagesToMetadata() { |
| metadata_[kErrorMessagesKey] = base::JoinString(error_messages_, "\n"); |
| } |
| |
| void SupportPacketMetadata::WriteMetadataFile( |
| base::FilePath target_path, |
| std::set<feedback::PIIType> pii_to_keep, |
| base::OnceClosure on_metadata_file_written) { |
| AddErrorMessagesToMetadata(); |
| base::FilePath metadata_file = |
| target_path.Append(FILE_PATH_LITERAL("metadata.txt")); |
| base::ThreadPool::PostTaskAndReply( |
| FROM_HERE, {base::MayBlock()}, |
| base::BindOnce(&WriteContentsOnFile, metadata_file, pii_to_keep, |
| metadata_), |
| std::move(on_metadata_file_written)); |
| } |