Skip to content

Commit

Permalink
[MERGE 115] Reland "Add source reporting origin per reporting site ra…
Browse files Browse the repository at this point in the history
…te limit"

This is a reland of commit 549124c

Original change's description:
> Add source reporting origin per reporting site rate limit
>
> Change-Id: Ie627750e26fd116a4b9da5ad6455bd29fa068b75
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4548158
> Auto-Submit: John Delaney <johnidel@chromium.org>
> Commit-Queue: Nan Lin <linnan@chromium.org>
> Reviewed-by: Nan Lin <linnan@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1148249}

(cherry picked from commit 2d9846e)

Bug: 1448836
Change-Id: I8f75cfe680202f0406ca511ffe12edc71b8f3b3a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4573784
Commit-Queue: John Delaney <johnidel@chromium.org>
Reviewed-by: Nan Lin <linnan@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1150782}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4591289
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Cr-Commit-Position: refs/branch-heads/5790@{#362}
Cr-Branched-From: 1d71a33-refs/heads/main@{#1148114}
  • Loading branch information
John Delaney authored and Chromium LUCI CQ committed Jun 5, 2023
1 parent 34ede36 commit 5bf564a
Show file tree
Hide file tree
Showing 13 changed files with 354 additions and 126 deletions.
17 changes: 17 additions & 0 deletions content/browser/attribution_reporting/attribution_config.cc
Expand Up @@ -6,10 +6,22 @@

#include <cmath>

#include "base/metrics/field_trial_params.h"
#include "base/time/time.h"
#include "third_party/blink/public/common/features.h"

namespace content {

namespace {

const base::FeatureParam<int> kMaxReportingOriginsPerSiteParam{
&blink::features::kConversionMeasurement,
"max_reporting_origins_per_source_reporting_site",
AttributionConfig::RateLimitConfig::
kDefaultMaxReportingOriginsPerSourceReportingSite};

}

bool AttributionConfig::Validate() const {
if (max_sources_per_origin <= 0) {
return false;
Expand Down Expand Up @@ -54,6 +66,11 @@ bool AttributionConfig::RateLimitConfig::Validate() const {
return true;
}

int AttributionConfig::RateLimitConfig::
GetMaxSourceReportingOriginsPerReportingSite() const {
return kMaxReportingOriginsPerSiteParam.Get();
}

bool AttributionConfig::EventLevelLimit::Validate() const {
if (navigation_source_trigger_data_cardinality == 0u) {
return false;
Expand Down
6 changes: 6 additions & 0 deletions content/browser/attribution_reporting/attribution_config.h
Expand Up @@ -35,6 +35,12 @@ struct CONTENT_EXPORT AttributionConfig {
// site, reporting site> in `time_window`.
int64_t max_attributions = 100;

static constexpr int kDefaultMaxReportingOriginsPerSourceReportingSite = 1;

base::TimeDelta origins_per_site_window = base::Days(1);

int GetMaxSourceReportingOriginsPerReportingSite() const;

// When adding new members, the corresponding `Validate()` definition and
// `operator==()` definition in `attribution_interop_parser_unittest.cc`
// should also be updated.
Expand Down
Expand Up @@ -51,7 +51,8 @@ bool operator==(const AttributionConfig::RateLimitConfig& a,
const auto tie = [](const AttributionConfig::RateLimitConfig& config) {
return std::make_tuple(
config.time_window, config.max_source_registration_reporting_origins,
config.max_attribution_reporting_origins, config.max_attributions);
config.max_attribution_reporting_origins, config.max_attributions,
config.origins_per_site_window);
};
return tie(a) == tie(b);
}
Expand Down
14 changes: 14 additions & 0 deletions content/browser/attribution_reporting/attribution_storage_sql.cc
Expand Up @@ -719,6 +719,20 @@ StoreSourceResult AttributionStorageSql::StoreSource(
return StoreSourceResult(StorableSource::Result::kInternalError);
}

switch (rate_limit_table_.SourceAllowedForReportingOriginPerSiteLimit(
&db_, source, source_time)) {
case RateLimitResult::kAllowed:
break;
case RateLimitResult::kNotAllowed:
// TODO(https://crbug.com/1448330): Report a different result
// type for this limit to avoid overlap with the other reporting
// origin limit.
return StoreSourceResult(
StorableSource::Result::kExcessiveReportingOrigins);
case RateLimitResult::kError:
return StoreSourceResult(StorableSource::Result::kInternalError);
}

sql::Transaction transaction(&db_);
if (!transaction.Begin()) {
return StoreSourceResult(StorableSource::Result::kInternalError);
Expand Down
Expand Up @@ -204,6 +204,15 @@ TEST_F(AttributionSqlQueryPlanTest, kRateLimitSourceAllowedSql) {
{"source_site", "reporting_site"}));
}

TEST_F(AttributionSqlQueryPlanTest, kRateLimitSourceReportingOriginsBySiteSql) {
const auto plan = GetPlan(
attribution_queries::kRateLimitSelectSourceReportingOriginsBySiteSql);
ASSERT_TRUE(plan.has_value());
EXPECT_THAT(plan.value(),
UsesIndex("rate_limit_source_site_reporting_site_idx",
{"source_site", "reporting_site"}));
}

TEST_F(AttributionSqlQueryPlanTest, kRateLimitSelectReportingOriginsSql) {
const auto plan =
GetPlan(attribution_queries::kRateLimitSelectReportingOriginsSql);
Expand Down
Expand Up @@ -3599,4 +3599,37 @@ TEST_F(AttributionStorageTest, MaximumAggregatableReportsPerSource) {
MaybeCreateAndStoreAggregatableReport(trigger));
}

TEST_F(AttributionStorageTest, MaxSourceReportingOriginsPerSite) {
auto store_source = [&](std::string source, std::string reporting) {
storage()->StoreSource(
SourceBuilder()
.SetSourceOrigin(*SuitableOrigin::Deserialize(source))
.SetReportingOrigin(*SuitableOrigin::Deserialize(reporting))
.SetExpiry(base::Days(2))
.Build());
};
store_source("https://a.test", "https://reporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));

store_source("https://a.test", "https://a.reporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(1));

store_source("https://b.test", "https://a.reporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(2));

store_source("https://b.test", "https://otherreporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(3));

task_environment_.FastForwardBy(base::Days(1));

// After 1 day a new origin can be used.
store_source("https://a.test", "https://a.reporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(4));

// The reporter used on the first day can't be used even though it is
// repeated.
store_source("https://a.test", "https://reporter.test");
EXPECT_THAT(storage()->GetActiveSources(), SizeIs(4));
}

} // namespace content
52 changes: 15 additions & 37 deletions content/browser/attribution_reporting/attributions_browsertest.cc
Expand Up @@ -1117,34 +1117,11 @@ ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
const char* kTestCases[] = {"createAttributionSrcImg($1);",
"createTrackingPixel($1);"};

for (const char* registration_js : kTestCases) {
auto https_server = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS);
https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server->ServeFilesFromSourceDirectory("content/test/data");

ExpectedReportWaiter expected_report(
GURL("https://a.test/.well-known/attribution-reporting/"
"report-event-attribution"),
/*attribution_destination=*/"https://d.test",
/*source_event_id=*/"5", /*source_type=*/"event", /*trigger_data=*/"1",
https_server.get());
ASSERT_TRUE(https_server->Start());

// Navigate to a page with impression creator.
const GURL kImpressionUrl = https_server->GetURL(
"a.test", "/attribution_reporting/page_with_impression_creator.html");
EXPECT_TRUE(NavigateToURL(web_contents(), kImpressionUrl));

// Register impression for the target conversion url.
GURL register_url = https_server->GetURL(
"a.test", "/attribution_reporting/register_source_headers.html");

EXPECT_TRUE(ExecJs(web_contents(), JsReplace("createAttributionSrcImg($1);",
register_url)));
ASSERT_TRUE(https_server()->Start());

for (const char* registration_js : kTestCases) {
// Navigate to a starting same origin page with the conversion url.
const GURL kEmptyUrl = https_server->GetURL("d.test", "/empty.html");
const GURL kEmptyUrl = https_server()->GetURL("d.test", "/empty.html");
{
auto url_loader_interceptor =
content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin(
Expand All @@ -1153,7 +1130,7 @@ ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
}

// Pre-render the conversion url.
const GURL kConversionUrl = https_server->GetURL(
const GURL kConversionUrl = https_server()->GetURL(
"d.test", "/attribution_reporting/page_with_conversion_redirect.html");
int host_id = prerender_helper_.AddPrerender(kConversionUrl);
content::test::PrerenderHostObserver host_observer(*web_contents(),
Expand All @@ -1163,26 +1140,27 @@ ATTRIBUTION_PRERENDER_BROWSER_TEST(ConversionsRegisteredOnActivatedPrerender) {
content::RenderFrameHost* prerender_rfh =
prerender_helper_.GetPrerenderedMainFrameHost(host_id);

const GURL register_trigger_url = https_server->GetURL(
const GURL register_trigger_url = https_server()->GetURL(
"a.test", "/attribution_reporting/register_trigger_headers.html");
EXPECT_TRUE(ExecJs(prerender_rfh,
JsReplace(registration_js, register_trigger_url)));

// Delay prerender activation so that subresource response is received
// earlier than that.
base::RunLoop run_loop;
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, run_loop.QuitClosure(), base::Milliseconds(100));
run_loop.Run();
MockAttributionObserver observer;
base::ScopedObservation<AttributionManager, AttributionObserver>
observation(&observer);
observation.Observe(attribution_manager());
base::RunLoop loop;
EXPECT_CALL(observer, OnTriggerHandled(_, _, _)).WillOnce([&]() {
loop.Quit();
});

// Navigate to pre-rendered page, bringing it to the fore.
prerender_helper_.NavigatePrimaryPage(kConversionUrl);

ASSERT_EQ(kConversionUrl, web_contents()->GetLastCommittedURL());
ASSERT_TRUE(host_observer.was_activated());

// Confirm that reports work as expected, and impressions were retrieved
// from the pre-rendered page, once it became a primary page.
expected_report.WaitForReport();
loop.Run();
}
}

Expand Down
45 changes: 45 additions & 0 deletions content/browser/attribution_reporting/rate_limit_table.cc
Expand Up @@ -241,6 +241,51 @@ RateLimitResult RateLimitTable::SourceAllowedForReportingOriginLimit(
source.registration().destination_set.destinations());
}

RateLimitResult RateLimitTable::SourceAllowedForReportingOriginPerSiteLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

size_t max_origins =
static_cast<size_t>(delegate_->GetRateLimits()
.GetMaxSourceReportingOriginsPerReportingSite());

base::Time min_timestamp =
source_time - delegate_->GetRateLimits().origins_per_site_window;

sql::Statement statement(db->GetCachedStatement(
SQL_FROM_HERE,
attribution_queries::kRateLimitSelectSourceReportingOriginsBySiteSql));
statement.BindString(0, source.common_info().source_site().Serialize());
statement.BindString(
1,
net::SchemefulSite(source.common_info().reporting_origin()).Serialize());
statement.BindTime(2, min_timestamp);

std::string serialized_reporting_origin =
source.common_info().reporting_origin().Serialize();
std::set<std::string> reporting_origins;
while (statement.Step()) {
std::string origin = statement.ColumnString(0);

if (origin == serialized_reporting_origin) {
return RateLimitResult::kAllowed;
}

reporting_origins.insert(std::move(origin));
if (reporting_origins.size() == max_origins) {
return RateLimitResult::kNotAllowed;
}
}

if (!statement.Succeeded()) {
return RateLimitResult::kError;
}

return RateLimitResult::kAllowed;
}

RateLimitResult RateLimitTable::SourceAllowedForDestinationLimit(
sql::Database* db,
const StorableSource& source,
Expand Down
5 changes: 5 additions & 0 deletions content/browser/attribution_reporting/rate_limit_table.h
Expand Up @@ -83,6 +83,11 @@ class CONTENT_EXPORT RateLimitTable {
const StorableSource& source,
base::Time source_time);

[[nodiscard]] RateLimitResult SourceAllowedForReportingOriginPerSiteLimit(
sql::Database* db,
const StorableSource& source,
base::Time source_time);

[[nodiscard]] RateLimitResult SourceAllowedForDestinationLimit(
sql::Database* db,
const StorableSource& source,
Expand Down

0 comments on commit 5bf564a

Please sign in to comment.