tor-browser

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

block-local-documents-inheriting-none.https.html (4683B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <script src="/resources/testharness.js"></script>
      4 <script src="/resources/testharnessreport.js"></script>
      5 <script src="/common/get-host-info.sub.js"></script>
      6 <script src="/common/utils.js"></script>
      7 <div id=log></div>
      8 
      9 <script>
     10 const script = `
     11    <script>
     12      top.postMessage({event: "loaded", type: location.protocol}, "*");
     13    <\/script>`;
     14 
     15 const test_cases = [
     16    {name: "data", url: encodeURI(`data:text/html,${script}`)},
     17    {name: "blob", url: URL.createObjectURL(new Blob([script], { type: "text/html" }))},
     18    {name: "about", url: "about:blank"},
     19  ];
     20 
     21 const observeReports = async (frame) => {
     22  const reports = [];
     23 
     24  const observer = new frame.contentWindow.ReportingObserver(
     25    rs => reports.push(...rs.map(r => r.toJSON()))
     26  );
     27  observer.observe();
     28 
     29  // Wait for reports. Use a timeout to catch both expected and unexpected
     30  // reports.
     31  await new Promise(resolve => step_timeout(resolve, 3000));
     32  return reports;
     33 };
     34 
     35 promise_test(async t => {
     36  const this_window_token = token();
     37 
     38  // Expect the nested frame to not load, since they inherit COEP: none from the
     39  // top frame, which is incompatible with first_frame's COEP: require-corp.
     40  const received_events = [];
     41  addEventListener("message", event => {
     42    if(event.data.event == "loaded")
     43      received_events.push(`Nested ${event.data.type} loaded!`);
     44  });
     45 
     46  // Create an iframe with COEP: require-corp
     47  const first_iframe = document.createElement("iframe");
     48  t.add_cleanup( () => first_iframe.remove() );
     49  first_iframe.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)";
     50  let iframe_load_promise = new Promise( resolve => first_iframe.addEventListener("load", resolve) );
     51 
     52  document.body.append(first_iframe);
     53  await iframe_load_promise;
     54 
     55  const reportPromise = observeReports(first_iframe);
     56  // 1. Create nested frames.
     57  // They initially navigate to blank.html and have COEP: require-corp set.
     58  // This initial navigation is required because it uses the parent frame as the
     59  // initiator. That is first_iframe is the initiator, while we want top to be
     60  // the initiator for this test, which will be done in step 4 with a second
     61  // navigation from that blank.html document to the local scheme one.
     62  const nested_frames = {};
     63  const nested_frames_promises = [];
     64  test_cases.forEach(test => {
     65    nested_frame = document.createElement("iframe");
     66    nested_frame.src = "/common/blank.html?pipe=header(cross-origin-embedder-policy,require-corp)";
     67    t.add_cleanup( () => nested_frame.remove() );
     68    nested_frames_promises.push(new Promise( resolve => nested_frame.addEventListener("load", resolve) ) );
     69    first_iframe.contentDocument.body.append(nested_frame);
     70    nested_frames[test.name] = nested_frame;
     71  });
     72 
     73  // 2. Wait for the loads of all iframes to complete.
     74  await Promise.all(nested_frames_promises);
     75 
     76  // 3. Navigate a dummy frame. This is required because some browsers (Chrome)
     77  // might consider the first navigation in 4. as a redirect otherwise.
     78  const dummy_Frame = document.createElement("iframe");
     79  t.add_cleanup( () => dummy_Frame.remove() );
     80  dummy_Frame.src = "/common/blank.html";
     81  iframe_load_promise = new Promise( resolve => dummy_Frame.addEventListener("load", resolve) );
     82  document.body.append(dummy_Frame);
     83  await iframe_load_promise;
     84 
     85  // 4. Navigate nested frames to a local scheme document.
     86  // COEP should be inherited from the initiator or blobURL's creator (top in both
     87  // cases), this results in COEP being none and the documents not being allowed
     88  // to load under the COEP: require-corp iframe (first_iframe).
     89  test_cases.forEach(test => {
     90    // Top navigates nested_frame_[test.name] to a test.url
     91    const frame = nested_frames[test.name];
     92    // Use frame.contentDocument.location to ensure the initiator is this (top)
     93    // frame. frame.src is not used here as this makes the parent of the nested
     94    // frame (first_iframe) the initiator.
     95    frame.contentDocument.location = test.url;
     96  });
     97 
     98  // 5. Wait and validate reports.
     99  const reports = await reportPromise;
    100  assert_equals(reports.length, test_cases.length);
    101  test_cases.forEach(test => {
    102    assert_true(reports.some( report => {
    103      return report.type == 'coep' &&
    104      report.body.type == 'navigation' &&
    105      report.body.blockedURL == test.url;
    106    }), `No report matched for test "${test.name}"`);
    107  });
    108  // Also verify that no message was sent by the nested frames and stored in
    109  // received_events.
    110  assert_equals(received_events.length, 0);
    111 }, "Prevent local scheme documents from loading within a COEP: require-corp iframe if they inherit COEP: none");
    112 </script>