tor-browser

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

browser_hsts_host.js (6179B)


      1 // Bug 1722489 - HTTPS-Only Mode - Tests evaluation order
      2 // https://bugzilla.mozilla.org/show_bug.cgi?id=1722489
      3 // This test ensures that an http request to an hsts host
      4 // gets upgraded by hsts and not by https-only.
      5 "use strict";
      6 
      7 // Set bools to track that tests ended.
      8 let readMessage = false;
      9 let testFinished = false;
     10 // Visit a secure site that sends an HSTS header to set up the rest of the
     11 // test.
     12 add_task(async function see_hsts_header() {
     13  let setHstsUrl =
     14    getRootDirectory(gTestPath).replace(
     15      "chrome://mochitests/content",
     16      "https://example.com"
     17    ) + "hsts_headers.sjs";
     18  Services.obs.addObserver(observer, "http-on-examine-response");
     19 
     20  let promiseLoaded = BrowserTestUtils.browserLoaded(
     21    gBrowser.selectedBrowser,
     22    false,
     23    setHstsUrl
     24  );
     25  BrowserTestUtils.startLoadingURIString(gBrowser.selectedBrowser, setHstsUrl);
     26  await promiseLoaded;
     27 
     28  await BrowserTestUtils.waitForCondition(() => readMessage);
     29  // Clean up
     30  Services.obs.removeObserver(observer, "http-on-examine-response");
     31 });
     32 
     33 // Test that HTTPS_Only is not performed if HSTS host is visited.
     34 add_task(async function () {
     35  // A longer timeout is necessary for this test than the plain mochitests
     36  // due to opening a new tab with the web console.
     37  requestLongerTimeout(4);
     38 
     39  // Enable HTTPS-Only Mode and register console-listener
     40  await SpecialPowers.pushPrefEnv({
     41    set: [["dom.security.https_only_mode", true]],
     42  });
     43 
     44  Services.console.registerListener(onNewMessage);
     45  const RESOURCE_LINK =
     46    getRootDirectory(gTestPath).replace(
     47      "chrome://mochitests/content",
     48      "http://example.com"
     49    ) + "hsts_headers.sjs";
     50 
     51  // 1. Upgrade page to https://
     52  let promiseLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
     53  BrowserTestUtils.startLoadingURIString(
     54    gBrowser.selectedBrowser,
     55    RESOURCE_LINK
     56  );
     57  await promiseLoaded;
     58 
     59  await BrowserTestUtils.waitForCondition(() => testFinished);
     60 
     61  // Clean up
     62  Services.console.unregisterListener(onNewMessage);
     63 
     64  await SpecialPowers.popPrefEnv();
     65 });
     66 
     67 // Test that when clicking on #fragment with a different scheme (http vs https)
     68 // DOES cause an actual navigation with HSTS, even though https-only mode is
     69 // enabled.
     70 add_task(async function () {
     71  await SpecialPowers.pushPrefEnv({
     72    set: [
     73      ["dom.security.https_only_mode", true],
     74      [
     75        "dom.security.https_only_mode_break_upgrade_downgrade_endless_loop",
     76        false,
     77      ],
     78    ],
     79  });
     80 
     81  const TEST_PAGE =
     82    "http://example.com/browser/dom/security/test/https-only/file_fragment_noscript.html";
     83 
     84  await BrowserTestUtils.withNewTab(
     85    {
     86      gBrowser,
     87      url: TEST_PAGE,
     88      waitForLoad: true,
     89    },
     90    async function (browser) {
     91      const UPGRADED_URL = TEST_PAGE.replace("http:", "https:");
     92 
     93      await SpecialPowers.spawn(browser, [UPGRADED_URL], async function (url) {
     94        is(content.window.location.href, url);
     95 
     96        content.window.addEventListener("scroll", () => {
     97          ok(false, "scroll event should not trigger");
     98        });
     99 
    100        let beforeUnload = new Promise(resolve => {
    101          content.window.addEventListener("beforeunload", resolve, {
    102            once: true,
    103          });
    104        });
    105 
    106        content.window.document.querySelector("#clickMeButton").click();
    107 
    108        // Wait for unload event.
    109        await beforeUnload;
    110      });
    111 
    112      await BrowserTestUtils.browserLoaded(browser);
    113 
    114      await SpecialPowers.spawn(browser, [UPGRADED_URL], async function (url) {
    115        is(content.window.location.href, url + "#foo");
    116      });
    117    }
    118  );
    119 
    120  await SpecialPowers.popPrefEnv();
    121 });
    122 
    123 add_task(async function () {
    124  // Reset HSTS header
    125  readMessage = false;
    126  let clearHstsUrl =
    127    getRootDirectory(gTestPath).replace(
    128      "chrome://mochitests/content",
    129      "https://example.com"
    130    ) + "hsts_headers.sjs?reset";
    131 
    132  Services.obs.addObserver(observer, "http-on-examine-response");
    133  // reset hsts header
    134  let promiseLoaded = BrowserTestUtils.browserLoaded(
    135    gBrowser.selectedBrowser,
    136    false,
    137    clearHstsUrl
    138  );
    139  await BrowserTestUtils.startLoadingURIString(
    140    gBrowser.selectedBrowser,
    141    clearHstsUrl
    142  );
    143  await promiseLoaded;
    144  await BrowserTestUtils.waitForCondition(() => readMessage);
    145  // Clean up
    146  Services.obs.removeObserver(observer, "http-on-examine-response");
    147 });
    148 
    149 function observer(subject, topic) {
    150  info("observer called with " + topic);
    151  if (topic == "http-on-examine-response") {
    152    onExamineResponse(subject);
    153  }
    154 }
    155 
    156 function onExamineResponse(subject) {
    157  let channel = subject.QueryInterface(Ci.nsIHttpChannel);
    158  // If message was already read or is not related to "example.com",
    159  // don't examine it.
    160  if (!channel.URI.spec.includes("example.com") || readMessage) {
    161    return;
    162  }
    163  info("onExamineResponse with " + channel.URI.spec);
    164  if (channel.URI.spec.includes("reset")) {
    165    try {
    166      let hsts = channel.getResponseHeader("Strict-Transport-Security");
    167      is(hsts, "max-age=0", "HSTS header is not set");
    168    } catch (e) {
    169      ok(false, "HSTS header still set");
    170    }
    171    readMessage = true;
    172    return;
    173  }
    174  try {
    175    let hsts = channel.getResponseHeader("Strict-Transport-Security");
    176    let csp = channel.getResponseHeader("Content-Security-Policy");
    177    // Check that HSTS and CSP upgrade headers are set
    178    is(hsts, "max-age=60", "HSTS header is set");
    179    is(csp, "upgrade-insecure-requests", "CSP header is set");
    180  } catch (e) {
    181    ok(false, "No header set");
    182  }
    183  readMessage = true;
    184 }
    185 
    186 function onNewMessage(msgObj) {
    187  const message = msgObj.message;
    188  // ensure that request is not upgraded HTTPS-Only.
    189  if (message.includes("Upgrading insecure request")) {
    190    ok(false, "Top-Level upgrade shouldn't get logged");
    191    testFinished = true;
    192  } else if (
    193    message.includes("Upgrading insecure speculative TCP connection")
    194  ) {
    195    // TODO: Check assertion
    196    // https://bugzilla.mozilla.org/show_bug.cgi?id=1735683
    197    ok(true, "Top-Level upgrade shouldn't get logged");
    198    testFinished = true;
    199  } else if (gBrowser.selectedBrowser.currentURI.scheme === "https") {
    200    ok(true, "Top-Level upgrade shouldn't get logged");
    201    testFinished = true;
    202  }
    203 }