blob: 56a25d5bbf8ecd5ebef7732868ba7094ab76632e [file] [log] [blame]
// 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