server-response-private-aggregation.https.window.js (8266B)
1 // META: script=/resources/testdriver.js 2 // META: script=/resources/testdriver-vendor.js 3 // META: script=/common/get-host-info.sub.js 4 // META: script=/common/utils.js 5 // META: script=resources/ba-fledge-util.sub.js 6 // META: script=resources/fledge-util.sub.js 7 // META: script=third_party/cbor-js/cbor.js 8 // META: script=/common/subset-tests.js 9 // META: timeout=long 10 // META: variant=?1-6 11 // META: variant=?7-last 12 13 "use strict"; 14 15 // These tests focus on the paggResponse field in AuctionConfig's 16 // serverResponse, i.e. auctions involving private aggregation reporting. NOTE: 17 // Due to debug mode being disabled for B&A's Private Aggregation reports, these 18 // tests just exercise the code paths and ensure that correct number of reports 19 // are sent -- they don't otherwise verify report content. 20 21 // To better isolate from private aggregation tests run in parallel, 22 // don't use the usual origin here. 23 const MAIN_ORIGIN = OTHER_ORIGIN1; 24 const MAIN_PATH = '/.well-known/private-aggregation/report-protected-audience'; 25 26 const BUCKET_ONE = new Uint8Array([ 27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 28 0x00, 0x00, 0x01 29 ]); 30 31 function BigEndianInteger128ToUint8Array(val) { 32 let buffer = new Uint8Array(16); 33 for (let i = 15; i >= 0; i--) { 34 buffer[i] = Number(val & 0xFFn); 35 val >>= 8n; 36 } 37 return buffer; 38 } 39 40 function createSimplePerOriginPAggResponse( 41 reportingOrigin = MAIN_ORIGIN, igIndex = 0, event = 'reserved.win', 42 bucket = BUCKET_ONE, value = 10, filteringId = null) { 43 let contribution = {}; 44 if (bucket !== null) { 45 contribution.bucket = bucket; 46 } 47 if (value !== null) { 48 contribution.value = value; 49 } 50 if (filteringId !== null) { 51 contribution.filteringId = filteringId; 52 } 53 return { 54 'reportingOrigin': reportingOrigin, 55 'igContributions': [{ 56 'igIndex': igIndex, 57 'eventContributions': [{'event': event, 'contributions': [contribution]}] 58 }] 59 }; 60 } 61 62 async function privateAggregationTestWithMutatedServerResponse( 63 test, expectWin, paggResponse, timeout = 5000 /*ms*/, 64 ownerOverride = null) { 65 await resetReports(MAIN_ORIGIN + MAIN_PATH); 66 let result = await BA.testWithMutatedServerResponse( 67 test, expectWin, 68 (msg, uuid) => { 69 msg.paggResponse = paggResponse; 70 }, 71 (ig, uuid) => { 72 ig.ads[0].renderURL = createRenderURL(uuid); 73 }, 74 ownerOverride); 75 createAndNavigateFencedFrame(test, result); 76 const reports = await pollReports(MAIN_PATH, timeout); 77 return reports; 78 } 79 80 async function testInvalidPAggResponseFields( 81 test, reportingOrigin = MAIN_ORIGIN, igIndex = 0, event = 'reserved.win', 82 bucket = '1', value = 10, filteringId = null) { 83 const paggResponse = [createSimplePerOriginPAggResponse( 84 reportingOrigin, igIndex, event, bucket, value, filteringId)]; 85 86 let reports = await privateAggregationTestWithMutatedServerResponse( 87 test, 88 /*expectWin=*/ true, paggResponse, /*timeout=*/ 5000, MAIN_ORIGIN); 89 assert_equals(reports, null); 90 } 91 92 // The next few methods are modified from Chrome-specific 93 // wpt_internal/private-aggregation/resources/utils.js 94 95 const resetReports = url => { 96 url = `${url}?clear_stash=true`; 97 const options = { 98 method: 'POST', 99 mode: 'no-cors', 100 }; 101 return fetch(url, options); 102 }; 103 104 const delay = ms => new Promise(resolve => step_timeout(resolve, ms)); 105 106 async function pollReports(path, wait_for = 1, timeout = 5000 /*ms*/) { 107 const targetUrl = new URL(path, MAIN_ORIGIN); 108 const endTime = performance.now() + timeout; 109 const outReports = []; 110 111 do { 112 const response = await fetch(targetUrl); 113 assert_true(response.ok, 'pollReports() fetch response should be OK.'); 114 const reports = await response.json(); 115 outReports.push(...reports); 116 if (outReports.length >= wait_for) { 117 break; 118 } 119 await delay(/*ms=*/ 100); 120 } while (performance.now() < endTime); 121 122 return outReports.length ? outReports : null; 123 }; 124 125 /** 126 * Verifies that a report's aggregation_service_payloads has the expected 127 * fields. Currently for B&A's PAgg reports, debug mode is disabled, so we 128 * cannot check contributions in payload. 129 */ 130 const verifyAggregationServicePayloads = (aggregation_service_payloads) => { 131 assert_equals(aggregation_service_payloads.length, 1); 132 const payload_obj = aggregation_service_payloads[0]; 133 134 assert_own_property(payload_obj, 'key_id'); 135 assert_own_property(payload_obj, 'payload'); 136 // Check the payload is base64 encoded. We do not decrypt the payload to 137 // test its contents. 138 atob(payload_obj.payload); 139 140 // Check there are no extra keys 141 assert_equals(Object.keys(payload_obj).length, expected_payload ? 3 : 2); 142 }; 143 144 /** 145 * Verifies that a report has the expected fields. The `expected_payload` should 146 * be undefined. 147 */ 148 const verifyReport = (report, reporting_origin) => { 149 assert_own_property(report, 'shared_info'); 150 let shared_info = JSON.parse(report.shared_info); 151 assert_own_property(shared_info, 'reporting_origin'); 152 assert_equals(shared_info.reporting_origin, reporting_origin); 153 assert_own_property(report, 'aggregation_service_payloads'); 154 assert_own_property(report, 'aggregation_coordinator_origin'); 155 // TODO(qingxinwu): Maybe add tests for coordinator origin. 156 157 assert_not_own_property(report, 'debug_key'); 158 159 // Check there are no extra keys 160 let expected_length = 3; 161 assert_equals(Object.keys(report).length, expected_length); 162 }; 163 164 subsetTest(promise_test, async test => { 165 await testInvalidPAggResponseFields(test, 'http://non-https.com'); 166 }, 'Private aggregation - invalid reporting origin'); 167 168 subsetTest( 169 promise_test, 170 async test => {await testInvalidPAggResponseFields(test, MAIN_ORIGIN, 100)}, 171 'Private aggregation - invalid index'); 172 173 subsetTest(promise_test, async test => { 174 await testInvalidPAggResponseFields( 175 test, MAIN_ORIGIN, 0, 'reserved.not-supported'); 176 }, 'Private aggregation - invalid event'); 177 178 subsetTest(promise_test, async test => { 179 await testInvalidPAggResponseFields( 180 test, MAIN_ORIGIN, 0, 'reserved.win', /*bucket=*/ null); 181 }, 'Private aggregation - missing required bucket'); 182 183 subsetTest(promise_test, async test => { 184 await testInvalidPAggResponseFields( 185 test, MAIN_ORIGIN, 0, 'reserved.win', new Uint8Array([ 186 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 187 0x00, 0x00, 0x00, 0x00, 0x01 188 ])); 189 }, 'Private aggregation - bucket is bigger than 128 bits'); 190 191 subsetTest(promise_test, async test => { 192 await testInvalidPAggResponseFields( 193 test, MAIN_ORIGIN, 0, 'reserved.win', BUCKET_ONE, /*value=*/ null); 194 }, 'Private aggregation - missing required value'); 195 196 subsetTest(promise_test, async test => { 197 await testInvalidPAggResponseFields( 198 test, MAIN_ORIGIN, 0, 'reserved.win', BUCKET_ONE, 10, 10000); 199 }, 'Private aggregation - invalid filteringId'); 200 201 subsetTest(promise_test, async test => { 202 const paggResponse = [createSimplePerOriginPAggResponse()]; 203 204 let reports = await privateAggregationTestWithMutatedServerResponse( 205 test, 206 /*expectWin=*/ true, paggResponse, /*timeout=*/ 6000, MAIN_ORIGIN); 207 assert_equals(reports.length, 1); 208 let report = JSON.parse(reports[0]); 209 verifyReport(report, MAIN_ORIGIN); 210 }, 'Private aggregation - successfully sent report'); 211 212 // TODO(qingxinwu): may add a test for custom event type if possible. 213 214 subsetTest(promise_test, async test => { 215 const paggResponse = [{ 216 'reportingOrigin': MAIN_ORIGIN, 217 'igContributions': [{ 218 'igIndex': 0, 219 'eventContributions': [ 220 { 221 'event': 'reserved.win', 222 'contributions': [{'value': 10}, {'bucket': BUCKET_ONE, 'value': 11}] 223 }, 224 { 225 'event': 'reserved.not-supported', 226 'contributions': 227 [{'bucket': BigEndianInteger128ToUint8Array(2n), 'value': 22}] 228 }, 229 ] 230 }] 231 }]; 232 233 let reports = await privateAggregationTestWithMutatedServerResponse( 234 test, 235 /*expectWin=*/ true, paggResponse, /*timeout=*/ 6000, MAIN_ORIGIN); 236 assert_equals(reports.length, 1); 237 let report = JSON.parse(reports[0]); 238 verifyReport(report, MAIN_ORIGIN); 239 }, 'Private aggregation - invalid contributions do not affect valid ones'); 240 241 // TODO(qingxinwu): privateAggregation multi-seller.