1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "components/feedback/feedback_util.h"
#include <optional>
#include <string>
#include <vector>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "components/feedback/feedback_report.h"
#include "third_party/zlib/google/zip.h"
namespace {
constexpr char kMultilineIndicatorString[] = "<multiline>\n";
constexpr char kMultilineStartString[] = "---------- START ----------\n";
constexpr char kMultilineEndString[] = "---------- END ----------\n\n";
} // namespace
namespace feedback_util {
std::optional<std::string> ZipString(const base::FilePath& filename,
std::string_view data) {
base::ScopedTempDir temp_dir;
base::FilePath zip_file;
// Create a temporary directory, put the logs into a file in it. Create
// another temporary file to receive the zip file in.
if (!temp_dir.CreateUniqueTempDir()) {
return std::nullopt;
}
if (!base::WriteFile(temp_dir.GetPath().Append(filename), data)) {
return std::nullopt;
}
if (!base::CreateTemporaryFile(&zip_file)) {
return std::nullopt;
}
std::string compressed_logs;
bool succeed = zip::Zip(temp_dir.GetPath(), zip_file, false) &&
base::ReadFileToString(zip_file, &compressed_logs);
base::DeleteFile(zip_file);
if (!succeed) {
return std::nullopt;
}
return compressed_logs;
}
std::string LogsToString(const FeedbackCommon::SystemLogsMap& sys_info) {
std::string syslogs_string;
for (const auto& iter : sys_info) {
std::string key = iter.first;
base::TrimString(key, "\n ", &key);
if (key == feedback::FeedbackReport::kCrashReportIdsKey ||
key == feedback::FeedbackReport::kAllCrashReportIdsKey) {
// Avoid adding the crash IDs to the system_logs.txt file for privacy
// reasons. They should just be part of the product specific data.
continue;
}
if (key == feedback::FeedbackReport::kFeedbackUserCtlConsentKey) {
// Avoid adding user consent to the system_logs.txt file. It just needs to
// be in the product specific data.
continue;
}
std::string value = iter.second;
base::TrimString(value, "\n ", &value);
if (value.find("\n") != std::string::npos) {
syslogs_string.append(key + "=" + kMultilineIndicatorString +
kMultilineStartString + value + "\n" +
kMultilineEndString);
} else {
syslogs_string.append(key + "=" + value + "\n");
}
}
return syslogs_string;
}
void RemoveUrlsFromAutofillData(std::string& autofill_metadata) {
std::optional<base::Value::Dict> autofill_data = base::JSONReader::ReadDict(
autofill_metadata, base::JSON_ALLOW_TRAILING_COMMAS);
if (!autofill_data) {
LOG(ERROR) << "base::JSONReader::Read failed to translate to JSON";
return;
}
if (base::Value::List* form_structures =
autofill_data->FindList("formStructures")) {
for (base::Value& item : *form_structures) {
auto& dict = item.GetDict();
dict.Remove("sourceUrl");
dict.Remove("mainFrameUrl");
}
}
base::JSONWriter::Write(*autofill_data, &autofill_metadata);
return;
}
// Note: This function is excluded from win build because its unit tests do
// not pass on OS_WIN.
// This function is only called on ChromeOS.
// See https://crbug.com/1119560.
#if !BUILDFLAG(IS_WIN)
std::optional<std::string> ReadEndOfFile(const base::FilePath& path,
size_t max_size) {
if (path.ReferencesParent()) {
LOG(ERROR) << "ReadEndOfFile can't be called on file paths with parent "
"references.";
return std::nullopt;
}
base::ScopedFILE fp(base::OpenFile(path, "r"));
if (!fp) {
PLOG(ERROR) << "Failed to open file " << path.value();
return std::nullopt;
}
std::vector<char> chunk(max_size);
std::vector<char> last_chunk(max_size);
size_t total_bytes_read = 0;
size_t bytes_read = 0;
// Since most logs are not seekable, read until the end keeping tracking of
// last two chunks.
while ((bytes_read = fread(chunk.data(), 1, max_size, fp.get())) ==
max_size) {
total_bytes_read += bytes_read;
last_chunk.swap(chunk);
chunk[0] = '\0';
}
total_bytes_read += bytes_read;
std::string contents;
if (total_bytes_read < max_size) {
// File is smaller than max_size
contents.assign(chunk.data(), bytes_read);
} else if (bytes_read == 0) {
// File is exactly max_size or a multiple of max_size
contents.assign(last_chunk.data(), max_size);
} else {
// Number of bytes to keep from last_chunk
size_t bytes_from_last = max_size - bytes_read;
// Shift left last_chunk by size of chunk and fit it in the back of
// last_chunk.
memmove(last_chunk.data(), last_chunk.data() + bytes_read, bytes_from_last);
memcpy(last_chunk.data() + bytes_from_last, chunk.data(), bytes_read);
contents.assign(last_chunk.data(), max_size);
}
return contents;
}
#endif // !BUILDFLAG(IS_WIN)
} // namespace feedback_util
|