tor-browser

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

reporting-navigation.https.window.js (4916B)


      1 // META: timeout=long
      2 // META: script=/common/get-host-info.sub.js
      3 // META: script=./resources/common.js
      4 const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
      5 const COEP = '|header(cross-origin-embedder-policy,credentialless)';
      6 const COEP_RO =
      7  '|header(cross-origin-embedder-policy-report-only,credentialless)';
      8 const CORP_CROSS_ORIGIN =
      9  '|header(cross-origin-resource-policy,cross-origin)';
     10 const FRAME_URL = `${ORIGIN}/common/blank.html?pipe=`;
     11 const REMOTE_FRAME_URL = `${REMOTE_ORIGIN}/common/blank.html?pipe=`;
     12 
     13 function checkCorpReport(report, contextUrl, blockedUrl, disposition) {
     14  assert_equals(report.type, 'coep');
     15  assert_equals(report.url, contextUrl);
     16  assert_equals(report.body.type, 'corp');
     17  assert_equals(report.body.blockedURL, blockedUrl);
     18  assert_equals(report.body.disposition, disposition);
     19  assert_equals(report.body.destination, 'iframe');
     20 }
     21 
     22 function checkCoepMismatchReport(report, contextUrl, blockedUrl, disposition) {
     23  assert_equals(report.type, 'coep');
     24  assert_equals(report.url, contextUrl);
     25  assert_equals(report.body.type, 'navigation');
     26  assert_equals(report.body.blockedURL, blockedUrl);
     27  assert_equals(report.body.disposition, disposition);
     28 }
     29 
     30 function loadFrame(document, url) {
     31  return new Promise((resolve, reject) => {
     32    const frame = document.createElement('iframe');
     33    frame.src = url;
     34    frame.onload = () => resolve(frame);
     35    frame.onerror = reject;
     36    document.body.appendChild(frame);
     37  });
     38 }
     39 
     40 // |parentSuffix| is a suffix for the parent frame URL.
     41 // |targetUrl| is a URL for the target frame.
     42 async function loadFrames(test, parentSuffix, targetUrl) {
     43  const frame = await loadFrame(document, FRAME_URL + parentSuffix);
     44  test.add_cleanup(() => frame.remove());
     45  // Here we don't need "await". This loading may or may not succeed, and
     46  // we're not interested in the result.
     47  loadFrame(frame.contentDocument, targetUrl);
     48 
     49  return frame;
     50 }
     51 
     52 async function observeReports(global, expected_count) {
     53  const reports = [];
     54  const receivedEveryReports = new Promise(resolve => {
     55    if (expected_count == 0)
     56      resolve();
     57 
     58    const observer = new global.ReportingObserver((rs) => {
     59      for (const r of rs) {
     60        reports.push(r.toJSON());
     61      }
     62      if (expected_count <= reports.length)
     63        resolve();
     64    });
     65    observer.observe();
     66 
     67  });
     68 
     69  // Wait 500ms more to catch additionnal unexpected reports.
     70  await receivedEveryReports;
     71  await new Promise(r => step_timeout(r, 500));
     72  return reports;
     73 }
     74 
     75 function desc(headers) {
     76  return headers === '' ? '(none)' : headers;
     77 }
     78 
     79 // CASES is a list of test case. Each test case consists of:
     80 //   parent_headers: the suffix of the URL of the parent frame.
     81 //   target_headers: the suffix of the URL of the target frame.
     82 //   expected_reports: one of:
     83 //     'CORP':    CORP violation
     84 //     'CORP-RO': CORP violation (report only)
     85 //     'NAV':     COEP mismatch between the frames.
     86 //     'NAV-RO':  COEP mismatch between the frames (report only).
     87 const reportingTest = function(
     88  parent_headers, target_headers, expected_reports) {
     89  // These tests are very slow, so they must be run in parallel using
     90  // async_test.
     91  promise_test_parallel(async t => {
     92    const targetUrl = REMOTE_FRAME_URL + target_headers;
     93    const parent = await loadFrames(t, parent_headers, targetUrl);
     94    const contextUrl = parent.src ? parent.src : 'about:blank';
     95    const reports = await observeReports(
     96        parent.contentWindow,
     97        expected_reports.length
     98      );
     99    assert_equals(reports.length, expected_reports.length);
    100    for (let i = 0; i < reports.length; i += 1) {
    101      const report = reports[i];
    102      switch (expected_reports[i]) {
    103        case 'CORP':
    104          checkCorpReport(report, contextUrl, targetUrl, 'enforce');
    105          break;
    106        case 'CORP-RO':
    107          checkCorpReport(report, contextUrl, targetUrl, 'reporting');
    108          break;
    109        case 'NAV':
    110          checkCoepMismatchReport(report, contextUrl, targetUrl, 'enforce');
    111          break;
    112        case 'NAV-RO':
    113          checkCoepMismatchReport(report, contextUrl, targetUrl, 'reporting');
    114          break;
    115        default:
    116          assert_unreached(
    117            'Unexpected report exception: ' + expected_reports[i]);
    118      }
    119    }
    120  }, `parent: ${desc(parent_headers)}, target: ${desc(target_headers)}, `);
    121 }
    122 
    123 reportingTest('', '', []);
    124 reportingTest('', COEP, []);
    125 reportingTest(COEP, COEP, ['CORP']);
    126 reportingTest(COEP, '', ['CORP']);
    127 
    128 reportingTest('', CORP_CROSS_ORIGIN, []);
    129 reportingTest(COEP, CORP_CROSS_ORIGIN, ['NAV']);
    130 
    131 reportingTest('', COEP + CORP_CROSS_ORIGIN, []);
    132 reportingTest(COEP, COEP + CORP_CROSS_ORIGIN, []);
    133 
    134 reportingTest(COEP_RO, COEP, ['CORP-RO']);
    135 reportingTest(COEP_RO, '', ['CORP-RO', 'NAV-RO']);
    136 reportingTest(COEP_RO, CORP_CROSS_ORIGIN, ['NAV-RO']);
    137 reportingTest(COEP_RO, COEP + CORP_CROSS_ORIGIN, []);
    138 
    139 reportingTest(COEP, COEP_RO + CORP_CROSS_ORIGIN, ['NAV']);