tor-browser

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

test_precursor_principal.js (11125B)


      1 "use strict";
      2 const { TestUtils } = ChromeUtils.importESModule(
      3  "resource://testing-common/TestUtils.sys.mjs"
      4 );
      5 const { XPCShellContentUtils } = ChromeUtils.importESModule(
      6  "resource://testing-common/XPCShellContentUtils.sys.mjs"
      7 );
      8 const { ExtensionTestUtils } = ChromeUtils.importESModule(
      9  "resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
     10 );
     11 
     12 XPCShellContentUtils.init(this);
     13 ExtensionTestUtils.init(this);
     14 
     15 const server = XPCShellContentUtils.createHttpServer({
     16  hosts: ["example.com", "example.org"],
     17 });
     18 
     19 server.registerPathHandler("/static_frames", (request, response) => {
     20  response.setHeader("Content-Type", "text/html");
     21  response.write(`
     22    <iframe name="same_origin" sandbox="allow-scripts allow-same-origin" src="http://example.com/frame"></iframe>
     23    <iframe name="same_origin_sandbox" sandbox="allow-scripts" src="http://example.com/frame"></iframe>
     24    <iframe name="cross_origin" sandbox="allow-scripts allow-same-origin" src="http://example.org/frame"></iframe>
     25    <iframe name="cross_origin_sandbox" sandbox="allow-scripts" src="http://example.org/frame"></iframe>
     26    <iframe name="data_uri" sandbox="allow-scripts allow-same-origin" src="data:text/html,<h1>Data Subframe</h1>"></iframe>
     27    <iframe name="data_uri_sandbox" sandbox="allow-scripts" src="data:text/html,<h1>Data Subframe</h1>"></iframe>
     28    <iframe name="srcdoc" sandbox="allow-scripts allow-same-origin" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe>
     29    <iframe name="srcdoc_sandbox" sandbox="allow-scripts" srcdoc="<h1>Srcdoc Subframe</h1>"></iframe>
     30    <iframe name="blank" sandbox="allow-scripts allow-same-origin"></iframe>
     31    <iframe name="blank_sandbox" sandbox="allow-scripts"></iframe>
     32    <iframe name="redirect_com" sandbox="allow-scripts allow-same-origin" src="/redirect?com"></iframe>
     33    <iframe name="redirect_com_sandbox" sandbox="allow-scripts" src="/redirect?com"></iframe>
     34    <iframe name="redirect_org" sandbox="allow-scripts allow-same-origin" src="/redirect?org"></iframe>
     35    <iframe name="redirect_org_sandbox" sandbox="allow-scripts" src="/redirect?org"></iframe>
     36    <iframe name="ext_redirect_com_com" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?com"></iframe>
     37    <iframe name="ext_redirect_com_com_sandbox" sandbox="allow-scripts" src="/ext_redirect?com"></iframe>
     38    <iframe name="ext_redirect_org_com" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?com"></iframe>
     39    <iframe name="ext_redirect_org_com_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?com"></iframe>
     40    <iframe name="ext_redirect_com_org" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?org"></iframe>
     41    <iframe name="ext_redirect_com_org_sandbox" sandbox="allow-scripts" src="/ext_redirect?org"></iframe>
     42    <iframe name="ext_redirect_org_org" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?org"></iframe>
     43    <iframe name="ext_redirect_org_org_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?org"></iframe>
     44    <iframe name="ext_redirect_com_data" sandbox="allow-scripts allow-same-origin" src="/ext_redirect?data"></iframe>
     45    <iframe name="ext_redirect_com_data_sandbox" sandbox="allow-scripts" src="/ext_redirect?data"></iframe>
     46    <iframe name="ext_redirect_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/ext_redirect?data"></iframe>
     47    <iframe name="ext_redirect_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/ext_redirect?data"></iframe>
     48 
     49    <!-- XXX(nika): These aren't static as they perform loads dynamically - perhaps consider testing them separately? -->
     50    <iframe name="client_replace_org_blank" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?blank"></iframe>
     51    <iframe name="client_replace_org_blank_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?blank"></iframe>
     52    <iframe name="client_replace_org_data" sandbox="allow-scripts allow-same-origin" src="http://example.org/client_replace?data"></iframe>
     53    <iframe name="client_replace_org_data_sandbox" sandbox="allow-scripts" src="http://example.org/client_replace?data"></iframe>
     54  `);
     55 });
     56 
     57 server.registerPathHandler("/frame", (request, response) => {
     58  response.setHeader("Content-Type", "text/html");
     59  response.write(`<h1>HTTP Subframe</h1>`);
     60 });
     61 
     62 server.registerPathHandler("/redirect", (request, response) => {
     63  let redirect;
     64  if (request.queryString == "com") {
     65    redirect = "http://example.com/frame";
     66  } else if (request.queryString == "org") {
     67    redirect = "http://example.org/frame";
     68  } else {
     69    response.setStatusLine(request.httpVersion, 404, "Not found");
     70    return;
     71  }
     72 
     73  response.setStatusLine(request.httpVersion, 302, "Found");
     74  response.setHeader("Location", redirect);
     75 });
     76 
     77 server.registerPathHandler("/client_replace", (request, response) => {
     78  let redirect;
     79  if (request.queryString == "blank") {
     80    redirect = "about:blank";
     81  } else if (request.queryString == "data") {
     82    redirect = "data:text/html,<h1>Data Subframe</h1>";
     83  } else {
     84    response.setStatusLine(request.httpVersion, 404, "Not found");
     85    return;
     86  }
     87 
     88  response.setHeader("Content-Type", "text/html");
     89  response.write(`
     90    <script>
     91      window.location.replace(${JSON.stringify(redirect)});
     92    </script>
     93  `);
     94 });
     95 
     96 add_task(async function sandboxed_precursor() {
     97  // Bug 1725345: Make XPCShellContentUtils.createHttpServer support https
     98  Services.prefs.setBoolPref("dom.security.https_first", false);
     99 
    100  let extension = await ExtensionTestUtils.loadExtension({
    101    manifest: {
    102      permissions: ["webRequest", "webRequestBlocking", "<all_urls>"],
    103    },
    104    background() {
    105      // eslint-disable-next-line no-undef
    106      browser.webRequest.onBeforeRequest.addListener(
    107        details => {
    108          let url = new URL(details.url);
    109          if (!url.pathname.includes("ext_redirect")) {
    110            return {};
    111          }
    112 
    113          let redirectUrl;
    114          if (url.search == "?com") {
    115            redirectUrl = "http://example.com/frame";
    116          } else if (url.search == "?org") {
    117            redirectUrl = "http://example.org/frame";
    118          } else if (url.search == "?data") {
    119            redirectUrl = "data:text/html,<h1>Data Subframe</h1>";
    120          }
    121          return { redirectUrl };
    122        },
    123        { urls: ["<all_urls>"] },
    124        ["blocking"]
    125      );
    126    },
    127  });
    128  await extension.startup();
    129 
    130  registerCleanupFunction(async function () {
    131    await extension.unload();
    132  });
    133 
    134  for (let userContextId of [undefined, 1]) {
    135    let comURI = Services.io.newURI("http://example.com");
    136    let comPrin = Services.scriptSecurityManager.createContentPrincipal(
    137      comURI,
    138      { userContextId }
    139    );
    140    let orgURI = Services.io.newURI("http://example.org");
    141    let orgPrin = Services.scriptSecurityManager.createContentPrincipal(
    142      orgURI,
    143      { userContextId }
    144    );
    145 
    146    let page = await XPCShellContentUtils.loadContentPage(
    147      "http://example.com/static_frames",
    148      {
    149        remote: true,
    150        remoteSubframes: true,
    151        userContextId,
    152      }
    153    );
    154    let bc = page.browsingContext;
    155 
    156    ok(
    157      bc.currentWindowGlobal.documentPrincipal.equals(comPrin),
    158      "toplevel principal matches"
    159    );
    160 
    161    // XXX: This is sketchy as heck, but it's also the easiest way to wait for
    162    // the `window.location.replace` loads to finish.
    163    await TestUtils.waitForCondition(
    164      () =>
    165        bc.children.every(
    166          child =>
    167            !child.currentWindowGlobal.documentURI.spec.includes(
    168              "/client_replace"
    169            )
    170        ),
    171      "wait for every client_replace global to be replaced"
    172    );
    173 
    174    let principals = {};
    175    for (let child of bc.children) {
    176      notEqual(child.name, "", "child frames must have names");
    177      ok(!(child.name in principals), "duplicate child frame name");
    178      principals[child.name] = child.currentWindowGlobal.documentPrincipal;
    179    }
    180 
    181    function principal_is(name, expected) {
    182      let principal = principals[name];
    183      info(`${name} = ${principal.origin}`);
    184      ok(principal.equals(expected), `${name} is correct`);
    185    }
    186    function precursor_is(name, precursor) {
    187      let principal = principals[name];
    188      info(`${name} = ${principal.origin}`);
    189      ok(principal.isNullPrincipal, `${name} is null`);
    190      ok(
    191        principal.precursorPrincipal.equals(precursor),
    192        `${name} has the correct precursor`
    193      );
    194    }
    195 
    196    // Basic loads should have the principals or precursor principals for the
    197    // document being loaded.
    198    principal_is("same_origin", comPrin);
    199    precursor_is("same_origin_sandbox", comPrin);
    200 
    201    principal_is("cross_origin", orgPrin);
    202    precursor_is("cross_origin_sandbox", orgPrin);
    203 
    204    // Loads of a data: URI should complete with a sandboxed principal based on
    205    // the principal which tried to perform the load.
    206    precursor_is("data_uri", comPrin);
    207    precursor_is("data_uri_sandbox", comPrin);
    208 
    209    // Loads which inherit principals, such as srcdoc an about:blank loads,
    210    // should also inherit sandboxed precursor principals.
    211    principal_is("srcdoc", comPrin);
    212    precursor_is("srcdoc_sandbox", comPrin);
    213 
    214    principal_is("blank", comPrin);
    215    precursor_is("blank_sandbox", comPrin);
    216 
    217    // Redirects shouldn't interfere with the final principal, and it should be
    218    // based only on the final URI.
    219    principal_is("redirect_com", comPrin);
    220    precursor_is("redirect_com_sandbox", comPrin);
    221 
    222    principal_is("redirect_org", orgPrin);
    223    precursor_is("redirect_org_sandbox", orgPrin);
    224 
    225    // Extension redirects should act like normal redirects, and still resolve
    226    // with the principal or sandboxed principal of the final URI.
    227    principal_is("ext_redirect_com_com", comPrin);
    228    precursor_is("ext_redirect_com_com_sandbox", comPrin);
    229 
    230    principal_is("ext_redirect_com_org", orgPrin);
    231    precursor_is("ext_redirect_com_org_sandbox", orgPrin);
    232 
    233    principal_is("ext_redirect_org_com", comPrin);
    234    precursor_is("ext_redirect_org_com_sandbox", comPrin);
    235 
    236    principal_is("ext_redirect_org_org", orgPrin);
    237    precursor_is("ext_redirect_org_org_sandbox", orgPrin);
    238 
    239    // When an extension redirects to a data: URI, we use the last non-data: URI
    240    // in the chain as the precursor principal.
    241    // FIXME: This should perhaps use the extension's principal instead?
    242    precursor_is("ext_redirect_com_data", comPrin);
    243    precursor_is("ext_redirect_com_data_sandbox", comPrin);
    244 
    245    precursor_is("ext_redirect_org_data", orgPrin);
    246    precursor_is("ext_redirect_org_data_sandbox", orgPrin);
    247 
    248    // Check that navigations triggred by script within the frames will have the
    249    // correct behaviour when navigating to blank and data URIs.
    250    principal_is("client_replace_org_blank", orgPrin);
    251    precursor_is("client_replace_org_blank_sandbox", orgPrin);
    252 
    253    precursor_is("client_replace_org_data", orgPrin);
    254    precursor_is("client_replace_org_data_sandbox", orgPrin);
    255 
    256    await page.close();
    257  }
    258  Services.prefs.clearUserPref("dom.security.https_first");
    259 });