From 3433a3e8ff358e3b93a3b57b31ce63204bf8afb4 Mon Sep 17 00:00:00 2001 From: Dan McArdle Date: Wed, 15 Jan 2025 13:01:44 -0500 Subject: [PATCH] Spec: Define per-context contribution limits (#164) This change adds the web-visible `maxContributions` field, which enables some callers to request different numbers of contributions per report. --- spec.bs | 81 ++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/spec.bs b/spec.bs index 3e6694f..c5e115b 100644 --- a/spec.bs +++ b/spec.bs @@ -378,6 +378,8 @@ An aggregatable report is a [=struct=] with the following items: :: A [=string=] or null : filtering ID max bytes :: A positive integer +: max contributions +:: A positive integer : queued :: A [=boolean=] @@ -410,6 +412,8 @@ items: :: A [=string=] or null : filtering ID max bytes (default: [=default filtering ID max bytes=]) :: A positive integer +: max contributions (default: null) +:: A positive integer or null @@ -466,10 +470,15 @@ controls which [=origins=] are valid [=aggregation coordinators=]. Every Default aggregation coordinator is an [=aggregation coordinator=] that controls which is used for a report if none is explicitly selected. -Maximum report contributions is a [=map=] from [=context type=] to -positive integers. Semantically, it defines the maximum number of contributions -that can be present in a single report for every kind of calling context, e.g. -Shared Storage. +Maximum maxContributions is a positive integer that defines an upper +bound on the number of contributions per [=aggregatable report=]. + +Default maxContributions by API is a [=map=] from [=context types=] +to positive integers. Semantically, it defines the default number of +contributions per report for every kind of calling context, e.g. Shared Storage. +The values in this map are used when callers do not specifically request another +value. Each value in this map must be less than or equal to [=maximum +maxContributions=]. Minimum report delay is a non-negative [=duration=] that controls the minimum delay to deliver an [=aggregatable report=]. @@ -569,12 +578,19 @@ steps. They return a [=boolean=]: not null, return true. 1. If |preSpecifiedParams|' [=pre-specified report parameters/filtering ID max bytes=] is not the [=default filtering ID max bytes=], return true. +1. If |preSpecifiedParams|' [=pre-specified report parameters/max + contributions=] is not null, return true. 1. Return false. -Note: If a context ID or non-default filtering ID max bytes was specified, a - report is sent, even if there are no contributions or there is insufficent - budget for the requested contributions. See [Protecting against leaks via - the number of reports](#protecting-against-leaks-via-the-number-of-reports). +Note: It is sometimes necessary to send a 'null report' to conceal the fact that + there were no contributions. For instance, it's possible that budget, which is + cross-site data in its own right, was insufficient for the requested + contributions. Alternatively, the caller might have chosen to make no + contributions after reading cross-site data. In these kinds of scenarios, the + absence of a report could reveal cross-site data to the reporting endpoint. See + [Protecting against leaks via the number of + reports](#protecting-against-leaks-via-the-number-of-reports). +
@@ -683,6 +699,9 @@ scope given a [=pre-specified report parameters=] |params| and a filtering ID max bytes=]. 1. [=Assert=]: |filteringIdMaxBytes| is [=set/contained=] in the [=valid filtering ID max bytes range=] +1. Let |maxContributions| be |params|' [=pre-specified report parameters/max + contributions=]. +1. [=Assert=]: |maxContributions| is null or greater than zero. 1. [=map/Set=] [=pre-specified report parameters map=][|batchingScope|] to |params|. @@ -718,11 +737,13 @@ null |timeout|: 1. [=iteration/Break=]. 1. If |hasProcessedContribution| is false, [=list/append=] |contribution| to |mergedContributions|. +1. Let |effectiveMaxContributions| be the result of [=determining the max + contributions=] with |preSpecifiedParams| and |api|. 1. Let |truncatedContributions| be a new [=list=]. -1. If |mergedContributions| has a [=list/size=] greater than [=maximum report - contributions=][|api|]: - 1. [=set/For each=] |n| of [=the exclusive range|the range=] 0 to [=maximum - report contributions=][|api|], exclusive: +1. If |mergedContributions| has a [=list/size=] greater than + |effectiveMaxContributions|: + 1. [=set/For each=] |n| of [=the exclusive range|the range=] 0 to + |effectiveMaxContributions|, exclusive: 1. [=set/Append=] |mergedContributions|[|n|] to |truncatedContributions|. 1. Otherwise, set |truncatedContributions| to |mergedContributions|. @@ -771,7 +792,7 @@ To obtain an aggregatable report given an [=origin=] |reportingOrigin|, a [=context type=] |api|, a [=list=] of {{PAHistogramContribution}}s |contributions|, a [=debug details=] |debugDetails|, an [=aggregation coordinator=] |aggregationCoordinator|, a -[=pre-specified report parameters=] |preSpecifiedParams|, a [=moment] or null +[=pre-specified report parameters=] |preSpecifiedParams|, a [=moment=] or null |timeout| and a [=moment=] |currentTime|, perform the following steps. They return an [=aggregatable report=]. 1. [=Assert=]: |reportingOrigin| is a [=potentially trustworthy origin=]. @@ -799,6 +820,9 @@ perform the following steps. They return an [=aggregatable report=]. : [=aggregatable report/filtering ID max bytes=] :: |preSpecifiedParams|' [=pre-specified report parameters/filtering ID max bytes=] + : [=aggregatable report/max contributions=] + :: The result of [=determining the max contributions=] with + |preSpecifiedParams| and |api|. : [=aggregatable report/queued=] :: false 1. Return |report|. @@ -821,6 +845,20 @@ They return a [=moment=].
+
+To determine the max contributions given a [=pre-specified report +parameters=] |preSpecifiedParams| and a [=context type=] |api|, perform the +following steps. They return a positive integer. +1. Let |maxContributions| be |preSpecifiedParams|' [=pre-specified report + parameters/max contributions=]. +1. If |maxContributions| is null, return [=default maxContributions by + API=][|api|]. +1. If |maxContributions| is greater than [=maximum maxContributions=], return + [=maximum maxContributions=]. +1. Return |maxContributions|. + +
+ Sending reports {#sending-reports} ---------------------------------- @@ -1044,8 +1082,7 @@ To obtain the plaintext payload given an [=aggregatable report=] |report|, perform the following steps. They return a [=byte sequence=]. 1. Let |payloadData| be a new [=list=]. 1. Let |contributions| be |report|'s [=aggregatable report/contributions=]. -1. Let |maxContributions| be - [=maximum report contributions=][[=aggregatable report/api=]]. +1. Let |maxContributions| be |report|'s [=aggregatable report/max contributions=]. 1. [=Assert=]: |contributions|' [=list/size=] is not greater than |maxContributions|. 1. [=iteration/While=] |contributions|' [=list/size=] is less than @@ -1258,18 +1295,20 @@ However, the number of reports with the given metadata could expose some cross-site information. To protect against this, the API delays sending reports by a randomized amount of time to make it difficult to determine whether a report was sent or not from any particular event. In the case that a -[=aggregatable report/context ID=] is supplied or a non-default [=aggregatable -report/filtering ID max bytes=] is specified, the API makes the number of -reports sent deterministic (sending 'null reports' if necessary -- each -containing only a contribution with a value of 0 in the payload). Additional -mitigations may also be possible in the future, e.g. adding noise to the report -count. +[=pre-specified report parameters/context ID=] is supplied, a non-default +[=pre-specified report parameters/filtering ID max bytes=] is specified, or a +[=pre-specified report parameters/max contributions=] is specified, the API +makes the number of reports sent deterministic (sending 'null reports' if +necessary — each containing only a contribution with a value of 0 in the +payload). Additional mitigations may also be possible in the future, e.g. adding +noise to the report count. ### Protecting against leaks via payload size ### {#protecting-against-leaks-via-payload-size} The length of the payload could additionally expose some cross-site information, namely how many contributions are included. To protect against this, the payload is padded to a fixed number of contributions. + ### Temporary debugging mechanism ### {#temporary-debugging-mechanism} The {{PrivateAggregation/enableDebugMode()}} method allows for many of the