tor-browser

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

browser_jsonview_data_blocking.js (5739B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const TEST_PATH = getRootDirectory(gTestPath).replace(
      7  "chrome://mochitests/content",
      8  "http://example.com"
      9 );
     10 
     11 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
     12 const nullP = Services.scriptSecurityManager.createNullPrincipal({});
     13 
     14 // We need 3 levels of nesting just to get to run something against a content
     15 // page, so the devtools limit of 4 levels of nesting don't help:
     16 /* eslint max-nested-callbacks: 0 */
     17 
     18 /**
     19 * Check that we don't expose a JSONView object on data: URI windows where
     20 * we block the load.
     21 */
     22 add_task(async function test_blocked_data_exposure() {
     23  await SpecialPowers.pushPrefEnv({
     24    set: [["security.data_uri.block_toplevel_data_uri_navigations", true]],
     25  });
     26  await BrowserTestUtils.withNewTab(TEST_PATH + "empty.html", async browser => {
     27    const tabCount = gBrowser.tabs.length;
     28    await SpecialPowers.spawn(browser, [], function () {
     29      content.w = content.window.open(
     30        "data:application/vnd.mozilla.json.view,1",
     31        "_blank"
     32      );
     33      ok(
     34        !Cu.waiveXrays(content.w).JSONView,
     35        "Should not have created a JSON View object"
     36      );
     37      // We need to wait for the JSON view machinery to actually have a chance to run.
     38      // We have no way to detect that it has or hasn't, so a setTimeout is the best we
     39      // can do, unfortunately.
     40      return new Promise(resolve => {
     41        content.setTimeout(function () {
     42          // Putting the resolve before the check to avoid JS errors potentially causing
     43          // the test to time out.
     44          resolve();
     45          ok(
     46            !Cu.waiveXrays(content.w).JSONView,
     47            "Should still not have a JSON View object"
     48          );
     49        }, 1000);
     50      });
     51    });
     52    // Without this, if somehow the data: protocol blocker stops working, the
     53    // test would just keep passing.
     54    is(
     55      tabCount,
     56      gBrowser.tabs.length,
     57      "Haven't actually opened a new window/tab"
     58    );
     59  });
     60 });
     61 
     62 /**
     63 * Check that aborted channels also abort sending data from the stream converter.
     64 */
     65 add_task(async function test_converter_abort_should_stop_data_sending() {
     66  const loadInfo = NetUtil.newChannel({
     67    uri: Services.io.newURI("data:text/plain,"),
     68    loadingPrincipal: nullP,
     69    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
     70    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
     71  }).loadInfo;
     72  // Stub all the things.
     73  const chan = {
     74    QueryInterface: ChromeUtils.generateQI([
     75      "nsIChannel",
     76      "nsIWritablePropertyBag",
     77    ]),
     78    URI: Services.io.newURI("data:application/json,{}"),
     79    // loadinfo is builtinclass, need to actually have one:
     80    loadInfo,
     81    notificationCallbacks: {
     82      QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
     83      getInterface() {
     84        // We want a loadcontext here, which is also builtinclass, can't stub.
     85        return docShell;
     86      },
     87    },
     88    status: Cr.NS_OK,
     89    setProperty() {},
     90  };
     91  let onStartFired = false;
     92  const listener = {
     93    QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]),
     94    onStartRequest() {
     95      onStartFired = true;
     96      // This should force the converter to abort, too:
     97      chan.status = Cr.NS_BINDING_ABORTED;
     98    },
     99    onDataAvailable() {
    100      ok(false, "onDataAvailable should never fire");
    101    },
    102  };
    103  const conv = Cc[
    104    "@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
    105  ].createInstance(Ci.nsIStreamConverter);
    106  conv.asyncConvertData(
    107    "application/vnd.mozilla.json.view",
    108    "text/html",
    109    listener,
    110    null
    111  );
    112  conv.onStartRequest(chan);
    113  ok(onStartFired, "Should have fired onStartRequest");
    114 });
    115 
    116 /**
    117 * Check that principal mismatches break things. Note that we're stubbing
    118 * the window associated with the channel to be a browser window; the
    119 * converter should be bailing out because the window's principal won't
    120 * match the null principal to which the converter tries to reset the
    121 * inheriting principal of the channel.
    122 */
    123 add_task(async function test_converter_principal_needs_matching() {
    124  const loadInfo = NetUtil.newChannel({
    125    uri: Services.io.newURI("data:text/plain,"),
    126    loadingPrincipal: nullP,
    127    securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    128    contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
    129  }).loadInfo;
    130  // Stub all the things.
    131  const chan = {
    132    QueryInterface: ChromeUtils.generateQI([
    133      "nsIChannel",
    134      "nsIWritablePropertyBag",
    135    ]),
    136    URI: Services.io.newURI("data:application/json,{}"),
    137    // loadinfo is builtinclass, need to actually have one:
    138    loadInfo,
    139    notificationCallbacks: {
    140      QueryInterface: ChromeUtils.generateQI(["nsIInterfaceRequestor"]),
    141      getInterface() {
    142        // We want a loadcontext here, which is also builtinclass, can't stub.
    143        return docShell;
    144      },
    145    },
    146    status: Cr.NS_OK,
    147    setProperty() {},
    148    cancel(arg) {
    149      this.status = arg;
    150    },
    151  };
    152  let onStartFired = false;
    153  const listener = {
    154    QueryInterface: ChromeUtils.generateQI(["nsIStreamListener"]),
    155    onStartRequest() {
    156      onStartFired = true;
    157    },
    158    onDataAvailable() {
    159      ok(false, "onDataAvailable should never fire");
    160    },
    161  };
    162  const conv = Cc[
    163    "@mozilla.org/streamconv;1?from=" + JSON_VIEW_MIME_TYPE + "&to=*/*"
    164  ].createInstance(Ci.nsIStreamConverter);
    165  conv.asyncConvertData(
    166    "application/vnd.mozilla.json.view",
    167    "text/html",
    168    listener,
    169    null
    170  );
    171  conv.onStartRequest(chan);
    172  ok(onStartFired, "Should have fired onStartRequest");
    173  is(chan.status, Cr.NS_BINDING_ABORTED, "Should have been aborted.");
    174 });