tor-browser

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

style.https.html (11221B)


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