tor-browser

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

script.https.html (10813B)


      1 <!doctype html>
      2 <meta name="timeout" content="long">
      3 <meta name="variant" content="?reporting=false">
      4 <meta name="variant" content="?reporting=true">
      5 <script src="/resources/testharness.js"></script>
      6 <script src="/resources/testharnessreport.js"></script>
      7 <script src="/common/dispatcher/dispatcher.js"></script>
      8 <script src="/common/utils.js"></script>
      9 <script src="/common/get-host-info.sub.js"></script>
     10 <script src="/reporting/resources/report-helper.js"></script>
     11 
     12 <body>
     13 <script>
     14  const params = new URLSearchParams(location.search);
     15  const with_reporting = params.get('reporting') === "true";
     16  const {ORIGIN} = get_host_info();
     17  const getAbsoluteUrl = url => {
     18    return new URL(url, window.location.href).href;
     19  }
     20 
     21  const check_report = async (reporting_endpoint, reporting_uuid, iframe_url, url, report_only) => {
     22    const reports = await pollReports(reporting_endpoint, reporting_uuid);
     23    const abs_iframe_url = getAbsoluteUrl(iframe_url);
     24    checkReportExists(reports, 'integrity-violation', abs_iframe_url);
     25    const abs_blocked_url = getAbsoluteUrl(url);
     26    const report = getReport(reports, 'integrity-violation', abs_iframe_url, abs_blocked_url);
     27    assert_not_equals(report, null);
     28    assert_equals(report.body.documentURL, abs_iframe_url);
     29    assert_equals(report.body.blockedURL, abs_blocked_url);
     30    assert_equals(report.body.destination, "script");
     31    assert_equals(report.body.reportOnly, report_only);
     32  };
     33  const blob = new Blob([`window.ran=true;`],
     34                        { type: 'application/javascript' });
     35 
     36  const blob_url = URL.createObjectURL(blob);
     37 
     38  const test_cases = [
     39    {
     40      description: "Ensure that a script without integrity did not run",
     41      url: "/content-security-policy/resources/ran.js",
     42      cross_origin: true,
     43      integrity: "",
     44      policy_violation: true,
     45      add_blocking_header: true,
     46      endpoints: true,
     47      expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false},
     48    },
     49    {
     50      description: "Ensure that a script with unknown integrity algorithm did not run",
     51      url: "/content-security-policy/resources/ran.js",
     52      cross_origin: true,
     53      integrity: "foobar-AAAAAAAAAAAAAAAAAAAa",
     54      policy_violation: true,
     55      add_blocking_header: true,
     56      endpoints: true,
     57      expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false},
     58    },
     59    {
     60      description: "Ensure that a script without integrity algorithm runs and gets reported in report-only mode",
     61      url: "/content-security-policy/resources/ran.js",
     62      cross_origin: true,
     63      integrity: "",
     64      policy_violation: true,
     65      add_blocking_header: false,
     66      endpoints: true,
     67      expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: true},
     68    },
     69    {
     70      description: "Ensure that a no-cors script gets blocked",
     71      url: "/content-security-policy/resources/ran.js",
     72      cross_origin: false,
     73      integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
     74      policy_violation: true,
     75      add_blocking_header: true,
     76      endpoints: true,
     77      expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false},
     78    },
     79    {
     80      description: "Ensure that ReportingObserver gets called without endpoints",
     81      url: "/content-security-policy/resources/ran.js",
     82      cross_origin: false,
     83      integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
     84      policy_violation: true,
     85      add_blocking_header: true,
     86      endpoints: false,
     87      expected: {blocked: ORIGIN + "/content-security-policy/resources/ran.js", ran: false},
     88    },
     89    {
     90      description: "Ensure that a script with integrity runs",
     91      url: "/content-security-policy/resources/ran.js",
     92      cross_origin: true,
     93      integrity: "sha384-tqyFpeo21WFM8HDeUtLqH20GUq/q3D1R6mqTzW3RtyTZ3dAYZJhC1wUcnkgOE2ak",
     94      policy_violation: false,
     95      add_blocking_header: true,
     96      endpoints: true,
     97      expected: {blocked: "", ran: true},
     98    },
     99    {
    100      description: "Ensure that a data URI script with no integrity runs",
    101      url: "data:application/javascript,window.ran=true",
    102      cross_origin: true,
    103      integrity: "",
    104      policy_violation: false,
    105      add_blocking_header: true,
    106      endpoints: true,
    107      expected: {blocked: "", ran: true},
    108    },
    109    {
    110      description: "Ensure that a no-CORS data URI script with no integrity runs",
    111      url: "data:application/javascript,window.ran=true",
    112      cross_origin: false,
    113      integrity: "",
    114      policy_violation: false,
    115      add_blocking_header: true,
    116      endpoints: true,
    117      expected: {blocked: "", ran: true},
    118    },
    119    {
    120      description: "Ensure that a blob URL script with no integrity runs",
    121      url: blob_url,
    122      cross_origin: true,
    123      integrity: "",
    124      policy_violation: false,
    125      add_blocking_header: true,
    126      endpoints: true,
    127      expected: {blocked: "", ran: true},
    128    },
    129    {
    130      description: "Ensure that a no-CORS blob URL script with no integrity runs",
    131      url: blob_url,
    132      cross_origin: false,
    133      integrity: "",
    134      policy_violation: false,
    135      add_blocking_header: true,
    136      endpoints: true,
    137      expected: {blocked: "", ran: true},
    138    },
    139    {
    140      description: "Ensure that an about:blank URL script with no integrity does not trigger a report",
    141      url: "about:blank",
    142      cross_origin: true,
    143      integrity: "",
    144      policy_violation: false,
    145      add_blocking_header: true,
    146      endpoints: false,
    147      expected: {blocked: "", ran: false},
    148    },
    149    {
    150      description: "Ensure that a no-CORS about:blank URL script with no integrity does not trigger a report",
    151      url: "about:blank",
    152      cross_origin: false,
    153      integrity: "",
    154      policy_violation: false,
    155      add_blocking_header: true,
    156      endpoints: false,
    157      expected: {blocked: "", ran: false},
    158    }
    159  ];
    160  test_cases.map(test_case => {
    161    promise_test(async () => {
    162      const REMOTE_EXECUTOR =
    163        `/common/dispatcher/remote-executor.html?pipe=`;
    164      const iframe_uuid = token();
    165 
    166      const params = new URLSearchParams(location.search);
    167      if (params.get('type') === "report") {
    168        if (test_case.expected.blocked) {
    169          return;
    170        }
    171        header_name += "-Report-Only";
    172      }
    173      const reporting_uuid_1 = token();
    174      const reporting_uuid_2 = token();
    175      const reporting_uuid_3 = token();
    176      const reporting_endpoint = `${ORIGIN}/reporting/resources/report.py`;
    177      let header = "";
    178      if (test_case.add_blocking_header) {
    179        header +=
    180          `header(Integrity-Policy,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-1 integrity-endpoint-2\\))`;
    181      }
    182      header +=
    183        `|header(Integrity-Policy-Report-Only,blocked-destinations=\\(script\\)\\, endpoints=\\(integrity-endpoint-3\\))`;
    184      if (test_case.endpoints) {
    185        header +=
    186          `|header(Reporting-Endpoints, integrity-endpoint-1=\"${reporting_endpoint}?reportID=${reporting_uuid_1}\"\\, ` +
    187          `integrity-endpoint-2=\"${reporting_endpoint}?reportID=${reporting_uuid_2}\"\\, ` +
    188          `integrity-endpoint-3=\"${reporting_endpoint}?reportID=${reporting_uuid_3}\")`;
    189      }
    190      const iframe_url = `${REMOTE_EXECUTOR}${encodeURIComponent(header)}&uuid=${iframe_uuid}`;
    191 
    192      const iframe = document.createElement('iframe');
    193      iframe.src = iframe_url;
    194      document.body.appendChild(iframe);
    195 
    196      // Execute code directly from the iframe.
    197      const ctx = new RemoteContext(iframe_uuid);
    198      const result = await ctx.execute_script(async (test_case, with_reporting) => {
    199        window.ran = false;
    200 
    201        // Always set up report observer to catch any unexpected reports
    202        const report_observed_promise = new Promise(r => {
    203          (new ReportingObserver((reports, observer) => {
    204            reports.forEach(report => {
    205              // Match the tested URL against the stripped one:
    206              // https://www.w3.org/TR/reporting-1/#strip-url-for-use-in-reports
    207              // 1. If url’s scheme is not an HTTP(S) scheme, then return url’s scheme.
    208              const test_url = test_case.url.startsWith("http") ?
    209                test_case.url :
    210                test_case.url.split(":")[0];
    211              if (report.body.blockedURL.endsWith(test_url)) {
    212                r(report.body);
    213                observer.disconnect();
    214              }
    215            });
    216          })).observe('integrity-violation');
    217        });
    218 
    219        // Load the script
    220        await new Promise(resolve => {
    221          const script = document.createElement('script');
    222          if (test_case.cross_origin) {
    223            script.crossOrigin="anonymous";
    224          }
    225          if (test_case.integrity) {
    226            script.integrity = test_case.integrity;
    227          }
    228          script.onload = resolve;
    229          script.onerror = resolve;
    230          script.src = test_case.url;
    231          document.body.appendChild(script);
    232        });
    233        let report_body = null;
    234        if (with_reporting) {
    235          if (test_case.policy_violation) {
    236            report_body = await report_observed_promise;
    237          } else {
    238            const timeout_promise = new Promise(r => setTimeout(() => r('timeout'), 100));
    239            const race_result = await Promise.race([report_observed_promise, timeout_promise]);
    240            if (race_result !== 'timeout') {
    241              report_body = race_result;
    242            }
    243          }
    244        }
    245        return { body: report_body, ran: window.ran };
    246      }, [test_case, with_reporting]);
    247 
    248      assert_equals(result.ran, test_case.expected.ran, "Ran");
    249      if (with_reporting) {
    250        if (test_case.policy_violation) {
    251          assert_not_equals(result.body, null, "Expected a policy violation report");
    252          assert_equals(result.body.blockedURL, test_case.expected.blocked);
    253          assert_true(result.body.documentURL.endsWith(iframe_url));
    254          assert_equals(result.body.destination, "script");
    255          assert_equals(result.body.reportOnly, !test_case.add_blocking_header);
    256        } else {
    257          assert_equals(result.body, null, "No policy violation report should be observed");
    258        }
    259 
    260        if (test_case.endpoints && test_case.policy_violation) {
    261          if (test_case.add_blocking_header) {
    262            await check_report(reporting_endpoint, reporting_uuid_1, iframe_url, test_case.url, !test_case.add_blocking_header);
    263            await check_report(reporting_endpoint, reporting_uuid_2, iframe_url, test_case.url, !test_case.add_blocking_header);
    264          }
    265          await check_report(reporting_endpoint, reporting_uuid_3, iframe_url, test_case.url, true);
    266        }
    267      }
    268    }, test_case.description);
    269  });
    270 </script>