#include "extensions/browser/api/web_request/web_request_event_details.h"
#include <utility>
#include <vector>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_host.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api/web_request/upload_data_presenter.h"
#include "extensions/browser/api/web_request/web_request_api_constants.h"
#include "extensions/browser/api/web_request/web_request_api_helpers.h"
#include "extensions/browser/api/web_request/web_request_info.h"
#include "extensions/browser/api/web_request/web_request_permissions.h"
#include "extensions/browser/api/web_request/web_request_resource_type.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/permissions/permissions_data.h"
#include "net/base/auth.h"
#include "net/base/upload_data_stream.h"
#include "net/cert/cert_status_flags.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));
using extension_web_request_api_helpers::ExtraInfoSpec;
namespace helpers = extension_web_request_api_helpers;
namespace keys = extension_web_request_api_constants;
namespace extensions {
namespace {
void EraseHeadersIf(
base::Value::List& headers,
base::RepeatingCallback<bool(const std::string&)> predicate) {
headers.EraseIf([&predicate](const base::Value& v) {
return predicate.Run(*v.GetDict().FindString(keys::kHeaderNameKey));
});
}
void FilterSecurityInfo(base::Value::Dict& result, int extra_info_spec) {
if (!(extra_info_spec & ExtraInfoSpec::SECURITY_INFO)) {
result.Remove(keys::kSecurityInfoKey);
return;
}
if (!(extra_info_spec & ExtraInfoSpec::SECURITY_INFO_RAW_DER)) {
auto* security_info = result.FindDict(keys::kSecurityInfoKey);
if (!security_info) {
return;
}
auto* certificates = security_info->FindList(keys::kCertificatesKey);
if (!certificates || certificates->size() <= 0) {
return;
}
if (certificates->front().is_dict()) {
certificates->front().GetDict().Remove(keys::kRawDerKey);
}
}
}
}
WebRequestEventDetails::WebRequestEventDetails(const WebRequestInfo& request,
int extra_info_spec)
: extra_info_spec_(extra_info_spec),
render_process_id_(content::ChildProcessHost::kInvalidUniqueID) {
dict_.Set(keys::kMethodKey, request.method);
dict_.Set(keys::kRequestIdKey, base::NumberToString(request.id));
dict_.Set(keys::kTimeStampKey,
base::Time::Now().InMillisecondsFSinceUnixEpoch());
dict_.Set(keys::kTypeKey,
WebRequestResourceTypeToString(request.web_request_type));
dict_.Set(keys::kUrlKey, request.url.spec());
dict_.Set(keys::kTabIdKey, request.frame_data.tab_id);
dict_.Set(keys::kFrameIdKey, request.frame_data.frame_id);
dict_.Set(keys::kParentFrameIdKey, request.frame_data.parent_frame_id);
if (request.frame_data.document_id) {
dict_.Set(keys::kDocumentIdKey, request.frame_data.document_id.ToString());
}
if (request.frame_data.parent_document_id) {
dict_.Set(keys::kParentDocumentIdKey,
request.frame_data.parent_document_id.ToString());
}
if (request.frame_data.frame_id >= 0) {
dict_.Set(keys::kFrameTypeKey, ToString(request.frame_data.frame_type));
dict_.Set(keys::kDocumentLifecycleKey,
ToString(request.frame_data.document_lifecycle));
}
initiator_ = request.initiator;
render_process_id_ = request.render_process_id;
}
WebRequestEventDetails::~WebRequestEventDetails() = default;
void WebRequestEventDetails::SetRequestBody(WebRequestInfo* request) {
if (!(extra_info_spec_ & ExtraInfoSpec::REQUEST_BODY)) {
return;
}
request_body_ = std::nullopt;
if (request->request_body_data) {
request_body_ = std::move(request->request_body_data);
request->request_body_data.reset();
}
}
void WebRequestEventDetails::SetRequestHeaders(
const net::HttpRequestHeaders& request_headers) {
if (!(extra_info_spec_ & ExtraInfoSpec::REQUEST_HEADERS)) {
return;
}
request_headers_ = base::Value::List();
for (net::HttpRequestHeaders::Iterator it(request_headers); it.GetNext();) {
request_headers_->Append(
helpers::CreateHeaderDictionary(it.name(), it.value()));
}
}
void WebRequestEventDetails::SetAuthInfo(
const net::AuthChallengeInfo& auth_info) {
dict_.Set(keys::kIsProxyKey, auth_info.is_proxy);
if (!auth_info.scheme.empty()) {
dict_.Set(keys::kSchemeKey, auth_info.scheme);
}
if (!auth_info.realm.empty()) {
dict_.Set(keys::kRealmKey, auth_info.realm);
}
base::Value::Dict challenger;
challenger.Set(keys::kHostKey, auth_info.challenger.host());
challenger.Set(keys::kPortKey, auth_info.challenger.port());
dict_.Set(keys::kChallengerKey, std::move(challenger));
}
void WebRequestEventDetails::SetResponseHeaders(
const WebRequestInfo& request,
const net::HttpResponseHeaders* response_headers) {
if (!response_headers) {
dict_.Set(keys::kStatusCodeKey, request.response_code);
dict_.Set(keys::kStatusLineKey, "");
} else {
dict_.Set(keys::kStatusCodeKey, response_headers->response_code());
dict_.Set(keys::kStatusLineKey, response_headers->GetStatusLine());
}
if (extra_info_spec_ & ExtraInfoSpec::RESPONSE_HEADERS) {
response_headers_ = base::Value::List();
if (response_headers) {
size_t iter = 0;
std::string name;
std::string value;
while (response_headers->EnumerateHeaderLines(&iter, &name, &value)) {
if (ExtensionsAPIClient::Get()->ShouldHideResponseHeader(request.url,
name)) {
continue;
}
response_headers_->Append(helpers::CreateHeaderDictionary(name, value));
}
}
}
}
void WebRequestEventDetails::SetSecurityInfo(const WebRequestInfo& request) {
if (!(extra_info_spec_ & ExtraInfoSpec::SECURITY_INFO)) {
return;
}
base::Value::Dict security_info;
if (!request.ssl_info || !request.ssl_info->cert) {
security_info.Set(keys::kStateKey, "insecure");
} else {
if (net::IsCertStatusError(request.ssl_info->cert_status)) {
security_info.Set(keys::kStateKey, "broken");
} else {
security_info.Set(keys::kStateKey, "secure");
}
base::Value::Dict leaf_cert;
if (extra_info_spec_ & ExtraInfoSpec::SECURITY_INFO_RAW_DER) {
base::span<const uint8_t> cert_span = request.ssl_info->cert->cert_span();
leaf_cert.Set(keys::kRawDerKey, base::Value::BlobStorage(
cert_span.begin(), cert_span.end()));
}
base::Value::Dict fingerprint;
std::array<uint8_t, 32> sha256_bytes =
net::X509Certificate::CalculateFingerprint256(
request.ssl_info->cert->cert_buffer());
fingerprint.Set(keys::kSha256Key, base::HexEncode(sha256_bytes));
leaf_cert.Set(keys::kFingerprintKey, std::move(fingerprint));
base::Value::List certificates;
certificates.Append(std::move(leaf_cert));
security_info.Set(keys::kCertificatesKey, std::move(certificates));
}
dict_.Set(keys::kSecurityInfoKey, std::move(security_info));
}
void WebRequestEventDetails::SetResponseSource(const WebRequestInfo& request) {
dict_.Set(keys::kFromCache, request.response_from_cache);
if (!request.response_ip.empty()) {
dict_.Set(keys::kIpKey, request.response_ip);
}
}
base::Value::Dict WebRequestEventDetails::GetFilteredDict(
int extra_info_spec,
PermissionHelper* permission_helper,
const extensions::ExtensionId& extension_id,
bool crosses_incognito) const {
base::Value::Dict result = dict_.Clone();
if ((extra_info_spec & ExtraInfoSpec::REQUEST_BODY) && request_body_) {
result.Set(keys::kRequestBodyKey, request_body_->Clone());
}
if ((extra_info_spec & ExtraInfoSpec::REQUEST_HEADERS) && request_headers_) {
content::RenderProcessHost* process =
content::RenderProcessHost::FromID(render_process_id_);
content::BrowserContext* browser_context =
process ? process->GetBrowserContext() : nullptr;
base::Value::List request_headers = request_headers_->Clone();
EraseHeadersIf(request_headers,
base::BindRepeating(helpers::ShouldHideRequestHeader,
browser_context, extra_info_spec));
result.Set(keys::kRequestHeadersKey, std::move(request_headers));
}
if ((extra_info_spec & ExtraInfoSpec::RESPONSE_HEADERS) &&
response_headers_) {
base::Value::List response_headers = response_headers_->Clone();
EraseHeadersIf(response_headers,
base::BindRepeating(helpers::ShouldHideResponseHeader,
extra_info_spec));
result.Set(keys::kResponseHeadersKey, std::move(response_headers));
}
FilterSecurityInfo(result, extra_info_spec);
if (initiator_) {
int tab_id = dict_.FindInt(keys::kTabIdKey).value_or(-1);
if (initiator_->opaque() ||
WebRequestPermissions::CanExtensionAccessInitiator(
permission_helper, extension_id, initiator_, tab_id,
crosses_incognito)) {
result.Set(keys::kInitiatorKey, initiator_->Serialize());
}
}
return result;
}
base::Value::Dict WebRequestEventDetails::GetAndClearDict() {
return std::move(dict_);
}
}