Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

// META: script=/resources/testdriver.js
// META: script=/resources/testdriver-vendor.js
// META: script=/common/get-host-info.sub.js
// META: script=/common/utils.js
// META: script=resources/ba-fledge-util.sub.js
// META: script=resources/fledge-util.sub.js
// META: script=third_party/cbor-js/cbor.js
// META: script=/common/subset-tests.js
// META: timeout=long
// META: variant=?1-6
// META: variant=?7-last
// These tests focus on the paggResponse field in AuctionConfig's
// serverResponse, i.e. auctions involving private aggregation reporting. NOTE:
// Due to debug mode being disabled for B&A's Private Aggregation reports, these
// tests just exercise the code paths and ensure that correct number of reports
// are sent -- they don't otherwise verify report content.
// To better isolate from private aggregation tests run in parallel,
// don't use the usual origin here.
const MAIN_ORIGIN = OTHER_ORIGIN1;
const MAIN_PATH = '/.well-known/private-aggregation/report-protected-audience';
const BUCKET_ONE = new Uint8Array([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01
]);
function BigEndianInteger128ToUint8Array(val) {
let buffer = new Uint8Array(16);
for (let i = 15; i >= 0; i--) {
buffer[i] = Number(val & 0xFFn);
val >>= 8n;
}
return buffer;
}
function createSimplePerOriginPAggResponse(
reportingOrigin = MAIN_ORIGIN, igIndex = 0, event = 'reserved.win',
bucket = BUCKET_ONE, value = 10, filteringId = null) {
let contribution = {};
if (bucket !== null) {
contribution.bucket = bucket;
}
if (value !== null) {
contribution.value = value;
}
if (filteringId !== null) {
contribution.filteringId = filteringId;
}
return {
'reportingOrigin': reportingOrigin,
'igContributions': [{
'igIndex': igIndex,
'eventContributions': [{'event': event, 'contributions': [contribution]}]
}]
};
}
async function privateAggregationTestWithMutatedServerResponse(
test, expectWin, paggResponse, timeout = 5000 /*ms*/,
ownerOverride = null) {
await resetReports(MAIN_ORIGIN + MAIN_PATH);
let result = await BA.testWithMutatedServerResponse(
test, expectWin,
(msg, uuid) => {
msg.paggResponse = paggResponse;
},
(ig, uuid) => {
ig.ads[0].renderURL = createRenderURL(uuid);
},
ownerOverride);
createAndNavigateFencedFrame(test, result);
const reports = await pollReports(MAIN_PATH, timeout);
return reports;
}
async function testInvalidPAggResponseFields(
test, reportingOrigin = MAIN_ORIGIN, igIndex = 0, event = 'reserved.win',
bucket = '1', value = 10, filteringId = null) {
const paggResponse = [createSimplePerOriginPAggResponse(
reportingOrigin, igIndex, event, bucket, value, filteringId)];
let reports = await privateAggregationTestWithMutatedServerResponse(
test,
/*expectWin=*/ true, paggResponse, /*timeout=*/ 5000, MAIN_ORIGIN);
assert_equals(reports, null);
}
// The next few methods are modified from Chrome-specific
// wpt_internal/private-aggregation/resources/utils.js
const resetReports = url => {
url = `${url}?clear_stash=true`;
const options = {
method: 'POST',
mode: 'no-cors',
};
return fetch(url, options);
};
const delay = ms => new Promise(resolve => step_timeout(resolve, ms));
async function pollReports(path, wait_for = 1, timeout = 5000 /*ms*/) {
const targetUrl = new URL(path, MAIN_ORIGIN);
const endTime = performance.now() + timeout;
const outReports = [];
do {
const response = await fetch(targetUrl);
assert_true(response.ok, 'pollReports() fetch response should be OK.');
const reports = await response.json();
outReports.push(...reports);
if (outReports.length >= wait_for) {
break;
}
await delay(/*ms=*/ 100);
} while (performance.now() < endTime);
return outReports.length ? outReports : null;
};
/**
* Verifies that a report's aggregation_service_payloads has the expected
* fields. Currently for B&A's PAgg reports, debug mode is disabled, so we
* cannot check contributions in payload.
*/
const verifyAggregationServicePayloads = (aggregation_service_payloads) => {
assert_equals(aggregation_service_payloads.length, 1);
const payload_obj = aggregation_service_payloads[0];
assert_own_property(payload_obj, 'key_id');
assert_own_property(payload_obj, 'payload');
// Check the payload is base64 encoded. We do not decrypt the payload to
// test its contents.
atob(payload_obj.payload);
// Check there are no extra keys
assert_equals(Object.keys(payload_obj).length, expected_payload ? 3 : 2);
};
/**
* Verifies that a report has the expected fields. The `expected_payload` should
* be undefined.
*/
const verifyReport = (report, reporting_origin) => {
assert_own_property(report, 'shared_info');
let shared_info = JSON.parse(report.shared_info);
assert_own_property(shared_info, 'reporting_origin');
assert_equals(shared_info.reporting_origin, reporting_origin);
assert_own_property(report, 'aggregation_service_payloads');
assert_own_property(report, 'aggregation_coordinator_origin');
// TODO(qingxinwu): Maybe add tests for coordinator origin.
assert_not_own_property(report, 'debug_key');
// Check there are no extra keys
let expected_length = 3;
assert_equals(Object.keys(report).length, expected_length);
};
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(test, 'http://non-https.com');
}, 'Private aggregation - invalid reporting origin');
subsetTest(
promise_test,
async test => {await testInvalidPAggResponseFields(test, MAIN_ORIGIN, 100)},
'Private aggregation - invalid index');
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(
test, MAIN_ORIGIN, 0, 'reserved.not-supported');
}, 'Private aggregation - invalid event');
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(
test, MAIN_ORIGIN, 0, 'reserved.win', /*bucket=*/ null);
}, 'Private aggregation - missing required bucket');
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(
test, MAIN_ORIGIN, 0, 'reserved.win', new Uint8Array([
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01
]));
}, 'Private aggregation - bucket is bigger than 128 bits');
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(
test, MAIN_ORIGIN, 0, 'reserved.win', BUCKET_ONE, /*value=*/ null);
}, 'Private aggregation - missing required value');
subsetTest(promise_test, async test => {
await testInvalidPAggResponseFields(
test, MAIN_ORIGIN, 0, 'reserved.win', BUCKET_ONE, 10, 10000);
}, 'Private aggregation - invalid filteringId');
subsetTest(promise_test, async test => {
const paggResponse = [createSimplePerOriginPAggResponse()];
let reports = await privateAggregationTestWithMutatedServerResponse(
test,
/*expectWin=*/ true, paggResponse, /*timeout=*/ 6000, MAIN_ORIGIN);
assert_equals(reports.length, 1);
let report = JSON.parse(reports[0]);
verifyReport(report, MAIN_ORIGIN);
}, 'Private aggregation - successfully sent report');
// TODO(qingxinwu): may add a test for custom event type if possible.
subsetTest(promise_test, async test => {
const paggResponse = [{
'reportingOrigin': MAIN_ORIGIN,
'igContributions': [{
'igIndex': 0,
'eventContributions': [
{
'event': 'reserved.win',
'contributions': [{'value': 10}, {'bucket': BUCKET_ONE, 'value': 11}]
},
{
'event': 'reserved.not-supported',
'contributions':
[{'bucket': BigEndianInteger128ToUint8Array(2n), 'value': 22}]
},
]
}]
}];
let reports = await privateAggregationTestWithMutatedServerResponse(
test,
/*expectWin=*/ true, paggResponse, /*timeout=*/ 6000, MAIN_ORIGIN);
assert_equals(reports.length, 1);
let report = JSON.parse(reports[0]);
verifyReport(report, MAIN_ORIGIN);
}, 'Private aggregation - invalid contributions do not affect valid ones');
// TODO(qingxinwu): privateAggregation multi-seller.