Skip to content

Commit

Permalink
Use source origin as context origin for fake attribution reports
Browse files Browse the repository at this point in the history
Instead of trying to obtain a trustworthy origin from the source's
destination site, which may not be possible and would have caused the
associated DCHECK to fail.

A context origin is the secondary owner of either a source or a trigger,
after the reporting origin.

Bug: 1412111
Change-Id: Ie1eaf7e157bbfeeaf9c1339cb0ee8a8caa27e948
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4213654
Reviewed-by: Charlie Harrison <csharrison@chromium.org>
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Reviewed-by: Nan Lin <linnan@chromium.org>
Quick-Run: Andrew Paseltiner <apaseltiner@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1100222}
  • Loading branch information
Andrew Paseltiner authored and Chromium LUCI CQ committed Feb 2, 2023
1 parent 1cd0274 commit e5e97e3
Show file tree
Hide file tree
Showing 7 changed files with 157 additions and 29 deletions.
37 changes: 18 additions & 19 deletions content/browser/attribution_reporting/attribution_storage_sql.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,11 @@ namespace content {
// Version number of the database.
// TODO: remove the active_unattributed_sources_by_site_reporting_origin index
// during the next DB migration.
const int AttributionStorageSql::kCurrentVersionNumber = 44;
const int AttributionStorageSql::kCurrentVersionNumber = 45;

// Earliest version which can use a |kCurrentVersionNumber| database
// without failing.
const int AttributionStorageSql::kCompatibleVersionNumber = 44;
const int AttributionStorageSql::kCompatibleVersionNumber = 45;

// Latest version of the database that cannot be upgraded to
// |kCurrentVersionNumber| without razing the database.
Expand Down Expand Up @@ -622,18 +622,16 @@ AttributionStorage::StoreSourceResult AttributionStorageSql::StoreSource(
DCHECK_LT(common_info.source_time(), fake_report.trigger_time);
DCHECK_LT(fake_report.trigger_time, fake_report.report_time);

// Use the destination site itself as the destination origin to meet the
// same-site requirement and because the origin itself is never included
// in the report, only the site.
auto fake_destination_origin =
SuitableOrigin::Create(common_info.DestinationSite().GetURL());
DCHECK(fake_destination_origin.has_value());

// Set the `context_origin` to be the source origin for fake reports,
// as these reports are generated only via the source site's context.
// The fake destinations are not relevant to the context that
// actually created the report.
if (!StoreEventLevelReport(
source_id, fake_report.trigger_data, fake_report.trigger_time,
fake_report.report_time,
/*priority=*/0, delegate_->NewReportID(),
/*trigger_debug_key=*/absl::nullopt, *fake_destination_origin)) {
/*trigger_debug_key=*/absl::nullopt,
/*context_origin=*/common_info.source_origin())) {
return StoreSourceResult(StorableSource::Result::kInternalError);
}

Expand Down Expand Up @@ -1172,7 +1170,7 @@ EventLevelResult AttributionStorageSql::MaybeStoreEventLevelReport(
int num_conversions,
absl::optional<AttributionReport>& replaced_report,
absl::optional<AttributionReport>& dropped_report,
const SuitableOrigin& destination_origin) {
const SuitableOrigin& context_origin) {
if (report.attribution_info().source.active_state() ==
StoredSource::ActiveState::kReachedEventLevelAttributionLimit) {
dropped_report = std::move(report);
Expand Down Expand Up @@ -1227,7 +1225,7 @@ EventLevelResult AttributionStorageSql::MaybeStoreEventLevelReport(
attribution_info.source.source_id(), event_level_data->trigger_data,
attribution_info.time, report.report_time(),
event_level_data->priority, report.external_report_id(),
attribution_info.debug_key, destination_origin);
attribution_info.debug_key, context_origin);
if (!id) {
return EventLevelResult::kInternalError;
}
Expand Down Expand Up @@ -1286,14 +1284,14 @@ AttributionStorageSql::StoreEventLevelReport(
int64_t priority,
const base::GUID& external_report_id,
absl::optional<uint64_t> trigger_debug_key,
const SuitableOrigin& destination_origin) {
const SuitableOrigin& context_origin) {
DCHECK(external_report_id.is_valid());

static constexpr char kStoreReportSql[] =
"INSERT INTO event_level_reports"
"(source_id,trigger_data,trigger_time,report_time,"
"priority,failed_send_attempts,external_report_id,debug_key,"
"destination_origin)"
"context_origin)"
"VALUES(?,?,?,?,?,0,?,?,?)";
sql::Statement store_report_statement(
db_->GetCachedStatement(SQL_FROM_HERE, kStoreReportSql));
Expand All @@ -1304,7 +1302,7 @@ AttributionStorageSql::StoreEventLevelReport(
store_report_statement.BindInt64(4, priority);
store_report_statement.BindString(5, external_report_id.AsLowercaseString());
BindUint64OrNull(store_report_statement, 6, trigger_debug_key);
store_report_statement.BindString(7, destination_origin.Serialize());
store_report_statement.BindString(7, context_origin.Serialize());
if (!store_report_statement.Run()) {
return absl::nullopt;
}
Expand Down Expand Up @@ -2226,9 +2224,10 @@ bool AttributionStorageSql::CreateSchema() {
// trigger was registered, and should be used for clearing site data.
// |report_time| is the time a <report, source> pair should be
// reported, and is specified by |delegate_|.
// |destination_origin| is the origin on which the trigger was registered; it
// may differ from the sources table's corresponding destination_origin, but
// both must be same-site with respect to each other.
// |context_origin| is the origin that secondarily owns the report for
// data-deletion purposes. For real reports, it is the destination origin on
// which the trigger was registered. For fake reports, it is the source
// origin.
//
// |id| uses AUTOINCREMENT to ensure that IDs aren't reused over
// the lifetime of the DB.
Expand All @@ -2243,7 +2242,7 @@ bool AttributionStorageSql::CreateSchema() {
"failed_send_attempts INTEGER NOT NULL,"
"external_report_id TEXT NOT NULL,"
"debug_key INTEGER,"
"destination_origin TEXT NOT NULL)";
"context_origin TEXT NOT NULL)";
if (!db_->Execute(kConversionTableSql)) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ class CONTENT_EXPORT AttributionStorageSql : public AttributionStorage {
int64_t priority,
const base::GUID& external_report_id,
absl::optional<uint64_t> trigger_debug_key,
const attribution_reporting::SuitableOrigin& destination_origin)
const attribution_reporting::SuitableOrigin& context_origin)
VALID_CONTEXT_REQUIRED(sequence_checker_);

absl::optional<AttributionReport> ReadReportFromStatement(sql::Statement&)
Expand Down Expand Up @@ -271,7 +271,7 @@ class CONTENT_EXPORT AttributionStorageSql : public AttributionStorage {
int num_conversions,
absl::optional<AttributionReport>& replaced_report,
absl::optional<AttributionReport>& dropped_report,
const attribution_reporting::SuitableOrigin& destination_origin)
const attribution_reporting::SuitableOrigin& context_origin)
VALID_CONTEXT_REQUIRED(sequence_checker_);

// Initializes the database if necessary, and returns whether the database is
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,25 @@ bool MigrateToVersion44(sql::Database* db, sql::MetaTable* meta_table) {
return transaction.Commit();
}

bool MigrateToVersion45(sql::Database* db, sql::MetaTable* meta_table) {
// Wrap each migration in its own transaction. See comment in
// `MigrateToVersion34`.
sql::Transaction transaction(db);
if (!transaction.Begin()) {
return false;
}

static constexpr char kRenameSql[] =
"ALTER TABLE event_level_reports "
"RENAME COLUMN destination_origin TO context_origin";
if (!db->Execute(kRenameSql)) {
return false;
}

meta_table->SetVersionNumber(45);
return transaction.Commit();
}

} // namespace

bool UpgradeAttributionStorageSqlSchema(sql::Database* db,
Expand Down Expand Up @@ -811,6 +830,11 @@ bool UpgradeAttributionStorageSqlSchema(sql::Database* db,
return false;
}
}
if (meta_table->GetVersionNumber() == 44) {
if (!MigrateToVersion45(db, meta_table)) {
return false;
}
}
// Add similar if () blocks for new versions here.

if (base::ThreadTicks::IsSupported()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -814,4 +814,55 @@ TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion43ToCurrent) {
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}

TEST_F(AttributionStorageSqlMigrationsTest, MigrateVersion44ToCurrent) {
base::HistogramTester histograms;
LoadDatabase(GetVersionFilePath(44), DbPath());

// Verify pre-conditions.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));

{
static constexpr char kSql[] =
"SELECT destination_origin FROM event_level_reports";
sql::Statement s(db.GetUniqueStatement(kSql));

ASSERT_TRUE(s.Step());
ASSERT_EQ("https://a.d.test", s.ColumnString(0));
ASSERT_FALSE(s.Step());
}
}

MigrateDatabase();

// Verify schema is current.
{
sql::Database db;
ASSERT_TRUE(db.Open(DbPath()));

// Check version.
EXPECT_EQ(AttributionStorageSql::kCurrentVersionNumber,
VersionFromDatabase(&db));

// Compare normalized schemas
EXPECT_EQ(NormalizeSchema(GetCurrentSchema()),
NormalizeSchema(db.GetSchema()));

{
static constexpr char kSql[] =
"SELECT context_origin FROM event_level_reports";
sql::Statement s(db.GetUniqueStatement(kSql));

ASSERT_TRUE(s.Step());
ASSERT_EQ("https://a.d.test", s.ColumnString(0));
ASSERT_FALSE(s.Step());
}
}

// DB creation histograms should be recorded.
histograms.ExpectTotalCount("Conversions.Storage.CreationTime", 0);
histograms.ExpectTotalCount("Conversions.Storage.MigrationTime", 1);
}

} // namespace content
Original file line number Diff line number Diff line change
Expand Up @@ -1169,7 +1169,7 @@ TEST_F(AttributionStorageSqlTest, ReportTablesStoreDestinationOrigin) {

{
sql::Statement s(raw_db.GetUniqueStatement(
"SELECT destination_origin FROM event_level_reports"));
"SELECT context_origin FROM event_level_reports"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(s.ColumnString(0), kDestinationOriginB);
}
Expand All @@ -1182,7 +1182,7 @@ TEST_F(AttributionStorageSqlTest, ReportTablesStoreDestinationOrigin) {
}
}

TEST_F(AttributionStorageSqlTest, FakeReportUsesDestinationSiteAsOrigin) {
TEST_F(AttributionStorageSqlTest, FakeReportUsesSourceOriginAsContext) {
OpenDatabase();

delegate()->set_randomized_response(
Expand All @@ -1191,10 +1191,13 @@ TEST_F(AttributionStorageSqlTest, FakeReportUsesDestinationSiteAsOrigin) {
.trigger_time = base::Time::Now() + base::Microseconds(1),
.report_time = base::Time::Now() + base::Microseconds(2)}});

storage()->StoreSource(SourceBuilder()
.SetDestinationOrigin(*SuitableOrigin::Deserialize(
"https://a.d.test"))
.Build());
storage()->StoreSource(
SourceBuilder()
.SetSourceOrigin(*SuitableOrigin::Deserialize("https://a.s.test"))
.SetDestinationOrigin(
*SuitableOrigin::Deserialize("https://b.d.test"))
.SetReportingOrigin(*SuitableOrigin::Deserialize("https://r.test"))
.Build());

CloseDatabase();

Expand All @@ -1203,9 +1206,9 @@ TEST_F(AttributionStorageSqlTest, FakeReportUsesDestinationSiteAsOrigin) {

{
sql::Statement s(raw_db.GetUniqueStatement(
"SELECT destination_origin FROM event_level_reports"));
"SELECT context_origin FROM event_level_reports"));
ASSERT_TRUE(s.Step());
ASSERT_EQ(s.ColumnString(0), "https://d.test");
ASSERT_EQ(s.ColumnString(0), "https://a.s.test");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,6 @@ CREATE INDEX aggregate_trigger_time_idx ON aggregatable_report_metadata(trigger_

CREATE INDEX aggregate_report_time_idx ON aggregatable_report_metadata(report_time);

INSERT INTO event_level_reports VALUES(0,1,2,3,4,5,6,7,8,'https://a.d.test');

COMMIT;
49 changes: 49 additions & 0 deletions content/test/data/attribution_reporting/databases/version_45.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
PRAGMA foreign_keys=OFF;

BEGIN TRANSACTION;

CREATE TABLE sources(source_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_event_id INTEGER NOT NULL,source_origin TEXT NOT NULL,destination_origin TEXT NOT NULL,reporting_origin TEXT NOT NULL,source_time INTEGER NOT NULL,expiry_time INTEGER NOT NULL,event_report_window_time INTEGER NOT NULL,aggregatable_report_window_time INTEGER NOT NULL,num_attributions INTEGER NOT NULL,event_level_active INTEGER NOT NULL,aggregatable_active INTEGER NOT NULL,destination_site TEXT NOT NULL,source_type INTEGER NOT NULL,attribution_logic INTEGER NOT NULL,priority INTEGER NOT NULL,source_site TEXT NOT NULL,debug_key INTEGER,aggregatable_budget_consumed INTEGER NOT NULL,aggregatable_source BLOB NOT NULL,filter_data BLOB NOT NULL);

CREATE TABLE event_level_reports(report_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_id INTEGER NOT NULL,trigger_data INTEGER NOT NULL,trigger_time INTEGER NOT NULL,report_time INTEGER NOT NULL,priority INTEGER NOT NULL,failed_send_attempts INTEGER NOT NULL,external_report_id TEXT NOT NULL,debug_key INTEGER,context_origin TEXT NOT NULL);

CREATE TABLE rate_limits(id INTEGER PRIMARY KEY NOT NULL,scope INTEGER NOT NULL,source_id INTEGER NOT NULL,source_site TEXT NOT NULL,destination_site TEXT NOT NULL,context_origin TEXT NOT NULL,reporting_origin TEXT NOT NULL,time INTEGER NOT NULL,source_expiry_or_attribution_time INTEGER NOT NULL);

CREATE TABLE dedup_keys(source_id INTEGER NOT NULL,report_type INTEGER NOT NULL,dedup_key INTEGER NOT NULL,PRIMARY KEY(source_id,report_type,dedup_key))WITHOUT ROWID;

CREATE TABLE aggregatable_report_metadata(aggregation_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,source_id INTEGER NOT NULL,trigger_time INTEGER NOT NULL,debug_key INTEGER,external_report_id TEXT NOT NULL,report_time INTEGER NOT NULL,failed_send_attempts INTEGER NOT NULL,initial_report_time INTEGER NOT NULL,aggregation_coordinator INTEGER NOT NULL,attestation_token TEXT,destination_origin TEXT NOT NULL);

CREATE TABLE aggregatable_contributions(aggregation_id INTEGER NOT NULL,contribution_id INTEGER NOT NULL,key_high_bits INTEGER NOT NULL,key_low_bits INTEGER NOT NULL,value INTEGER NOT NULL,PRIMARY KEY(aggregation_id,contribution_id))WITHOUT ROWID;

CREATE TABLE meta(key LONGVARCHAR NOT NULL UNIQUE PRIMARY KEY, value LONGVARCHAR);

INSERT INTO meta VALUES('mmap_status','-1');
INSERT INTO meta VALUES('version','45');
INSERT INTO meta VALUES('last_compatible_version','45');

CREATE INDEX sources_by_active_destination_site_reporting_origin ON sources(event_level_active,aggregatable_active,destination_site,reporting_origin);

CREATE INDEX sources_by_expiry_time ON sources(expiry_time);

CREATE INDEX active_sources_by_source_origin ON sources(source_origin)WHERE event_level_active=1 OR aggregatable_active=1;

CREATE INDEX active_unattributed_sources_by_site_reporting_origin ON sources(source_site,reporting_origin)WHERE event_level_active=1 AND num_attributions=0 AND aggregatable_active=1 AND aggregatable_budget_consumed=0;

CREATE INDEX event_level_reports_by_report_time ON event_level_reports(report_time);

CREATE INDEX event_level_reports_by_source_id ON event_level_reports(source_id);

CREATE INDEX rate_limit_source_site_reporting_origin_idx ON rate_limits(scope,source_site,reporting_origin);

CREATE INDEX rate_limit_reporting_origin_idx ON rate_limits(scope,destination_site,source_site);

CREATE INDEX rate_limit_time_idx ON rate_limits(time);

CREATE INDEX rate_limit_source_id_idx ON rate_limits(source_id);

CREATE INDEX aggregate_source_id_idx ON aggregatable_report_metadata(source_id);

CREATE INDEX aggregate_trigger_time_idx ON aggregatable_report_metadata(trigger_time);

CREATE INDEX aggregate_report_time_idx ON aggregatable_report_metadata(report_time);

COMMIT;

0 comments on commit e5e97e3

Please sign in to comment.