tor-browser

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

join-leave-ad-interest-group-in-fenced-frame.https.window.js (14719B)


      1 // META: script=/resources/testdriver.js
      2 // META: script=/resources/testdriver-vendor.js
      3 // META: script=/common/utils.js
      4 // META: script=resources/fledge-util.sub.js
      5 // META: script=/common/subset-tests.js
      6 // META: timeout=long
      7 // META: variant=?1-4
      8 // META: variant=?5-8
      9 // META: variant=?9-last
     10 
     11 "use strict";
     12 
     13 // These are separate from the other join-leave tests because these all create
     14 // and navigate fenced frames, which is much slower than just joining/leaving
     15 // interest groups, and running the occasional auction. Most tests use a
     16 // buyer with an origin of OTHER_ORIGIN1, so it has a distinct origin from the
     17 // seller and publisher.
     18 
     19 // Creates a tracker URL that's requested when a call succeeds in a fenced
     20 // frame.
     21 function createSuccessURL(uuid, origin = document.location.origin) {
     22  return createTrackerURL(origin, uuid, "track_get", "success");
     23 }
     24 
     25 // Creates a tracker URL that's requested when a call fails in a fenced frame, with
     26 // the expected exception.
     27 function createExceptionURL(uuid, origin = document.location.origin) {
     28  return createTrackerURL(origin, uuid, "track_get", "exception");
     29 }
     30 
     31 // Creates a tracker URL that's requested when joinAdInterestGroup() or
     32 // leaveAdInterestGroup() fails with an exception other than the one that's
     33 // expected.
     34 function createBadExceptionURL(uuid, origin = document.location.origin) {
     35  return createTrackerURL(origin, uuid, "track_get", "bad_exception");
     36 }
     37 
     38 // Creates render URL that calls "navigator.leaveAdInterestGroup()" when
     39 // loaded, with no arguments. It then fetches a URL depending on whether it
     40 // threw an exception. No exception should ever be thrown when this is run
     41 // in an ad URL, so only fetch the "bad exception" URL on error.
     42 function createNoArgsTryLeaveRenderURL(uuid, origin = document.location.origin) {
     43  return createRenderURL(
     44    uuid,
     45    `async function TryLeave() {
     46       try {
     47         await navigator.leaveAdInterestGroup();
     48         await fetch("${createSuccessURL(uuid, origin)}");
     49       } catch (e) {
     50         await fetch("${createBadExceptionURL(uuid, origin)}");
     51       }
     52     }
     53 
     54     TryLeave();`,
     55    /*signalsParams=*/null,
     56    origin);
     57 }
     58 
     59 subsetTest(promise_test, async test => {
     60  const uuid = generateUuid(test);
     61 
     62  // Interest group that an ad fenced frame attempts to join. The join should
     63  // fail.
     64  let interestGroupJoinedInFrame = createInterestGroupForOrigin(
     65      uuid, document.location.origin, {name: 'group2'});
     66 
     67  // Create a render URL that tries to join "interestGroupJoinedInFrame".
     68  const renderURL = createRenderURL(
     69    uuid,
     70    `async function TryJoin() {
     71       try {
     72         await navigator.joinAdInterestGroup(
     73             ${JSON.stringify(interestGroupJoinedInFrame)});
     74         await fetch("${createSuccessURL(uuid)}");
     75       } catch (e) {
     76         if (e instanceof DOMException && e.name === "NotAllowedError") {
     77           await fetch("${createExceptionURL(uuid)}");
     78         } else {
     79           await fetch("${createBadExceptionURL(uuid)}");
     80         }
     81       }
     82     }
     83 
     84     TryJoin();`);
     85 
     86  await joinInterestGroup(test, uuid, {ads: [{ renderURL: renderURL}]});
     87 
     88  await runBasicFledgeAuctionAndNavigate(test, uuid);
     89 
     90  // This should wait until the leave call has thrown an exception.
     91  await waitForObservedRequests(
     92      uuid,
     93      [createBidderReportURL(uuid), createSellerReportURL(uuid), createExceptionURL(uuid)]);
     94 
     95  // Leave the initial interest group.
     96  await leaveInterestGroup();
     97 
     98  // Check the interest group was not successfully joined in the fenced frame
     99  // by running an auction, to make sure the thrown exception accurately
    100  // indicates the group wasn't joined.
    101  await runBasicFledgeTestExpectingNoWinner(test, uuid);
    102 }, 'joinAdInterestGroup() in ad fenced frame.');
    103 
    104 subsetTest(promise_test, async test => {
    105  const uuid = generateUuid(test);
    106 
    107  // Create a render URL that tries to leave the default test interest group by
    108  // name. Even a though a render URL can leave its own interest group by using
    109  // the 0-argument version of leaveAdInterestGroup(), it can't leave its own
    110  // interest group by using the 1-argument version, so this should fail.
    111  const renderURL = createRenderURL(
    112      uuid,
    113      `async function TryLeave() {
    114         try {
    115           await navigator.leaveAdInterestGroup(
    116               {owner: "${window.location.origin}", name: "${DEFAULT_INTEREST_GROUP_NAME}"});
    117           await fetch("${createSuccessURL(uuid)}");
    118         } catch (e) {
    119           if (e instanceof DOMException && e.name === "NotAllowedError") {
    120             await fetch("${createExceptionURL(uuid)}");
    121           } else {
    122             await fetch("${createBadExceptionURL(uuid)}");
    123           }
    124         }
    125       }
    126 
    127       TryLeave();`);
    128 
    129  await joinInterestGroup(
    130      test, uuid,
    131      {ads: [{ renderURL: renderURL}]});
    132 
    133  await runBasicFledgeAuctionAndNavigate(test, uuid);
    134 
    135  // This should wait until the leave call has thrown an exception.
    136  await waitForObservedRequests(
    137      uuid,
    138      [createBidderReportURL(uuid), createSellerReportURL(uuid), createExceptionURL(uuid)]);
    139 
    140  // Check the interest group was not left.
    141  await runBasicFledgeTestExpectingWinner(test, uuid);
    142 }, 'leaveAdInterestGroup() in ad fenced frame, specify an interest group.');
    143 
    144 subsetTest(promise_test, async test => {
    145  const uuid = generateUuid(test);
    146 
    147  const bidder_origin = OTHER_ORIGIN1;
    148  const render_url_origin = window.location.origin;
    149 
    150  await joinCrossOriginInterestGroup(
    151      test, uuid, bidder_origin,
    152      {ads: [{ renderURL: createNoArgsTryLeaveRenderURL(uuid, render_url_origin) }]});
    153 
    154  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    155 
    156  // Leaving the interest group should claim to succeed, to avoid leaking
    157  // whether or not the buyer was same-origin or to the fenced frame.
    158  await waitForObservedRequests(
    159      uuid,
    160      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    161        createSuccessURL(uuid, render_url_origin)]);
    162 
    163  // Check the interest group was not actually left.
    164  await runBasicFledgeTestExpectingWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    165 }, 'leaveAdInterestGroup() in non-buyer origin ad fenced frame, no parameters.');
    166 
    167 subsetTest(promise_test, async test => {
    168  const uuid = generateUuid(test);
    169 
    170  const bidder_origin = OTHER_ORIGIN1;
    171  const render_url_origin = OTHER_ORIGIN1;
    172 
    173  // Use a different origin for the buyer, to make sure that's the origin
    174  // that matters.
    175  await joinCrossOriginInterestGroup(
    176      test, uuid, bidder_origin,
    177      {ads: [{ renderURL: createNoArgsTryLeaveRenderURL(uuid, render_url_origin) }]});
    178 
    179  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    180 
    181  // This should wait until the leave call has completed.
    182  await waitForObservedRequests(
    183      uuid,
    184      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    185        createSuccessURL(uuid, render_url_origin)]);
    186 
    187  // Check the interest group was actually left.
    188  await runBasicFledgeTestExpectingNoWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    189 }, 'leaveAdInterestGroup() in buyer origin ad fenced frame, no parameters.');
    190 
    191 subsetTest(promise_test, async test => {
    192  const uuid = generateUuid(test);
    193 
    194  const bidder_origin = OTHER_ORIGIN1;
    195  const render_url_origin = OTHER_ORIGIN1;
    196  const iframe_origin = OTHER_ORIGIN1;
    197 
    198  // Create a render URL which, in an iframe, loads the common "try leave"
    199  // render URL from the buyer's origin (which isn't technically being used as
    200  // a render URL, in this case).
    201  const renderURL = createRenderURL(
    202      uuid,
    203      `let iframe = document.createElement("iframe");
    204       iframe.permissions = "join-ad-interest-group";
    205       iframe.src = "${createNoArgsTryLeaveRenderURL(uuid, iframe_origin)}";
    206       document.body.appendChild(iframe);`,
    207      /*signalsParams=*/null,
    208      render_url_origin);
    209 
    210  await joinCrossOriginInterestGroup(
    211      test, uuid, bidder_origin,
    212      {ads: [{ renderURL: renderURL }]});
    213 
    214  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    215 
    216  // This should wait until the leave call has completed.
    217  await waitForObservedRequests(
    218      uuid,
    219      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    220        createSuccessURL(uuid, iframe_origin)]);
    221 
    222  // Check the interest group was actually left.
    223  await runBasicFledgeTestExpectingNoWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    224 }, 'leaveAdInterestGroup() in same-origin iframe inside buyer origin ad fenced frame, no parameters.');
    225 
    226 subsetTest(promise_test, async test => {
    227  const uuid = generateUuid(test);
    228 
    229  const bidder_origin = OTHER_ORIGIN1;
    230  const render_url_origin = OTHER_ORIGIN1;
    231  const iframe_origin = document.location.origin;
    232 
    233  // Create a render URL which, in an iframe, loads the common "try leave"
    234  // render URL from an origin other than the buyer's origin.
    235  const renderURL = createRenderURL(
    236      uuid,
    237      `let iframe = document.createElement("iframe");
    238       iframe.permissions = "join-ad-interest-group";
    239       iframe.src = "${createNoArgsTryLeaveRenderURL(uuid, iframe_origin)}";
    240       document.body.appendChild(iframe);`,
    241      /*signalsParams=*/null,
    242      render_url_origin);
    243 
    244  await joinCrossOriginInterestGroup(
    245      test, uuid, bidder_origin,
    246      {ads: [{ renderURL: renderURL }]});
    247 
    248  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    249 
    250  // Leaving the interest group should claim to succeed, to avoid leaking
    251  // whether or not the buyer was same-origin or to the iframe.
    252  await waitForObservedRequests(
    253      uuid,
    254      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    255        createSuccessURL(uuid, iframe_origin)]);
    256 
    257  // Check the interest group was not actually left.
    258  await runBasicFledgeTestExpectingWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    259 }, 'leaveAdInterestGroup() in cross-origin iframe inside buyer origin ad fenced frame, no parameters.');
    260 
    261 subsetTest(promise_test, async test => {
    262  const uuid = generateUuid(test);
    263 
    264  const bidder_origin = OTHER_ORIGIN1;
    265  const render_url_origin = document.location.origin;
    266  const iframe_origin = document.location.origin;
    267 
    268  // Create a render URL which, in an iframe, loads the common "try leave"
    269  // render URL from an origin other than the buyer's origin (which isn't
    270  // technically being used as a render URL, in this case).
    271  const renderURL = createRenderURL(
    272      uuid,
    273      `let iframe = document.createElement("iframe");
    274       iframe.permissions = "join-ad-interest-group";
    275       iframe.src = "${createNoArgsTryLeaveRenderURL(uuid, iframe_origin)}";
    276       document.body.appendChild(iframe);`,
    277      /*signalsParams=*/null,
    278      render_url_origin);
    279 
    280  await joinCrossOriginInterestGroup(
    281      test, uuid, bidder_origin,
    282      {ads: [{ renderURL: renderURL }]});
    283 
    284  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    285 
    286  // Leaving the interest group should claim to succeed, to avoid leaking
    287  // whether or not the buyer was same-origin or to the fenced frame.
    288  await waitForObservedRequests(
    289      uuid,
    290      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    291        createSuccessURL(uuid, iframe_origin)]);
    292 
    293  // Check the interest group was not actually left.
    294  await runBasicFledgeTestExpectingWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    295 }, 'leaveAdInterestGroup() in same-origin iframe inside non-buyer origin ad fenced frame, no parameters.');
    296 
    297 subsetTest(promise_test, async test => {
    298  const uuid = generateUuid(test);
    299 
    300  const bidder_origin = OTHER_ORIGIN1;
    301  const render_url_origin = document.location.origin;
    302  const iframe_origin = OTHER_ORIGIN1;
    303 
    304  // Create a render URL which, in an iframe, loads the common "try leave"
    305  // render URL from the buyer's origin (which isn't technically being used as
    306  // a render URL, in this case).
    307  const renderURL = createRenderURL(
    308      uuid,
    309      `let iframe = document.createElement("iframe");
    310       iframe.permissions = "join-ad-interest-group";
    311       iframe.src = "${createNoArgsTryLeaveRenderURL(uuid, iframe_origin)}";
    312       document.body.appendChild(iframe);`,
    313      /*signalsParams=*/null,
    314      render_url_origin);
    315 
    316  await joinCrossOriginInterestGroup(
    317      test, uuid, bidder_origin,
    318      {ads: [{ renderURL: renderURL }]});
    319 
    320  await runBasicFledgeAuctionAndNavigate(test, uuid, {interestGroupBuyers : [bidder_origin]});
    321  // Leaving the interest group should succeed.
    322  await waitForObservedRequests(
    323      uuid,
    324      [ createBidderReportURL(uuid), createSellerReportURL(uuid),
    325        createSuccessURL(uuid, iframe_origin)]);
    326 
    327  // Check the interest group was left.
    328  await runBasicFledgeTestExpectingNoWinner(test, uuid, {interestGroupBuyers : [bidder_origin]});
    329 }, 'leaveAdInterestGroup() in cross-origin buyer iframe inside non-buyer origin ad fenced frame, no parameters.');
    330 
    331 subsetTest(promise_test, async test => {
    332  const uuid = generateUuid(test);
    333 
    334  // Render URL that loads the first ad component in a nested fenced frame.
    335  let loadFirstComponentAdURL =
    336      createRenderURL(
    337        uuid,
    338        `let fencedFrame = document.createElement("fencedframe");
    339         fencedFrame.mode = "opaque-ads";
    340         fencedFrame.config = window.fence.getNestedConfigs()[0];
    341         document.body.appendChild(fencedFrame);`,
    342        /*signalsParams=*/null,
    343        OTHER_ORIGIN1);
    344 
    345  await joinInterestGroup(
    346      test, uuid,
    347      // Interest group that makes a bid with a component ad. The render URL
    348      // will open the component ad in a fenced frame, and the component ad
    349      // URL is the common URL that tries to leave the ad's current interest
    350      // group, reporting the result to a tracker URL.
    351      { biddingLogicURL: createBiddingScriptURL(
    352          { generateBid: `return {
    353                            bid: 1,
    354                            render: interestGroup.ads[0].renderURL,
    355                            adComponents: [interestGroup.adComponents[0].renderURL]
    356                          };` }),
    357        ads: [{ renderURL: loadFirstComponentAdURL }],
    358        adComponents: [{ renderURL: createNoArgsTryLeaveRenderURL(uuid) }]});
    359 
    360  await runBasicFledgeAuctionAndNavigate(test, uuid);
    361 
    362  // Leaving the interest group should claim to succeed, to avoid leaking
    363  // whether or not the buyer was same-origin or to the fenced frame.
    364  await waitForObservedRequests(
    365      uuid,
    366      [createSellerReportURL(uuid), createSuccessURL(uuid)]);
    367 
    368  // Check the interest group was left.
    369  await runBasicFledgeTestExpectingNoWinner(test, uuid);
    370 }, 'leaveAdInterestGroup() in component ad fenced frame, no parameters.');