tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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.');