tor-browser

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

server-response.https.window.js (51106B)


      1 // META: script=/resources/testdriver.js
      2 // META: script=/resources/testdriver-vendor.js
      3 // META: script=/common/utils.js
      4 // META: script=resources/ba-fledge-util.sub.js
      5 // META: script=resources/fledge-util.sub.js
      6 // META: script=third_party/cbor-js/cbor.js
      7 // META: script=/common/subset-tests.js
      8 // META: timeout=long
      9 // META: variant=?1-6
     10 // META: variant=?7-10
     11 // META: variant=?11-14
     12 // META: variant=?15-18
     13 // META: variant=?19-22
     14 // META: variant=?23-26
     15 // META: variant=?27-30
     16 // META: variant=?31-34
     17 // META: variant=?35-38
     18 // META: variant=?39-42
     19 // META: variant=?43-46
     20 // META: variant=?47-50
     21 // META: variant=?51-54
     22 // META: variant=?55-58
     23 // META: variant=?59-62
     24 // META: variant=?63-66
     25 // META: variant=?67-70
     26 
     27 "use strict";
     28 
     29 // These tests focus on the serverResponse field in AuctionConfig, e.g.
     30 // auctions involving bidding and auction services.
     31 
     32 subsetTest(promise_test, async test => {
     33  const uuid = generateUuid(test);
     34  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
     35  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
     36  const adsArray =
     37      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
     38  await joinInterestGroup(test, uuid, {ads: adsArray});
     39 
     40  const result = await navigator.getInterestGroupAdAuctionData({
     41    coordinatorOrigin: await BA.configureCoordinator(),
     42    seller: window.location.origin
     43  });
     44  assert_true(result.requestId !== null);
     45  assert_true(result.request.length > 0);
     46 
     47  let decoded = await BA.decodeInterestGroupData(result.request);
     48 
     49  let serverResponseMsg = {
     50    'biddingGroups': {},
     51    'adRenderURL': adsArray[0].renderURL,
     52    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
     53    'interestGroupOwner': window.location.origin,
     54  };
     55  serverResponseMsg.biddingGroups[window.location.origin] = [0];
     56 
     57  let serverResponse =
     58      await BA.encodeServerResponse(serverResponseMsg, decoded);
     59 
     60  let hashString = await BA.payloadHash(serverResponse);
     61  await BA.authorizeServerResponseHashes([hashString]);
     62 
     63  let auctionResult = await navigator.runAdAuction({
     64    'seller': window.location.origin,
     65    'requestId': result.requestId,
     66    'serverResponse': serverResponse,
     67    'resolveToConfig': true,
     68  });
     69  expectSuccess(auctionResult);
     70  createAndNavigateFencedFrame(test, auctionResult);
     71  await waitForObservedRequests(uuid, [adA]);
     72 }, 'Basic B&A auction');
     73 
     74 subsetTest(promise_test, async test => {
     75  const uuid = generateUuid(test);
     76  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
     77  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
     78  const adsArray =
     79      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
     80  await joinInterestGroup(test, uuid, {ads: adsArray});
     81 
     82  const result = await navigator.getInterestGroupAdAuctionData({
     83    coordinatorOrigin: await BA.configureCoordinator(),
     84    seller: window.location.origin
     85  });
     86  assert_true(result.requestId !== null);
     87  assert_true(result.request.length > 0);
     88 
     89  let decoded = await BA.decodeInterestGroupData(result.request);
     90 
     91  let serverResponseMsg = {
     92    'biddingGroups': {},
     93    'adRenderURL': adsArray[0].renderURL,
     94    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
     95    'interestGroupOwner': window.location.origin,
     96  };
     97  serverResponseMsg.biddingGroups[window.location.origin] = [0];
     98 
     99  let serverResponse =
    100      await BA.encodeServerResponse(serverResponseMsg, decoded);
    101 
    102  let auctionResult = await navigator.runAdAuction({
    103    'seller': window.location.origin,
    104    'requestId': result.requestId,
    105    'serverResponse': serverResponse,
    106    'resolveToConfig': true,
    107  });
    108  expectNoWinner(auctionResult);
    109 }, 'Basic B&A auction - not authorized');
    110 
    111 subsetTest(promise_test, async test => {
    112  const uuid = generateUuid(test);
    113  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
    114  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
    115  const adsArray =
    116      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    117  await joinInterestGroup(test, uuid, {ads: adsArray});
    118 
    119  const result = await navigator.getInterestGroupAdAuctionData({
    120    coordinatorOrigin: await BA.configureCoordinator(),
    121    seller: window.location.origin
    122  });
    123  assert_true(result.requestId !== null);
    124  assert_true(result.request.length > 0);
    125 
    126  let decoded = await BA.decodeInterestGroupData(result.request);
    127 
    128  const trackSeller = createSellerReportURL(uuid);
    129  const trackBuyer = createBidderReportURL(uuid);
    130  // This one should still work since the server may have run an auction with
    131  // components on its own.
    132  const trackComponentSeller = createSellerReportURL(uuid, 'component');
    133  let serverResponseMsg = {
    134    'biddingGroups': {},
    135    'adRenderURL': adsArray[1].renderURL,
    136    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    137    'interestGroupOwner': window.location.origin,
    138    'winReportingURLs': {
    139      'buyerReportingURLs': {'reportingURL': trackBuyer},
    140      'topLevelSellerReportingURLs': {'reportingURL': trackSeller},
    141      'componentSellerReportingURLs': {'reportingURL': trackComponentSeller}
    142    }
    143  };
    144  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    145 
    146  let serverResponse =
    147      await BA.encodeServerResponse(serverResponseMsg, decoded);
    148 
    149  let hashString = await BA.payloadHash(serverResponse);
    150  await BA.authorizeServerResponseHashes([hashString]);
    151 
    152  let auctionResult = await navigator.runAdAuction({
    153    'seller': window.location.origin,
    154    'requestId': result.requestId,
    155    'serverResponse': serverResponse,
    156    'resolveToConfig': true,
    157  });
    158  expectSuccess(auctionResult);
    159  createAndNavigateFencedFrame(test, auctionResult);
    160  await waitForObservedRequests(
    161      uuid, [adB, trackBuyer, trackSeller, trackComponentSeller]);
    162 }, 'Basic B&A auction with reporting URLs');
    163 
    164 subsetTest(promise_test, async test => {
    165  const uuid = generateUuid(test);
    166  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
    167  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
    168  const adsArray =
    169      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    170  await joinInterestGroup(test, uuid, {
    171    ads: adsArray,
    172    biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
    173  });
    174 
    175  const result = await navigator.getInterestGroupAdAuctionData({
    176      coordinatorOrigin: await BA.configureCoordinator(),
    177      seller: window.location.origin
    178  });
    179  assert_true(result.requestId !== null);
    180  assert_true(result.request.length > 0);
    181 
    182  let decoded = await BA.decodeInterestGroupData(result.request);
    183 
    184  // The server-side auction uses a bid of 10, for second ad, so it should
    185  // win over the client-side component auctions bid of 9.
    186  let serverResponseMsg = {
    187    'biddingGroups': {},
    188    'adRenderURL': adsArray[1].renderURL,
    189    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    190    'interestGroupOwner': window.location.origin,
    191    'topLevelSeller': window.location.origin,
    192    'bid': 10,
    193  };
    194  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    195 
    196  let serverResponse =
    197      await BA.encodeServerResponse(serverResponseMsg, decoded);
    198 
    199  let hashString = await BA.payloadHash(serverResponse);
    200  await BA.authorizeServerResponseHashes([hashString]);
    201 
    202  let auctionConfig = {
    203    seller: window.location.origin,
    204    decisionLogicURL: createDecisionScriptURL(uuid),
    205    interestGroupBuyers: [],
    206    resolveToConfig: true,
    207    componentAuctions: [
    208      {
    209        seller: window.location.origin,
    210        decisionLogicURL: createDecisionScriptURL(uuid),
    211        interestGroupBuyers: [window.location.origin],
    212      },
    213      {
    214        seller: window.location.origin,
    215        requestId: result.requestId,
    216        serverResponse: serverResponse,
    217      }
    218    ]
    219  };
    220 
    221  let auctionResult = await navigator.runAdAuction(auctionConfig);
    222  expectSuccess(auctionResult);
    223  createAndNavigateFencedFrame(test, auctionResult);
    224  await waitForObservedRequests(uuid, [adB]);
    225 }, 'Hybrid B&A auction');
    226 
    227 subsetTest(promise_test, async test => {
    228  const uuid = generateUuid(test);
    229  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
    230  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
    231  const adsArray =
    232      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    233  await joinInterestGroup(test, uuid, {
    234    ads: adsArray,
    235    biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
    236  });
    237 
    238  const result = await navigator.getInterestGroupAdAuctionData({
    239    coordinatorOrigin: await BA.configureCoordinator(),
    240    seller: window.location.origin
    241  });
    242  assert_true(result.requestId !== null);
    243  assert_true(result.request.length > 0);
    244 
    245  let decoded = await BA.decodeInterestGroupData(result.request);
    246 
    247  // The server-side auction uses a bid of 10, for second ad, so it should
    248  // win over the client-side component auctions bid of 9.
    249  const trackServerSeller = createSellerReportURL(uuid);
    250  const trackBuyer = createBidderReportURL(uuid);
    251  // This one shouldn't show up.
    252  const trackTopLevelServerSeller = createSellerReportURL(uuid, 'top');
    253  let serverResponseMsg = {
    254    'biddingGroups': {},
    255    'adRenderURL': adsArray[1].renderURL,
    256    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    257    'interestGroupOwner': window.location.origin,
    258    'topLevelSeller': window.location.origin,
    259    'bid': 10,
    260    'winReportingURLs': {
    261      'buyerReportingURLs': {'reportingURL': trackBuyer},
    262      'componentSellerReportingURLs': {'reportingURL': trackServerSeller},
    263      'topLevelSellerReportingURLs': {'reportingURL': trackTopLevelServerSeller}
    264    }
    265  };
    266  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    267 
    268  let serverResponse =
    269      await BA.encodeServerResponse(serverResponseMsg, decoded);
    270 
    271  let hashString = await BA.payloadHash(serverResponse);
    272  await BA.authorizeServerResponseHashes([hashString]);
    273 
    274  let trackTopSeller = createSellerReportURL(uuid, 'top');
    275  let trackClientSeller = createSellerReportURL(uuid, 'client');
    276  let auctionConfig = {
    277    seller: window.location.origin,
    278    decisionLogicURL: createDecisionScriptURL(
    279        uuid, {reportResult: `sendReportTo("${trackTopSeller}")`}),
    280    interestGroupBuyers: [],
    281    resolveToConfig: true,
    282    componentAuctions: [
    283      {
    284        seller: window.location.origin,
    285        decisionLogicURL: createDecisionScriptURL(
    286            uuid, {reportResult: `sendReportTo("${trackClientSeller}")`}),
    287        interestGroupBuyers: [window.location.origin],
    288      },
    289      {
    290        seller: window.location.origin,
    291        requestId: result.requestId,
    292        serverResponse: serverResponse,
    293      }
    294    ]
    295  };
    296 
    297  let auctionResult = await navigator.runAdAuction(auctionConfig);
    298  expectSuccess(auctionResult);
    299  createAndNavigateFencedFrame(test, auctionResult);
    300  await waitForObservedRequests(
    301      uuid, [adB, trackBuyer, trackServerSeller, trackTopSeller]);
    302 }, 'Hybrid B&A auction with reporting URLs');
    303 
    304 async function runFaultInjectTest(test, fault) {
    305  const uuid = generateUuid(test);
    306  const adA = 'https://example.org/a';
    307  const adB = 'https://example.org/b';
    308  const adsArray =
    309      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    310  await joinInterestGroup(test, uuid, {ads: adsArray});
    311 
    312  const result = await navigator.getInterestGroupAdAuctionData({
    313    coordinatorOrigin: await BA.configureCoordinator(),
    314    seller: window.location.origin
    315  });
    316  assert_true(result.requestId !== null);
    317  assert_true(result.request.length > 0);
    318 
    319  let decoded = await BA.decodeInterestGroupData(result.request);
    320 
    321  let serverResponseMsg = {
    322    'biddingGroups': {},
    323    'adRenderURL': adsArray[0].renderURL,
    324    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    325    'interestGroupOwner': window.location.origin,
    326  };
    327  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    328 
    329  let serverResponse =
    330      await BA.encodeServerResponse(serverResponseMsg, decoded, fault);
    331 
    332  let hashString = await BA.payloadHash(serverResponse);
    333  await BA.authorizeServerResponseHashes([hashString]);
    334 
    335  let auctionResult = await navigator.runAdAuction({
    336    'seller': window.location.origin,
    337    'requestId': result.requestId,
    338    'serverResponse': serverResponse,
    339    'resolveToConfig': true,
    340  });
    341  expectNoWinner(auctionResult);
    342 }
    343 
    344 subsetTest(promise_test, async test => {
    345  return runFaultInjectTest(test, BA.injectCborFault);
    346 }, 'Basic B&A auction - fault inject at CBOR');
    347 
    348 subsetTest(promise_test, async test => {
    349  return runFaultInjectTest(test, BA.injectGzipFault);
    350 }, 'Basic B&A auction - fault inject at gzip');
    351 
    352 subsetTest(promise_test, async test => {
    353  return runFaultInjectTest(test, BA.injectFrameFault);
    354 }, 'Basic B&A auction - fault inject at framing');
    355 
    356 subsetTest(promise_test, async test => {
    357  return runFaultInjectTest(test, BA.injectEncryptFault);
    358 }, 'Basic B&A auction - fault inject at encryption');
    359 
    360 subsetTest(promise_test, async test => {
    361  const uuid = generateUuid(test);
    362  const adA = 'https://example.org/a';
    363  const adB = 'https://example.org/b';
    364  const adsArray =
    365      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    366  await joinInterestGroup(test, uuid, {ads: adsArray});
    367 
    368  const result = await navigator.getInterestGroupAdAuctionData({
    369    coordinatorOrigin: await BA.configureCoordinator(),
    370    seller: window.location.origin
    371  });
    372  assert_true(result.requestId !== null);
    373  assert_true(result.request.length > 0);
    374 
    375  let decoded = await BA.decodeInterestGroupData(result.request);
    376 
    377  let serverResponseMsg = {
    378    'biddingGroups': {},
    379    'adRenderURL': adsArray[0].renderURL,
    380    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    381    'interestGroupOwner': window.location.origin,
    382  };
    383  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    384 
    385  let serverResponse =
    386      await BA.encodeServerResponse(serverResponseMsg, decoded);
    387 
    388  // Mess up the array for a bit before computing hash to get the wrong hash,
    389  // then undo.
    390  serverResponse[0] ^= 0xBE;
    391  let hashString = await BA.payloadHash(serverResponse);
    392  await BA.authorizeServerResponseHashes([hashString]);
    393  serverResponse[0] ^= 0xBE;
    394 
    395  let auctionResult = await navigator.runAdAuction({
    396    'seller': window.location.origin,
    397    'requestId': result.requestId,
    398    'serverResponse': serverResponse,
    399    'resolveToConfig': true,
    400  });
    401  expectNoWinner(auctionResult);
    402 }, 'Basic B&A auction - Wrong authorization');
    403 
    404 subsetTest(promise_test, async test => {
    405  const uuid = generateUuid(test);
    406  const adA = 'https://example.org/a';
    407  const adB = 'https://example.org/b';
    408  const adsArray =
    409      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    410  await joinInterestGroup(test, uuid, {ads: adsArray});
    411 
    412  const result = await navigator.getInterestGroupAdAuctionData({
    413    coordinatorOrigin: await BA.configureCoordinator(),
    414    seller: OTHER_ORIGIN1
    415  });
    416  assert_true(result.requestId !== null);
    417  assert_true(result.request.length > 0);
    418 
    419  let decoded = await BA.decodeInterestGroupData(result.request);
    420 
    421  let serverResponseMsg = {
    422    'biddingGroups': {},
    423    'adRenderURL': adsArray[0].renderURL,
    424    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    425    'interestGroupOwner': window.location.origin,
    426  };
    427  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    428 
    429  let serverResponse =
    430      await BA.encodeServerResponse(serverResponseMsg, decoded);
    431 
    432  let hashString = await BA.payloadHash(serverResponse);
    433  await BA.authorizeServerResponseHashes([hashString]);
    434 
    435  let auctionResult = await navigator.runAdAuction({
    436    'seller': window.location.origin,
    437    'requestId': result.requestId,
    438    'serverResponse': serverResponse,
    439    'resolveToConfig': true,
    440  });
    441  expectNoWinner(auctionResult);
    442 }, 'Basic B&A auction - Wrong seller');
    443 
    444 subsetTest(promise_test, async test => {
    445  const uuid = generateUuid(test);
    446  const adA = 'https://example.org/a';
    447  const adB = 'https://example.org/b';
    448  const adsArray =
    449      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    450  await joinInterestGroup(test, uuid, {ads: adsArray});
    451 
    452  const result = await navigator.getInterestGroupAdAuctionData({
    453    coordinatorOrigin: await BA.configureCoordinator(),
    454    seller: window.location.origin
    455  });
    456  assert_true(result.requestId !== null);
    457  assert_true(result.request.length > 0);
    458 
    459  let decoded = await BA.decodeInterestGroupData(result.request);
    460 
    461  let serverResponseMsg = {
    462    'biddingGroups': {},
    463    'adRenderURL': adsArray[0].renderURL,
    464    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    465    'interestGroupOwner': window.location.origin,
    466  };
    467  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    468 
    469  let serverResponse =
    470      await BA.encodeServerResponse(serverResponseMsg, decoded);
    471 
    472  let hashString = await BA.payloadHash(serverResponse);
    473  await BA.authorizeServerResponseHashes([hashString]);
    474 
    475  let auctionResult = await navigator.runAdAuction({
    476    'seller': window.location.origin,
    477    'requestId': token(),
    478    'serverResponse': serverResponse,
    479    'resolveToConfig': true,
    480  });
    481  expectNoWinner(auctionResult);
    482 }, 'Basic B&A auction - Wrong request Id');
    483 
    484 subsetTest(promise_test, async test => {
    485  await BA.testWithMutatedServerResponse(
    486      test, /*expectSuccess=*/ false, msg => {msg.error = {}});
    487 }, 'Basic B&A auction - response marked as error');
    488 
    489 subsetTest(promise_test, async test => {
    490  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
    491    msg.error = 4;
    492  });
    493 }, 'Basic B&A auction - nonsense error field');
    494 
    495 subsetTest(promise_test, async test => {
    496  await BA.testWithMutatedServerResponse(
    497      test, /*expectSuccess=*/ false, msg => {
    498        msg.error = {message: 'oh no'};
    499      });
    500 }, 'Basic B&A auction - response marked as error, with message');
    501 
    502 subsetTest(promise_test, async test => {
    503  await BA.testWithMutatedServerResponse(
    504      test, /*expectSuccess=*/ false, msg => {
    505        msg.error = {message: {}};
    506      });
    507 }, 'Basic B&A auction - response marked as error, with bad message');
    508 
    509 subsetTest(promise_test, async test => {
    510  await BA.testWithMutatedServerResponse(
    511      test, /*expectSuccess=*/ false, msg => {msg.isChaff = true});
    512 }, 'Basic B&A auction - response marked as chaff');
    513 
    514 subsetTest(promise_test, async test => {
    515  await BA.testWithMutatedServerResponse(
    516      test, /*expectSuccess=*/ true, msg => {msg.isChaff = false});
    517 }, 'Basic B&A auction - response marked as non-chaff');
    518 
    519 subsetTest(promise_test, async test => {
    520  await BA.testWithMutatedServerResponse(
    521      test, /*expectSuccess=*/ false, msg => {msg.isChaff = 'yes'});
    522 }, 'Basic B&A auction - response marked as chaff incorrectly');
    523 
    524 subsetTest(promise_test, async test => {
    525  await BA.testWithMutatedServerResponse(
    526      test, /*expectSuccess=*/ false,
    527      msg => {msg.topLevelSeller = 'https://example.org/'});
    528 }, 'Basic B&A auction - incorrectly includes topLevelSeller');
    529 
    530 subsetTest(promise_test, async test => {
    531  await BA.testWithMutatedServerResponse(
    532      test, /*expectSuccess=*/ false, msg => {msg.topLevelSeller = 1});
    533 }, 'Basic B&A auction - non-string top-level seller invalid');
    534 
    535 subsetTest(promise_test, async test => {
    536  await BA.testWithMutatedServerResponse(
    537      test, /*expectSuccess=*/ false,
    538      msg => {msg.topLevelSeller = 'http://example.org/'});
    539 }, 'Basic B&A auction - http:// topLevelSeller is bad, too');
    540 
    541 subsetTest(promise_test, async test => {
    542  await BA.testWithMutatedServerResponse(
    543      test, /*expectSuccess=*/ false, msg => {msg.bid = '10 cents'});
    544 }, 'Basic B&A auction - non-number bid is invalid');
    545 
    546 subsetTest(promise_test, async test => {
    547  await BA.testWithMutatedServerResponse(
    548      test, /*expectSuccess=*/ true, msg => {msg.bid = 50});
    549 }, 'Basic B&A auction - positive bid is good');
    550 
    551 subsetTest(promise_test, async test => {
    552  await BA.testWithMutatedServerResponse(
    553      test, /*expectSuccess=*/ false, msg => {msg.bid = -50});
    554 }, 'Basic B&A auction - negative bid is bad');
    555 
    556 subsetTest(promise_test, async test => {
    557  await BA.testWithMutatedServerResponse(
    558      test, /*expectSuccess=*/ false, msg => {msg.bid = 0});
    559 }, 'Basic B&A auction - zero bid is bad');
    560 
    561 subsetTest(promise_test, async test => {
    562  await BA.testWithMutatedServerResponse(
    563      test, /*expectSuccess=*/ false,
    564      msg => {msg.biddingGroups[window.location.origin] = []});
    565 }, 'Basic B&A auction - winning group did not bid');
    566 
    567 subsetTest(promise_test, async test => {
    568  await BA.testWithMutatedServerResponse(
    569      test, /*expectSuccess=*/ false,
    570      msg => {msg.biddingGroups[window.location.origin] = [-1, 0]});
    571 }, 'Basic B&A auction - negative bidding group index');
    572 
    573 subsetTest(promise_test, async test => {
    574  await BA.testWithMutatedServerResponse(
    575      test, /*expectSuccess=*/ false,
    576      msg => {msg.biddingGroups[window.location.origin] = [0, 1]});
    577 }, 'Basic B&A auction - too large bidding group index');
    578 
    579 subsetTest(promise_test, async test => {
    580  await BA.testWithMutatedServerResponse(
    581      test, /*expectSuccess=*/ false, msg => {
    582        msg.interestGroupName += 'not';
    583      });
    584 }, 'Basic B&A auction - wrong IG name');
    585 
    586 subsetTest(promise_test, async test => {
    587  await BA.testWithMutatedServerResponse(
    588      test, /*expectSuccess=*/ false, async msg => {
    589        await leaveInterestGroup();
    590      });
    591 }, 'Basic B&A auction - left IG in the middle');
    592 
    593 subsetTest(promise_test, async test => {
    594  await BA.testWithMutatedServerResponse(
    595      test, /*expectSuccess=*/ false, msg => {
    596        msg.adRenderURL += 'not';
    597      });
    598 }, 'Basic B&A auction - ad URL not in ad');
    599 
    600 subsetTest(promise_test, async test => {
    601  await BA.testWithMutatedServerResponse(
    602      test, /*expectSuccess=*/ false, msg => {
    603        msg.buyerReportingId = 'bid1';
    604      });
    605 }, 'Basic B&A auction - buyerReportingId not in ad');
    606 
    607 subsetTest(promise_test, async test => {
    608  await BA.testWithMutatedServerResponse(
    609      test, /*expectSuccess=*/ true,
    610      msg => {
    611        msg.buyerReportingId = 'bid1';
    612      },
    613      ig => {
    614        ig.ads[0].buyerReportingId = 'bid1';
    615        ig.ads[1].buyerReportingId = 'bid2';
    616      });
    617 }, 'Basic B&A auction - buyerReportingId in ad');
    618 
    619 subsetTest(promise_test, async test => {
    620  await BA.testWithMutatedServerResponse(
    621      test, /*expectSuccess=*/ false,
    622      msg => {
    623        msg.buyerReportingId = 'bid2';
    624      },
    625      ig => {
    626        ig.ads[0].buyerReportingId = 'bid1';
    627        ig.ads[1].buyerReportingId = 'bid2';
    628      });
    629 }, 'Basic B&A auction - buyerReportingId in wrong ad');
    630 
    631 subsetTest(promise_test, async test => {
    632  await BA.testWithMutatedServerResponse(
    633      test, /*expectSuccess=*/ false, msg => {
    634        msg.buyerAndSellerReportingId = 'bsid1';
    635      });
    636 }, 'Basic B&A auction - buyerAndSellerReportingId not in ad');
    637 
    638 subsetTest(promise_test, async test => {
    639  await BA.testWithMutatedServerResponse(
    640      test, /*expectSuccess=*/ true,
    641      msg => {
    642        msg.buyerAndSellerReportingId = 'bsid1';
    643      },
    644      ig => {
    645        ig.ads[0].buyerAndSellerReportingId = 'bsid1';
    646        ig.ads[1].buyerAndSellerReportingId = 'bsid2';
    647      });
    648 }, 'Basic B&A auction - buyerAndSellerReportingId in ad');
    649 
    650 subsetTest(promise_test, async test => {
    651  await BA.testWithMutatedServerResponse(
    652      test, /*expectSuccess=*/ false,
    653      msg => {
    654        msg.buyerAndSellerReportingId = 'bsid2';
    655      },
    656      ig => {
    657        ig.ads[0].buyerAndSellerReportingId = 'bsid1';
    658        ig.ads[1].buyerAndSellerReportingId = 'bsid2';
    659      });
    660 }, 'Basic B&A auction - buyerAndSellerReportingId in wrong ad');
    661 
    662 subsetTest(promise_test, async test => {
    663  await BA.testWithMutatedServerResponse(
    664      test, /*expectSuccess=*/ false, msg => {
    665        msg.components = ['https://example.org'];
    666      });
    667 }, 'Basic B&A auction - ad component URL not in ad');
    668 
    669 subsetTest(promise_test, async test => {
    670  await BA.testWithMutatedServerResponse(
    671      test, /*expectSuccess=*/ true,
    672      msg => {
    673        msg.components = ['https://example.org'];
    674      },
    675      ig => {
    676        ig.adComponents = [{renderURL: 'https://example.org/'}];
    677      });
    678 }, 'Basic B&A auction - ad component URL in ad');
    679 
    680 subsetTest(promise_test, async test => {
    681  let savedUuid;
    682  let savedExpectUrls;
    683  let result = await BA.testWithMutatedServerResponse(
    684      test, /*expectSuccess=*/ true,
    685      (msg, uuid) => {
    686        savedUuid = uuid;
    687        msg.components = [
    688          createTrackerURL(window.location.origin, uuid, 'track_get', 'c_a'),
    689          createTrackerURL(window.location.origin, uuid, 'track_get', 'c_c')
    690        ];
    691        savedExpectUrls = msg.components;
    692      },
    693      (ig, uuid) => {
    694        ig.ads[0].renderURL = createRenderURL(uuid, `
    695          const componentAds = window.fence.getNestedConfigs();
    696          // Limit the number of fenced frames we try to load at once, since loading too many
    697          // completely breaks some Chrome test set ups, and we only really care about 3 of them
    698          // anyway.
    699          //
    700          // See https://crbug.com/370533823 for more context.
    701          const limit = 5;
    702          for (var i = 0; i < Math.min(limit, componentAds.length); ++i) {
    703            let fencedFrame = document.createElement("fencedframe");
    704            fencedFrame.mode = "opaque-ads";
    705            fencedFrame.config = componentAds[i];
    706            document.body.appendChild(fencedFrame);
    707          }`);
    708        ig.adComponents = [
    709          {
    710            renderURL: createTrackerURL(
    711                window.location.origin, uuid, 'track_get', 'c_a')
    712          },
    713          {
    714            renderURL: createTrackerURL(
    715                window.location.origin, uuid, 'track_get', 'c_c')
    716          },
    717          {
    718            renderURL: createTrackerURL(
    719                window.location.origin, uuid, 'track_get', 'c_c')
    720          }
    721        ];
    722      });
    723  createAndNavigateFencedFrame(test, result);
    724  await waitForObservedRequests(savedUuid, savedExpectUrls);
    725 }, 'Basic B&A auction - loading winning component ads');
    726 
    727 
    728 subsetTest(promise_test, async test => {
    729  let savedUuid;
    730  let result = await BA.testWithMutatedServerResponse(
    731      test, /*expectWin=*/ true,
    732      (msg, uuid) => {
    733        savedUuid = uuid;
    734        msg.winReportingURLs = {
    735          'buyerReportingURLs': {
    736            'interactionReportingURLs': {
    737              'click': createBidderBeaconURL(uuid, 'i'),
    738              'cluck': createBidderBeaconURL(uuid, 'u')
    739            }
    740          },
    741          'topLevelSellerReportingURLs': {
    742            'interactionReportingURLs': {
    743              'click': createSellerBeaconURL(uuid, 'i'),
    744              'cluck': createSellerBeaconURL(uuid, 'u')
    745            }
    746          }
    747        };
    748      },
    749      (ig, uuid) => {
    750        ig.ads[0].renderURL = createRenderURL(uuid, `window.fence.reportEvent({
    751              eventType: 'click',
    752              eventData: 'click_body',
    753              destination: ['seller', 'buyer']
    754            });
    755            window.fence.reportEvent({
    756              eventType: 'cluck',
    757              eventData: 'cluck_body',
    758              destination: ['direct-seller']
    759            });`);
    760      });
    761 
    762  createAndNavigateFencedFrame(test, result);
    763  // The script triggers seller and buyer 'click', and seller 'cluck'.
    764  // No tracker for page itself.
    765  await waitForObservedRequests(savedUuid, [
    766    createBidderBeaconURL(savedUuid, 'i') + ', body: click_body',
    767    createSellerBeaconURL(savedUuid, 'i') + ', body: click_body',
    768    createSellerBeaconURL(savedUuid, 'u') + ', body: cluck_body'
    769  ]);
    770 }, 'Basic B&A auction --- beacon reporting');
    771 
    772 subsetTest(promise_test, async test => {
    773  await BA.testWithMutatedServerResponse(
    774      test, /*expectSuccess=*/ false, msg => {
    775        msg.bidCurrency = 'cents';
    776      });
    777 }, 'Basic B&A auction - invalid ad currency');
    778 
    779 subsetTest(promise_test, async test => {
    780  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
    781    msg.bidCurrency = 'USD';
    782  });
    783 }, 'Basic B&A auction - valid ad currency');
    784 
    785 // Runs whatever is set in `mutators` on a minimal correct hybrid B&A/local
    786 // auction, and expects either the B&A bid or local bid to win depending on
    787 // expectBaWin.
    788 async function testHybridAuctionWithMutatedServerResponse(
    789    test, expectBaWin, mutators = {
    790      responseMutator: undefined,
    791      igMutator: undefined,
    792      auctionConfigMutator: undefined,
    793      expectUrlsMutator: undefined
    794    }) {
    795  const uuid = generateUuid(test);
    796  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
    797  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
    798  const adsArray =
    799      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
    800  let interestGroup = {
    801    ads: adsArray,
    802    biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
    803  };
    804  if (mutators.igMutator) {
    805    mutators.igMutator(interestGroup, uuid);
    806  }
    807  await joinInterestGroup(test, uuid, interestGroup);
    808 
    809  const result = await navigator.getInterestGroupAdAuctionData({
    810    coordinatorOrigin: await BA.configureCoordinator(),
    811    seller: window.location.origin
    812  });
    813  assert_true(result.requestId !== null);
    814  assert_true(result.request.length > 0);
    815 
    816  let decoded = await BA.decodeInterestGroupData(result.request);
    817 
    818  // The server-side auction uses a bid of 10, for second ad, so it should
    819  // win over the client-side component auctions bid of 9 (unless something
    820  // mutators did made the server response unacceptable).
    821  let serverResponseMsg = {
    822    'biddingGroups': {},
    823    'adRenderURL': interestGroup.ads[1].renderURL,
    824    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
    825    'interestGroupOwner': window.location.origin,
    826    'topLevelSeller': window.location.origin,
    827    'bid': 10,
    828  };
    829  serverResponseMsg.biddingGroups[window.location.origin] = [0];
    830  if (mutators.responseMutator) {
    831    mutators.responseMutator(serverResponseMsg, uuid);
    832  }
    833 
    834  let serverResponse =
    835      await BA.encodeServerResponse(serverResponseMsg, decoded);
    836 
    837  let hashString = await BA.payloadHash(serverResponse);
    838  await BA.authorizeServerResponseHashes([hashString]);
    839 
    840  let auctionConfig = {
    841    seller: window.location.origin,
    842    decisionLogicURL: createDecisionScriptURL(uuid),
    843    interestGroupBuyers: [],
    844    resolveToConfig: true,
    845    componentAuctions: [
    846      {
    847        seller: window.location.origin,
    848        decisionLogicURL: createDecisionScriptURL(uuid),
    849        interestGroupBuyers: [window.location.origin],
    850      },
    851      {
    852        seller: window.location.origin,
    853        requestId: result.requestId,
    854        serverResponse: serverResponse,
    855      }
    856    ]
    857  };
    858  if (mutators.auctionConfigMutator) {
    859    mutators.auctionConfigMutator(auctionConfig, uuid);
    860  }
    861 
    862  let auctionResult = await navigator.runAdAuction(auctionConfig);
    863  expectSuccess(auctionResult);
    864  createAndNavigateFencedFrame(test, auctionResult);
    865  let expectUrls = expectBaWin ? [adB] : [adA];
    866  if (mutators.expectUrlsMutator) {
    867    mutators.expectUrlsMutator(expectUrls, uuid);
    868  }
    869  await waitForObservedRequests(uuid, expectUrls);
    870 }
    871 
    872 subsetTest(promise_test, async test => {
    873  await testHybridAuctionWithMutatedServerResponse(
    874      test, /*expectBaWin=*/ false, {
    875        responseMutator: (response) => {
    876          delete response.topLevelSeller;
    877        }
    878      });
    879 }, 'Hybrid B&A auction --- missing top-level seller');
    880 
    881 subsetTest(promise_test, async test => {
    882  await testHybridAuctionWithMutatedServerResponse(
    883      test, /*expectBaWin=*/ false, {
    884        responseMutator: (response) => {
    885          response.topLevelSeller = 'https://www.example.org/';
    886        }
    887      });
    888 }, 'Hybrid B&A auction --- wrong top-level seller');
    889 
    890 subsetTest(promise_test, async test => {
    891  await testHybridAuctionWithMutatedServerResponse(
    892      test, /*expectBaWin=*/ false, {
    893        responseMutator: (response) => {
    894          delete response.bid;
    895        }
    896      });
    897 }, 'Hybrid B&A auction --- no bid');
    898 
    899 subsetTest(promise_test, async test => {
    900  await testHybridAuctionWithMutatedServerResponse(
    901      test, /*expectBaWin=*/ true, {
    902        responseMutator: (response) => {
    903          response.bidCurrency = 'USD';
    904        }
    905      });
    906 }, 'Hybrid B&A auction --- currency check --- nothing configured');
    907 
    908 subsetTest(promise_test, async test => {
    909  await testHybridAuctionWithMutatedServerResponse(
    910      test, /*expectBaWin=*/ false, {
    911        responseMutator: (response) => {
    912          response.bidCurrency = 'USD';
    913        },
    914        auctionConfigMutator: (auctionConfig) => {
    915          auctionConfig.componentAuctions[1].sellerCurrency = 'EUR';
    916        }
    917      });
    918 }, 'Hybrid B&A auction --- sellerCurrency mismatch');
    919 
    920 subsetTest(promise_test, async test => {
    921  await testHybridAuctionWithMutatedServerResponse(
    922      test, /*expectBaWin=*/ true, {
    923        auctionConfigMutator: (auctionConfig) => {
    924          auctionConfig.componentAuctions[1].sellerCurrency = 'EUR';
    925        }
    926      });
    927 }, 'Hybrid B&A auction --- sellerCurrency config, no bidCurrency');
    928 
    929 subsetTest(promise_test, async test => {
    930  await testHybridAuctionWithMutatedServerResponse(
    931      test, /*expectBaWin=*/ false, {
    932        responseMutator: (response) => {
    933          response.bidCurrency = 'USD';
    934        },
    935        auctionConfigMutator: (auctionConfig) => {
    936          auctionConfig.perBuyerCurrencies = {};
    937          auctionConfig.perBuyerCurrencies[window.location.origin] = 'EUR';
    938        }
    939      });
    940 }, 'Hybrid B&A auction --- top perBuyerCurrencies mismatch');
    941 
    942 subsetTest(promise_test, async test => {
    943  await testHybridAuctionWithMutatedServerResponse(
    944      test, /*expectBaWin=*/ true, {
    945        auctionConfigMutator: (auctionConfig) => {
    946          auctionConfig.perBuyerCurrencies = {};
    947          auctionConfig.perBuyerCurrencies[window.location.origin] = 'EUR';
    948        }
    949      });
    950 }, 'Hybrid B&A auction --- perBuyerCurrencies config, no bidCurrency');
    951 
    952 subsetTest(promise_test, async test => {
    953  await testHybridAuctionWithMutatedServerResponse(
    954      test, /*expectBaWin=*/ true, {
    955        responseMutator: (response) => {
    956          response.bidCurrency = 'USD';
    957          response.bid = 50;
    958          response.adMetadata = '[1, "hello"]';
    959        },
    960        auctionConfigMutator: (auctionConfig, uuid) => {
    961          let trackTopSeller = createSellerReportURL(uuid, 'top');
    962          auctionConfig.decisionLogicURL = createDecisionScriptURL(uuid, {
    963            // Note: this will throw on the local bid as well as an incorrect
    964            // server bid.
    965            scoreAd: `
    966              let origin = '${window.location.origin}';
    967              if (!(adMetadata instanceof Array) ||
    968                  adMetadata.length !== 2 ||
    969                  adMetadata[0] !== 1 ||
    970                  adMetadata[1] !== 'hello') {
    971                throw 'bad adMetadata ' + JSON.stringify(adMetadata);
    972              }
    973              if (bid !== 50)
    974                throw 'bad bid ' + bid;
    975              if (browserSignals.bidCurrency !== 'USD')
    976                throw 'bad currency ' + browserSignals.bidCurrency;
    977              if (browserSignals.interestGroupOwner != origin)
    978                throw 'bad IG owner ' + browserSignals.interestGroupOwner;
    979              if (browserSignals.componentSeller != origin) {
    980                throw 'bad component seller ' +
    981                    browserSignals.interestGroupOwner;
    982              }`
    983          });
    984        }
    985      });
    986 }, 'Hybrid B&A auction --- bid info passed to top-level scoreAd');
    987 
    988 subsetTest(promise_test, async test => {
    989  await testHybridAuctionWithMutatedServerResponse(
    990      test, /*expectBaWin=*/ true, {
    991        responseMutator: (response) => {
    992          response.bidCurrency = 'USD';
    993          response.bid = 50;
    994          response.buyerAndSellerReportingId = 'bsid2';
    995        },
    996        igMutator: (ig) => {
    997          ig.ads[0].buyerAndSellerReportingId = 'bsid1';
    998          ig.ads[1].buyerAndSellerReportingId = 'bsid2';
    999        },
   1000        auctionConfigMutator: (auctionConfig, uuid) => {
   1001          let trackTopSeller = createSellerReportURL(uuid, 'top');
   1002          auctionConfig.decisionLogicURL = createDecisionScriptURL(uuid, {
   1003            reportResult: `sendReportTo("${trackTopSeller}&" +
   1004                browserSignals.bid + '&' +
   1005                browserSignals.buyerAndSellerReportingId)`
   1006          });
   1007        },
   1008        expectUrlsMutator: (expectUrls, uuid) => {
   1009          expectUrls.push(createSellerReportURL(uuid, 'top') + '&50&bsid2');
   1010        }
   1011      });
   1012 }, 'Hybrid B&A auction --- bid info passed to top-level reporting');
   1013 
   1014 subsetTest(promise_test, async test => {
   1015  await testHybridAuctionWithMutatedServerResponse(
   1016      test, /*expectBaWin=*/ true, {
   1017        igMutator: (ig, uuid) => {
   1018          ig.ads[1].renderURL =
   1019              createRenderURL(uuid, `window.fence.reportEvent({
   1020                eventType: 'click',
   1021                eventData: 'click_body',
   1022                destination: ['component-seller', 'buyer']
   1023              });
   1024              window.fence.reportEvent({
   1025                eventType: 'clack',
   1026                eventData: 'clack_body',
   1027                destination: ['direct-seller']
   1028              });`);
   1029        },
   1030        responseMutator: (response, uuid) => {
   1031          response.winReportingURLs = {
   1032            'buyerReportingURLs': {
   1033              'interactionReportingURLs': {
   1034                'click': createBidderBeaconURL(uuid, 'i'),
   1035                'clack': createBidderBeaconURL(uuid, 'a')
   1036              }
   1037            },
   1038            'componentSellerReportingURLs': {
   1039              'interactionReportingURLs': {
   1040                'click': createSellerBeaconURL(uuid, 'i'),
   1041                'clack': createSellerBeaconURL(uuid, 'a')
   1042              }
   1043            }
   1044          };
   1045        },
   1046        expectUrlsMutator: (expectUrls, uuid) => {
   1047          // The script triggers seller and buyer 'click', and seller 'clack'.
   1048          // No tracker for page itself.
   1049          expectUrls.pop();
   1050          expectUrls.push(
   1051              createBidderBeaconURL(uuid, 'i') + ', body: click_body',
   1052              createSellerBeaconURL(uuid, 'i') + ', body: click_body',
   1053              createSellerBeaconURL(uuid, 'a') + ', body: clack_body');
   1054        }
   1055      });
   1056 }, 'Hybrid B&A auction --- beacon reporting');
   1057 
   1058 /////////////////////////////////////////////////////////////////////////////
   1059 // updateIfOlderThanMs tests
   1060 //
   1061 // NOTE: Due to the lack of mock time in wpt, these tests just exercise the code
   1062 // paths and ensure that no crash occurs -- they don't otherwise verify
   1063 // behavior.
   1064 /////////////////////////////////////////////////////////////////////////////
   1065 
   1066 subsetTest(promise_test, async test => {
   1067  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
   1068    msg.updateGroups =
   1069        {[window.location.origin]: [{index: 2048, updateIfOlderThanMs: 1000}]};
   1070  });
   1071 }, 'Basic B&A auction - updateIfOlderThanMs - invalid index');
   1072 
   1073 
   1074 subsetTest(promise_test, async test => {
   1075  await BA.testWithMutatedServerResponse(test, /*expectSuccess=*/ true, msg => {
   1076    msg.updateGroups = {
   1077      [window.location.origin]: [
   1078        {index: 0, updateIfOlderThanMs: 1000},
   1079        {index: 1, updateIfOlderThanMs: 10000}
   1080      ]
   1081    };
   1082  });
   1083 }, 'Basic B&A auction - updateIfOlderThanMs');
   1084 
   1085 /////////////////////////////////////////////////////////////////////////////
   1086 //
   1087 // K-anonymity support tests
   1088 //
   1089 /////////////////////////////////////////////////////////////////////////////
   1090 
   1091 // Runs responseMutator on a minimal correct server response, and expects
   1092 // either success/failure based on expectWin.
   1093 async function kAnonTestWithMutatedServerResponse(
   1094    test, expectWin, responseMutator, igMutator = undefined) {
   1095  const uuid = generateUuid(test);
   1096  const adA = createTrackerURL(window.location.origin, uuid, 'track_get', 'a');
   1097  const adB = createTrackerURL(window.location.origin, uuid, 'track_get', 'b');
   1098  const adsArray =
   1099      [{renderURL: adA, adRenderId: 'a'}, {renderURL: adB, adRenderId: 'b'}];
   1100  let ig = {
   1101    owner: window.location.origin,
   1102    name: DEFAULT_INTEREST_GROUP_NAME,
   1103    ads: adsArray,
   1104    biddingLogicURL: createBiddingScriptURL({allowComponentAuction: true})
   1105  };
   1106  if (igMutator) {
   1107    igMutator(ig, uuid);
   1108  }
   1109 
   1110  const encoder = new TextEncoder();
   1111  const adARenderKAnonKey =
   1112      encoder.encode(`AdBid\n${ig.owner}/\n${ig.biddingLogicURL}\n${adA}`);
   1113  const adBRenderKAnonKey =
   1114      encoder.encode(`AdBid\n${ig.owner}/\n${ig.biddingLogicURL}\n${adB}`);
   1115  const adARenderKAnonKeyHash = new Uint8Array(
   1116      await window.crypto.subtle.digest('SHA-256', adARenderKAnonKey));
   1117  const adBRenderKAnonKeyHash = new Uint8Array(
   1118      await window.crypto.subtle.digest('SHA-256', adBRenderKAnonKey));
   1119 
   1120  const adANameReportingIdKAnonKey = encoder.encode(
   1121      `NameReport\n${ig.owner}/\n${ig.biddingLogicURL}\n${adA}\n${ig.name}`);
   1122  const adBNameReportingIdKAnonKey = encoder.encode(
   1123      `NameReport\n${ig.owner}/\n${ig.biddingLogicURL}\n${adB}\n${ig.name}`);
   1124  const adANameReportingIdKAnonKeyHash = new Uint8Array(
   1125      await window.crypto.subtle.digest('SHA-256', adANameReportingIdKAnonKey));
   1126  const adBNameReportingIdKAnonKeyHash = new Uint8Array(
   1127      await window.crypto.subtle.digest('SHA-256', adBNameReportingIdKAnonKey));
   1128 
   1129  const adABuyerReportingIdKAnonKey = encoder.encode(`BuyerReportId\n${
   1130      ig.owner}/\n${ig.biddingLogicURL}\n${adA}\n${adA.buyerReportingId}`);
   1131  const adBBuyerReportingIdKAnonKey = encoder.encode(`BuyerReportId\n${
   1132      ig.owner}/\n${ig.biddingLogicURL}\n${adB}\n${adB.buyerReportingId}`);
   1133  const adABuyerReportingIdKAnonKeyHash =
   1134      new Uint8Array(await window.crypto.subtle.digest(
   1135          'SHA-256', adABuyerReportingIdKAnonKey));
   1136  const adBBuyerReportingIdKAnonKeyHash =
   1137      new Uint8Array(await window.crypto.subtle.digest(
   1138          'SHA-256', adBBuyerReportingIdKAnonKey));
   1139 
   1140  const adABASReportingIdKAnonKey =
   1141      encoder.encode(`BuyerAndSellerReportId\n${ig.owner}/\n${
   1142          ig.biddingLogicURL}\n${adA}\n${adA.buyerAndSellerReportingId}`);
   1143  const adBBASReportingIdKAnonKey =
   1144      encoder.encode(`BuyerAndSellerReportId\n${ig.owner}/\n${
   1145          ig.biddingLogicURL}\n${adB}\n${adB.buyerAndSellerReportingId}`);
   1146  const adABASReportingIdKAnonKeyHash = new Uint8Array(
   1147      await window.crypto.subtle.digest('SHA-256', adABASReportingIdKAnonKey));
   1148  const adBBASReportingIdKAnonKeyHash = new Uint8Array(
   1149      await window.crypto.subtle.digest('SHA-256', adBBASReportingIdKAnonKey));
   1150 
   1151  const hashes = {
   1152    adARenderKAnonKeyHash: adARenderKAnonKeyHash,
   1153    adBRenderKAnonKeyHash: adBRenderKAnonKeyHash,
   1154    adANameReportingIdKAnonKeyHash: adANameReportingIdKAnonKeyHash,
   1155    adBNameReportingIdKAnonKeyHash: adBNameReportingIdKAnonKeyHash,
   1156    adABuyerReportingIdKAnonKeyHash: adABuyerReportingIdKAnonKeyHash,
   1157    adBBuyerReportingIdKAnonKeyHash: adBBuyerReportingIdKAnonKeyHash,
   1158    adABASReportingIdKAnonKeyHash: adABASReportingIdKAnonKeyHash,
   1159    adBBASReportingIdKAnonKeyHash: adBBASReportingIdKAnonKeyHash
   1160  };
   1161 
   1162  await joinInterestGroup(test, uuid, ig);
   1163 
   1164  const result = await navigator.getInterestGroupAdAuctionData({
   1165    coordinatorOrigin: await BA.configureCoordinator(),
   1166    seller: window.location.origin
   1167  });
   1168  assert_true(result.requestId !== null);
   1169  assert_true(result.request.length > 0);
   1170 
   1171  let decoded = await BA.decodeInterestGroupData(result.request);
   1172 
   1173  let serverResponseMsg = {
   1174    'biddingGroups': {},
   1175    'adRenderURL': ig.ads[0].renderURL,
   1176    'interestGroupName': DEFAULT_INTEREST_GROUP_NAME,
   1177    'interestGroupOwner': window.location.origin,
   1178  };
   1179  serverResponseMsg.biddingGroups[window.location.origin] = [0];
   1180 
   1181  await responseMutator(serverResponseMsg, ig, hashes, uuid);
   1182 
   1183  let serverResponse =
   1184      await BA.encodeServerResponse(serverResponseMsg, decoded);
   1185 
   1186  let hashString = await BA.payloadHash(serverResponse);
   1187  await BA.authorizeServerResponseHashes([hashString]);
   1188 
   1189  let auctionResult = await navigator.runAdAuction({
   1190    'seller': window.location.origin,
   1191    'interestGroupBuyers': [window.location.origin],
   1192    'requestId': result.requestId,
   1193    'serverResponse': serverResponse,
   1194    'resolveToConfig': true,
   1195  });
   1196  if (expectWin) {
   1197    expectSuccess(auctionResult);
   1198    return auctionResult;
   1199  } else {
   1200    expectNoWinner(auctionResult);
   1201  }
   1202 }
   1203 
   1204 subsetTest(promise_test, async test => {
   1205  await kAnonTestWithMutatedServerResponse(
   1206      test, /*expectSuccess=*/ true, (msg, ig, hashes) => {
   1207        msg.kAnonWinnerJoinCandidates = {
   1208          adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1209          reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1210        };
   1211      });
   1212 }, 'Basic B&A auction - winner with candidates');
   1213 
   1214 subsetTest(promise_test, async test => {
   1215  await kAnonTestWithMutatedServerResponse(
   1216      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1217        msg.kAnonWinnerJoinCandidates = {
   1218          adRenderURLHash: new Uint8Array(),
   1219          reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1220        };
   1221      });
   1222 }, 'Basic B&A auction - winner with bad render hash');
   1223 
   1224 subsetTest(promise_test, async test => {
   1225  await kAnonTestWithMutatedServerResponse(
   1226      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1227        msg.kAnonWinnerJoinCandidates = {
   1228          adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1229          reportingIdHash: new Uint8Array(),
   1230        };
   1231      });
   1232 }, 'Basic B&A auction - winner with bad reporting hash');
   1233 
   1234 subsetTest(promise_test, async test => {
   1235  await kAnonTestWithMutatedServerResponse(
   1236      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1237        delete msg.adRenderURL;
   1238        delete msg.interestGroupName;
   1239        delete msg.interestGroupOwner;
   1240        msg.kAnonGhostWinners = [{
   1241          kAnonJoinCandidates: {
   1242            // missing adRenderURLHash
   1243            reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1244          },
   1245          interestGroupIndex: 0,
   1246          owner: window.location.origin,
   1247        }]
   1248      });
   1249 }, 'Basic B&A auction - invalid ghost winner');
   1250 
   1251 subsetTest(promise_test, async test => {
   1252  await kAnonTestWithMutatedServerResponse(
   1253      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1254        delete msg.adRenderURL;
   1255        delete msg.interestGroupName;
   1256        delete msg.interestGroupOwner;
   1257        msg.kAnonGhostWinners = [{
   1258          kAnonJoinCandidates: {
   1259            adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1260            reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1261          },
   1262          interestGroupIndex: 0,
   1263          owner: window.location.origin,
   1264        }]
   1265      });
   1266 }, 'Basic B&A auction - only ghost winner');
   1267 
   1268 subsetTest(promise_test, async test => {
   1269  await kAnonTestWithMutatedServerResponse(
   1270      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1271        delete msg.adRenderURL;
   1272        delete msg.interestGroupName;
   1273        delete msg.interestGroupOwner;
   1274        msg.kAnonGhostWinners = [
   1275          {
   1276            kAnonJoinCandidates: {
   1277              adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1278              reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1279            },
   1280            interestGroupIndex: 0,
   1281            owner: window.location.origin,
   1282          },
   1283          {
   1284            kAnonJoinCandidates: {
   1285              adRenderURLHash: hashes.adBRenderKAnonKeyHash,
   1286              reportingIdHash: hashes.adBNameReportingIdKAnonKeyHash,
   1287            },
   1288            interestGroupIndex: 0,
   1289            owner: window.location.origin,
   1290          }
   1291        ]
   1292      });
   1293 }, 'Basic B&A auction - multiple ghost winners');
   1294 
   1295 subsetTest(promise_test, async test => {
   1296  await kAnonTestWithMutatedServerResponse(
   1297      test, /*expectSuccess=*/ false, (msg, ig, hashes) => {
   1298        delete msg.adRenderURL;
   1299        delete msg.interestGroupName;
   1300        delete msg.interestGroupOwner;
   1301        msg.kAnonGhostWinners = [
   1302          {
   1303            kAnonJoinCandidates: {
   1304              adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1305              reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1306            },
   1307            interestGroupIndex: 0,
   1308            owner: window.location.origin,
   1309          },
   1310          {
   1311            kAnonJoinCandidates: {
   1312              // missing adRenderURLHash
   1313              reportingIdHash: hashes.adBNameReportingIdKAnonKeyHash,
   1314            },
   1315            interestGroupIndex: 0,
   1316            owner: window.location.origin,
   1317          }
   1318        ]
   1319      });
   1320 }, 'Basic B&A auction - second ghost winner invalid');
   1321 
   1322 subsetTest(promise_test, async test => {
   1323  await kAnonTestWithMutatedServerResponse(
   1324      test, /*expectSuccess=*/ true, (msg, ig, hashes) => {
   1325        msg.kAnonWinnerJoinCandidates = {
   1326          adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1327          reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1328        };
   1329        msg.kAnonGhostWinners = [{
   1330          kAnonJoinCandidates: {
   1331            adRenderURLHash: hashes.adBRenderKAnonKeyHash,
   1332            reportingIdHash: hashes.adBNameReportingIdKAnonKeyHash,
   1333          },
   1334          interestGroupIndex: 0,
   1335          owner: window.location.origin,
   1336        }];
   1337      });
   1338 }, 'Basic B&A auction - winner with ghost winner');
   1339 
   1340 subsetTest(promise_test, async test => {
   1341  await kAnonTestWithMutatedServerResponse(
   1342      test, /*expectSuccess=*/ true, (msg, ig, hashes) => {
   1343        msg.kAnonWinnerJoinCandidates = {
   1344          adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1345          reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1346        };
   1347        msg.kAnonGhostWinners = [{
   1348          kAnonJoinCandidates: {
   1349            adRenderURLHash: hashes.adBRenderKAnonKeyHash,
   1350            reportingIdHash: hashes.adBNameReportingIdKAnonKeyHash,
   1351          },
   1352          interestGroupIndex: 0,
   1353          owner: window.location.origin,
   1354          ghostWinnerForTopLevelAuction: {
   1355            // missing adRenderURL
   1356            modifiedBid: 100,
   1357          },
   1358        }];
   1359      });
   1360 }, 'Basic B&A auction - invalid GhostWinnerForTopLevelAuction');
   1361 
   1362 subsetTest(promise_test, async test => {
   1363  await kAnonTestWithMutatedServerResponse(
   1364      test, /*expectSuccess=*/ true, (msg, ig, hashes) => {
   1365        msg.kAnonWinnerJoinCandidates = {
   1366          adRenderURLHash: hashes.adARenderKAnonKeyHash,
   1367          reportingIdHash: hashes.adANameReportingIdKAnonKeyHash,
   1368        };
   1369        msg.kAnonGhostWinners = [{
   1370          kAnonJoinCandidates: {
   1371            adRenderURLHash: hashes.adBRenderKAnonKeyHash,
   1372            reportingIdHash: hashes.adBNameReportingIdKAnonKeyHash,
   1373          },
   1374          interestGroupIndex: 0,
   1375          owner: window.location.origin,
   1376          ghostWinnerForTopLevelAuction: {
   1377            adRenderURL: ig.ads[1].renderURL,
   1378            modifiedBid: 100,
   1379          },
   1380        }];
   1381      });
   1382 }, 'Basic B&A auction - winner with full ghost winner');
   1383 
   1384 // TODO(behamilton): Add Multi-seller k-anon tests.
   1385 // TODO(behamilton): Add k-anon tests with different reporting IDs.
   1386 
   1387 /* Some things that are not currently tested that probably should be; this is
   1388   not exhaustive, merely to keep track of things that come to mind as tests are
   1389   written:
   1390 
   1391   - forDebugOnly --- it will be straightforward now, but will break.
   1392   - Some of the parsing details that need to match the spec language exactly.
   1393 */