trusted-scoring-signals.https.window.js (38789B)
1 // META: script=/resources/testdriver.js 2 // META: script=/resources/testdriver-vendor.js 3 // META: script=/common/utils.js 4 // META: script=resources/fledge-util.sub.js 5 // META: script=/common/subset-tests.js 6 // META: timeout=long 7 // META: variant=?1-5 8 // META: variant=?6-10 9 // META: variant=?11-15 10 // META: variant=?16-20 11 // META: variant=?21-25 12 // META: variant=?26-30 13 // META: variant=?31-35 14 // META: variant=?36-40 15 // META: variant=?41-45 16 // META: variant=?46-50 17 // META: variant=?51-last 18 19 "use strict"; 20 21 // These tests focus on trustedScoringSignals: Requesting them, handling network 22 // errors, handling the renderURLs portion of the response, passing renderURLs 23 // to worklet scripts, and handling the Data-Version header. 24 25 // Helper for trusted scoring signals tests. Runs an auction with 26 // trustedSignalsURL and a single interest group, failing the test if there's no 27 // winner. "scoreAdCheck" is an expression that should be true 28 // when evaluated in scoreAd(). "renderURL" can be used to control the response 29 // given for trustedSignalsURL. 30 async function runTrustedScoringSignalsTest(test, uuid, renderURL, scoreAdCheck, 31 additionalInterestGroupOverrides, 32 trustedSignalsURL = TRUSTED_SCORING_SIGNALS_URL, 33 decisionScriptParamOverrides = {}) { 34 const auctionConfigOverrides = { 35 trustedScoringSignalsURL: trustedSignalsURL, 36 decisionLogicURL: 37 createDecisionScriptURL(uuid, { 38 scoreAd: `if (!(${scoreAdCheck})) throw "error";`, 39 ...decisionScriptParamOverrides})}; 40 await joinInterestGroup(test, uuid, 41 {ads: [{ renderURL: renderURL }], 42 ...additionalInterestGroupOverrides}); 43 return await runBasicFledgeTestExpectingWinner( 44 test, uuid, auctionConfigOverrides); 45 } 46 47 // Much like runTrustedScoringSignalsTest, but runs auctions through reporting 48 // as well, and evaluates `check` both in scodeAd() and reportResult(). Also 49 // makes sure browserSignals.dataVersion is undefined in generateBid() and 50 // reportWin(). 51 async function runTrustedScoringSignalsDataVersionTest( 52 test, uuid, renderURL, check) { 53 const interestGroupOverrides = { 54 biddingLogicURL: 55 createBiddingScriptURL({ 56 generateBid: 57 `if (browserSignals.dataVersion !== undefined) 58 throw "Bad browserSignals.dataVersion"`, 59 reportWin: 60 `if (browserSignals.dataVersion !== undefined) 61 sendReportTo('${createSellerReportURL(uuid, '1-error')}'); 62 else 63 sendReportTo('${createSellerReportURL(uuid, '1')}');` }), 64 ads: [{ renderURL: renderURL }] 65 }; 66 await joinInterestGroup(test, uuid, interestGroupOverrides); 67 68 const auctionConfigOverrides = { 69 decisionLogicURL: createDecisionScriptURL( 70 uuid, 71 { scoreAd: 72 `if (!(${check})) return false;`, 73 reportResult: 74 `if (!(${check})) 75 sendReportTo('${createSellerReportURL(uuid, '2-error')}') 76 sendReportTo('${createSellerReportURL(uuid, '2')}')`, 77 }), 78 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL 79 } 80 await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides); 81 await waitForObservedRequests( 82 uuid, [createSellerReportURL(uuid, '1'), createSellerReportURL(uuid, '2')]); 83 } 84 85 // Creates a render URL that, when sent to the trusted-scoring-signals.py, 86 // results in a trusted scoring signals response with the provided response 87 // body. 88 function createScoringSignalsRenderURLWithBody(uuid, responseBody) { 89 return createRenderURL(uuid, /*script=*/null, 90 /*signalsParam=*/`replace-body:${responseBody}`); 91 } 92 93 ///////////////////////////////////////////////////////////////////////////// 94 // Tests where no renderURL value is received for the passed in renderURL. 95 ///////////////////////////////////////////////////////////////////////////// 96 97 subsetTest(promise_test, async test => { 98 const uuid = generateUuid(test); 99 const decisionLogicScriptURL = createDecisionScriptURL( 100 uuid, 101 { scoreAd: 'if (trustedScoringSignals !== null) throw "error";' }); 102 await joinGroupAndRunBasicFledgeTestExpectingWinner( 103 test, 104 { uuid: uuid, 105 auctionConfigOverrides: { decisionLogicURL: decisionLogicScriptURL } 106 }); 107 }, 'No trustedScoringSignalsURL.'); 108 109 subsetTest(promise_test, async test => { 110 const uuid = generateUuid(test); 111 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'close-connection'); 112 await runTrustedScoringSignalsTest( 113 test, uuid, renderURL, 114 'trustedScoringSignals === null'); 115 }, 'Trusted scoring signals closes the connection without sending anything.'); 116 117 subsetTest(promise_test, async test => { 118 const uuid = generateUuid(test); 119 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'http-error'); 120 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 121 }, 'Trusted scoring signals response is HTTP 404 error.'); 122 123 subsetTest(promise_test, async test => { 124 const uuid = generateUuid(test); 125 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-content-type'); 126 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 127 }, 'Trusted scoring signals response has no content-type.'); 128 129 subsetTest(promise_test, async test => { 130 const uuid = generateUuid(test); 131 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-content-type'); 132 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 133 }, 'Trusted scoring signals response has wrong content-type.'); 134 135 subsetTest(promise_test, async test => { 136 const uuid = generateUuid(test); 137 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'ad-auction-not-allowed'); 138 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 139 }, 'Trusted scoring signals response does not allow FLEDGE.'); 140 141 subsetTest(promise_test, async test => { 142 const uuid = generateUuid(test); 143 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'bad-ad-auction-allowed'); 144 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 145 }, 'Trusted scoring signals response has wrong Ad-Auction-Allowed header.'); 146 147 subsetTest(promise_test, async test => { 148 const uuid = generateUuid(test); 149 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-ad-auction-allow'); 150 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 151 }, 'Trusted scoring signals response has no Ad-Auction-Allowed header.'); 152 153 subsetTest(promise_test, async test => { 154 const uuid = generateUuid(test); 155 const renderURL = createScoringSignalsRenderURLWithBody( 156 uuid, /*responseBody=*/''); 157 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 158 }, 'Trusted scoring signals response has no body.'); 159 160 subsetTest(promise_test, async test => { 161 const uuid = generateUuid(test); 162 const renderURL = createScoringSignalsRenderURLWithBody( 163 uuid, /*responseBody=*/'Not JSON'); 164 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 165 }, 'Trusted scoring signals response is not JSON.'); 166 167 subsetTest(promise_test, async test => { 168 const uuid = generateUuid(test); 169 const renderURL = createScoringSignalsRenderURLWithBody( 170 uuid, /*responseBody=*/'[]'); 171 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 172 }, 'Trusted scoring signals response is a JSON array.'); 173 174 subsetTest(promise_test, async test => { 175 const uuid = generateUuid(test); 176 const renderURL = createScoringSignalsRenderURLWithBody( 177 uuid, /*responseBody=*/'{JSON_keys_need_quotes: 1}'); 178 await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null'); 179 }, 'Trusted scoring signals response is invalid JSON object.'); 180 181 subsetTest(promise_test, async test => { 182 const uuid = generateUuid(test); 183 const renderURL = createScoringSignalsRenderURLWithBody( 184 uuid, /*responseBody=*/'{}'); 185 await runTrustedScoringSignalsTest( 186 test, uuid, renderURL, 187 `trustedScoringSignals.renderURL["${renderURL}"] === null`); 188 }, 'Trusted scoring signals response has no renderURL object.'); 189 190 subsetTest(promise_test, async test => { 191 const uuid = generateUuid(test); 192 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-value'); 193 await runTrustedScoringSignalsTest( 194 test, uuid, renderURL, 195 `trustedScoringSignals.renderURL["${renderURL}"] === null`); 196 }, 'Trusted scoring signals response has no renderURLs.'); 197 198 subsetTest(promise_test, async test => { 199 const uuid = generateUuid(test); 200 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-url'); 201 await runTrustedScoringSignalsTest( 202 test, uuid, renderURL, 203 `trustedScoringSignals.renderURL["${renderURL}"] === null && 204 Object.keys(trustedScoringSignals.renderURL).length === 1`); 205 }, 'Trusted scoring signals response has renderURL not in response.'); 206 207 ///////////////////////////////////////////////////////////////////////////// 208 // Tests where renderURL value is received for the passed in renderURL. 209 ///////////////////////////////////////////////////////////////////////////// 210 211 subsetTest(promise_test, async test => { 212 const uuid = generateUuid(test); 213 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'null-value'); 214 await runTrustedScoringSignalsTest( 215 test, uuid, renderURL, 216 `trustedScoringSignals.renderURL["${renderURL}"] === null`); 217 }, 'Trusted scoring signals response has null value for renderURL.'); 218 219 subsetTest(promise_test, async test => { 220 const uuid = generateUuid(test); 221 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value'); 222 await runTrustedScoringSignalsTest( 223 test, uuid, renderURL, 224 `trustedScoringSignals.renderURL["${renderURL}"] === 1`); 225 }, 'Trusted scoring signals response has a number value for renderURL.'); 226 227 subsetTest(promise_test, async test => { 228 const uuid = generateUuid(test); 229 const renderURL = createRenderURL(uuid, /*script=*/null, 230 /*signalsParam=*/'string-value'); 231 await runTrustedScoringSignalsTest( 232 test, uuid, renderURL, 233 `trustedScoringSignals.renderURL["${renderURL}"] === "1"`); 234 }, 'Trusted scoring signals response has a string value for renderURL.'); 235 236 subsetTest(promise_test, async test => { 237 const uuid = generateUuid(test); 238 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'array-value'); 239 await runTrustedScoringSignalsTest( 240 test, uuid, renderURL, 241 `JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]) === '[1,"foo",null]'`); 242 }, 'Trusted scoring signals response has an array value for renderURL.'); 243 244 subsetTest(promise_test, async test => { 245 const uuid = generateUuid(test); 246 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'object-value'); 247 await runTrustedScoringSignalsTest( 248 test, uuid, renderURL, 249 `Object.keys(trustedScoringSignals.renderURL["${renderURL}"]).length === 2 && 250 trustedScoringSignals.renderURL["${renderURL}"]["a"] === "b" && 251 JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]["c"]) === '["d"]'`); 252 }, 'Trusted scoring signals response has an object value for renderURL.'); 253 254 subsetTest(promise_test, async test => { 255 const uuid = generateUuid(test); 256 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'+%20 \x00?,3#&'); 257 await runTrustedScoringSignalsTest( 258 test, uuid, renderURL, 259 `trustedScoringSignals.renderURL["${renderURL}"] === "default value"`); 260 }, 'Trusted scoring signals with escaped renderURL.'); 261 262 subsetTest(promise_test, async test => { 263 const uuid = generateUuid(test); 264 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'hostname'); 265 await runTrustedScoringSignalsTest( 266 test, uuid, renderURL, 267 `trustedScoringSignals.renderURL["${renderURL}"] === "${window.location.hostname}"`); 268 }, 'Trusted scoring signals receives hostname field.'); 269 270 // Joins two interest groups and makes sure the scoring signals for one are never leaked 271 // to the seller script when scoring the other. 272 // 273 // There's no guarantee in this test that a single request to the server will be made with 274 // render URLs from two different IGs, though that's the case this is trying to test - 275 // browsers are not required to support batching, and even if they do, joining any two 276 // particular requests may be racy. 277 subsetTest(promise_test, async test => { 278 const uuid = generateUuid(test); 279 const renderURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value'); 280 const renderURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'string-value'); 281 await joinInterestGroup(test, uuid, { ads: [{ renderURL: renderURL1 }], name: '1' }); 282 await joinInterestGroup(test, uuid, { ads: [{ renderURL: renderURL2 }], name: '2' }); 283 let auctionConfigOverrides = { trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL }; 284 285 // scoreAd() only accepts the first IG's bid, validating its trustedScoringSignals. 286 auctionConfigOverrides.decisionLogicURL = 287 createDecisionScriptURL(uuid, { 288 scoreAd: `if (browserSignals.renderURL === "${renderURL1}" && 289 trustedScoringSignals.renderURL["${renderURL1}"] !== 1 || 290 trustedScoringSignals.renderURL["${renderURL2}"] !== undefined) 291 return;` }); 292 let config = await runBasicFledgeAuction( 293 test, uuid, auctionConfigOverrides); 294 assert_true(config instanceof FencedFrameConfig, 295 `Wrong value type returned from first auction: ${config.constructor.type}`); 296 297 // scoreAd() only accepts the second IG's bid, validating its trustedScoringSignals. 298 auctionConfigOverrides.decisionLogicURL = 299 createDecisionScriptURL(uuid, { 300 scoreAd: `if (browserSignals.renderURL === "${renderURL2}" && 301 trustedScoringSignals.renderURL["${renderURL1}"] !== undefined || 302 trustedScoringSignals.renderURL["${renderURL2}"] !== '1') 303 return;` }); 304 config = await runBasicFledgeAuction( 305 test, uuid, auctionConfigOverrides); 306 assert_true(config instanceof FencedFrameConfig, 307 `Wrong value type returned from second auction: ${config.constructor.type}`); 308 }, 'Trusted scoring signals multiple renderURLs.'); 309 310 ///////////////////////////////////////////////////////////////////////////// 311 // Cross-origin trusted scoring signals tests 312 ///////////////////////////////////////////////////////////////////////////// 313 subsetTest(promise_test, async test => { 314 const uuid = generateUuid(test); 315 const renderURL = createRenderURL(uuid, /*script=*/null, 316 /*signalsParam=*/'string-value,data-version:3,cors'); 317 await runTrustedScoringSignalsTest( 318 test, uuid, renderURL, 319 `trustedScoringSignals === null && 320 !('dataVersion' in browserSignals) && 321 crossOriginTrustedScoringSignals['${OTHER_ORIGIN1}'].renderURL[ 322 "${renderURL}"] === "1" && 323 browserSignals.crossOriginDataVersion === 3`, 324 /*additionalInterestGroupOverrides=*/ {}, 325 CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL, 326 {permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`}); 327 }, 'Basic cross-origin trusted scoring signals.'); 328 329 subsetTest(promise_test, async test => { 330 const uuid = generateUuid(test); 331 const renderURL = createRenderURL(uuid, /*script=*/null, 332 /*signalsParam=*/'string-value,data-version:3'); 333 await runTrustedScoringSignalsTest( 334 test, uuid, renderURL, 335 `trustedScoringSignals === null && 336 !('dataVersion' in browserSignals) && 337 crossOriginTrustedScoringSignals === null && 338 !('crossOriginDataVersion' in browserSignals)`, 339 /*additionalInterestGroupOverrides=*/ {}, 340 CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL, 341 {permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`}); 342 }, 'Cross-origin trusted scoring signals w/o CORS authorization.'); 343 344 subsetTest(promise_test, async test => { 345 const uuid = generateUuid(test); 346 const sellerReportURL = createSellerReportURL(uuid); 347 const renderURL = createRenderURL(uuid, /*script=*/null, 348 /*signalsParam=*/`string-value,data-version:3,uuid:${uuid},dispatch:track_get`); 349 // Use the request tracker for trusted scoring signals, to have it 350 // record whether they got fetched or not. 351 const crossOriginRequestTrackerURL = OTHER_ORIGIN1 + BASE_PATH + 352 'resources/request-tracker.py'; 353 354 let combinedTrustedSignalsURL = new URL(crossOriginRequestTrackerURL); 355 combinedTrustedSignalsURL.search = 356 `hostname=${window.location.hostname}&renderUrls=${encodeURIComponent(renderURL)}` 357 358 let result = await runTrustedScoringSignalsTest( 359 test, uuid, renderURL, 360 `trustedScoringSignals === null && 361 !('dataVersion' in browserSignals) && 362 crossOriginTrustedScoringSignals === null && 363 !('crossOriginDataVersion' in browserSignals)`, 364 /*additionalInterestGroupOverrides=*/ {}, 365 crossOriginRequestTrackerURL, 366 {permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`, 367 reportResult: `sendReportTo("${sellerReportURL}")`}); 368 createAndNavigateFencedFrame(test, result); 369 await waitForObservedRequests( 370 uuid, [combinedTrustedSignalsURL.href, createBidderReportURL(uuid), sellerReportURL]); 371 }, 'Cross-origin trusted scoring signals w/o CORS authorization sends request.'); 372 373 subsetTest(promise_test, async test => { 374 const uuid = generateUuid(test); 375 const renderURL = createRenderURL(uuid, /*script=*/null, 376 /*signalsParam=*/'string-value,data-version:3, cors'); 377 await runTrustedScoringSignalsTest( 378 test, uuid, renderURL, 379 `trustedScoringSignals === null && 380 !('dataVersion' in browserSignals) && 381 crossOriginTrustedScoringSignals === null && 382 !('crossOriginDataVersion' in browserSignals)`, 383 /*additionalInterestGroupOverrides=*/ {}, 384 CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL); 385 }, 'Cross-origin trusted scoring signals w/o script allow header.'); 386 387 subsetTest(promise_test, async test => { 388 const uuid = generateUuid(test); 389 const renderURL = createRenderURL(uuid, /*script=*/null, 390 /*signalsParam=*/'string-value,data-version:3'); 391 await runTrustedScoringSignalsTest( 392 test, uuid, renderURL, 393 `trustedScoringSignals === null && 394 !('dataVersion' in browserSignals) && 395 crossOriginTrustedScoringSignals === null && 396 !('crossOriginDataVersion' in browserSignals)`, 397 /*additionalInterestGroupOverrides=*/ {}, 398 CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL, 399 {permitCrossOriginTrustedSignals: 400 `"${OTHER_ORIGIN2}", "${window.location.origin}"`}); 401 }, 'Cross-origin trusted scoring signals with wrong script allow header.'); 402 403 subsetTest(promise_test, async test => { 404 const uuid = generateUuid(test); 405 const sellerReportURL = createSellerReportURL(uuid); 406 const renderURL = createRenderURL(uuid, /*script=*/null, 407 /*signalsParam=*/`string-value,data-version:3,uuid:${uuid},dispatch:track_get`); 408 // Use the request tracker for trusted scoring signals, to have it 409 // record whether they got fetched or not. 410 const crossOriginRequestTrackerURL = OTHER_ORIGIN1 + BASE_PATH + 411 'resources/request-tracker.py'; 412 let result = await runTrustedScoringSignalsTest( 413 test, uuid, renderURL, 414 `trustedScoringSignals === null && 415 !('dataVersion' in browserSignals) && 416 crossOriginTrustedScoringSignals === null && 417 !('crossOriginDataVersion' in browserSignals)`, 418 /*additionalInterestGroupOverrides=*/ {}, 419 crossOriginRequestTrackerURL, 420 {permitCrossOriginTrustedSignals: 421 `"${OTHER_ORIGIN2}", "${window.location.origin}"`, 422 reportResult: `sendReportTo("${sellerReportURL}")`}); 423 createAndNavigateFencedFrame(test, result); 424 await waitForObservedRequests(uuid, 425 [createBidderReportURL(uuid), sellerReportURL]); 426 }, 'Cross-origin trusted scoring signals with wrong script allow header not fetched.'); 427 428 ///////////////////////////////////////////////////////////////////////////// 429 // Data-Version tests 430 ///////////////////////////////////////////////////////////////////////////// 431 432 subsetTest(promise_test, async test => { 433 const uuid = generateUuid(test); 434 const renderURL = createRenderURL(uuid); 435 await runTrustedScoringSignalsDataVersionTest( 436 test, uuid, renderURL, 437 'browserSignals.dataVersion === undefined'); 438 }, 'Trusted scoring signals response has no Data-Version.'); 439 440 subsetTest(promise_test, async test => { 441 const uuid = generateUuid(test); 442 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3'); 443 await runTrustedScoringSignalsDataVersionTest( 444 test, uuid, renderURL, 445 'browserSignals.dataVersion === 3'); 446 }, 'Trusted scoring signals response has valid Data-Version.'); 447 448 subsetTest(promise_test, async test => { 449 const uuid = generateUuid(test); 450 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:0'); 451 await runTrustedScoringSignalsDataVersionTest( 452 test, uuid, renderURL, 453 'browserSignals.dataVersion === 0'); 454 }, 'Trusted scoring signals response has min Data-Version.'); 455 456 subsetTest(promise_test, async test => { 457 const uuid = generateUuid(test); 458 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:4294967295'); 459 await runTrustedScoringSignalsDataVersionTest( 460 test, uuid, renderURL, 461 'browserSignals.dataVersion === 4294967295'); 462 }, 'Trusted scoring signals response has max Data-Version.'); 463 464 subsetTest(promise_test, async test => { 465 const uuid = generateUuid(test); 466 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:4294967296'); 467 await runTrustedScoringSignalsDataVersionTest( 468 test, uuid, renderURL, 469 'browserSignals.dataVersion === undefined'); 470 }, 'Trusted scoring signals response has too large Data-Version.'); 471 472 subsetTest(promise_test, async test => { 473 const uuid = generateUuid(test); 474 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:03'); 475 await runTrustedScoringSignalsDataVersionTest( 476 test, uuid, renderURL, 477 'browserSignals.dataVersion === undefined'); 478 }, 'Trusted scoring signals response has data-version with leading 0.'); 479 480 subsetTest(promise_test, async test => { 481 const uuid = generateUuid(test); 482 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:-1'); 483 await runTrustedScoringSignalsDataVersionTest( 484 test, uuid, renderURL, 485 'browserSignals.dataVersion === undefined'); 486 }, 'Trusted scoring signals response has negative Data-Version.'); 487 488 subsetTest(promise_test, async test => { 489 const uuid = generateUuid(test); 490 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:1.3'); 491 await runTrustedScoringSignalsDataVersionTest( 492 test, uuid, renderURL, 493 'browserSignals.dataVersion === undefined'); 494 }, 'Trusted scoring signals response has decimal in Data-Version.'); 495 496 subsetTest(promise_test, async test => { 497 const uuid = generateUuid(test); 498 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:2 2'); 499 await runTrustedScoringSignalsDataVersionTest( 500 test, uuid, renderURL, 501 'browserSignals.dataVersion === undefined'); 502 }, 'Trusted scoring signals response has space in Data-Version.'); 503 504 subsetTest(promise_test, async test => { 505 const uuid = generateUuid(test); 506 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:0x4'); 507 await runTrustedScoringSignalsDataVersionTest( 508 test, uuid, renderURL, 509 'browserSignals.dataVersion === undefined'); 510 }, 'Trusted scoring signals response has hex Data-Version.'); 511 512 subsetTest(promise_test, async test => { 513 const uuid = generateUuid(test); 514 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:'); 515 await runTrustedScoringSignalsDataVersionTest( 516 test, uuid, renderURL, 517 'browserSignals.dataVersion === undefined'); 518 }, 'Trusted scoring signals response has data-version and empty body.'); 519 520 subsetTest(promise_test, async test => { 521 const uuid = generateUuid(test); 522 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:[]'); 523 await runTrustedScoringSignalsDataVersionTest( 524 test, uuid, renderURL, 525 'browserSignals.dataVersion === undefined'); 526 }, 'Trusted scoring signals response has data-version and JSON array body.'); 527 528 subsetTest(promise_test, async test => { 529 const uuid = generateUuid(test); 530 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:{} {}'); 531 await runTrustedScoringSignalsDataVersionTest( 532 test, uuid, renderURL, 533 'browserSignals.dataVersion === undefined'); 534 }, 'Trusted scoring signals response has data-version and double JSON object body.'); 535 536 subsetTest(promise_test, async test => { 537 const uuid = generateUuid(test); 538 const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:{}'); 539 await runTrustedScoringSignalsDataVersionTest( 540 test, uuid, renderURL, 541 'browserSignals.dataVersion === 3'); 542 }, 'Trusted scoring signals response has data-version and no renderURLs.'); 543 544 ///////////////////////////////////////////////////////////////////////////// 545 // Trusted scoring signals + component ad tests. 546 ///////////////////////////////////////////////////////////////////////////// 547 548 subsetTest(promise_test, async test => { 549 const uuid = generateUuid(test); 550 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'close-connection'); 551 const componentURL = createRenderURL(uuid, /*script=*/null); 552 await runTrustedScoringSignalsTest( 553 test, uuid, renderURL, 554 `trustedScoringSignals === null`, 555 {adComponents: [{ renderURL: componentURL }], 556 biddingLogicURL: createBiddingScriptURL({ 557 generateBid: `return {bid: 1, 558 render: interestGroup.ads[0].renderURL, 559 adComponents: [interestGroup.adComponents[0].renderURL]};`}) 560 }); 561 }, 'Component ads trusted scoring signals, server closes the connection without sending anything.'); 562 563 subsetTest(promise_test, async test => { 564 const uuid = generateUuid(test); 565 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value'); 566 // This should not be sent. If it is, it will take precedence over the "num-value" parameter 567 // from "renderURL", resulting in the "renderURL" having a null "trustedScoringSignals" value. 568 const componentURL = createScoringSignalsRenderURLWithBody( 569 uuid, /*responseBody=*/'{}'); 570 await runTrustedScoringSignalsTest( 571 test, uuid, renderURL, 572 `Object.keys(trustedScoringSignals.renderURL).length === 1 && 573 trustedScoringSignals.renderURL["${renderURL}"] === 1 && 574 trustedScoringSignals.adComponentRenderURLs === undefined`, 575 { adComponents: [{ renderURL: componentURL }] }); 576 }, 'Trusted scoring signals request without component ads in bid.'); 577 578 subsetTest(promise_test, async test => { 579 const uuid = generateUuid(test); 580 const renderURL = createScoringSignalsRenderURLWithBody( 581 uuid, /*responseBody=*/'{}'); 582 const componentURL = createRenderURL(uuid, /*script=*/null); 583 await runTrustedScoringSignalsTest( 584 test, uuid, renderURL, 585 `Object.keys(trustedScoringSignals.renderURL).length === 1 && 586 trustedScoringSignals.renderURL["${renderURL}"] === null && 587 Object.keys(trustedScoringSignals.adComponentRenderURLs).length === 1 && 588 trustedScoringSignals.adComponentRenderURLs["${componentURL}"] === null`, 589 {adComponents: [{ renderURL: componentURL }], 590 biddingLogicURL: createBiddingScriptURL({ 591 generateBid: `return {bid: 1, 592 render: interestGroup.ads[0].renderURL, 593 adComponents: [interestGroup.adComponents[0].renderURL]};`}) 594 }); 595 }, 'Component ads trusted scoring signals trusted scoring signals response is empty JSON object.'); 596 597 subsetTest(promise_test, async test => { 598 const uuid = generateUuid(test); 599 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'hostname'); 600 const componentURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'null-value'); 601 const componentURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value'); 602 const componentURL3 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'string-value'); 603 const componentURL4 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'array-value'); 604 const componentURL5 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'object-value'); 605 const componentURL6 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-url'); 606 607 await runTrustedScoringSignalsTest( 608 test, uuid, renderURL, 609 `Object.keys(trustedScoringSignals.renderURL).length === 1 && 610 trustedScoringSignals.renderURL["${renderURL}"] === "${window.location.hostname}" && 611 612 Object.keys(trustedScoringSignals.adComponentRenderURLs).length === 6 && 613 trustedScoringSignals.adComponentRenderURLs["${componentURL1}"] === null && 614 trustedScoringSignals.adComponentRenderURLs["${componentURL2}"] === 1 && 615 trustedScoringSignals.adComponentRenderURLs["${componentURL3}"] === "1" && 616 JSON.stringify(trustedScoringSignals.adComponentRenderURLs["${componentURL4}"]) === '[1,"foo",null]' && 617 Object.keys(trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]).length === 2 && 618 trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]["a"] === "b" && 619 JSON.stringify(trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]["c"]) === '["d"]' && 620 trustedScoringSignals.adComponentRenderURLs["${componentURL6}"] === null`, 621 622 {adComponents: [{ renderURL: componentURL1 }, { renderURL: componentURL2 }, 623 { renderURL: componentURL3 }, { renderURL: componentURL4 }, 624 { renderURL: componentURL5 }, { renderURL: componentURL6 }], 625 biddingLogicURL: createBiddingScriptURL({ 626 generateBid: `return {bid: 1, 627 render: interestGroup.ads[0].renderURL, 628 adComponents: ["${componentURL1}", "${componentURL2}", 629 "${componentURL3}", "${componentURL4}", 630 "${componentURL5}", "${componentURL6}"]};` 631 }) 632 }); 633 }, 'Component ads trusted scoring signals.'); 634 635 ///////////////////////////////////////////////////////////////////////////// 636 // maxTrustedBiddingSignalsURLLength tests 637 ///////////////////////////////////////////////////////////////////////////// 638 // To detect whether two signals are fetched together or separately, the trusted scoring signals 639 // Python server will return the request URL as the signal value if the request URL contains the 640 // string `url` in its query parameters. 641 642 // Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to 0. 643 // Check if the returned signal contains value of `renderURL` to make sure the signal is fetched 644 // with the full URL. 645 subsetTest(promise_test, async test => { 646 const uuid = generateUuid(test); 647 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url'); 648 const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; 649 const auctionConfigOverrides = { 650 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, 651 maxTrustedScoringSignalsURLLength: 0, 652 decisionLogicURL: 653 createDecisionScriptURL(uuid, { 654 scoreAd: 655 `if (!trustedScoringSignals.renderURL["${renderURL}"].includes(encodeURIComponent("${renderURL}"))) 656 throw "error";` 657 }) 658 }; 659 660 await joinGroupAndRunBasicFledgeTestExpectingWinner( 661 test, 662 { 663 uuid: uuid, 664 interestGroupOverrides: interestGroupOverrides, 665 auctionConfigOverrides: auctionConfigOverrides 666 }); 667 }, 'Trusted scoring signals request works with a URL length limit set to 0.'); 668 669 // Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to 670 // a non-zero value smaller than the length of the request URL. Check if the returned signal 671 // contains value of `renderURL` to make sure the signal was fetched with the full URL. 672 subsetTest(promise_test, async test => { 673 const uuid = generateUuid(test); 674 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url'); 675 const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; 676 const auctionConfigOverrides = { 677 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, 678 maxTrustedScoringSignalsURLLength: 1, 679 decisionLogicURL: 680 createDecisionScriptURL(uuid, { 681 scoreAd: 682 `if (!trustedScoringSignals.renderURL["${renderURL}"].includes(encodeURIComponent("${renderURL}"))) 683 throw "error";` 684 }) 685 }; 686 687 await joinGroupAndRunBasicFledgeTestExpectingWinner( 688 test, 689 { 690 uuid: uuid, 691 interestGroupOverrides: interestGroupOverrides, 692 auctionConfigOverrides: auctionConfigOverrides 693 }); 694 }, 'Trusted scoring signals request works with a URL length limit smaller than the URL length.'); 695 696 // Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to 697 // a value larger than the length of the request URL. Check if the returned signal contains 698 // value of `renderURL` to make sure the signal was fetched with the full URL. 699 subsetTest(promise_test, async test => { 700 const uuid = generateUuid(test); 701 const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url'); 702 const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; 703 const auctionConfigOverrides = { 704 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, 705 maxTrustedScoringSignalsURLLength: 1000, 706 decisionLogicURL: 707 createDecisionScriptURL(uuid, { 708 scoreAd: 709 `if (!trustedScoringSignals.renderURL["${renderURL}"].includes(encodeURIComponent("${renderURL}"))) 710 throw "error";` 711 }) 712 }; 713 714 await joinGroupAndRunBasicFledgeTestExpectingWinner( 715 test, 716 { 717 uuid: uuid, 718 interestGroupOverrides: interestGroupOverrides, 719 auctionConfigOverrides: auctionConfigOverrides 720 }); 721 }, 'Trusted scoring signals request works with a URL length limit larger than the URL length.'); 722 723 // Test whether an oversized trusted scoring signals request URL, generated from two interest 724 // groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a 725 // value larger than a single URL length and smaller than the combined URL length. Check the returned 726 // signal for interest group `group 1` only contains string `group1` but not `group2` to ensure the 727 // fetch requests are not combined together. 728 subsetTest(promise_test, async test => { 729 const uuid = generateUuid(test); 730 const renderURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url,group1'); 731 const renderURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url,group2'); 732 const auctionConfigOverrides = { 733 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, 734 maxTrustedScoringSignalsURLLength: 300, 735 decisionLogicURL: 736 createDecisionScriptURL(uuid, { 737 // This will make the auction reject `renderURL2`, and if `renderURL1` passes check, 738 // we consider `renderURL2` is fetched by itself in the trusted scoring signals request. 739 scoreAd: 740 `if (!trustedScoringSignals.renderURL.hasOwnProperty("${renderURL1}") || 741 trustedScoringSignals.renderURL.hasOwnProperty("${renderURL2}") || 742 trustedScoringSignals.renderURL["${renderURL1}"].includes('group2') || 743 !trustedScoringSignals.renderURL["${renderURL1}"].includes('group1')) { 744 throw "error"; 745 }` 746 }) 747 }; 748 749 await Promise.all( 750 [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }), 751 joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ] 752 ); 753 754 await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides); 755 }, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of regular value.'); 756 757 // Test whether an oversized trusted scoring signals request URL, generated from two interest 758 // groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a 759 // value smaller than a single URL length. Check the returned signal for interest group `group 1` 760 // only contains `group1` but not `group2` to ensure the fetch requests are not combined together. 761 subsetTest(promise_test, async test => { 762 const uuid = generateUuid(test); 763 const renderURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url,group1'); 764 const renderURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'url,group2'); 765 const auctionConfigOverrides = { 766 trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, 767 maxTrustedScoringSignalsURLLength: 1, 768 decisionLogicURL: 769 createDecisionScriptURL(uuid, { 770 // This will make the auction reject `renderURL2`, and if `renderURL1` passes check, 771 // we consider `renderURL2` is fetched by itself in the trusted scoring signals request. 772 scoreAd: 773 `if (!trustedScoringSignals.renderURL.hasOwnProperty("${renderURL1}") || 774 trustedScoringSignals.renderURL.hasOwnProperty("${renderURL2}") || 775 trustedScoringSignals.renderURL["${renderURL1}"].includes('group2') || 776 !trustedScoringSignals.renderURL["${renderURL1}"].includes('group1')) { 777 throw "error"; 778 }` 779 }) 780 }; 781 782 await Promise.all( 783 [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }), 784 joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ] 785 ); 786 787 await runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides); 788 }, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of small value.');