diff options
Diffstat (limited to 'testing/web-platform/tests/fledge/tentative/resources')
6 files changed, 505 insertions, 66 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py index 060606b41d..721909a045 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py +++ b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py @@ -13,6 +13,7 @@ with a value of b"?1"; this entrypoint otherwise returns a 400 response. import json import base64 +import fledge.tentative.resources.ed25519 as ed25519 import fledge.tentative.resources.fledge_http_server_util as fledge_http_server_util @@ -20,6 +21,57 @@ class BadRequestError(Exception): pass +def _generate_signature(message, base64_encoded_secret_key): + """Returns a signature entry for a signed additional bid. + + Args: + base64_encoded_secret_key: base64-encoded Ed25519 key with which to sign + the message. From this secret key, the public key can be deduced, which + becomes part of the signature entry. + message: The additional bid text (or other text if generating an invalid + signature) to sign. + """ + secret_key = base64.b64decode(base64_encoded_secret_key.encode("utf-8")) + public_key = ed25519.publickey_unsafe(secret_key) + signature = ed25519.signature_unsafe( + message.encode("utf-8"), secret_key, public_key) + return { + "key": base64.b64encode(public_key).decode("utf-8"), + "signature": base64.b64encode(signature).decode("utf-8") + } + + +def _sign_additional_bid(additional_bid_string, + secret_keys_for_valid_signatures, + secret_keys_for_invalid_signatures): + """Returns a signed additional bid given an additional bid and secret keys. + + Args: + additional_bid_string: string representation of the additional bid + secret_keys_for_valid_signatures: a list of strings, each a base64-encoded + Ed25519 secret key with which to sign the additional bid + secret_keys_for_invalid_signatures: a list of strings, each a base64-encoded + Ed25519 secret key with which to incorrectly sign the additional bid + """ + signatures = [] + signatures.extend( + _generate_signature(additional_bid_string, secret_key) + for secret_key in secret_keys_for_valid_signatures) + + # For invalid signatures, we use the correct secret key to sign a different + # message - the additional bid prepended by 'invalid' - so that the signature + # is a structually valid signature but can't be used to verify the additional + # bid. + signatures.extend( + _generate_signature("invalid" + additional_bid_string, secret_key) + for secret_key in secret_keys_for_invalid_signatures) + + return json.dumps({ + "bid": additional_bid_string, + "signatures": signatures + }) + + def main(request, response): try: if fledge_http_server_util.handle_cors_headers_and_preflight(request, response): @@ -34,14 +86,16 @@ def main(request, response): if not additional_bids: raise BadRequestError("Missing 'additionalBids' parameter") for additional_bid in json.loads(additional_bids): - additional_bid_string = json.dumps(additional_bid) + # Each additional bid may have associated testMetadata. Remove this from + # the additional bid and use it to adjust the behavior of this handler. + test_metadata = additional_bid.pop("testMetadata", {}) auction_nonce = additional_bid.get("auctionNonce", None) if not auction_nonce: raise BadRequestError("Additional bid missing required 'auctionNonce' field") - signed_additional_bid = json.dumps({ - "bid": additional_bid_string, - "signatures": [] - }) + signed_additional_bid = _sign_additional_bid( + json.dumps(additional_bid), + test_metadata.get("secretKeysForValidSignatures", []), + test_metadata.get("secretKeysForInvalidSignatures", [])) additional_bid_header_value = (auction_nonce.encode("utf-8") + b":" + base64.b64encode(signed_additional_bid.encode("utf-8"))) response.headers.append(b"Ad-Auction-Additional-Bid", additional_bid_header_value) diff --git a/testing/web-platform/tests/fledge/tentative/resources/ed25519.py b/testing/web-platform/tests/fledge/tentative/resources/ed25519.py new file mode 100644 index 0000000000..53e548ab8e --- /dev/null +++ b/testing/web-platform/tests/fledge/tentative/resources/ed25519.py @@ -0,0 +1,289 @@ +# ed25519.py - Optimized version of the reference implementation of Ed25519 +# +# Written in 2011? by Daniel J. Bernstein <djb@cr.yp.to> +# 2013 by Donald Stufft <donald@stufft.io> +# 2013 by Alex Gaynor <alex.gaynor@gmail.com> +# 2013 by Greg Price <price@mit.edu> +# +# To the extent possible under law, the author(s) have dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication along +# with this software. If not, see +# <http://creativecommons.org/publicdomain/zero/1.0/>. +# +# Downloaded from https://raw.githubusercontent.com/pyca/ed25519/main/ed25519.py +# on April 1, 2024. + +""" +NB: This code is not safe for use with secret keys or secret data. +The only safe use of this code is for verifying signatures on public messages. + +Functions for computing the public key of a secret key and for signing +a message are included, namely publickey_unsafe and signature_unsafe, +for testing purposes only. + +The root of the problem is that Python's long-integer arithmetic is +not designed for use in cryptography. Specifically, it may take more +or less time to execute an operation depending on the values of the +inputs, and its memory access patterns may also depend on the inputs. +This opens it to timing and cache side-channel attacks which can +disclose data to an attacker. We rely on Python's long-integer +arithmetic, so we cannot handle secrets without risking their disclosure. +""" + +import hashlib + + +__version__ = "1.0.dev0" + + +b = 256 +q = 2**255 - 19 +l = 2**252 + 27742317777372353535851937790883648493 + + +def H(m): + return hashlib.sha512(m).digest() + + +def pow2(x, p): + """== pow(x, 2**p, q)""" + while p > 0: + x = x * x % q + p -= 1 + return x + + +def inv(z): + r"""$= z^{-1} \mod q$, for z != 0""" + # Adapted from curve25519_athlon.c in djb's Curve25519. + z2 = z * z % q # 2 + z9 = pow2(z2, 2) * z % q # 9 + z11 = z9 * z2 % q # 11 + z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0 + z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0 + z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ... + z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q + z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q + z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q + z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q + z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0 + return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2 + + +d = -121665 * inv(121666) % q +I = pow(2, (q - 1) // 4, q) + + +def xrecover(y): + xx = (y * y - 1) * inv(d * y * y + 1) + x = pow(xx, (q + 3) // 8, q) + + if (x * x - xx) % q != 0: + x = (x * I) % q + + if x % 2 != 0: + x = q - x + + return x + + +By = 4 * inv(5) +Bx = xrecover(By) +B = (Bx % q, By % q, 1, (Bx * By) % q) +ident = (0, 1, 1, 0) + + +def edwards_add(P, Q): + # This is formula sequence 'addition-add-2008-hwcd-3' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + (x2, y2, z2, t2) = Q + + a = (y1 - x1) * (y2 - x2) % q + b = (y1 + x1) * (y2 + x2) % q + c = t1 * 2 * d * t2 % q + dd = z1 * 2 * z2 % q + e = b - a + f = dd - c + g = dd + c + h = b + a + x3 = e * f + y3 = g * h + t3 = e * h + z3 = f * g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def edwards_double(P): + # This is formula sequence 'dbl-2008-hwcd' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + + a = x1 * x1 % q + b = y1 * y1 % q + c = 2 * z1 * z1 % q + # dd = -a + e = ((x1 + y1) * (x1 + y1) - a - b) % q + g = -a + b # dd + b + f = g - c + h = -a - b # dd - b + x3 = e * f + y3 = g * h + t3 = e * h + z3 = f * g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def scalarmult(P, e): + if e == 0: + return ident + Q = scalarmult(P, e // 2) + Q = edwards_double(Q) + if e & 1: + Q = edwards_add(Q, P) + return Q + + +# Bpow[i] == scalarmult(B, 2**i) +Bpow = [] + + +def make_Bpow(): + P = B + for i in range(253): + Bpow.append(P) + P = edwards_double(P) + + +make_Bpow() + + +def scalarmult_B(e): + """ + Implements scalarmult(B, e) more efficiently. + """ + # scalarmult(B, l) is the identity + e = e % l + P = ident + for i in range(253): + if e & 1: + P = edwards_add(P, Bpow[i]) + e = e // 2 + assert e == 0, e + return P + + +def encodeint(y): + bits = [(y >> i) & 1 for i in range(b)] + return bytes( + [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)] + ) + + +def encodepoint(P): + (x, y, z, t) = P + zi = inv(z) + x = (x * zi) % q + y = (y * zi) % q + bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] + return bytes( + [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)] + ) + + +def bit(h, i): + return (h[i // 8] >> (i % 8)) & 1 + + +def publickey_unsafe(sk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + h = H(sk) + a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2)) + A = scalarmult_B(a) + return encodepoint(A) + + +def Hint(m): + h = H(m) + return sum(2**i * bit(h, i) for i in range(2 * b)) + + +def signature_unsafe(m, sk, pk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + h = H(sk) + a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2)) + r = Hint(bytes([h[j] for j in range(b // 8, b // 4)]) + m) + R = scalarmult_B(r) + S = (r + Hint(encodepoint(R) + pk + m) * a) % l + return encodepoint(R) + encodeint(S) + + +def isoncurve(P): + (x, y, z, t) = P + return ( + z % q != 0 + and x * y % q == z * t % q + and (y * y - x * x - z * z - d * t * t) % q == 0 + ) + + +def decodeint(s): + return sum(2**i * bit(s, i) for i in range(0, b)) + + +def decodepoint(s): + y = sum(2**i * bit(s, i) for i in range(0, b - 1)) + x = xrecover(y) + if x & 1 != bit(s, b - 1): + x = q - x + P = (x, y, 1, (x * y) % q) + if not isoncurve(P): + raise ValueError("decoding point that is not on curve") + return P + + +class SignatureMismatch(Exception): + pass + + +def checkvalid(s, m, pk): + """ + Not safe to use when any argument is secret. + + See module docstring. This function should be used only for + verifying public signatures of public messages. + """ + if len(s) != b // 4: + raise ValueError("signature length is wrong") + + if len(pk) != b // 8: + raise ValueError("public-key length is wrong") + + R = decodepoint(s[: b // 8]) + A = decodepoint(pk) + S = decodeint(s[b // 8 : b // 4]) + h = Hint(encodepoint(R) + pk + m) + + (x1, y1, z1, t1) = P = scalarmult_B(S) + (x2, y2, z2, t2) = Q = edwards_add(R, scalarmult(A, h)) + + if ( + not isoncurve(P) + or not isoncurve(Q) + or (x1 * z2 - x2 * z1) % q != 0 + or (y1 * z2 - y2 * z1) % q != 0 + ): + raise SignatureMismatch("signature does not pass verification") diff --git a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js index 5819357e29..7be02e34ff 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js +++ b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js @@ -177,7 +177,7 @@ async function waitForObservedRequestsIgnoreDebugOnlyReports( function createBiddingScriptURL(params = {}) { let origin = params.origin ? params.origin : new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}bidding-logic.sub.py`); - // These checks use "==" to ignore null and not provided arguments, while + // These checks use "!=" to ignore null and not provided arguments, while // treating '' as a valid argument. if (params.generateBid != null) url.searchParams.append('generateBid', params.generateBid); @@ -213,7 +213,7 @@ function createDecisionScriptURL(uuid, params = {}) { let origin = params.origin ? params.origin : new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}decision-logic.sub.py`); url.searchParams.append('uuid', uuid); - // These checks use "==" to ignore null and not provided arguments, while + // These checks use "!=" to ignore null and not provided arguments, while // treating '' as a valid argument. if (params.scoreAd != null) url.searchParams.append('scoreAd', params.scoreAd); @@ -230,8 +230,8 @@ function createDecisionScriptURL(uuid, params = {}) { // be last. "signalsParams" also has no effect, but is used by // trusted-scoring-signals.py to affect the response. function createRenderURL(uuid, script, signalsParams, origin) { - // These checks use "==" to ignore null and not provided arguments, while - // treating '' as a valid argument. + // These checks use "==" and "!=" to ignore null and not provided + // arguments, while treating '' as a valid argument. if (origin == null) origin = new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}fenced-frame.sub.py`); @@ -260,6 +260,15 @@ function createInterestGroupForOrigin(uuid, origin, }; } +// Waits for the join command to complete. Adds cleanup command to `test` to +// leave the interest group when the test completes. +async function joinInterestGroupWithoutDefaults(test, interestGroup, + durationSeconds = 60) { + await navigator.joinAdInterestGroup(interestGroup, durationSeconds); + test.add_cleanup( + async () => { await navigator.leaveAdInterestGroup(interestGroup); }); +} + // Joins an interest group that, by default, is owned by the current frame's // origin, is named DEFAULT_INTEREST_GROUP_NAME, has a bidding script that // issues a bid of 9 with a renderURL of "https://not.checked.test/${uuid}", @@ -271,12 +280,33 @@ function createInterestGroupForOrigin(uuid, origin, // interest group. async function joinInterestGroup(test, uuid, interestGroupOverrides = {}, durationSeconds = 60) { - let interestGroup = createInterestGroupForOrigin(uuid, window.location.origin, - interestGroupOverrides); - - await navigator.joinAdInterestGroup(interestGroup, durationSeconds); - test.add_cleanup( - async () => { await navigator.leaveAdInterestGroup(interestGroup) }); + await joinInterestGroupWithoutDefaults( + test, createInterestGroupForOrigin( + uuid, window.location.origin, interestGroupOverrides), + durationSeconds); +} + +// Joins a negative interest group with the specified owner, name, and +// additionalBidKey. Because these are the only valid fields for a negative +// interest groups, this function doesn't expose an 'overrides' parameter. +// Adds cleanup command to `test` to leave the interest group when the test +// completes. +async function joinNegativeInterestGroup( + test, owner, name, additionalBidKey) { + let interestGroup = { + owner: owner, + name: name, + additionalBidKey: additionalBidKey + }; + if (owner !== window.location.origin) { + let iframe = await createIframe(test, owner, 'join-ad-interest-group'); + await runInFrame( + test, iframe, + `await joinInterestGroupWithoutDefaults(` + + `test_instance, ${JSON.stringify(interestGroup)})`); + } else { + await joinInterestGroupWithoutDefaults(test_instance, interestGroup); + } } // Similar to joinInterestGroup, but leaves the interest group instead. @@ -487,6 +517,17 @@ async function runReportTest(test, uuid, codeToInsert, expectedReportURLs, await waitForObservedRequests(uuid, expectedReportURLs); } +// Helper function for running a standard test of the additional bid and +// negative targeting features. This helper verifies that the auction produces a +// winner. It takes the following arguments: +// - test/uuid: the test object and uuid from the test case (see generateUuid) +// - buyers: array of strings, each a domain for a buyer participating in this +// auction +// - actionNonce: string, the auction nonce for this auction, typically +// retrieved from a prior call to navigator.createAuctionNonce +// - highestScoringOtherBid: the amount of the second-highest bid, +// or zero if there's no second-highest bid +// - winningAdditionalBidId: the label of the winning bid async function runAdditionalBidTest(test, uuid, buyers, auctionNonce, additionalBidsPromise, highestScoringOtherBid, @@ -516,7 +557,7 @@ async function runInFrame(test, child_window, script, param) { let promise = new Promise(function(resolve, reject) { function WaitForMessage(event) { - if (event.data.messageUuid != messageUuid) + if (event.data.messageUuid !== messageUuid) return; receivedResponse = event.data; if (event.data.result === 'success') { @@ -548,7 +589,7 @@ async function createFrame(test, origin, is_iframe = true, permissions = null) { `${origin}${RESOURCE_PATH}subordinate-frame.sub.html?uuid=${frameUuid}`; let promise = new Promise(function(resolve, reject) { function WaitForMessage(event) { - if (event.data.messageUuid != frameUuid) + if (event.data.messageUuid !== frameUuid) return; if (event.data.result === 'load complete') { resolve(); @@ -662,79 +703,130 @@ function directFromSellerSignalsValidatorCode(uuid, expectedSellerSignals, return { // Seller worklets scoreAd: - `if (directFromSellerSignals === null || + `if (directFromSellerSignals == null || directFromSellerSignals.sellerSignals !== ${expectedSellerSignals} || directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} || - Object.keys(directFromSellerSignals).length != 2) { + Object.keys(directFromSellerSignals).length !== 2) { throw 'Failed to get expected directFromSellerSignals in scoreAd(): ' + JSON.stringify(directFromSellerSignals); }`, reportResultSuccessCondition: - `directFromSellerSignals !== null && + `directFromSellerSignals != null && directFromSellerSignals.sellerSignals === ${expectedSellerSignals} && directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} && - Object.keys(directFromSellerSignals).length == 2`, + Object.keys(directFromSellerSignals).length === 2`, reportResult: `sendReportTo("${createSellerReportURL(uuid)}");`, // Bidder worklets generateBid: - `if (directFromSellerSignals === null || + `if (directFromSellerSignals == null || directFromSellerSignals.perBuyerSignals !== ${expectedPerBuyerSignals} || directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} || - Object.keys(directFromSellerSignals).length != 2) { + Object.keys(directFromSellerSignals).length !== 2) { throw 'Failed to get expected directFromSellerSignals in generateBid(): ' + JSON.stringify(directFromSellerSignals); }`, reportWinSuccessCondition: - `directFromSellerSignals !== null && + `directFromSellerSignals != null && directFromSellerSignals.perBuyerSignals === ${expectedPerBuyerSignals} && directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} && - Object.keys(directFromSellerSignals).length == 2`, + Object.keys(directFromSellerSignals).length === 2`, reportWin: `sendReportTo("${createBidderReportURL(uuid)}");`, }; } -// Creates an additional bid with the given parameters. This additional bid -// specifies a biddingLogicURL that provides an implementation of -// reportAdditionalBidWin that triggers a sendReportTo() to the bidder report -// URL of the winning additional bid. Additional bids are described in more -// detail at -// https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids. -function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount, - additionalBidOverrides = {}) { - return { - interestGroup: { - name: interestGroupName, - biddingLogicURL: createBiddingScriptURL( - { - origin: buyer, - reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");` - }), - owner: buyer - }, - bid: { - ad: ['metadata'], - bid: bidAmount, - render: createRenderURL(uuid) - }, - auctionNonce: auctionNonce, - seller: seller, - ...additionalBidOverrides +let additionalBidHelper = function() { + // Creates an additional bid with the given parameters. This additional bid + // specifies a biddingLogicURL that provides an implementation of + // reportAdditionalBidWin that triggers a sendReportTo() to the bidder report + // URL of the winning additional bid. Additional bids are described in more + // detail at + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids. + function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount, + additionalBidOverrides = {}) { + return { + interestGroup: { + name: interestGroupName, + biddingLogicURL: createBiddingScriptURL( + { + origin: buyer, + reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");` + }), + owner: buyer + }, + bid: { + ad: ['metadata'], + bid: bidAmount, + render: createRenderURL(uuid) + }, + auctionNonce: auctionNonce, + seller: seller, + ...additionalBidOverrides + }; } -} -// Fetch some number of additional bid from a seller and verify that the -// 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context. -// The `additionalBids` parameter is a list of additional bids. -async function fetchAdditionalBids(seller, additionalBids) { - const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`); - url.searchParams.append('additionalBids', JSON.stringify(additionalBids)); - const response = await fetch(url.href, {adAuctionHeaders: true}); + // Gets the testMetadata for an additional bid, initializing it if needed. + function getAndMaybeInitializeTestMetadata(additionalBid) { + if (additionalBid.testMetadata === undefined) { + additionalBid.testMetadata = {}; + } + return additionalBid.testMetadata; + } - assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text()); - assert_false( - response.headers.has('Ad-Auction-Additional-Bid'), - 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.'); -} + // Tells the additional bid endpoint to correctly sign the additional bid with + // the given secret keys before returning that as a signed additional bid. + function signWithSecretKeys(additionalBid, secretKeys) { + getAndMaybeInitializeTestMetadata(additionalBid). + secretKeysForValidSignatures = secretKeys; + } + + // Tells the additional bid endpoint to incorrectly sign the additional bid with + // the given secret keys before returning that as a signed additional bid. This + // is used for testing the behavior when the auction encounters an invalid + // signature. + function incorrectlySignWithSecretKeys(additionalBid, secretKeys) { + getAndMaybeInitializeTestMetadata(additionalBid). + secretKeysForInvalidSignatures = secretKeys; + } + + // Adds a single negative interest group to an additional bid, as described at: + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups + function addNegativeInterestGroup(additionalBid, negativeInterestGroup) { + additionalBid["negativeInterestGroup"] = negativeInterestGroup; + } + + // Adds multiple negative interest groups to an additional bid, as described at: + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups + function addNegativeInterestGroups(additionalBid, negativeInterestGroups, + joiningOrigin) { + additionalBid["negativeInterestGroups"] = { + joiningOrigin: joiningOrigin, + interestGroupNames: negativeInterestGroups + }; + } + + // Fetch some number of additional bid from a seller and verify that the + // 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context. + // The `additionalBids` parameter is a list of additional bids. + async function fetchAdditionalBids(seller, additionalBids) { + const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`); + url.searchParams.append('additionalBids', JSON.stringify(additionalBids)); + const response = await fetch(url.href, {adAuctionHeaders: true}); + + assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text()); + assert_false( + response.headers.has('Ad-Auction-Additional-Bid'), + 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.'); + } + + return { + createAdditionalBid: createAdditionalBid, + signWithSecretKeys: signWithSecretKeys, + incorrectlySignWithSecretKeys: incorrectlySignWithSecretKeys, + addNegativeInterestGroup: addNegativeInterestGroup, + addNegativeInterestGroups: addNegativeInterestGroups, + fetchAdditionalBids: fetchAdditionalBids + }; +}(); diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py index 45bede2c45..f9ca9031f1 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py +++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py @@ -110,6 +110,8 @@ def main(request, response): value = request.GET.first(b"slotSize", b"not-found").decode("ASCII") elif key == "allSlotsRequestedSizes": value = request.GET.first(b"allSlotsRequestedSizes", b"not-found").decode("ASCII") + elif key == "url": + value = request.url responseBody["keys"][key] = value if "data-version" in interestGroupNames: diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py index eccef5e762..ce53e76295 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py +++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py @@ -122,6 +122,8 @@ def main(request, response): value = request.GET.first(b"hostname", b"not-found").decode("ASCII") elif signalsParam == "headers": value = fledge_http_server_util.headers_to_ascii(request.headers) + elif signalsParam == "url": + value = request.url if addValue: if urlList["type"] not in responseBody: responseBody[urlList["type"]] = {} diff --git a/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js b/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js index 2147a026ae..0bac1b99a9 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js +++ b/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js @@ -11,10 +11,10 @@ function deepEquals(a, b) { return a === b; let aKeys = Object.keys(a); - if (aKeys.length != Object.keys(b).length) + if (aKeys.length !== Object.keys(b).length) return false; for (let key of aKeys) { - if (a.hasOwnProperty(key) != b.hasOwnProperty(key) || + if (a.hasOwnProperty(key) !== b.hasOwnProperty(key) || !deepEquals(a[key], b[key])) { return false; } |