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

#include "content/browser/attribution_reporting/attribution_debug_report.h"

#include <stdint.h>

#include <optional>
#include <string_view>
#include <utility>
#include <variant>

#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/function_ref.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "components/attribution_reporting/constants.h"
#include "components/attribution_reporting/debug_types.h"
#include "components/attribution_reporting/debug_types.mojom.h"
#include "components/attribution_reporting/destination_set.h"
#include "components/attribution_reporting/os_registration.h"
#include "components/attribution_reporting/registration_header_error.h"
#include "components/attribution_reporting/source_registration.h"
#include "components/attribution_reporting/suitable_origin.h"
#include "components/attribution_reporting/trigger_registration.h"
#include "content/browser/attribution_reporting/attribution_constants.h"
#include "content/browser/attribution_reporting/attribution_reporting.mojom.h"
#include "content/browser/attribution_reporting/attribution_trigger.h"
#include "content/browser/attribution_reporting/common_source_info.h"
#include "content/browser/attribution_reporting/create_report_result.h"
#include "content/browser/attribution_reporting/os_registration.h"
#include "content/browser/attribution_reporting/storable_source.h"
#include "content/browser/attribution_reporting/store_source_result.h"
#include "net/base/schemeful_site.h"
#include "third_party/abseil-cpp/absl/functional/overload.h"
#include "url/gurl.h"

namespace content {

namespace {

using ::attribution_reporting::mojom::DebugDataType;

constexpr char kAttributionDestination[] = "attribution_destination";

struct DebugDataTypeAndBody {
  DebugDataType debug_data_type;
  base::Value::Dict body;

  explicit DebugDataTypeAndBody(DebugDataType debug_data_type,
                                base::Value limit = base::Value(),
                                base::Value::Dict body = base::Value::Dict())
      : debug_data_type(debug_data_type), body(std::move(body)) {
    if (!limit.is_none()) {
      this->body.Set("limit", std::move(limit));
    }
  }
};

template <typename T>
base::Value GetLimit(T limit) {
  return base::Value(base::NumberToString(limit));
}

std::optional<DebugDataTypeAndBody> GetReportDataBody(
    const StoreSourceResult& result) {
  const auto make_report_body = [&](DebugDataType type,
                                    base::Value limit = base::Value()) {
    base::Value::Dict body;
    if (result.destination_limit().has_value()) {
      body.Set("source_destination_limit",
               GetLimit(result.destination_limit().value()));
    }

    return std::make_optional<DebugDataTypeAndBody>(type, std::move(limit),
                                                    std::move(body));
  };

  return std::visit(
      absl::Overload{
          [](StoreSourceResult::ProhibitedByBrowserPolicy) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [&](std::variant<StoreSourceResult::Success,
                           // `kSourceSuccess` is sent for a few errors as well
                           // to mitigate the security concerns on reporting
                           // these errors. Because these errors are thrown
                           // based on information across reporting origins,
                           // reporting on them would violate the same-origin
                           // policy.
                           StoreSourceResult::ExcessiveReportingOrigins,
                           StoreSourceResult::DestinationGlobalLimitReached>) {
            return make_report_body(result.is_noised()
                                        ? DebugDataType::kSourceNoised
                                        : DebugDataType::kSourceSuccess);
          },
          [&](StoreSourceResult::InsufficientUniqueDestinationCapacity v) {
            return make_report_body(DebugDataType::kSourceDestinationLimit,
                                    GetLimit(v.limit));
          },
          [&](std::variant<StoreSourceResult::DestinationReportingLimitReached,
                           StoreSourceResult::DestinationBothLimitsReached> v) {
            return make_report_body(
                DebugDataType::kSourceDestinationRateLimit,
                std::visit([](auto v) { return GetLimit(v.limit); }, v));
          },
          [&](StoreSourceResult::DestinationPerDayReportingLimitReached v) {
            return make_report_body(
                DebugDataType::kSourceDestinationPerDayRateLimit,
                GetLimit(v.limit));
          },
          [&](StoreSourceResult::InsufficientSourceCapacity v) {
            return make_report_body(DebugDataType::kSourceStorageLimit,
                                    GetLimit(v.limit));
          },
          [&](StoreSourceResult::InternalError) {
            return make_report_body(DebugDataType::kSourceUnknownError);
          },
          [&](StoreSourceResult::ReportingOriginsPerSiteLimitReached v) {
            return make_report_body(
                DebugDataType::kSourceReportingOriginPerSiteLimit,
                GetLimit(v.limit));
          },
          [&](StoreSourceResult::ExceedsMaxChannelCapacity v) {
            return make_report_body(DebugDataType::kSourceChannelCapacityLimit,
                                    base::Value(v.limit));
          },
          [&](StoreSourceResult::ExceedsMaxScopesChannelCapacity v) {
            return make_report_body(
                DebugDataType::kSourceScopesChannelCapacityLimit,
                base::Value(v.limit));
          },
          [&](StoreSourceResult::ExceedsMaxTriggerStateCardinality v) {
            return make_report_body(
                DebugDataType::kSourceTriggerStateCardinalityLimit,
                GetLimit(v.limit));
          },
          [&](StoreSourceResult::ExceedsMaxEventStatesLimit v) {
            return make_report_body(DebugDataType::kSourceMaxEventStatesLimit,
                                    GetLimit(v.limit));
          },
      },
      result.result());
}

std::optional<DebugDataTypeAndBody> GetReportDataTypeAndLimit(
    const CreateReportResult::EventLevel& result) {
  return std::visit(
      absl::Overload{
          [](const CreateReportResult::EventLevelSuccess&) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::ProhibitedByBrowserPolicy) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::NotRegistered) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::InternalError) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerUnknownError);
          },
          [](CreateReportResult::NoCapacityForConversionDestination v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventStorageLimit, GetLimit(v.max));
          },
          [](CreateReportResult::ExcessiveReportingOrigins v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerReportingOriginLimit, GetLimit(v.max));
          },
          [](CreateReportResult::NoMatchingImpressions) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerNoMatchingSource);
          },
          [](CreateReportResult::ExcessiveAttributions v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::
                    kTriggerEventAttributionsPerSourceDestinationLimit,
                GetLimit(v.max));
          },
          [](CreateReportResult::NoMatchingSourceFilterData) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerNoMatchingFilterData);
          },
          [](CreateReportResult::Deduplicated) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventDeduplicated);
          },
          [](CreateReportResult::NoMatchingConfigurations) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventNoMatchingConfigurations);
          },
          [](CreateReportResult::NeverAttributedSource) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventNoise);
          },
          [](CreateReportResult::FalselyAttributedSource) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventNoise);
          },
          [](const CreateReportResult::PriorityTooLow&) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventLowPriority);
          },
          [](const CreateReportResult::ExcessiveEventLevelReports&) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventExcessiveReports);
          },
          [](CreateReportResult::ReportWindowNotStarted) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventReportWindowNotStarted);
          },
          [](CreateReportResult::ReportWindowPassed) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventReportWindowPassed);
          },
          [](CreateReportResult::NoMatchingTriggerData) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerEventNoMatchingTriggerData);
          },
      },
      result);
}

std::optional<DebugDataTypeAndBody> GetReportDataTypeAndLimit(
    const CreateReportResult::Aggregatable& result) {
  return std::visit(
      absl::Overload{
          [](const CreateReportResult::AggregatableSuccess&) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::NotRegistered) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::ProhibitedByBrowserPolicy) {
            return std::optional<DebugDataTypeAndBody>();
          },
          [](CreateReportResult::InternalError) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerUnknownError);
          },
          [](CreateReportResult::NoCapacityForConversionDestination v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateStorageLimit, GetLimit(v.max));
          },
          [](CreateReportResult::ExcessiveReportingOrigins v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerReportingOriginLimit, GetLimit(v.max));
          },
          [](CreateReportResult::NoMatchingImpressions) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerNoMatchingSource);
          },
          [](CreateReportResult::ExcessiveAttributions v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::
                    kTriggerAggregateAttributionsPerSourceDestinationLimit,
                GetLimit(v.max));
          },
          [](CreateReportResult::NoMatchingSourceFilterData) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerNoMatchingFilterData);
          },
          [](CreateReportResult::Deduplicated) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateDeduplicated);
          },
          [](CreateReportResult::NoHistograms) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateNoContributions);
          },
          [](CreateReportResult::InsufficientBudget) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateInsufficientBudget,
                GetLimit(attribution_reporting::kMaxAggregatableValue));
          },
          [](const CreateReportResult::InsufficientNamedBudget& v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateInsufficientNamedBudget,
                GetLimit(v.budget), base::Value::Dict().Set("name", v.name));
          },
          [](CreateReportResult::ReportWindowPassed) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateReportWindowPassed);
          },
          [](CreateReportResult::ExcessiveAggregatableReports v) {
            return std::make_optional<DebugDataTypeAndBody>(
                DebugDataType::kTriggerAggregateExcessiveReports,
                GetLimit(v.max));
          },
      },
      result);
}

void SetSourceData(base::Value::Dict& data_body,
                   uint64_t source_event_id,
                   const net::SchemefulSite& source_site,
                   std::optional<uint64_t> source_debug_key) {
  data_body.Set("source_event_id", base::NumberToString(source_event_id));
  data_body.Set("source_site", source_site.Serialize());
  if (source_debug_key) {
    data_body.Set("source_debug_key", base::NumberToString(*source_debug_key));
  }
}

base::Value::Dict GetReportDataBody(DebugDataTypeAndBody data,
                                    const CreateReportResult& result) {
  if (data.debug_data_type == DebugDataType::kTriggerEventExcessiveReports ||
      data.debug_data_type == DebugDataType::kTriggerEventLowPriority) {
    CHECK(result.dropped_event_level_report());
    return result.dropped_event_level_report()->ReportBody();
  }

  data.body.Set(
      kAttributionDestination,
      net::SchemefulSite(result.trigger().destination_origin()).Serialize());

  if (std::optional<uint64_t> debug_key =
          result.trigger().registration().debug_key) {
    data.body.Set("trigger_debug_key", base::NumberToString(*debug_key));
  }

  if (const std::optional<StoredSource>& source = result.source()) {
    SetSourceData(data.body, source->source_event_id(),
                  source->common_info().source_site(), source->debug_key());
  }

  return std::move(data.body);
}

base::Value::Dict GetReportData(DebugDataType type, base::Value::Dict body) {
  return base::Value::Dict()
      .Set("type", attribution_reporting::SerializeDebugDataType(type))
      .Set("body", std::move(body));
}

void RecordVerboseDebugReportType(DebugDataType type) {
  base::UmaHistogramEnumeration("Conversions.SentVerboseDebugReportType4",
                                type);
}

}  // namespace

GURL AttributionDebugReport::ReportUrl() const {
  static constexpr char kPath[] =
      "/.well-known/attribution-reporting/debug/verbose";

  GURL::Replacements replacements;
  replacements.SetPathStr(kPath);
  return reporting_origin_->GetURL().ReplaceComponents(replacements);
}

// static
std::optional<AttributionDebugReport> AttributionDebugReport::Create(
    base::FunctionRef<bool()> is_operation_allowed,
    const StoreSourceResult& result) {
  const StorableSource& source = result.source();
  if (!source.registration().debug_reporting ||
      !source.common_info().cookie_based_debug_allowed() ||
      source.is_within_fenced_frame() || !is_operation_allowed()) {
    return std::nullopt;
  }

  std::optional<DebugDataTypeAndBody> data = GetReportDataBody(result);
  if (!data) {
    return std::nullopt;
  }

  RecordVerboseDebugReportType(data->debug_data_type);

  const attribution_reporting::SourceRegistration& registration =
      source.registration();

  data->body.Set(kAttributionDestination,
                 registration.destination_set.ToJson());
  SetSourceData(data->body, registration.source_event_id,
                source.common_info().source_site(), registration.debug_key);

  return AttributionDebugReport(
      base::Value::List::with_capacity(1).Append(
          GetReportData(data->debug_data_type, std::move(data->body))),
      source.common_info().reporting_origin());
}

// static
std::optional<AttributionDebugReport> AttributionDebugReport::Create(
    base::FunctionRef<bool()> is_operation_allowed,
    bool cookie_based_debug_allowed,
    const CreateReportResult& result) {
  if (!result.trigger().registration().debug_reporting ||
      !cookie_based_debug_allowed ||
      result.trigger().is_within_fenced_frame() || !is_operation_allowed()) {
    return std::nullopt;
  }

  if (result.source() &&
      !result.source()->common_info().cookie_based_debug_allowed()) {
    return std::nullopt;
  }

  base::Value::List report_body;

  std::optional<DebugDataType> event_level_type;
  if (std::optional<DebugDataTypeAndBody> event_level_data_type_limit =
          GetReportDataTypeAndLimit(result.event_level_result())) {
    event_level_type = event_level_data_type_limit->debug_data_type;
    report_body.Append(GetReportData(
        *event_level_type,
        GetReportDataBody(*std::move(event_level_data_type_limit), result)));
    RecordVerboseDebugReportType(*event_level_type);
  }

  if (std::optional<DebugDataTypeAndBody> aggregatable_data_type_limit =
          GetReportDataTypeAndLimit(result.aggregatable_result());
      aggregatable_data_type_limit &&
      aggregatable_data_type_limit->debug_data_type != event_level_type) {
    DebugDataType aggregatable_type =
        aggregatable_data_type_limit->debug_data_type;
    report_body.Append(GetReportData(
        aggregatable_type,
        GetReportDataBody(*std::move(aggregatable_data_type_limit), result)));
    RecordVerboseDebugReportType(aggregatable_type);
  }

  if (report_body.empty()) {
    return std::nullopt;
  }

  return AttributionDebugReport(std::move(report_body),
                                result.trigger().reporting_origin());
}

// static
std::optional<AttributionDebugReport> AttributionDebugReport::Create(
    const OsRegistration& registration,
    size_t item_index,
    base::FunctionRef<bool(const url::Origin&)> is_operation_allowed) {
  CHECK_LT(item_index, registration.registration_items.size());
  const auto& registration_item = registration.registration_items[item_index];
  if (!registration_item.debug_reporting ||
      registration.is_within_fenced_frame) {
    return std::nullopt;
  }

  auto registration_origin =
      attribution_reporting::SuitableOrigin::Create(registration_item.url);
  if (!registration_origin.has_value() ||
      !is_operation_allowed(*registration_origin)) {
    return std::nullopt;
  }

  DebugDataType data_type;
  switch (registration.GetType()) {
    case attribution_reporting::mojom::RegistrationType::kSource:
      data_type = DebugDataType::kOsSourceDelegated;
      break;
    case attribution_reporting::mojom::RegistrationType::kTrigger:
      data_type = DebugDataType::kOsTriggerDelegated;
      break;
  }

  RecordVerboseDebugReportType(data_type);

  return AttributionDebugReport(
      base::Value::List::with_capacity(1).Append(GetReportData(
          data_type,
          base::Value::Dict()
              .Set(
                  "context_site",
                  net::SchemefulSite(registration.top_level_origin).Serialize())
              .Set("registration_url", registration_item.url.spec()))),
      *std::move(registration_origin));
}

std::optional<AttributionDebugReport> AttributionDebugReport::Create(
    attribution_reporting::SuitableOrigin reporting_origin,
    attribution_reporting::RegistrationHeaderError error,
    const attribution_reporting::SuitableOrigin& context_origin,
    bool is_within_fenced_frame,
    base::FunctionRef<bool(const url::Origin&)> is_operation_allowed) {
  if (is_within_fenced_frame || !is_operation_allowed(*reporting_origin)) {
    return std::nullopt;
  }

  constexpr DebugDataType kDataType = DebugDataType::kHeaderParsingError;

  RecordVerboseDebugReportType(kDataType);

  return AttributionDebugReport(
      base::Value::List::with_capacity(1).Append(GetReportData(
          kDataType, base::Value::Dict()
                         .Set("context_site",
                              net::SchemefulSite(context_origin).Serialize())
                         .Set("header", error.HeaderName())
                         .Set("value", std::move(error.header_value)))),
      std::move(reporting_origin));
}

AttributionDebugReport::AttributionDebugReport(
    base::Value::List report_body,
    attribution_reporting::SuitableOrigin reporting_origin)
    : report_body_(std::move(report_body)),
      reporting_origin_(std::move(reporting_origin)) {
  CHECK(!report_body_.empty());
}

AttributionDebugReport::~AttributionDebugReport() = default;

AttributionDebugReport::AttributionDebugReport(AttributionDebugReport&&) =
    default;

AttributionDebugReport& AttributionDebugReport::operator=(
    AttributionDebugReport&&) = default;

}  // namespace content