tor-browser

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

additional-bids.https.window.js (28804B)


      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-last
      8 
      9 "use strict";
     10 
     11 // This file contains tests for additional bids and negative targeting.
     12 //
     13 // TODO:
     14 // - test that an additional bid with some correct signatures can be negative
     15 //       targeted for those negative interest groups whose signatures match.
     16 // - test that additional bids can be fetched using an iframe navigation.
     17 // - test that additional bids are not fetched using an iframe navigation for
     18 //      which the `adAuctionHeaders=true` attribute is not specified.
     19 // - test that additional bids are not fetched using a Fetch request for which
     20 //      `adAuctionHeaders: true` is not specified.
     21 // - test that an additional bid with an incorrect seller and / or top-level
     22 //       seller is not included in an auction.
     23 // - lots of tests for different types of malformed additional bids, e.g.
     24 //       missing fields, malformed signature, invalid currency code,
     25 //       missing joining origin for multiple negative interest groups, etc.
     26 // - test that correctly formatted additional bids are included in an auction
     27 //       when fetched alongside malformed additional bid headers by a Fetch
     28 //       request (both invalid headers and invalid additional bids)
     29 // - test that an additional bid is rejected if its from a buyer who is not
     30 //       allowed to participate in the auction.
     31 // - test that an additional bid is rejected if its currency doesn't match the
     32 //       buyer's associated per-buyer currency from the auction config.
     33 // - test that correctly formatted additional bids are included in an auction
     34 //       when fetched alongside malformed additional bid headers by an iframe
     35 //       navigation (both invalid headers and invalid additional bids).
     36 // - test that reportWin is not used for reporting an additional bid win.
     37 // - test that additional bids can *not* be fetched from iframe subresource
     38 //       requests.
     39 // - test that an auction nonce can only be used once, and a second auction
     40 //       trying to reuse an auction immediately fails.
     41 // - test that an auction nonce must be created in the same window/tab as the
     42 //       call to runAdAuction.
     43 // - test reportAdditionalBidWin with each of no metadata, null metadata, and
     44 //       an object metadata.
     45 // - test that an auction running in one tab can't see an additional bid loaded
     46 //       in a new tab.
     47 // - test that two auctions running with different nonces only get the
     48 //       additional bids fetched with their auction nonce.
     49 // - test that two auctions running with different nonces only get the
     50 //       additional bids fetched with their auction nonce, when both additional
     51 //       bids are retrieved with one fetch.
     52 // - test that a multiseller auction with two component auctions can direct
     53 //       additional bids to the correct component auctions.
     54 // - test that two auctions running with different nonces only get the
     55 //       additional bids fetched with their auction nonce.
     56 // - test that two auctions running with different nonces only get the
     57 //       additional bids fetched with their auction nonce, when both additional
     58 //       bids are retrieved with one fetch.
     59 // - test that an additional bid can compete against an interest group bid and
     60 //       lose.
     61 // - test that an additional bid can compete against an interest group bid and
     62 //       win.
     63 // - test (in join-leave-ad-interest-group.https.window.js) that an IG that
     64 //       provides `additionalBidKey` fails if the key fails to decode, or if
     65 //       that IG also provides `ads`, or if it provides `updateURL`.
     66 // - test that an IG update cannot cause a regular interest group (one that
     67 //       does not provide `additionalBidKey`) to become a negative interest
     68 //       group (one that does provide `additionalBidKey`).
     69 // - test (in auction-config-passed-to-worklets.https.window.js) that a
     70 //       multi-seller auction fails if the top-level auction provides
     71 //       a value for `additionalBids`.
     72 // - test (in auction-config-passed-to-worklets.https.window.js) that an auction
     73 //       fails if it provides `additionalBids` but not `auctionNonce`, or if it
     74 //       provides `additionalBids` but not `interestGroupBuyers`.
     75 
     76 // The auction is run with the seller being the same as the document origin.
     77 // The request to fetch additional bids must be issued to the seller's origin
     78 // for ad auction headers interception to associate it with this auction.
     79 const SINGLE_SELLER_AUCTION_SELLER = window.location.origin;
     80 
     81 const ADDITIONAL_BID_SECRET_KEY = 'nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A=';
     82 const ADDITIONAL_BID_PUBLIC_KEY = '11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=';
     83 
     84 // Single-seller auction with a single buyer who places a single additional
     85 // bid. As the only bid, this wins.
     86 subsetTest(promise_test, async test => {
     87  const uuid = generateUuid(test);
     88  const auctionNonce = await navigator.createAuctionNonce();
     89  const seller = SINGLE_SELLER_AUCTION_SELLER;
     90 
     91  const buyer = OTHER_ORIGIN1;
     92  const additionalBid = additionalBidHelper.createAdditionalBid(
     93      uuid, seller, buyer, 'horses', 1.99);
     94  additionalBid.auctionNonce = auctionNonce;
     95  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
     96 
     97  await runAdditionalBidTest(
     98      test, uuid, [buyer], auctionNonce,
     99      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]),
    100      /*highestScoringOtherBid=*/0,
    101      /*winningAdditionalBidId=*/'horses');
    102 }, 'single valid additional bid');
    103 
    104 // Single-seller auction with a two buyers competing with additional bids.
    105 subsetTest(promise_test, async test => {
    106  const uuid = generateUuid(test);
    107  const auctionNonce = await navigator.createAuctionNonce();
    108  const seller = SINGLE_SELLER_AUCTION_SELLER;
    109 
    110  const buyer1 = OTHER_ORIGIN1;
    111  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    112      uuid, seller, buyer1, 'horses', 1.99);
    113  additionalBid1.auctionNonce = auctionNonce;
    114  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    115 
    116  const buyer2 = OTHER_ORIGIN2;
    117  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    118      uuid, seller, buyer2, 'planes', 2.99);
    119  additionalBid2.auctionNonce = auctionNonce;
    120  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    121 
    122  await runAdditionalBidTest(
    123      test, uuid, [buyer1, buyer2], auctionNonce,
    124      additionalBidHelper.fetchAdditionalBids(
    125          seller, [additionalBid1, additionalBid2]),
    126      /*highestScoringOtherBid=*/1.99,
    127      /*winningAdditionalBidId=*/'planes');
    128 }, 'two valid additional bids');
    129 
    130 // Same as the test above, except that this uses two Fetch requests instead of
    131 // one to retrieve the additional bids.
    132 subsetTest(promise_test, async test => {
    133  const uuid = generateUuid(test);
    134  const auctionNonce = await navigator.createAuctionNonce();
    135  const seller = SINGLE_SELLER_AUCTION_SELLER;
    136 
    137  const buyer1 = OTHER_ORIGIN1;
    138  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    139      uuid, seller, buyer1, 'horses', 1.99);
    140  additionalBid1.auctionNonce = auctionNonce;
    141  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    142 
    143  const buyer2 = OTHER_ORIGIN2;
    144  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    145      uuid, seller, buyer2, 'planes', 2.99);
    146  additionalBid2.auctionNonce = auctionNonce;
    147  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    148 
    149  await runAdditionalBidTest(
    150    test, uuid, [buyer1, buyer2], auctionNonce,
    151    Promise.all([
    152        additionalBidHelper.fetchAdditionalBids(seller, [additionalBid1]),
    153        additionalBidHelper.fetchAdditionalBids(seller, [additionalBid2])
    154    ]),
    155    /*highestScoringOtherBid=*/1.99,
    156    /*winningAdditionalBidId=*/'planes');
    157 }, 'two valid additional bids from two distinct Fetch requests');
    158 
    159 // Single-seller auction with a single buyer who places a single additional
    160 // bid with the wrong `auctionNonce` in the bid, causing the bid to fail.
    161 subsetTest(promise_test, async test => {
    162  const uuid = generateUuid(test);
    163  const auctionNonceInHeader = await navigator.createAuctionNonce();
    164  const auctionNonceInBid = crypto.randomUUID();
    165  const seller = SINGLE_SELLER_AUCTION_SELLER;
    166 
    167  const buyer = OTHER_ORIGIN1;
    168  const additionalBid = additionalBidHelper.createAdditionalBid(
    169      uuid, seller, buyer, 'horses', 1.99);
    170  additionalBid.additionalBid = auctionNonceInBid;
    171  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonceInHeader);
    172 
    173  await runAdditionalBidTestNoWinner(
    174      test, uuid, [buyer], auctionNonceInHeader,
    175      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    176 }, 'single valid additional bid with wrong auctionNonce in bid');
    177 
    178 // Single-seller auction with a single buyer who places a single additional
    179 // bid with no `auctionNonce` in the bid, causing the bid to fail.
    180 subsetTest(promise_test, async test => {
    181  const uuid = generateUuid(test);
    182  const auctionNonce = await navigator.createAuctionNonce();
    183  const seller = SINGLE_SELLER_AUCTION_SELLER;
    184 
    185  const buyer = OTHER_ORIGIN1;
    186  const additionalBid = additionalBidHelper.createAdditionalBid(
    187      uuid, seller, buyer, 'horses', 1.99);
    188  // Notably missing: `additionalBid.auctionNonce`
    189  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    190 
    191  await runAdditionalBidTestNoWinner(
    192      test, uuid, [buyer], auctionNonce,
    193      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    194 }, 'single valid additional bid with no auctionNonce in bid');
    195 
    196 // Single-seller auction with a single buyer who places a single additional
    197 // bid that elides `auctionNonce` in favor of a `bidNonce`, which is correctly
    198 // computed from the combination of `auctionNonce` and `sellerNonce`. However,
    199 // this test fails to include the `sellerNonce` in the header,
    200 // causing the bid to fail. `bidNonce` in an additional bid is only valid with
    201 // a `sellerNonce` on the response header.
    202 subsetTest(promise_test, async test => {
    203  const uuid = generateUuid(test);
    204  const auctionNonce = await navigator.createAuctionNonce();
    205  const sellerNonce = crypto.randomUUID();
    206  const seller = SINGLE_SELLER_AUCTION_SELLER;
    207 
    208  const buyer = OTHER_ORIGIN1;
    209  const additionalBid = additionalBidHelper.createAdditionalBid(
    210      uuid, seller, buyer, 'horses', 1.99);
    211  additionalBid.bidNonce =
    212      await additionalBidHelper.computeBidNonce(auctionNonce, sellerNonce);
    213  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    214  // Notably missing: `additionalBidHelper.setSellerNonceInHeader`
    215 
    216  await runAdditionalBidTestNoWinner(
    217      test, uuid, [buyer], auctionNonce,
    218      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    219 }, 'single valid additional bid with bidNonce in bid but no sellerNonce in header');
    220 
    221 // Single-seller auction with a single buyer who places a single additional
    222 // bid that uses `sellerNonce` / `bidNonce`. As the only bid, this wins.
    223 subsetTest(promise_test, async test => {
    224  const uuid = generateUuid(test);
    225  const auctionNonce = await navigator.createAuctionNonce();
    226  const sellerNonce = crypto.randomUUID();
    227  const seller = SINGLE_SELLER_AUCTION_SELLER;
    228 
    229  const buyer = OTHER_ORIGIN1;
    230  const additionalBid = additionalBidHelper.createAdditionalBid(
    231      uuid, seller, buyer, 'horses', 1.99);
    232  additionalBid.bidNonce =
    233      await additionalBidHelper.computeBidNonce(auctionNonce, sellerNonce);
    234  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    235  additionalBidHelper.setSellerNonceInHeader(additionalBid, sellerNonce);
    236 
    237  await runAdditionalBidTest(
    238      test, uuid, [buyer], auctionNonce,
    239      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]),
    240      /*highestScoringOtherBid=*/0,
    241      /*winningAdditionalBidId=*/'horses');
    242 }, 'single valid additional bid with bidNonce and sellerNonce');
    243 
    244 // Single-seller auction with a single buyer who places a single additional bid
    245 // that uses `sellerNonce`, but with an `auctionNonce` on the bid instead of a
    246 // `bidNonce`. Since `sellerNonce` is only compatible with `bidNonce`, there is
    247 // no winner.
    248 subsetTest(promise_test, async test => {
    249  const uuid = generateUuid(test);
    250  const auctionNonce = await navigator.createAuctionNonce();
    251  const sellerNonce = crypto.randomUUID();
    252  const seller = SINGLE_SELLER_AUCTION_SELLER;
    253 
    254  const buyer = OTHER_ORIGIN1;
    255  const additionalBid = additionalBidHelper.createAdditionalBid(
    256      uuid, seller, buyer, 'horses', 1.99);
    257  additionalBid.auctionNonce = auctionNonce;
    258  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    259  additionalBidHelper.setSellerNonceInHeader(additionalBid, sellerNonce);
    260 
    261  await runAdditionalBidTestNoWinner(
    262      test, uuid, [buyer], auctionNonce,
    263      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    264 }, 'single additional bid with sellerNonce in the header but auctionNonce in the bid');
    265 
    266 // Single-seller auction with a single buyer who places a single additional bid
    267 // that uses `sellerNonce`, but no `bidNonce` or `auctionNonce`. Since the
    268 // `bidNonce` is missing, there is no winner. Related to 'single valid
    269 // additional bid with no auctionNonce in bid', except with `sellerNonce`.
    270 subsetTest(promise_test, async test => {
    271  const uuid = generateUuid(test);
    272  const auctionNonce = await navigator.createAuctionNonce();
    273  const sellerNonce = crypto.randomUUID();
    274  const seller = SINGLE_SELLER_AUCTION_SELLER;
    275 
    276  const buyer = OTHER_ORIGIN1;
    277  const additionalBid = additionalBidHelper.createAdditionalBid(
    278      uuid, seller, buyer, 'horses', 1.99);
    279  // Notably missing: `additionalBid.bidNonce`.
    280  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    281  additionalBidHelper.setSellerNonceInHeader(additionalBid, sellerNonce);
    282 
    283  await runAdditionalBidTestNoWinner(
    284      test, uuid, [buyer], auctionNonce,
    285      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    286 }, 'single additional bid with sellerNonce in the header but no bidNonce or ' +
    287   'auctionNonce in the bid');
    288 
    289 // Single-seller auction with a single buyer who places a single additional
    290 // bid that uses `sellerNonce` / `bidNonce`, but also `auctionNonce` in the bid.
    291 // As exactly one of `bidNonce` / `auctionNonce` is allowed in the bid, this
    292 // fails.
    293 subsetTest(promise_test, async test => {
    294  const uuid = generateUuid(test);
    295  const auctionNonce = await navigator.createAuctionNonce();
    296  const sellerNonce = crypto.randomUUID();
    297  const seller = SINGLE_SELLER_AUCTION_SELLER;
    298 
    299  const buyer = OTHER_ORIGIN1;
    300  const additionalBid = additionalBidHelper.createAdditionalBid(
    301      uuid, seller, buyer, 'horses', 1.99);
    302  additionalBid.auctionNonce = auctionNonce;
    303  additionalBid.bidNonce =
    304      await additionalBidHelper.computeBidNonce(auctionNonce, sellerNonce);
    305  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    306  additionalBidHelper.setSellerNonceInHeader(additionalBid, sellerNonce);
    307 
    308  await runAdditionalBidTestNoWinner(
    309      test, uuid, [buyer], auctionNonce,
    310      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    311 }, 'single additional bid with sellerNonce in the header but with both ' +
    312   ' bidNonce and auctionNonce in the bid');
    313 
    314 // Single-seller auction with a single buyer who places a single additional
    315 // bid that uses `sellerNonce` / `bidNonce`. Since the `bidNonce` is invalid
    316 // there is no winner.
    317 subsetTest(promise_test, async test => {
    318  const uuid = generateUuid(test);
    319  const auctionNonce = await navigator.createAuctionNonce();
    320  const sellerNonce = crypto.randomUUID();
    321  const seller = SINGLE_SELLER_AUCTION_SELLER;
    322 
    323  const buyer = OTHER_ORIGIN1;
    324  const additionalBid = additionalBidHelper.createAdditionalBid(
    325      uuid, seller, buyer, 'horses', 1.99);
    326  // Computes a `bidNonce` using a randomly generated `sellerNonce`, not the
    327  // one used in the header.
    328  additionalBid.bidNonce = await additionalBidHelper.computeBidNonce(
    329      auctionNonce, crypto.randomUUID());
    330  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    331  additionalBidHelper.setSellerNonceInHeader(additionalBid, sellerNonce);
    332 
    333  await runAdditionalBidTestNoWinner(
    334      test, uuid, [buyer], auctionNonce,
    335      additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]));
    336 }, 'single additional bid with invalid bidNonce');
    337 
    338 // Single-seller auction with a two buyers competing with additional bids, each
    339 // using a distinct `sellerNonce` / `bidNonce`.
    340 subsetTest(promise_test, async test => {
    341  const uuid = generateUuid(test);
    342  const auctionNonce = await navigator.createAuctionNonce();
    343  const sellerNonce1 = crypto.randomUUID();
    344  const sellerNonce2 = crypto.randomUUID();
    345  const seller = SINGLE_SELLER_AUCTION_SELLER;
    346 
    347  const buyer1 = OTHER_ORIGIN1;
    348  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    349      uuid, seller, buyer1, 'horses', 1.99);
    350  additionalBid1.bidNonce =
    351      await additionalBidHelper.computeBidNonce(auctionNonce, sellerNonce1);
    352  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    353  additionalBidHelper.setSellerNonceInHeader(additionalBid1, sellerNonce1);
    354 
    355  const buyer2 = OTHER_ORIGIN2;
    356  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    357      uuid, seller, buyer2, 'planes', 2.99);
    358  additionalBid2.bidNonce =
    359      await additionalBidHelper.computeBidNonce(auctionNonce, sellerNonce2);
    360  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    361  additionalBidHelper.setSellerNonceInHeader(additionalBid2, sellerNonce2);
    362 
    363  await runAdditionalBidTest(
    364      test, uuid, [buyer1, buyer2], auctionNonce,
    365      additionalBidHelper.fetchAdditionalBids(
    366          seller, [additionalBid1, additionalBid2]),
    367      /*highestScoringOtherBid=*/1.99,
    368      /*winningAdditionalBidId=*/'planes');
    369 }, 'two valid additional bids using distinct bidNonces and sellerNonces');
    370 
    371 // Single-seller auction with a single additional bid. Because this additional
    372 // bid is filtered by negative targeting, this auction has no winner.
    373 subsetTest(promise_test, async test => {
    374  const uuid = generateUuid(test);
    375  const auctionNonce = await navigator.createAuctionNonce();
    376  const seller = SINGLE_SELLER_AUCTION_SELLER;
    377 
    378  const negativeInterestGroupName = 'already-owns-a-plane';
    379 
    380  const buyer = OTHER_ORIGIN1;
    381  const additionalBid = additionalBidHelper.createAdditionalBid(
    382      uuid, seller, buyer, 'planes', 2.99);
    383  additionalBid.auctionNonce = auctionNonce;
    384  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    385  additionalBidHelper.addNegativeInterestGroup(
    386      additionalBid, negativeInterestGroupName);
    387  additionalBidHelper.signWithSecretKeys(
    388      additionalBid, [ADDITIONAL_BID_SECRET_KEY]);
    389 
    390  await joinNegativeInterestGroup(
    391      test, buyer, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
    392 
    393  await runBasicFledgeTestExpectingNoWinner(
    394      test, uuid,
    395      { interestGroupBuyers: [buyer],
    396        auctionNonce: auctionNonce,
    397        additionalBids: additionalBidHelper.fetchAdditionalBids(
    398            seller, [additionalBid])});
    399 }, 'one additional bid filtered by negative targeting, so auction has no ' +
    400   'winner');
    401 
    402 // Single-seller auction with a two buyers competing with additional bids.
    403 // The higher of these has a negative interest group specified, and that
    404 // negative interest group has been joined, so the lower bid wins.
    405 subsetTest(promise_test, async test => {
    406  const uuid = generateUuid(test);
    407  const auctionNonce = await navigator.createAuctionNonce();
    408  const seller = SINGLE_SELLER_AUCTION_SELLER;
    409 
    410  const negativeInterestGroupName = 'already-owns-a-plane';
    411 
    412  const buyer1 = OTHER_ORIGIN1;
    413  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    414      uuid, seller, buyer1, 'horses', 1.99);
    415  additionalBid1.auctionNonce = auctionNonce;
    416  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    417 
    418  const buyer2 = OTHER_ORIGIN2;
    419  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    420      uuid, seller, buyer2, 'planes', 2.99);
    421  additionalBid2.auctionNonce = auctionNonce;
    422  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    423  additionalBidHelper.addNegativeInterestGroup(
    424      additionalBid2, negativeInterestGroupName);
    425  additionalBidHelper.signWithSecretKeys(
    426      additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
    427 
    428  await joinNegativeInterestGroup(
    429      test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
    430 
    431  await runAdditionalBidTest(
    432    test, uuid, [buyer1, buyer2], auctionNonce,
    433    additionalBidHelper.fetchAdditionalBids(
    434        seller, [additionalBid1, additionalBid2]),
    435    /*highestScoringOtherBid=*/0,
    436    /*winningAdditionalBidId=*/'horses');
    437 }, 'higher additional bid is filtered by negative targeting, so ' +
    438   'lower additional bid wins');
    439 
    440 // Same as above, except that the bid is missing a signature, so that the
    441 // negative targeting interest group is ignored, and the higher bid, which
    442 // would have otherwise been filtered by negative targeting, wins.
    443 subsetTest(promise_test, async test => {
    444  const uuid = generateUuid(test);
    445  const auctionNonce = await navigator.createAuctionNonce();
    446  const seller = SINGLE_SELLER_AUCTION_SELLER;
    447 
    448  const negativeInterestGroupName = 'already-owns-a-plane';
    449 
    450  const buyer1 = OTHER_ORIGIN1;
    451  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    452      uuid, seller, buyer1, 'horses', 1.99);
    453  additionalBid1.auctionNonce = auctionNonce;
    454  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    455 
    456  const buyer2 = OTHER_ORIGIN2;
    457  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    458      uuid, seller, buyer2, 'planes', 2.99);
    459  additionalBid2.auctionNonce = auctionNonce;
    460  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    461  additionalBidHelper.addNegativeInterestGroup(
    462      additionalBid2, negativeInterestGroupName);
    463 
    464  await joinNegativeInterestGroup(
    465      test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
    466 
    467  await runAdditionalBidTest(
    468    test, uuid, [buyer1, buyer2], auctionNonce,
    469    additionalBidHelper.fetchAdditionalBids(
    470        seller, [additionalBid1, additionalBid2]),
    471    /*highestScoringOtherBid=*/1.99,
    472    /*winningAdditionalBidId=*/'planes');
    473 }, 'higher additional bid is filtered by negative targeting, but it is ' +
    474   'missing a signature, so it still wins');
    475 
    476 // Same as above, except that the bid is signed incorrectly, so that the
    477 // negative targeting interest group is ignored, and the higher bid, which
    478 // would have otherwise been filtered by negative targeting, wins.
    479 subsetTest(promise_test, async test => {
    480  const uuid = generateUuid(test);
    481  const auctionNonce = await navigator.createAuctionNonce();
    482  const seller = SINGLE_SELLER_AUCTION_SELLER;
    483 
    484  const negativeInterestGroupName = 'already-owns-a-plane';
    485 
    486  const buyer1 = OTHER_ORIGIN1;
    487  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    488      uuid, seller, buyer1, 'horses', 1.99);
    489  additionalBid1.auctionNonce = auctionNonce;
    490  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    491 
    492  const buyer2 = OTHER_ORIGIN2;
    493  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    494      uuid, seller, buyer2, 'planes', 2.99);
    495  additionalBid2.auctionNonce = auctionNonce;
    496  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    497  additionalBidHelper.addNegativeInterestGroup(
    498      additionalBid2, negativeInterestGroupName);
    499  additionalBidHelper.incorrectlySignWithSecretKeys(
    500      additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
    501 
    502  await joinNegativeInterestGroup(
    503      test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
    504 
    505  await runAdditionalBidTest(
    506    test, uuid, [buyer1, buyer2], auctionNonce,
    507    additionalBidHelper.fetchAdditionalBids(
    508        seller, [additionalBid1, additionalBid2]),
    509    /*highestScoringOtherBid=*/1.99,
    510    /*winningAdditionalBidId=*/'planes');
    511 }, 'higher additional bid is filtered by negative targeting, but it has an ' +
    512   'invalid signature, so it still wins');
    513 
    514 // A test of an additional bid with multiple negative interest groups.
    515 subsetTest(promise_test, async test => {
    516  const uuid = generateUuid(test);
    517  const auctionNonce = await navigator.createAuctionNonce();
    518  const seller = SINGLE_SELLER_AUCTION_SELLER;
    519 
    520  const negativeInterestGroupName1 = 'already-owns-a-plane';
    521  const negativeInterestGroupName2 = 'another-negative-interest-group';
    522 
    523  const buyer1 = OTHER_ORIGIN1;
    524  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    525      uuid, seller, buyer1, 'horses', 1.99);
    526  additionalBid1.auctionNonce = auctionNonce;
    527  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    528 
    529  const buyer2 = OTHER_ORIGIN2;
    530  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    531      uuid, seller, buyer2, 'planes', 2.99);
    532  additionalBid2.auctionNonce = auctionNonce;
    533  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    534  additionalBidHelper.addNegativeInterestGroups(
    535      additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
    536      /*joiningOrigin=*/window.location.origin);
    537  additionalBidHelper.signWithSecretKeys(
    538      additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
    539 
    540  await joinNegativeInterestGroup(
    541      test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
    542 
    543  await runAdditionalBidTest(
    544    test, uuid, [buyer1, buyer2], auctionNonce,
    545    additionalBidHelper.fetchAdditionalBids(
    546        seller, [additionalBid1, additionalBid2]),
    547    /*highestScoringOtherBid=*/0,
    548    /*winningAdditionalBidId=*/'horses');
    549 }, 'higher additional bid is filtered by negative targeting by two negative ' +
    550   'interest groups, and since one is on the device, the lower bid wins');
    551 
    552 // Same as above, but with a mismatched joining origin.
    553 subsetTest(promise_test, async test => {
    554  const uuid = generateUuid(test);
    555  const auctionNonce = await navigator.createAuctionNonce();
    556  const seller = SINGLE_SELLER_AUCTION_SELLER;
    557 
    558  const negativeInterestGroupName1 = 'already-owns-a-plane';
    559  const negativeInterestGroupName2 = 'another-negative-interest-group';
    560 
    561  const buyer1 = OTHER_ORIGIN1;
    562  const additionalBid1 = additionalBidHelper.createAdditionalBid(
    563      uuid, seller, buyer1, 'horses', 1.99);
    564  additionalBid1.auctionNonce = auctionNonce;
    565  additionalBidHelper.setAuctionNonceInHeader(additionalBid1, auctionNonce);
    566 
    567  const buyer2 = OTHER_ORIGIN2;
    568  const additionalBid2 = additionalBidHelper.createAdditionalBid(
    569      uuid, seller, buyer2, 'planes', 2.99);
    570  additionalBid2.auctionNonce = auctionNonce;
    571  additionalBidHelper.setAuctionNonceInHeader(additionalBid2, auctionNonce);
    572  additionalBidHelper.addNegativeInterestGroups(
    573      additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
    574      /*joiningOrigin=*/OTHER_ORIGIN1);
    575  additionalBidHelper.signWithSecretKeys(
    576      additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
    577 
    578  await joinNegativeInterestGroup(
    579      test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
    580 
    581  await runAdditionalBidTest(
    582    test, uuid, [buyer1, buyer2], auctionNonce,
    583    additionalBidHelper.fetchAdditionalBids(
    584        seller, [additionalBid1, additionalBid2]),
    585    /*highestScoringOtherBid=*/1.99,
    586    /*winningAdditionalBidId=*/'planes');
    587 }, 'higher additional bid is filtered by negative targeting by two negative ' +
    588   'interest groups, but because of a joining origin mismatch, it still wins');
    589 
    590 // Ensure that trusted seller signals are retrieved for additional bids.
    591 subsetTest(promise_test, async test => {
    592  const uuid = generateUuid(test);
    593  const auctionNonce = await navigator.createAuctionNonce();
    594  const seller = SINGLE_SELLER_AUCTION_SELLER;
    595 
    596  const buyer = OTHER_ORIGIN1;
    597  const additionalBid = additionalBidHelper.createAdditionalBid(
    598      uuid, seller, buyer, 'horses', 1.99);
    599  additionalBid.auctionNonce = auctionNonce;
    600  additionalBidHelper.setAuctionNonceInHeader(additionalBid, auctionNonce);
    601 
    602  let renderURL = createRenderURL(uuid);
    603  await runBasicFledgeTestExpectingWinner(
    604      test, uuid,
    605      { interestGroupBuyers: [buyer],
    606        auctionNonce: auctionNonce,
    607        additionalBids: additionalBidHelper.fetchAdditionalBids(
    608            seller, [additionalBid]),
    609        decisionLogicURL: createDecisionScriptURL(
    610            uuid,
    611            { scoreAd:
    612                `if(!"${renderURL}" in trustedScoringSignals.renderURL) ` +
    613                  'throw "missing trusted signals";'}),
    614        trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL});
    615 }, 'trusted seller signals retrieved for additional bids');