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