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/fenced_frame/fenced_frame_config.h"

#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/uuid.h"
#include "content/browser/fenced_frame/fenced_frame_reporter.h"
#include "services/network/public/cpp/permissions_policy/fenced_frame_permissions_policies.h"
#include "services/network/public/cpp/permissions_policy/permissions_policy.h"
#include "services/network/public/cpp/permissions_policy/permissions_policy_declaration.h"
#include "third_party/blink/public/common/interest_group/ad_auction_constants.h"

namespace content {

GURL GenerateUrnUuid() {
  return GURL(kUrnUuidPrefix +
              base::Uuid::GenerateRandomV4().AsLowercaseString());
}

std::string SubstituteMappedStrings(
    const std::string& input,
    const std::vector<std::pair<std::string, std::string>>& substitutions) {
  std::vector<std::string> output_vec;
  size_t input_idx = 0;
  while (input_idx < input.size()) {
    size_t replace_idx = input.size();
    size_t replace_end_idx = input.size();
    std::pair<std::string, std::string> const* next_replacement = nullptr;
    for (const auto& substitution : substitutions) {
      size_t found_idx = input.find(substitution.first, input_idx);
      if (found_idx < replace_idx) {
        replace_idx = found_idx;
        replace_end_idx = found_idx + substitution.first.size();
        next_replacement = &substitution;
      }
    }
    output_vec.push_back(input.substr(input_idx, replace_idx - input_idx));
    if (replace_idx < input.size()) {
      output_vec.push_back(next_replacement->second);
    }
    // move input index to after what we replaced (or end of string).
    input_idx = replace_end_idx;
  }
  return base::StrCat(output_vec);
}

namespace {

template <typename Property>
void RedactProperty(
    const std::optional<FencedFrameProperty<Property>>& property,
    FencedFrameEntity entity,
    std::optional<blink::FencedFrame::RedactedFencedFrameProperty<Property>>&
        out) {
  if (property.has_value()) {
    out = blink::FencedFrame::RedactedFencedFrameProperty(
        property->GetValueForEntity(entity));
  }
}

}  // namespace

FencedFrameConfig::FencedFrameConfig() = default;

FencedFrameConfig::FencedFrameConfig(const GURL& mapped_url)
    : mapped_url_(std::in_place,
                  mapped_url,
                  VisibilityToEmbedder::kOpaque,
                  VisibilityToContent::kTransparent),
      mode_(DeprecatedFencedFrameMode::kOpaqueAds) {}

FencedFrameConfig::FencedFrameConfig(
    const GURL& mapped_url,
    const gfx::Size& content_size,
    scoped_refptr<FencedFrameReporter> fenced_frame_reporter,
    bool is_ad_component)
    : mapped_url_(std::in_place,
                  mapped_url,
                  VisibilityToEmbedder::kOpaque,
                  VisibilityToContent::kTransparent),
      content_size_(std::in_place,
                    content_size,
                    VisibilityToEmbedder::kTransparent,
                    VisibilityToContent::kTransparent),
      deprecated_should_freeze_initial_size_(std::in_place,
                                             false,
                                             VisibilityToEmbedder::kTransparent,
                                             VisibilityToContent::kOpaque),
      fenced_frame_reporter_(fenced_frame_reporter),
      is_ad_component_(is_ad_component) {}

FencedFrameConfig::FencedFrameConfig(const GURL& urn_uuid,
                                     const GURL& mapped_url)
    : urn_uuid_(urn_uuid),
      mapped_url_(std::in_place,
                  mapped_url,
                  VisibilityToEmbedder::kOpaque,
                  VisibilityToContent::kTransparent),
      mode_(DeprecatedFencedFrameMode::kOpaqueAds) {}

FencedFrameConfig::FencedFrameConfig(
    const GURL& mapped_url,
    scoped_refptr<FencedFrameReporter> fenced_frame_reporter,
    bool is_ad_component)
    : FencedFrameConfig(mapped_url) {
  fenced_frame_reporter_ = fenced_frame_reporter;
  is_ad_component_ = is_ad_component;
}

FencedFrameConfig::FencedFrameConfig(
    const GURL& urn_uuid,
    const GURL& mapped_url,
    const SharedStorageBudgetMetadata& shared_storage_budget_metadata,
    scoped_refptr<FencedFrameReporter> fenced_frame_reporter)
    : urn_uuid_(urn_uuid),
      mapped_url_(std::in_place,
                  mapped_url,
                  VisibilityToEmbedder::kOpaque,
                  VisibilityToContent::kTransparent),
      deprecated_should_freeze_initial_size_(std::in_place,
                                             false,
                                             VisibilityToEmbedder::kTransparent,
                                             VisibilityToContent::kOpaque),
      shared_storage_budget_metadata_(std::in_place,
                                      shared_storage_budget_metadata,
                                      VisibilityToEmbedder::kOpaque,
                                      VisibilityToContent::kOpaque),
      fenced_frame_reporter_(std::move(fenced_frame_reporter)),
      mode_(DeprecatedFencedFrameMode::kOpaqueAds) {}

FencedFrameConfig::FencedFrameConfig(const FencedFrameConfig&) = default;
FencedFrameConfig::FencedFrameConfig(FencedFrameConfig&&) = default;
FencedFrameConfig::~FencedFrameConfig() = default;

FencedFrameConfig& FencedFrameConfig::operator=(const FencedFrameConfig&) =
    default;
FencedFrameConfig& FencedFrameConfig::operator=(FencedFrameConfig&&) = default;

blink::FencedFrame::RedactedFencedFrameConfig FencedFrameConfig::RedactFor(
    FencedFrameEntity entity) const {
  blink::FencedFrame::RedactedFencedFrameConfig redacted_config;
  if (urn_uuid_.has_value()) {
    redacted_config.urn_uuid_ = urn_uuid_;
  }

  RedactProperty(mapped_url_, entity, redacted_config.mapped_url_);
  RedactProperty(container_size_, entity, redacted_config.container_size_);
  RedactProperty(content_size_, entity, redacted_config.content_size_);
  RedactProperty(deprecated_should_freeze_initial_size_, entity,
                 redacted_config.deprecated_should_freeze_initial_size_);
  RedactProperty(ad_auction_data_, entity, redacted_config.ad_auction_data_);

  if (nested_configs_.has_value()) {
    std::optional<std::vector<FencedFrameConfig>>
        partially_redacted_nested_configs =
            nested_configs_->GetValueForEntity(entity);
    if (partially_redacted_nested_configs.has_value()) {
      redacted_config.nested_configs_.emplace(
          std::vector<blink::FencedFrame::RedactedFencedFrameConfig>());
      for (const FencedFrameConfig& nested_config :
           partially_redacted_nested_configs.value()) {
        redacted_config.nested_configs_->potentially_opaque_value->emplace_back(
            nested_config.RedactFor(FencedFrameEntity::kEmbedder));
      }
    } else {
      redacted_config.nested_configs_.emplace(std::nullopt);
    }
  }

  RedactProperty(shared_storage_budget_metadata_, entity,
                 redacted_config.shared_storage_budget_metadata_);

  // The mode never needs to be redacted, because it is a function of which API
  // was called to generate the config, rather than any cross-site data.
  redacted_config.mode_ = mode_;

  redacted_config.effective_enabled_permissions_ =
      effective_enabled_permissions_;

  redacted_config.parent_permissions_info_ = parent_permissions_info_;

  return redacted_config;
}

FencedFrameProperties::FencedFrameProperties()
    : ad_auction_data_(std::nullopt),
      nested_urn_config_pairs_(std::nullopt),
      shared_storage_budget_metadata_(std::nullopt),
      embedder_shared_storage_context_(std::nullopt),
      partition_nonce_(std::in_place,
                       base::UnguessableToken::Create(),
                       VisibilityToEmbedder::kOpaque,
                       VisibilityToContent::kOpaque) {}

FencedFrameProperties::FencedFrameProperties(const GURL& mapped_url)
    : mapped_url_(std::in_place,
                  mapped_url,
                  VisibilityToEmbedder::kTransparent,
                  VisibilityToContent::kTransparent),
      partition_nonce_(std::in_place,
                       base::UnguessableToken::Create(),
                       VisibilityToEmbedder::kOpaque,
                       VisibilityToContent::kOpaque),
      allows_information_inflow_(true) {}

FencedFrameProperties::FencedFrameProperties(const FencedFrameConfig& config)
    : mapped_url_(config.mapped_url_),
      container_size_(config.container_size_),
      content_size_(config.content_size_),
      deprecated_should_freeze_initial_size_(
          config.deprecated_should_freeze_initial_size_),
      ad_auction_data_(config.ad_auction_data_),
      on_navigate_callback_(config.on_navigate_callback_),
      nested_urn_config_pairs_(std::nullopt),
      shared_storage_budget_metadata_(std::nullopt),
      embedder_shared_storage_context_(std::nullopt),
      fenced_frame_reporter_(config.fenced_frame_reporter_),
      partition_nonce_(std::in_place,
                       base::UnguessableToken::Create(),
                       VisibilityToEmbedder::kOpaque,
                       VisibilityToContent::kOpaque),
      mode_(config.mode_),
      allows_information_inflow_(config.allows_information_inflow_),
      is_ad_component_(config.is_ad_component_),
      effective_enabled_permissions_(config.effective_enabled_permissions_),
      parent_permissions_info_(config.parent_permissions_info_) {
  if (config.shared_storage_budget_metadata_) {
    shared_storage_budget_metadata_.emplace(
        &config.shared_storage_budget_metadata_->GetValueIgnoringVisibility(),
        config.shared_storage_budget_metadata_->visibility_to_embedder_,
        config.shared_storage_budget_metadata_->visibility_to_content_);
  }
  if (config.nested_configs_) {
    nested_urn_config_pairs_.emplace(
        GenerateURNConfigVectorForConfigs(
            config.nested_configs_->GetValueIgnoringVisibility()),
        config.nested_configs_->visibility_to_embedder_,
        config.nested_configs_->visibility_to_content_);
  }
}

FencedFrameProperties::FencedFrameProperties(const FencedFrameProperties&) =
    default;
FencedFrameProperties::FencedFrameProperties(FencedFrameProperties&&) = default;
FencedFrameProperties::~FencedFrameProperties() = default;

FencedFrameProperties& FencedFrameProperties::operator=(
    const FencedFrameProperties&) = default;
FencedFrameProperties& FencedFrameProperties::operator=(
    FencedFrameProperties&&) = default;

blink::FencedFrame::RedactedFencedFrameProperties
FencedFrameProperties::RedactFor(FencedFrameEntity entity) const {
  blink::FencedFrame::RedactedFencedFrameProperties redacted_properties;
  RedactProperty(mapped_url_, entity, redacted_properties.mapped_url_);
  RedactProperty(container_size_, entity, redacted_properties.container_size_);
  RedactProperty(content_size_, entity, redacted_properties.content_size_);
  RedactProperty(deprecated_should_freeze_initial_size_, entity,
                 redacted_properties.deprecated_should_freeze_initial_size_);
  RedactProperty(ad_auction_data_, entity,
                 redacted_properties.ad_auction_data_);

  if (nested_urn_config_pairs_.has_value()) {
    std::optional<std::vector<std::pair<GURL, FencedFrameConfig>>>
        partially_redacted_nested_urn_config_pairs =
            nested_urn_config_pairs_->GetValueForEntity(entity);
    if (partially_redacted_nested_urn_config_pairs.has_value()) {
      redacted_properties.nested_urn_config_pairs_.emplace(
          std::vector<std::pair<
              GURL, blink::FencedFrame::RedactedFencedFrameConfig>>());
      for (const std::pair<GURL, FencedFrameConfig>& nested_urn_config_pair :
           *partially_redacted_nested_urn_config_pairs) {
        redacted_properties.nested_urn_config_pairs_->potentially_opaque_value
            ->emplace_back(nested_urn_config_pair.first,
                           nested_urn_config_pair.second.RedactFor(
                               FencedFrameEntity::kEmbedder));
      }
    } else {
      redacted_properties.nested_urn_config_pairs_.emplace(std::nullopt);
    }
  }
  if (shared_storage_budget_metadata_.has_value()) {
    std::optional<raw_ptr<const SharedStorageBudgetMetadata>>
        potentially_opaque_ptr =
            shared_storage_budget_metadata_->GetValueForEntity(entity);
    if (potentially_opaque_ptr.has_value()) {
      redacted_properties.shared_storage_budget_metadata_ =
          blink::FencedFrame::RedactedFencedFrameProperty(
              std::make_optional(*potentially_opaque_ptr.value()));
    } else {
      redacted_properties.shared_storage_budget_metadata_.emplace(std::nullopt);
    }
  }

  // The mode never needs to be redacted, because it is a function of which API
  // was called to generate the config, rather than any cross-site data.
  redacted_properties.mode_ = mode_;

  redacted_properties.effective_enabled_permissions_ =
      effective_enabled_permissions_;

  redacted_properties.parent_permissions_info_ = parent_permissions_info_;

  if (entity != FencedFrameEntity::kCrossOriginContent) {
    redacted_properties.can_disable_untrusted_network_ =
        can_disable_untrusted_network_;
  }

  redacted_properties.is_cross_origin_content_ =
      entity == FencedFrameEntity::kCrossOriginContent;
  redacted_properties.allow_cross_origin_event_reporting_ =
      allow_cross_origin_event_reporting_;

  return redacted_properties;
}

void FencedFrameProperties::UpdateMappedURL(GURL url) {
  CHECK(mapped_url_.has_value());
  mapped_url_->value_ = url;
}

std::vector<std::pair<GURL, FencedFrameConfig>>
FencedFrameProperties::GenerateURNConfigVectorForConfigs(
    const std::vector<FencedFrameConfig>& nested_configs) {
  std::vector<std::pair<GURL, FencedFrameConfig>> nested_urn_config_pairs;
  const size_t kMaxAdAuctionAdComponents = blink::MaxAdAuctionAdComponents();
  DCHECK_LE(nested_configs.size(), kMaxAdAuctionAdComponents);
  for (const FencedFrameConfig& config : nested_configs) {
    // Give each config its own urn:uuid. This ensures that if the same config
    // is loaded into multiple fenced frames, they will not share the same
    // urn:uuid across processes.
    GURL urn_uuid = GenerateUrnUuid();
    FencedFrameConfig config_with_urn = config;
    config_with_urn.urn_uuid_ = urn_uuid;
    nested_urn_config_pairs.emplace_back(urn_uuid, config_with_urn);
  }

  // Pad `component_ads_` to contain exactly MaxAdAuctionAdComponents() ads, to
  // avoid leaking any data to the fenced frame the component ads array is
  // exposed to.
  while (nested_urn_config_pairs.size() < kMaxAdAuctionAdComponents) {
    GURL urn_uuid = GenerateUrnUuid();
    nested_urn_config_pairs.emplace_back(
        urn_uuid, FencedFrameConfig(urn_uuid, GURL(url::kAboutBlankURL)));
  }
  return nested_urn_config_pairs;
}

void FencedFrameProperties::UpdateParentParsedPermissionsPolicy(
    const network::PermissionsPolicy* parent_policy,
    const url::Origin& parent_origin) {
  // Sanity check that a fenced frame loaded through Protected Audience or
  // Shared Storage did not reach this point. `effective_enabled_permissions_`
  // is populated in `fenced_frame_url_mapping.cc` if loaded through an API. If
  // loaded through any other means, the vector remains empty.
  CHECK_EQ(effective_enabled_permissions_.size(), 0u);
  CHECK(parent_policy);
  std::vector<network::ParsedPermissionsPolicyDeclaration> parsed_policies;
  for (auto feature : network::kFencedFrameAllowedFeatures) {
    const network::PermissionsPolicy::Allowlist allow_list =
        parent_policy->GetAllowlistForFeature(feature);
    parsed_policies.emplace_back(
        feature, allow_list.AllowedOrigins(), allow_list.SelfIfMatches(),
        allow_list.MatchesAll(), allow_list.MatchesOpaqueSrc());
  }
  parent_permissions_info_.emplace(parsed_policies, parent_origin);
}

}  // namespace content