910e62b5创建于 1月15日历史提交
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 {

// Removes all headers for which predicate(header_name) returns true.
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);
    }
  }
}

}  // namespace

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) {
    // Not all URLRequestJobs specify response headers. E.g. URLRequestFTPJob,
    // URLRequestFileJob and some redirects.
    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);

  // Only listeners with a permission for the initiator should receive it.
  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_);
}

}  // namespace extensions