31
31
#include " content/browser/devtools/network_service_devtools_observer.h"
32
32
#include " content/browser/devtools/protocol/network_handler.h"
33
33
#include " content/browser/devtools/render_frame_devtools_agent_host.h"
34
+ #include " content/browser/fenced_frame/fenced_frame_config.h"
34
35
#include " content/browser/interest_group/interest_group_pa_report_util.h"
35
36
#include " content/browser/private_aggregation/private_aggregation_budget_key.h"
36
37
#include " content/browser/private_aggregation/private_aggregation_manager.h"
@@ -307,7 +308,8 @@ bool FencedFrameReporter::SendReport(
307
308
// the map is empty, can't send a request. An entry with a null (not empty)
308
309
// map means the map is pending, and is handled below.
309
310
if (it == reporting_metadata_.end () ||
310
- (it->second .reporting_url_map && it->second .reporting_url_map ->empty ())) {
311
+ (absl::holds_alternative<DestinationEnumEvent>(event_variant) &&
312
+ it->second .reporting_url_map && it->second .reporting_url_map ->empty ())) {
311
313
error_message = base::StrCat (
312
314
{" This frame did not register reporting metadata for destination '" ,
313
315
ReportingDestinationAsString (reporting_destination), " '." });
@@ -389,7 +391,7 @@ bool FencedFrameReporter::SendReportInternal(
389
391
return false ;
390
392
}
391
393
392
- // Validate the reporting url .
394
+ // Validate the reporting URL .
393
395
url = url_iter->second ;
394
396
if (!url.is_valid () || !url.SchemeIsHTTPOrHTTPS ()) {
395
397
error_message = base::StrCat (
@@ -401,12 +403,84 @@ bool FencedFrameReporter::SendReportInternal(
401
403
return false ;
402
404
}
403
405
} else {
404
- CHECK (absl::holds_alternative<DestinationURLEvent>(event_variant));
405
406
// Since the event references a destination URL, use it directly.
406
- // The URL should have been validated previously, to be a valid HTTPS url.
407
- // TODO(gtanzer): Substitute macros from the reporting metadata as needed.
408
- // TODO(gtanzer): Check whether the url is an allowlisted origin.
409
- url = absl::get<DestinationURLEvent>(event_variant).url ;
407
+ // The URL should have been validated previously, to be a valid HTTPS URL.
408
+ CHECK (absl::holds_alternative<DestinationURLEvent>(event_variant));
409
+
410
+ // Check that reportEvent to custom destination URLs with macro
411
+ // substitution is allowed in this context. (i.e., The macro map has a
412
+ // value.)
413
+ if (!reporting_destination_info.reporting_ad_macro_map .has_value ()) {
414
+ error_message =
415
+ " This frame attempted to send a report to a custom destination URL "
416
+ " with macro substitution, which is not supported by the API that "
417
+ " created this frame's fenced frame config." ;
418
+ console_message_level = blink::mojom::ConsoleMessageLevel::kError ;
419
+ NotifyFencedFrameReportingBeaconFailed (attribution_reporting_data);
420
+ return false ;
421
+ }
422
+
423
+ // If there is no allowlist, or the allowlist is empty, provide a more
424
+ // specific error message.
425
+ if (!allowed_reporting_origins_.has_value () ||
426
+ allowed_reporting_origins_->empty ()) {
427
+ error_message =
428
+ " This frame attempted to send a report to a custom destination URL "
429
+ " with macro substitution, but no origins are allowed by its "
430
+ " allowlist." ;
431
+ console_message_level = blink::mojom::ConsoleMessageLevel::kError ;
432
+ NotifyFencedFrameReportingBeaconFailed (attribution_reporting_data);
433
+ return false ;
434
+ }
435
+
436
+ // If the origin allowlist has previously been violated, this feature is
437
+ // disabled for the lifetime of the FencedFrameReporter. This prevents
438
+ // an interest group from encoding cross-site data about a user in binary
439
+ // with its choices of allowed/disallowed origins.
440
+ if (attempted_custom_url_report_to_disallowed_origin_) {
441
+ error_message =
442
+ " This frame attempted to send a report to a custom destination URL "
443
+ " with macro substitution, but this functionality is disabled because "
444
+ " a request was previously attempted to a disallowed origin." ;
445
+ console_message_level = blink::mojom::ConsoleMessageLevel::kError ;
446
+ NotifyFencedFrameReportingBeaconFailed (attribution_reporting_data);
447
+ return false ;
448
+ }
449
+
450
+ // Substitute macros in the specified URL using the macro map.
451
+ // TODO(qingxinwu): Lift these changes up out of FencedFrameReporter into
452
+ // the code that constructs the reporting ad macro map.
453
+ std::vector<std::pair<std::string, std::string>> macro_map;
454
+ for (const auto & entry :
455
+ reporting_destination_info.reporting_ad_macro_map .value ()) {
456
+ macro_map.emplace_back (" ${" + entry.first + " }" , entry.second );
457
+ }
458
+ url = GURL (SubstituteMappedStrings (
459
+ absl::get<DestinationURLEvent>(event_variant).url .spec (), macro_map));
460
+ url::Origin destination_origin = url::Origin::Create (url);
461
+
462
+ // Check whether the destination URL has an allowed origin.
463
+ bool is_allowed_origin = false ;
464
+ for (auto & origin : allowed_reporting_origins_.value ()) {
465
+ if (origin.IsSameOriginWith (destination_origin)) {
466
+ is_allowed_origin = true ;
467
+ break ;
468
+ }
469
+ }
470
+
471
+ // If the destination URL has a disallowed origin, disable this feature for
472
+ // the lifetime of the FencedFrameReporter and return.
473
+ if (!is_allowed_origin) {
474
+ attempted_custom_url_report_to_disallowed_origin_ = true ;
475
+ error_message =
476
+ " This frame attempted to send a report to a custom destination URL "
477
+ " with macro substitution to a disallowed origin. No further reports "
478
+ " to custom destination URLs will be allowed for this fenced frame "
479
+ " config." ;
480
+ console_message_level = blink::mojom::ConsoleMessageLevel::kError ;
481
+ NotifyFencedFrameReportingBeaconFailed (attribution_reporting_data);
482
+ return false ;
483
+ }
410
484
}
411
485
412
486
if (!GetContentClient ()
0 commit comments