tor-browser

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

service-worker-request-visibility.https.window.js (5918B)


      1 // META: script=/resources/testdriver.js
      2 // META: script=/resources/testdriver-vendor.js
      3 // META: script=/common/utils.js
      4 // META: script=resources/fledge-util.sub.js
      5 // META: script=/common/subset-tests.js
      6 // META: timeout=long
      7 // META: variant=?1-last
      8 
      9 "use strict";
     10 
     11 const SERVICE_WORKER_SCRIPT = "resources/service-worker-helper.js";
     12 
     13 // List of URL fragments that uniquely identify private requests.
     14 // These are used to detect if a service worker has inadvertently
     15 // accessed any of these private requests, which should not be visible to it.
     16 const PRIVATE_REQUEST_FILE_NAMES = [
     17  'trusted-bidding-signals.py',
     18  'update-url.py', // This requires another test, since it takes a while,
     19  // see TODO file for more info.
     20  'wasm-helper.py',
     21  'bidding-logic.py',
     22  'decision-logic.py',
     23  'trusted-scoring-signals.py',
     24  'trusted-bidding-signals.py',
     25  'bidder_report',
     26  'seller_report'
     27 ];
     28 
     29 // List of URL fragments that uniquely identify public requests.
     30 // These are used to verify that a service worker can correctly
     31 // access and intercept these public requests as expected.
     32 const PUBLIC_REQUEST_FILE_NAMES = [
     33  'direct-from-seller-signals.py',
     34 ];
     35 
     36 const COMPLETE_TEST_URL = 'complete-test'
     37 
     38 const CURRENT_SCOPE = "/fledge/tentative/"
     39 
     40 async function registerAndActivateServiceWorker(test) {
     41  // Unregister existing service worker (if any)
     42  const existingRegistration = await navigator.serviceWorker.getRegistration(CURRENT_SCOPE);
     43  if (existingRegistration) {
     44    await existingRegistration.unregister();
     45  }
     46 
     47  // Register new service worker
     48  var newRegistration = await navigator.serviceWorker.register(`./${SERVICE_WORKER_SCRIPT}`, { scope: CURRENT_SCOPE });
     49 
     50  test.add_cleanup(async () => {
     51    await newRegistration.unregister();
     52  });
     53 
     54  await navigator.serviceWorker.ready;
     55 
     56  // Wait for the page to be controlled by the service worker.
     57  // This is needed as navigator.serviceWorker.ready does not
     58  // guarantee that the page is being controlled.
     59  // See https://github.com/slightlyoff/ServiceWorker/issues/799.
     60  await new Promise(resolve => {
     61    if (navigator.serviceWorker.controller) {
     62      resolve();
     63    } else {
     64      navigator.serviceWorker.addEventListener('controllerchange', resolve);
     65    }
     66  });
     67 
     68  // Validate the service worker
     69  if (!navigator.serviceWorker.controller.scriptURL.includes(SERVICE_WORKER_SCRIPT)) {
     70    throw new Error('Failed to register service worker');
     71  }
     72 }
     73 
     74 async function setUpServiceWorkerAndGetBroadcastChannel(test) {
     75  await registerAndActivateServiceWorker(test);
     76  return new BroadcastChannel("requests-test");
     77 }
     78 
     79 // Waits for a service worker to observe specific URL filenames via a BroadcastChannel.
     80 // Resolves when all expected URL filenames are seen.
     81 function awaitServiceWorkerURLPromise(broadcastChannel, expectedURLFileNames,
     82  unexpectedURLFileNames) {
     83  const seenURLs = new Set();
     84  return new Promise((resolve, reject) => {
     85    broadcastChannel.addEventListener('message', (event) => {
     86      var url = event.data.url;
     87      var fileName = url.substring(url.lastIndexOf('/') + 1);
     88      if (expectedURLFileNames.includes(fileName)) {
     89        seenURLs.add(fileName);
     90      }
     91      if (unexpectedURLFileNames.includes(fileName)) {
     92        reject(`unexpected result: ${fileName}`);
     93      }
     94      // Resolve when all `expectedURLs` have been seen.
     95      if (seenURLs.size === expectedURLFileNames.length) {
     96        resolve();
     97      }
     98    });
     99  });
    100 }
    101 
    102 // Tests that public requests are seen by the service worker.
    103 // Specifically anything that contains:
    104 // - 'direct-from-seller-signals.py'
    105 
    106 // This test works by having the service worker send a message over
    107 // the broadcastChannel, if it sees a request that contains any of
    108 // the following strings above, it will send a 'passed' result and
    109 // also change the variable 'finish_test', to true, so that guarantees
    110 // that the request was seen before we complete the test.
    111 subsetTest(promise_test, async test => {
    112  const broadcastChannel = await setUpServiceWorkerAndGetBroadcastChannel(test);
    113  let finishTest = awaitServiceWorkerURLPromise(
    114    broadcastChannel,
    115    PUBLIC_REQUEST_FILE_NAMES,
    116    PRIVATE_REQUEST_FILE_NAMES);
    117 
    118  await fetchDirectFromSellerSignals({ 'Buyer-Origin': window.location.origin });
    119  await finishTest;
    120 }, "Make sure service workers do see public requests.");
    121 
    122 // Tests that private requests are not seen by the service worker.
    123 // Specifically anything that contains:
    124 // - 'resources/trusted-bidding-signals.py'
    125 // - 'resources/trusted-scoring-signals.py'
    126 // - 'wasm-helper.py'
    127 // - 'bidding-logic.py'
    128 // - 'decision-logic.py'
    129 // - 'seller_report'
    130 // - 'bidder_report'
    131 
    132 // This test works by having the service worker send a message
    133 // over the broadcastChannel, if it sees a request that contains
    134 // any of the following strings above, it will send a 'failed'
    135 // result which will cause assert_false case to fail.
    136 subsetTest(promise_test, async test => {
    137  const uuid = generateUuid(test);
    138  const broadcastChannel = await setUpServiceWorkerAndGetBroadcastChannel(test);
    139 
    140  let finishTest = awaitServiceWorkerURLPromise(
    141    broadcastChannel,
    142    /*expectedURLFileNames=*/[COMPLETE_TEST_URL],
    143    PRIVATE_REQUEST_FILE_NAMES)
    144 
    145  let interestGroupOverrides = {
    146    biddingWasmHelperURL: `${RESOURCE_PATH}wasm-helper.py`,
    147    trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL,
    148    trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL,
    149  };
    150 
    151  await joinInterestGroup(test, uuid, interestGroupOverrides);
    152  await runBasicFledgeAuctionAndNavigate(test, uuid);
    153  // By verifying that these requests are observed we can assume
    154  // none of the other requests were seen by the service-worker.
    155  await waitForObservedRequests(
    156    uuid,
    157    [createBidderReportURL(uuid), createSellerReportURL(uuid)]);
    158 
    159  // We use this fetch to complete the test.
    160  await fetch(COMPLETE_TEST_URL);
    161  await finishTest;
    162 }, "Make sure service workers do not see private requests");