// META: script=/resources/testdriver.js
// META: script=/common/utils.js
// META: script=resources/fledge-util.sub.js
// META: script=/common/subset-tests.js
// META: timeout=long
// META: variant=?1-5
// META: variant=?6-10
// META: variant=?11-15
// META: variant=?16-20
// META: variant=?21-25
// META: variant=?26-30
// META: variant=?31-35
// META: variant=?36-40
// META: variant=?41-45
// META: variant=?45-50
// META: variant=?50-last
"use strict";
// These tests focus on trustedScoringSignals: Requesting them, handling network
// errors, handling the renderURLs portion of the response, passing renderURLs
// to worklet scripts, and handling the Data-Version header.
// Helper for trusted scoring signals tests. Runs an auction with
// trustedSignalsURL and a single interest group, failing the test if there's no
// winner. "scoreAdCheck" is an expression that should be true
// when evaluated in scoreAd(). "renderURL" can be used to control the response
// given for trustedSignalsURL.
async function runTrustedScoringSignalsTest(test, uuid, renderURL, scoreAdCheck,
additionalInterestGroupOverrides,
trustedSignalsURL = TRUSTED_SCORING_SIGNALS_URL,
decisionScriptParamOverrides = {}) {
const auctionConfigOverrides = {
trustedScoringSignalsURL: trustedSignalsURL,
decisionLogicURL:
createDecisionScriptURL(uuid, {
scoreAd: `if (!(${scoreAdCheck})) throw "error";`,
...decisionScriptParamOverrides})};
await joinInterestGroup(test, uuid,
{ads: [{ renderURL: renderURL }],
...additionalInterestGroupOverrides});
return await runBasicFledgeTestExpectingWinner(
test, uuid, auctionConfigOverrides);
}
// Much like runTrustedScoringSignalsTest, but runs auctions through reporting
// as well, and evaluates `check` both in scodeAd() and reportResult(). Also
// makes sure browserSignals.dataVersion is undefined in generateBid() and
// reportWin().
async function runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL, check) {
const interestGroupOverrides = {
biddingLogicURL:
createBiddingScriptURL({
generateBid:
`if (browserSignals.dataVersion !== undefined)
throw "Bad browserSignals.dataVersion"`,
reportWin:
`if (browserSignals.dataVersion !== undefined)
sendReportTo('${createSellerReportURL(uuid, '1-error')}');
else
sendReportTo('${createSellerReportURL(uuid, '1')}');` }),
ads: [{ renderURL: renderURL }]
};
await joinInterestGroup(test, uuid, interestGroupOverrides);
const auctionConfigOverrides = {
decisionLogicURL: createDecisionScriptURL(
uuid,
{ scoreAd:
`if (!(${check})) return false;`,
reportResult:
`if (!(${check}))
sendReportTo('${createSellerReportURL(uuid, '2-error')}')
sendReportTo('${createSellerReportURL(uuid, '2')}')`,
}),
trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL
}
await runBasicFledgeAuctionAndNavigate(test, uuid, auctionConfigOverrides);
await waitForObservedRequests(
uuid, [createSellerReportURL(uuid, '1'), createSellerReportURL(uuid, '2')]);
}
// Creates a render URL that, when sent to the trusted-scoring-signals.py,
// results in a trusted scoring signals response with the provided response
// body.
function createScoringSignalsRenderURLWithBody(uuid, responseBody) {
return createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/`replace-body:${responseBody}`);
}
/////////////////////////////////////////////////////////////////////////////
// Tests where no renderURL value is received for the passed in renderURL.
/////////////////////////////////////////////////////////////////////////////
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const decisionLogicScriptURL = createDecisionScriptURL(
uuid,
{ scoreAd: 'if (trustedScoringSignals !== null) throw "error";' });
await joinGroupAndRunBasicFledgeTestExpectingWinner(
test,
{ uuid: uuid,
auctionConfigOverrides: { decisionLogicURL: decisionLogicScriptURL }
});
}, 'No trustedScoringSignalsURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'close-connection');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
'trustedScoringSignals === null');
}, 'Trusted scoring signals closes the connection without sending anything.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'http-error');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is HTTP 404 error.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-content-type');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has no content-type.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-content-type');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has wrong content-type.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'ad-auction-not-allowed');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response does not allow FLEDGE.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'bad-ad-auction-allowed');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has wrong Ad-Auction-Allowed header.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-ad-auction-allow');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has no Ad-Auction-Allowed header.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response has no body.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'Not JSON');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is not JSON.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'[]');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is a JSON array.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{JSON_keys_need_quotes: 1}');
await runTrustedScoringSignalsTest(test, uuid, renderURL, 'trustedScoringSignals === null');
}, 'Trusted scoring signals response is invalid JSON object.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === null`);
}, 'Trusted scoring signals response has no renderURL object.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'no-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === null`);
}, 'Trusted scoring signals response has no renderURLs.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-url');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === null &&
Object.keys(trustedScoringSignals.renderURL).length === 1`);
}, 'Trusted scoring signals response has renderURL not in response.');
/////////////////////////////////////////////////////////////////////////////
// Tests where renderURL value is received for the passed in renderURL.
/////////////////////////////////////////////////////////////////////////////
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'null-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === null`);
}, 'Trusted scoring signals response has null value for renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === 1`);
}, 'Trusted scoring signals response has a number value for renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/'string-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === "1"`);
}, 'Trusted scoring signals response has a string value for renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'array-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]) === '[1,"foo",null]'`);
}, 'Trusted scoring signals response has an array value for renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'object-value');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`Object.keys(trustedScoringSignals.renderURL["${renderURL}"]).length === 2 &&
trustedScoringSignals.renderURL["${renderURL}"]["a"] === "b" &&
JSON.stringify(trustedScoringSignals.renderURL["${renderURL}"]["c"]) === '["d"]'`);
}, 'Trusted scoring signals response has an object value for renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'+%20 \x00?,3#&');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === "default value"`);
}, 'Trusted scoring signals with escaped renderURL.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'hostname');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals.renderURL["${renderURL}"] === "${window.location.hostname}"`);
}, 'Trusted scoring signals receives hostname field.');
// Joins two interest groups and makes sure the scoring signals for one are never leaked
// to the seller script when scoring the other.
//
// There's no guarantee in this test that a single request to the server will be made with
// render URLs from two different IGs, though that's the case this is trying to test -
// browsers are not required to support batching, and even if they do, joining any two
// particular requests may be racy.
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value');
const renderURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'string-value');
await joinInterestGroup(test, uuid, { ads: [{ renderURL: renderURL1 }], name: '1' });
await joinInterestGroup(test, uuid, { ads: [{ renderURL: renderURL2 }], name: '2' });
let auctionConfigOverrides = { trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL };
// scoreAd() only accepts the first IG's bid, validating its trustedScoringSignals.
auctionConfigOverrides.decisionLogicURL =
createDecisionScriptURL(uuid, {
scoreAd: `if (browserSignals.renderURL === "${renderURL1}" &&
trustedScoringSignals.renderURL["${renderURL1}"] !== 1 ||
trustedScoringSignals.renderURL["${renderURL2}"] !== undefined)
return;` });
let config = await runBasicFledgeAuction(
test, uuid, auctionConfigOverrides);
assert_true(config instanceof FencedFrameConfig,
`Wrong value type returned from first auction: ${config.constructor.type}`);
// scoreAd() only accepts the second IG's bid, validating its trustedScoringSignals.
auctionConfigOverrides.decisionLogicURL =
createDecisionScriptURL(uuid, {
scoreAd: `if (browserSignals.renderURL === "${renderURL2}" &&
trustedScoringSignals.renderURL["${renderURL1}"] !== undefined ||
trustedScoringSignals.renderURL["${renderURL2}"] !== '1')
return;` });
config = await runBasicFledgeAuction(
test, uuid, auctionConfigOverrides);
assert_true(config instanceof FencedFrameConfig,
`Wrong value type returned from second auction: ${config.constructor.type}`);
}, 'Trusted scoring signals multiple renderURLs.');
/////////////////////////////////////////////////////////////////////////////
// Cross-origin trusted scoring signals tests
/////////////////////////////////////////////////////////////////////////////
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/'string-value,data-version:3,cors');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals['${OTHER_ORIGIN1}'].renderURL[
"${renderURL}"] === "1" &&
browserSignals.crossOriginDataVersion === 3`,
/*additionalInterestGroupOverrides=*/ {},
CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL,
{permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`});
}, 'Basic cross-origin trusted scoring signals.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/'string-value,data-version:3');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals === null &&
!('crossOriginDataVersion' in browserSignals)`,
/*additionalInterestGroupOverrides=*/ {},
CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL,
{permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`});
}, 'Cross-origin trusted scoring signals w/o CORS authorization.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const sellerReportURL = createSellerReportURL(uuid);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/`string-value,data-version:3,uuid:${uuid},dispatch:track_get`);
// Use the request tracker for trusted scoring signals, to have it
// record whether they got fetched or not.
const crossOriginRequestTrackerURL = OTHER_ORIGIN1 + BASE_PATH +
'resources/request-tracker.py';
let combinedTrustedSignalsURL = new URL(crossOriginRequestTrackerURL);
combinedTrustedSignalsURL.search =
`hostname=${window.location.hostname}&renderUrls=${encodeURIComponent(renderURL)}`
let result = await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals === null &&
!('crossOriginDataVersion' in browserSignals)`,
/*additionalInterestGroupOverrides=*/ {},
crossOriginRequestTrackerURL,
{permitCrossOriginTrustedSignals: `"${OTHER_ORIGIN1}"`,
reportResult: `sendReportTo("${sellerReportURL}")`});
createAndNavigateFencedFrame(test, result);
await waitForObservedRequests(
uuid, [combinedTrustedSignalsURL.href, createBidderReportURL(uuid), sellerReportURL]);
}, 'Cross-origin trusted scoring signals w/o CORS authorization sends request.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/'string-value,data-version:3, cors');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals === null &&
!('crossOriginDataVersion' in browserSignals)`,
/*additionalInterestGroupOverrides=*/ {},
CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL);
}, 'Cross-origin trusted scoring signals w/o script allow header.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/'string-value,data-version:3');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals === null &&
!('crossOriginDataVersion' in browserSignals)`,
/*additionalInterestGroupOverrides=*/ {},
CROSS_ORIGIN_TRUSTED_SCORING_SIGNALS_URL,
{permitCrossOriginTrustedSignals:
`"${OTHER_ORIGIN2}", "${window.location.origin}"`});
}, 'Cross-origin trusted scoring signals with wrong script allow header.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const sellerReportURL = createSellerReportURL(uuid);
const renderURL = createRenderURL(uuid, /*script=*/null,
/*signalsParam=*/`string-value,data-version:3,uuid:${uuid},dispatch:track_get`);
// Use the request tracker for trusted scoring signals, to have it
// record whether they got fetched or not.
const crossOriginRequestTrackerURL = OTHER_ORIGIN1 + BASE_PATH +
'resources/request-tracker.py';
let result = await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null &&
!('dataVersion' in browserSignals) &&
crossOriginTrustedScoringSignals === null &&
!('crossOriginDataVersion' in browserSignals)`,
/*additionalInterestGroupOverrides=*/ {},
crossOriginRequestTrackerURL,
{permitCrossOriginTrustedSignals:
`"${OTHER_ORIGIN2}", "${window.location.origin}"`,
reportResult: `sendReportTo("${sellerReportURL}")`});
createAndNavigateFencedFrame(test, result);
await waitForObservedRequests(uuid,
[createBidderReportURL(uuid), sellerReportURL]);
}, 'Cross-origin trusted scoring signals with wrong script allow header not fetched.');
/////////////////////////////////////////////////////////////////////////////
// Data-Version tests
/////////////////////////////////////////////////////////////////////////////
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid);
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has no Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === 3');
}, 'Trusted scoring signals response has valid Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:0');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === 0');
}, 'Trusted scoring signals response has min Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:4294967295');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === 4294967295');
}, 'Trusted scoring signals response has max Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:4294967296');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has too large Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:03');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has data-version with leading 0.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:-1');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has negative Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:1.3');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has decimal in Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:2 2');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has space in Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:0x4');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has hex Data-Version.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has data-version and empty body.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:[]');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has data-version and JSON array body.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:{} {}');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === undefined');
}, 'Trusted scoring signals response has data-version and double JSON object body.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, 'data-version:3,replace-body:{}');
await runTrustedScoringSignalsDataVersionTest(
test, uuid, renderURL,
'browserSignals.dataVersion === 3');
}, 'Trusted scoring signals response has data-version and no renderURLs.');
/////////////////////////////////////////////////////////////////////////////
// Trusted scoring signals + component ad tests.
/////////////////////////////////////////////////////////////////////////////
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'close-connection');
const componentURL = createRenderURL(uuid, /*script=*/null);
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`trustedScoringSignals === null`,
{adComponents: [{ renderURL: componentURL }],
biddingLogicURL: createBiddingScriptURL({
generateBid: `return {bid: 1,
render: interestGroup.ads[0].renderURL,
adComponents: [interestGroup.adComponents[0].renderURL]};`})
});
}, 'Component ads trusted scoring signals, server closes the connection without sending anything.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value');
// This should not be sent. If it is, it will take precedence over the "num-value" parameter
// from "renderURL", resulting in the "renderURL" having a null "trustedScoringSignals" value.
const componentURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`Object.keys(trustedScoringSignals.renderURL).length === 1 &&
trustedScoringSignals.renderURL["${renderURL}"] === 1 &&
trustedScoringSignals.adComponentRenderURLs === undefined`,
{ adComponents: [{ renderURL: componentURL }] });
}, 'Trusted scoring signals request without component ads in bid.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createScoringSignalsRenderURLWithBody(
uuid, /*responseBody=*/'{}');
const componentURL = createRenderURL(uuid, /*script=*/null);
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`Object.keys(trustedScoringSignals.renderURL).length === 1 &&
trustedScoringSignals.renderURL["${renderURL}"] === null &&
Object.keys(trustedScoringSignals.adComponentRenderURLs).length === 1 &&
trustedScoringSignals.adComponentRenderURLs["${componentURL}"] === null`,
{adComponents: [{ renderURL: componentURL }],
biddingLogicURL: createBiddingScriptURL({
generateBid: `return {bid: 1,
render: interestGroup.ads[0].renderURL,
adComponents: [interestGroup.adComponents[0].renderURL]};`})
});
}, 'Component ads trusted scoring signals trusted scoring signals response is empty JSON object.');
subsetTest(promise_test, async test => {
const uuid = generateUuid(test);
const renderURL = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'hostname');
const componentURL1 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'null-value');
const componentURL2 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'num-value');
const componentURL3 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'string-value');
const componentURL4 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'array-value');
const componentURL5 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'object-value');
const componentURL6 = createRenderURL(uuid, /*script=*/null, /*signalsParam=*/'wrong-url');
await runTrustedScoringSignalsTest(
test, uuid, renderURL,
`Object.keys(trustedScoringSignals.renderURL).length === 1 &&
trustedScoringSignals.renderURL["${renderURL}"] === "${window.location.hostname}" &&
Object.keys(trustedScoringSignals.adComponentRenderURLs).length === 6 &&
trustedScoringSignals.adComponentRenderURLs["${componentURL1}"] === null &&
trustedScoringSignals.adComponentRenderURLs["${componentURL2}"] === 1 &&
trustedScoringSignals.adComponentRenderURLs["${componentURL3}"] === "1" &&
JSON.stringify(trustedScoringSignals.adComponentRenderURLs["${componentURL4}"]) === '[1,"foo",null]' &&
Object.keys(trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]).length === 2 &&
trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]["a"] === "b" &&
JSON.stringify(trustedScoringSignals.adComponentRenderURLs["${componentURL5}"]["c"]) === '["d"]' &&
trustedScoringSignals.adComponentRenderURLs["${componentURL6}"] === null`,
{adComponents: [{ renderURL: componentURL1 }, { renderURL: componentURL2 },
{ renderURL: componentURL3 }, { renderURL: componentURL4 },
{ renderURL: componentURL5 }, { renderURL: componentURL6 }],
biddingLogicURL: createBiddingScriptURL({
generateBid: `return {bid: 1,
render: interestGroup.ads[0].renderURL,
adComponents: ["${componentURL1}", "${componentURL2}",
"${componentURL3}", "${componentURL4}",
"${componentURL5}", "${componentURL6}"]};`
})
});
}, '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.');