summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fledge
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/fledge')
-rw-r--r--testing/web-platform/tests/fledge/tentative/TODO2
-rw-r--r--testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js267
-rw-r--r--testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js23
-rw-r--r--testing/web-platform/tests/fledge/tentative/auction-config.https.window.js64
-rw-r--r--testing/web-platform/tests/fledge/tentative/component-ads.https.window.js4
-rw-r--r--testing/web-platform/tests/fledge/tentative/component-auction.https.window.js4
-rw-r--r--testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/currency.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js52
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/additional-bids.py64
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/ed25519.py289
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js210
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py2
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py2
-rw-r--r--testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js4
-rw-r--r--testing/web-platform/tests/fledge/tentative/tie.https.window.js2
-rw-r--r--testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js156
-rw-r--r--testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js151
19 files changed, 1198 insertions, 104 deletions
diff --git a/testing/web-platform/tests/fledge/tentative/TODO b/testing/web-platform/tests/fledge/tentative/TODO
index 0f68a7c914..6fd378c035 100644
--- a/testing/web-platform/tests/fledge/tentative/TODO
+++ b/testing/web-platform/tests/fledge/tentative/TODO
@@ -47,6 +47,7 @@ Need tests for (likely not a complete list):
* adAuctionConfig passed to reportResult().
* Component auctions.
* Including cross-origin sellers.
+ * Timeouts (seller timeout, buyer timeout, reporting timeout).
* browserSignals fields in scoring/bidding methods.
* In reporting methods, browserSignals fields: topLevelSeller,
componentSeller, modifiedBid, adCost, madeHighestScoringOtherBid
@@ -88,3 +89,4 @@ If possible:
* Signals request batching. This is an optional feature, so can't require it,
but maybe a test where batching could be used, and make sure things work,
whether batching is used or not?
+* reporting timeout being 0.
diff --git a/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js
index 0e1d22c261..965f9a60c7 100644
--- a/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js
@@ -10,15 +10,8 @@
// This file contains tests for additional bids and negative targeting.
//
// TODO:
-// - test that an negatively targeted additional bid is suppressed.
-// - test that an incorrectly signed additional bid is not negative targeted.
-// - test that an missing-signature additional bid is not negative targeted.
-// - test that an additional bid with some correct signatures can be negative.
-// negative targeted for those negative interest groups whose signatures
-// match.
-// - test an additional bid with multiple negative interest groups.
-// - test that multiple negative interest groups with mismatched joining origins
-// is not negative targeted.
+// - test that an additional bid with some correct signatures can be negative
+// targeted for those negative interest groups whose signatures match.
// - test that additional bids can be fetched using an iframe navigation.
// - test that additional bids are not fetched using an iframe navigation for
// which the `adAuctionHeaders=true` attribute is not specified.
@@ -26,12 +19,19 @@
// `adAuctionHeaders: true` is not specified.
// - test that an additional bid with an incorrect auction nonce is not used
// included in an auction. Same for seller and top-level seller.
+// - lots of tests for different types of malformed additional bids, e.g.
+// missing fields, malformed signature, invalid currency code,
+// missing joining origin for multiple negative interest groups, etc.
// - test that correctly formatted additional bids are included in an auction
// when fetched alongside malformed additional bid headers by a Fetch
-// request.
+// request (both invalid headers and invalid additional bids)
+// - test that an additional bid is rejected if its from a buyer who is not
+// allowed to participate in the auction.
+// - test that an additional bid is rejected if its currency doesn't match the
+// buyer's associated per-buyer currency from the auction config.
// - test that correctly formatted additional bids are included in an auction
// when fetched alongside malformed additional bid headers by an iframe
-// navigation.
+// navigation (both invalid headers and invalid additional bids).
// - test that reportWin is not used for reporting an additional bid win.
// - test that additional bids can *not* be fetched from iframe subresource
// requests.
@@ -80,6 +80,9 @@
// for ad auction headers interception to associate it with this auction.
const SINGLE_SELLER_AUCTION_SELLER = window.location.origin;
+const ADDITIONAL_BID_SECRET_KEY = 'nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A=';
+const ADDITIONAL_BID_PUBLIC_KEY = '11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=';
+
// Single-seller auction with a single buyer who places a single additional
// bid. As the only bid, this wins.
subsetTest(promise_test, async test => {
@@ -88,12 +91,12 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer = OTHER_ORIGIN1;
- const additionalBid = createAdditionalBid(
+ const additionalBid = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer, 'horses', 1.99);
await runAdditionalBidTest(
test, uuid, [buyer], auctionNonce,
- fetchAdditionalBids(seller, [additionalBid]),
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]),
/*highestScoringOtherBid=*/0,
/*winningAdditionalBidId=*/'horses');
}, 'single valid additional bid');
@@ -105,16 +108,17 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer1 = OTHER_ORIGIN1;
- const additionalBid1 = createAdditionalBid(
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
const buyer2 = OTHER_ORIGIN2;
- const additionalBid2 = createAdditionalBid(
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
await runAdditionalBidTest(
test, uuid, [buyer1, buyer2], auctionNonce,
- fetchAdditionalBids(seller, [additionalBid1, additionalBid2]),
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
/*highestScoringOtherBid=*/1.99,
/*winningAdditionalBidId=*/'planes');
}, 'two valid additional bids');
@@ -127,20 +131,241 @@ subsetTest(promise_test, async test => {
const seller = SINGLE_SELLER_AUCTION_SELLER;
const buyer1 = OTHER_ORIGIN1;
- const additionalBid1 = createAdditionalBid(
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
const buyer2 = OTHER_ORIGIN2;
- const additionalBid2 = createAdditionalBid(
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
-
await runAdditionalBidTest(
test, uuid, [buyer1, buyer2], auctionNonce,
Promise.all([
- fetchAdditionalBids(seller, [additionalBid1]),
- fetchAdditionalBids(seller, [additionalBid2])
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid1]),
+ additionalBidHelper.fetchAdditionalBids(seller, [additionalBid2])
]),
/*highestScoringOtherBid=*/1.99,
/*winningAdditionalBidId=*/'planes');
}, 'two valid additional bids from two distinct Fetch requests');
+
+// Single-seller auction with a single additional bid. Because this additional
+// bid is filtered by negative targeting, this auction has no winner.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer = OTHER_ORIGIN1;
+ const additionalBid = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid, negativeInterestGroupName);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runBasicFledgeTestExpectingNoWinner(
+ test, uuid,
+ { interestGroupBuyers: [buyer],
+ auctionNonce: auctionNonce,
+ additionalBids: additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid])});
+}, 'one additional bid filtered by negative targeting, so auction has no ' +
+ 'winner');
+
+// Single-seller auction with a two buyers competing with additional bids.
+// The higher of these has a negative interest group specified, and that
+// negative interest group has been joined, so the lower bid wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/0,
+ /*winningAdditionalBidId=*/'horses');
+}, 'higher additional bid is filtered by negative targeting, so ' +
+ 'lower additional bid win');
+
+// Same as above, except that the bid is missing a signature, so that the
+// negative targeting interest group is ignored, and the higher bid, which
+// would have otherwise been filtered by negative targeting, wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting, but it is ' +
+ 'missing a signature, so it still wins');
+
+// Same as above, except that the bid is signed incorrectly, so that the
+// negative targeting interest group is ignored, and the higher bid, which
+// would have otherwise been filtered by negative targeting, wins.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName = 'already-owns-a-plane';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroup(
+ additionalBid2, negativeInterestGroupName);
+ additionalBidHelper.incorrectlySignWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting, but it has an ' +
+ 'invalid signature, so it still wins');
+
+// A test of an additional bid with multiple negative interest groups.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName1 = 'already-owns-a-plane';
+ const negativeInterestGroupName2 = 'another-negative-interest-group';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroups(
+ additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
+ /*joiningOrigin=*/window.location.origin);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/0,
+ /*winningAdditionalBidId=*/'horses');
+}, 'higher additional bid is filtered by negative targeting by two negative ' +
+ 'interest groups, and since one is on the device, the lower bid wins');
+
+// Same as above, but with a mismatched joining origin.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const negativeInterestGroupName1 = 'already-owns-a-plane';
+ const negativeInterestGroupName2 = 'another-negative-interest-group';
+
+ const buyer1 = OTHER_ORIGIN1;
+ const additionalBid1 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer1, 'horses', 1.99);
+
+ const buyer2 = OTHER_ORIGIN2;
+ const additionalBid2 = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer2, 'planes', 2.99);
+ additionalBidHelper.addNegativeInterestGroups(
+ additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2],
+ /*joiningOrigin=*/OTHER_ORIGIN1);
+ additionalBidHelper.signWithSecretKeys(
+ additionalBid2, [ADDITIONAL_BID_SECRET_KEY]);
+
+ await joinNegativeInterestGroup(
+ test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY);
+
+ await runAdditionalBidTest(
+ test, uuid, [buyer1, buyer2], auctionNonce,
+ additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid1, additionalBid2]),
+ /*highestScoringOtherBid=*/1.99,
+ /*winningAdditionalBidId=*/'planes');
+}, 'higher additional bid is filtered by negative targeting by two negative ' +
+ 'interest groups, but because of a joining origin mismatch, it still wins');
+
+// Ensure that trusted seller signals are retrieved for additional bids.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const auctionNonce = await navigator.createAuctionNonce();
+ const seller = SINGLE_SELLER_AUCTION_SELLER;
+
+ const buyer = OTHER_ORIGIN1;
+ const additionalBid = additionalBidHelper.createAdditionalBid(
+ uuid, auctionNonce, seller, buyer, 'horses', 1.99);
+
+ let renderURL = createRenderURL(uuid);
+ await runBasicFledgeTestExpectingWinner(
+ test, uuid,
+ { interestGroupBuyers: [buyer],
+ auctionNonce: auctionNonce,
+ additionalBids: additionalBidHelper.fetchAdditionalBids(
+ seller, [additionalBid]),
+ decisionLogicURL: createDecisionScriptURL(
+ uuid,
+ { scoreAd:
+ `if(!"${renderURL}" in trustedScoringSignals.renderURL) ` +
+ 'throw "missing trusted signals";'}),
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL});
+}, 'trusted seller signals retrieved for additional bids');
diff --git a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
index c78a27bb87..9b12d077ba 100644
--- a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js
@@ -6,7 +6,8 @@
// META: variant=?1-5
// META: variant=?6-10
// META: variant=?11-15
-// META: variant=?16-last
+// META: variant=?16-20
+// META: variant=?21-last
"use strict;"
@@ -206,3 +207,23 @@ makeTest({
[{width: ' 100', height: '200.50px '},
{width: ' 70.00sh ', height: '80.50sw'}]}
});
+
+makeTest({
+ name: 'AuctionConfig.reportingTimeout with positive within-cap value.',
+ fieldName: 'reportingTimeout',
+ fieldValue: 100,
+});
+
+makeTest({
+ name: 'AuctionConfig.reportingTimeout above the cap value.',
+ fieldName: 'reportingTimeout',
+ fieldValue: 5000,
+ auctionConfigOverrides: {fieldValue: 1234567890}
+});
+
+makeTest({
+ name: 'AuctionConfig.reportingTimeout not provided',
+ fieldName: 'reportingTimeout',
+ fieldValue: 50,
+ auctionConfigOverrides: {fieldValue: undefined}
+}); \ No newline at end of file
diff --git a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
index 8fbdc95dfc..5fa4fa252f 100644
--- a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js
@@ -10,7 +10,9 @@
// META: variant=?21-25
// META: variant=?26-30
// META: variant=?31-35
-// META: variant=?36-last
+// META: variant=?36-40
+// META: variant=?40-45
+// META: variant=?46-last
"use strict;"
@@ -86,6 +88,14 @@ const EXPECT_NO_WINNER = auctionResult => {
assert_equals(auctionResult, null, 'Auction unexpected had a winner');
};
+// Expect a winner (FencedFrameConfig).
+const EXPECT_WINNER =
+ auctionResult => {
+ assert_true(
+ auctionResult instanceof FencedFrameConfig,
+ 'Auction did not return expected FencedFrameConfig');
+ }
+
// Expect an exception of the given type.
const EXPECT_EXCEPTION = exceptionType => auctionResult => {
assert_not_equals(auctionResult, null, "got null instead of expected error");
@@ -130,6 +140,50 @@ makeTest({
});
makeTest({
+ name: 'valid trustedScoringSignalsURL',
+ expect: EXPECT_WINNER,
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have a fragment',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json#foo'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL with an empty fragment is not OK',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json#'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have a query',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json?foo'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL with an empty query is not OK',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides:
+ {trustedScoringSignalsURL: window.location.origin + '/resource.json?'}
+});
+
+makeTest({
+ name: 'trustedScoringSignalsURL should not have embedded credentials',
+ expect: EXPECT_EXCEPTION(TypeError),
+ auctionConfigOverrides: {
+ trustedScoringSignalsURL: (window.location.origin + '/resource.json')
+ .replace('https://', 'https://user:pass@')
+ }
+});
+
+makeTest({
name: 'trustedScoringSignalsURL is cross-origin with seller',
expect: EXPECT_EXCEPTION(TypeError),
auctionConfigOverrides: { trustedScoringSignalsURL: "https://example.com" },
@@ -420,9 +474,9 @@ subsetTest(promise_test, async test => {
let bid = browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
return {bid: bid, render: '${renderURL}'};`,
reportWin: `
- if (browserSignals.bid == 1)
+ if (browserSignals.bid === 1)
sendReportTo('${bidderReportURL1}');
- if (browserSignals.bid == 2)
+ if (browserSignals.bid === 2)
sendReportTo('${bidderReportURL2}');`
})
@@ -443,9 +497,9 @@ subsetTest(promise_test, async test => {
browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2;
return {desirability: desirability};`,
reportResult: `
- if (browserSignals.desirability == 1)
+ if (browserSignals.desirability === 1)
sendReportTo('${sellerReportURL1}');
- if (browserSignals.desirability == 2)
+ if (browserSignals.desirability === 2)
sendReportTo('${sellerReportURL2}');`
})
};
diff --git a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
index 7e98570b9e..6b22585d57 100644
--- a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js
@@ -61,7 +61,7 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr
`// "status" is passed to the beacon URL, to be verified by waitForObservedRequests().
let status = "ok";
const componentAds = window.fence.getNestedConfigs()
- if (componentAds.length != 40)
+ if (componentAds.length !== 40)
status = "unexpected getNestedConfigs() length";
for (let i of ${JSON.stringify(componentAdsToLoad)}) {
let fencedFrame = document.createElement("fencedframe");
@@ -144,7 +144,7 @@ subsetTest(promise_test, async test => {
const nestedConfigsLength = window.fence.getNestedConfigs().length
// "getNestedConfigs()" should return a list of 40 configs, to avoid leaking
// whether there were any component URLs to the page.
- if (nestedConfigsLength != 40)
+ if (nestedConfigsLength !== 40)
status = "unexpected getNestedConfigs() length: " + nestedConfigsLength;
window.fence.reportEvent({eventType: "beacon",
eventData: status,
diff --git a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
index c70532024c..015c20a5c2 100644
--- a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js
@@ -685,7 +685,7 @@ subsetTest(promise_test, async test => {
auctionConfig.componentAuctions[0].decisionLogicURL =
createDecisionScriptURL(
uuid,
- { scoreAd: `if (browserSignals.renderURL != '${renderURL1}')
+ { scoreAd: `if (browserSignals.renderURL !== '${renderURL1}')
throw 'Wrong ad';`,
reportResult: `sendReportTo('${seller1ReportURL}');`}
);
@@ -696,7 +696,7 @@ subsetTest(promise_test, async test => {
decisionLogicURL: createDecisionScriptURL(
uuid,
{ origin: OTHER_ORIGIN1,
- scoreAd: `if (browserSignals.renderURL != '${renderURL2}')
+ scoreAd: `if (browserSignals.renderURL !== '${renderURL2}')
throw 'Wrong ad';`,
reportResult: `sendReportTo('${seller2ReportURL}');`}
)
diff --git a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
index a8cf93049f..eed74c522f 100644
--- a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js
@@ -356,7 +356,7 @@ subsetTest(promise_test, async test => {
throw "Wrong origin: " + interestGroup.owner;
if (!interestGroup.biddingLogicURL.startsWith("${bidderOrigin}"))
throw "Wrong origin: " + interestGroup.biddingLogicURL;
- if (interestGroup.ads[0].renderURL != "${renderURL}")
+ if (interestGroup.ads[0].renderURL !== "${renderURL}")
throw "Wrong renderURL: " + interestGroup.ads[0].renderURL;
if (browserSignals.seller !== "${sellerOrigin}")
throw "Wrong origin: " + browserSignals.seller;`,
diff --git a/testing/web-platform/tests/fledge/tentative/currency.https.window.js b/testing/web-platform/tests/fledge/tentative/currency.https.window.js
index 9a33d12148..99943cecbf 100644
--- a/testing/web-platform/tests/fledge/tentative/currency.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/currency.https.window.js
@@ -501,7 +501,7 @@ subsetTest(promise_test, async test => {
topLevelSellerScriptParamsOverride: {
scoreAd: `
// scoreAd sees what's actually passed in.
- if (bid != 9)
+ if (bid !== 9)
throw 'Wrong bid';
if (browserSignals.bidCurrency !== '???')
throw 'Wrong currency';`
diff --git a/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
index 8687e3f296..c7078ae08a 100644
--- a/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js
@@ -939,7 +939,7 @@ subsetTest(promise_test, async test => {
if (!deepEquals(Object.keys(instance.exports), ["increment"]))
throw "Unexpected exports: " + JSON.stringify(instance.exports);
- if (instance.exports.increment(1) != 2)
+ if (instance.exports.increment(1) !== 2)
throw "Unexpected increment result: " + instance.exports.increment(1);` })
}
});
diff --git a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
index c7c7120240..db6ef2d35a 100644
--- a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js
@@ -259,7 +259,7 @@ subsetTest(promise_test, async test => {
// reportResultSuccessCondition:
`browserSignals.interestGroupName === undefined`,
// reportWinSuccessCondition:
- `browserSignals.interestGroupName === ''`
+ `browserSignals.interestGroupName === 'default name'`
);
}, 'browserSignals.interestGroupName test.');
@@ -303,3 +303,53 @@ await runReportArgumentValidationTest(
uuid
);
}, 'browserSignals.madeHighestScoringOtherBid with other bid.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runReportTest(
+ test, uuid,
+ { reportResultSuccessCondition:
+ `browserSignals.reportingTimeout === undefined`,
+ reportResult:
+ `sendReportTo('${createSellerReportURL(uuid)}');`,
+ reportWinSuccessCondition:
+ 'browserSignals.reportingTimeout === 100',
+ reportWin:
+ `sendReportTo('${createBidderReportURL(uuid)}');` },
+ // expectedReportURLs:
+ [createSellerReportURL(uuid), createBidderReportURL(uuid)],
+ // renderURLOverride
+ null,
+ // auctionConfigOverrides
+ {reportingTimeout: 100});
+}, 'browserSignals.reportingTimeout with custom value from auction config.');
+
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ await runReportTest(
+ test, uuid,
+ { reportResultSuccessCondition:
+ `browserSignals.reportingTimeout === undefined`,
+ reportResult:
+ `sendReportTo('${createSellerReportURL(uuid)}');`,
+ reportWinSuccessCondition:
+ 'browserSignals.reportingTimeout === 5000',
+ reportWin:
+ `sendReportTo('${createBidderReportURL(uuid)}');` },
+ // expectedReportURLs:
+ [createSellerReportURL(uuid), createBidderReportURL(uuid)],
+ // renderURLOverride
+ null,
+ // auctionConfigOverrides
+ {reportingTimeout: 1234567890});
+}, 'browserSignals.reportingTimeout above the cap value.');
+
+subsetTest(promise_test, async test => {
+ await runReportArgumentValidationTest(
+ test,
+ // reportResultSuccessCondition:
+ `browserSignals.reportingTimeout === undefined`,
+ // reportWinSuccessCondition:
+ `browserSignals.reportingTimeout === 50`
+ );
+}, 'browserSignals.reportingTimeout default value.');
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;
}
diff --git a/testing/web-platform/tests/fledge/tentative/tie.https.window.js b/testing/web-platform/tests/fledge/tentative/tie.https.window.js
index 48d6e95e5c..c87d10f201 100644
--- a/testing/web-platform/tests/fledge/tentative/tie.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/tie.https.window.js
@@ -98,7 +98,7 @@ promise_test(async test => {
auctionConfigOverrides.decisionLogicURL =
createDecisionScriptURL(
uuid,
- {scoreAd: `if (browserSignals.renderURL == "${winningAdURL}")
+ {scoreAd: `if (browserSignals.renderURL === "${winningAdURL}")
return 0;`});
// Add an abort controller, so can cancel extra auctions.
diff --git a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
index 9799af6ac1..d0b9a82086 100644
--- a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js
@@ -16,7 +16,8 @@
// META: variant=?51-55
// META: variant=?56-60
// META: variant=?61-65
-// META: variant=?66-last
+// META: variant=?66-70
+// META: variant=?71-last
"use strict";
@@ -785,3 +786,156 @@ subsetTest(promise_test, async test => {
auctionConfigOverrides,
uuid);
}, 'all-slots-requested-sizes trustedBiddingSignalsSlotSizeMode in a component auction');
+
+/////////////////////////////////////////////////////////////////////////////
+// maxTrustedBiddingSignalsURLLength tests
+/////////////////////////////////////////////////////////////////////////////
+
+// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to 0,
+// which means infinite length limit.
+// In the following three tests, the generated request URL contains approximately 166 characters.
+// The target of the tests is primarily to make sure all the signals are fetched with the full URL.
+subsetTest(promise_test, async test => {
+ const name = 'group';
+ await runTrustedBiddingSignalsTest(
+ test,
+ // Check the URL length is within an approximate range to ensure the URL is not truncated.
+ ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' &&
+ trustedBiddingSignals["url"].length > 150 &&
+ trustedBiddingSignals["url"].length < 180 `,
+ {
+ name: name,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 0
+ });
+}, 'Trusted bidding signals request works with a URL length limit set to 0.');
+
+// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to
+// a non-zero value smaller than the length of the request URL. It also tests that multiple
+// bidding keys from the same interest group will not be separated even the full URL length is
+// larger than the limit.
+subsetTest(promise_test, async test => {
+ const name = 'group';
+ await runTrustedBiddingSignalsTest(
+ test,
+ ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' &&
+ trustedBiddingSignals["url"].length > 150 &&
+ trustedBiddingSignals["url"].length < 180 `,
+ {
+ name: name,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 1
+ });
+}, 'Trusted bidding signals request works with a URL length limit smaller than the URL length.');
+
+// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to
+// a value larger than the length of the request URL.
+subsetTest(promise_test, async test => {
+ const name = 'group';
+ await runTrustedBiddingSignalsTest(
+ test,
+ ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' &&
+ trustedBiddingSignals["url"].length < 180 `,
+ {
+ name: name,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 1000
+ });
+}, 'Trusted bidding signals request works with a URL length limit larger than the URL length.');
+
+// Test whether an oversized trusted bidding signals request URL, generated from two interest
+// groups, will be split into two parts when `maxTrustedBiddingSignalsURLLength` is set to a
+// value larger than a single URL length and smaller than the combined URL length. A request
+// URL from a single interest group contains about 188 characters, while a request URL from
+// two interest groups contains about 216 characters. Note that this test can only verifies
+// the fetch status of the winner's trusted bidding signal, which is the second interest
+// group. We consider the request to be split if the URL length check passes for the second
+// interest group.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const name1 = 'extraordinarilyLongNameGroup1';
+ const name2 = 'extraordinarilyLongNameGroup2';
+
+ await Promise.all(
+ [ joinInterestGroup(
+ test, uuid,
+ {
+ name: name1,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 200,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ // Return 0 as bid to force the second interest group to win. This interest group
+ // is considered as fetching trusted bidding signals by itself if the winner's
+ // URL length passes the limit check.
+ generateBid:
+ `return { bid: 0, render: interestGroup.ads[0].renderURL };`
+ })
+ }),
+ joinInterestGroup(
+ test, uuid,
+ {
+ name: name2,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 200,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ generateBid:
+ `if (trustedBiddingSignals["interest-group-names"] !== '["${name2}"]' ||
+ trustedBiddingSignals["url"].length > 200) {
+ throw "unexpected trustedBiddingSignals";
+ }
+ return { bid: 10, render: interestGroup.ads[0].renderURL };`})
+ })
+ ]
+ );
+ runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of regular value.');
+
+// Test whether an oversized trusted bidding signals request URL, generated from two interest
+// groups, will be split into two parts when `maxTrustedBiddingSignalsURLLength` is set to a
+// value smaller than a single URL length.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const name1 = 'extraordinaryLongNameGroup1';
+ const name2 = 'extraordinaryLongNameGroup2';
+
+ await Promise.all(
+ [ joinInterestGroup(
+ test, uuid,
+ {
+ name: name1,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 1,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ generateBid:
+ `return { bid: 0, render: interestGroup.ads[0].renderURL };`
+ })
+ }),
+ joinInterestGroup(
+ test, uuid,
+ {
+ name: name2,
+ trustedBiddingSignalsKeys: ['interest-group-names', 'url'],
+ trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
+ maxTrustedBiddingSignalsURLLength: 1,
+ biddingLogicURL: createBiddingScriptURL(
+ {
+ generateBid:
+ `if (trustedBiddingSignals["interest-group-names"] !== '["${name2}"]' ||
+ trustedBiddingSignals["url"].length > 200) {
+ throw "unexpected trustedBiddingSignals";
+ }
+ return { bid: 10, render: interestGroup.ads[0].renderURL };`})
+ })
+ ]
+ );
+ runBasicFledgeTestExpectingWinner(test, uuid);
+}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of small value.'); \ No newline at end of file
diff --git a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
index 4de5cfc0f3..105b09fb3b 100644
--- a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
+++ b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js
@@ -11,7 +11,8 @@
// META: variant=?26-30
// META: variant=?31-35
// META: variant=?36-40
-// META: variant=?41-last
+// META: variant=?41-45
+// META: variant=?45-last
"use strict";
@@ -510,3 +511,151 @@ subsetTest(promise_test, async test => {
})
});
}, 'Component ads trusted scoring signals.');
+
+/////////////////////////////////////////////////////////////////////////////
+// maxTrustedBiddingSignalsURLLength tests
+/////////////////////////////////////////////////////////////////////////////
+
+// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to 0.
+// In the following three tests, the generated request URL contains approximately 294 characters.
+// The target of the tests is primarily to make sure the signals were fetched with the full URL.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderURL(uuid, /*script=*/null, 'url');
+ const interestGroupOverrides = { ads: [{ renderURL: renderURL }] };
+ const auctionConfigOverrides = {
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
+ maxTrustedScoringSignalsURLLength: 0,
+ decisionLogicURL:
+ createDecisionScriptURL(uuid, {
+ // Check the URL length is within an approximate range to ensure the URL is not truncated.
+ scoreAd:
+ `if (trustedScoringSignals.renderURL["${renderURL}"].length < 280 ||
+ trustedScoringSignals.renderURL["${renderURL}"].length > 300)
+ throw "error";`
+ })
+ };
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ {
+ uuid: uuid,
+ interestGroupOverrides: interestGroupOverrides,
+ auctionConfigOverrides: auctionConfigOverrides
+ });
+}, 'Trusted scoring signals request works with a URL length limit set to 0.');
+
+// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to
+// a non-zero value smaller than the length of the request URL.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderURL(uuid, /*script=*/null, 'url');
+ const interestGroupOverrides = { ads: [{ renderURL: renderURL }] };
+ const auctionConfigOverrides = {
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
+ maxTrustedScoringSignalsURLLength: 1,
+ decisionLogicURL:
+ createDecisionScriptURL(uuid, {
+ // Check the URL length is within an approximate range to ensure the URL is not truncated.
+ scoreAd:
+ `if (trustedScoringSignals.renderURL["${renderURL}"].length < 280 ||
+ trustedScoringSignals.renderURL["${renderURL}"].length > 300)
+ throw "error";`
+ })
+ };
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ {
+ uuid: uuid,
+ interestGroupOverrides: interestGroupOverrides,
+ auctionConfigOverrides: auctionConfigOverrides
+ });
+}, 'Trusted scoring signals request works with a URL length limit smaller than the URL length.');
+
+// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to
+// a value larger than the length of the request URL.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const renderURL = createRenderURL(uuid, /*script=*/null, 'url');
+ const interestGroupOverrides = { ads: [{ renderURL: renderURL }] };
+ const auctionConfigOverrides = {
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
+ maxTrustedScoringSignalsURLLength: 1000,
+ decisionLogicURL:
+ createDecisionScriptURL(uuid, {
+ scoreAd: `if (trustedScoringSignals.renderURL["${renderURL}"].length > 300) throw "error";`
+ })
+ };
+
+ await joinGroupAndRunBasicFledgeTestExpectingWinner(
+ test,
+ {
+ uuid: uuid,
+ interestGroupOverrides: interestGroupOverrides,
+ auctionConfigOverrides: auctionConfigOverrides
+ });
+}, 'Trusted scoring signals request works with a URL length limit larger than the URL length.');
+
+// Test whether an oversized trusted scoring signals request URL, generated from two interest
+// groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a
+// value larger than a single URL length and smaller than the combined URL length. A request
+// URL from a single interest group contains about 294 characters, while a request URL from
+// two interest groups contains about 466 characters.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const renderURL1 = createRenderURL(uuid, /*script=*/null, 'url,group1');
+ const renderURL2 = createRenderURL(uuid, /*script=*/null, 'url,group2');
+ const auctionConfigOverrides = {
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
+ maxTrustedScoringSignalsURLLength: 300,
+ decisionLogicURL:
+ createDecisionScriptURL(uuid, {
+ // This will make the auction reject `renderURL2`, and if `renderURL1` passes the URL
+ // length check, we consider `renderURL2` is fetched by itself in the trusted scoring
+ // signals request.
+ scoreAd:
+ `if (!trustedScoringSignals.renderURL.has("${renderURL1}") ||
+ trustedScoringSignals.renderURL.has("${renderURL2}") ||
+ trustedScoringSignals.renderURL["${renderURL1}"].length > 300) {
+ throw "error";
+ }`
+ })
+ };
+
+ await Promise.all(
+ [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }),
+ joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ]
+ );
+
+ runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
+}, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of regular value.');
+
+// Test whether an oversized trusted scoring signals request URL, generated from two interest
+// groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a
+// value smaller than a single URL length.
+subsetTest(promise_test, async test => {
+ const uuid = generateUuid(test);
+ const renderURL1 = createRenderURL(uuid, /*script=*/null, 'url,group1');
+ const renderURL2 = createRenderURL(uuid, /*script=*/null, 'url,group2');
+ const auctionConfigOverrides = {
+ trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
+ maxTrustedScoringSignalsURLLength: 1,
+ decisionLogicURL:
+ createDecisionScriptURL(uuid, {
+ scoreAd:
+ `if (!trustedScoringSignals.renderURL.has("${renderURL1}") ||
+ trustedScoringSignals.renderURL.has("${renderURL2}") ||
+ trustedScoringSignals.renderURL["${renderURL1}"].length > 300) {
+ throw "error";
+ }`
+ })
+ };
+
+ await Promise.all(
+ [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }),
+ joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ]
+ );
+
+ runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides);
+}, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of small value.'); \ No newline at end of file