tor-browser

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

basic-popup-and-iframe-tests.https.js (10730B)


      1 /**
      2 * This test checks the Secure Context state of documents for various
      3 * permutations of document URI types and loading methods.
      4 *
      5 * The hierarchy that is tested is:
      6 *
      7 *   creator-doc > createe-doc
      8 *
      9 * The creator-doc is one of:
     10 *
     11 *   http:
     12 *   https:
     13 *
     14 * The createe-doc is loaded as either a:
     15 *
     16 *   popup
     17 *   iframe
     18 *   sandboxed-iframe
     19 *
     20 * into which we load and test:
     21 *
     22 *   http:
     23 *   https:
     24 *   blob:
     25 *   javascript:
     26 *   about:blank
     27 *   initial about:blank
     28 *   srcdoc
     29 *
     30 * TODO once web-platform-tests supports it:
     31 *   - test http://localhost
     32 *   - test file:
     33 */
     34 
     35 
     36 setup({explicit_done:true});
     37 
     38 
     39 const host_and_dirname = location.host +
     40                         location.pathname.substr(0, location.pathname.lastIndexOf("/") + 1);
     41 
     42 
     43 // Flags to indicate where document types should be loaded for testing:
     44 const eLoadInPopup             = (1 << 0);
     45 const eLoadInUnsandboxedIframe = (1 << 1);
     46 const eLoadInSandboxedIframe   = (1 << 2);
     47 const eLoadInEverything        = eLoadInPopup | eLoadInUnsandboxedIframe | eLoadInSandboxedIframe;
     48 
     49 // Flags indicating if a document type is expected to be a Secure Context:
     50 const eSecureNo              = 1;
     51 const eSecureIfNewWindow     = 2;
     52 const eSecureIfCreatorSecure = 3;
     53 
     54 // Flags indicating how the result of a test is obtained:
     55 const eResultFromPostMessage       = 1;
     56 const eResultFromExaminationOnLoad = 2;
     57 const eResultFromExaminationSync   = 3;
     58 
     59 
     60 const loadTypes = [
     61  new LoadType("an http: URI",
     62               eLoadInEverything,
     63               http_dir + "postMessage-helper.html",
     64               eSecureNo,
     65               eResultFromPostMessage),
     66  new LoadType("an https: URI",
     67               eLoadInEverything,
     68               https_dir + "postMessage-helper.https.html",
     69               eSecureIfNewWindow,
     70               eResultFromPostMessage),
     71  new LoadType("a blob: URI",
     72               eLoadInEverything,
     73               URL.createObjectURL(new Blob(["<script>(opener||parent).postMessage(isSecureContext, '*')</script>"], {type: "text/html"})),
     74               eSecureIfCreatorSecure,
     75               eResultFromPostMessage),
     76  new LoadType("a srcdoc",
     77               // popup not relevant:
     78               eLoadInUnsandboxedIframe | eLoadInSandboxedIframe,
     79               "<script>(opener||parent).postMessage(isSecureContext, '*')</script>",
     80               eSecureIfNewWindow,
     81               eResultFromPostMessage),
     82  new LoadType("a javascript: URI",
     83               // can't load in sandbox:
     84               eLoadInUnsandboxedIframe | eLoadInPopup,
     85               "javascript:(opener||parent).postMessage(isSecureContext, '*')",
     86               eSecureIfCreatorSecure,
     87               eResultFromPostMessage),
     88  new LoadType("about:blank",
     89               // can't obtain state if sandboxed:
     90               eLoadInUnsandboxedIframe | eLoadInPopup,
     91               "about:blank",
     92               eSecureIfCreatorSecure,
     93               eResultFromExaminationOnLoad),
     94  new LoadType("initial about:blank",
     95               // can't obtain state if sandboxed:
     96               eLoadInUnsandboxedIframe | eLoadInPopup,
     97               "about:blank", // we don't wait for this to load, so whatever
     98               eSecureIfCreatorSecure,
     99               eResultFromExaminationSync),
    100  new LoadType("a data: URL",
    101               // can't load in a top-level browsing context
    102               eLoadInUnsandboxedIframe | eLoadInSandboxedIframe,
    103               "data:text/html,<script>parent.postMessage(isSecureContext, '*')</script>",
    104               eSecureIfCreatorSecure,
    105               eResultFromPostMessage),
    106 ];
    107 
    108 const loadTargets = [
    109  new LoadTarget("an iframe",          eLoadInUnsandboxedIframe),
    110  new LoadTarget("a sandboxed iframe", eLoadInSandboxedIframe),
    111  new LoadTarget("a popup",            eLoadInPopup),
    112 ];
    113 
    114 
    115 function LoadType(description, loadInFlags, uri, expectedSecureFlag, resultFrom) {
    116  this.desc = description;
    117  this.loadInFlags = loadInFlags;
    118  this.uri = uri;
    119  this.expectedSecureFlag = expectedSecureFlag;
    120  this.resultFrom = resultFrom;
    121 }
    122 
    123 
    124 function LoadTarget(description, loadInFlag) {
    125  this.desc = description;
    126  this.loadInFlag = loadInFlag;
    127 }
    128 
    129 LoadTarget.prototype.open = function(loadType) {
    130  let loadTarget = this;
    131  this.currentTest.step(function() {
    132    assert_true((loadTarget.loadInFlag & loadType.loadInFlags) != 0,
    133                loadType.desc + " cannot be tested in " + loadTarget.desc);
    134  });
    135  if (this.loadInFlag == eLoadInUnsandboxedIframe) {
    136    let iframe = document.createElement("iframe");
    137    document.body.appendChild(iframe);
    138    iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
    139    return iframe;
    140  }
    141  if (this.loadInFlag == eLoadInSandboxedIframe) {
    142    let iframe = document.body.appendChild(document.createElement("iframe"));
    143    iframe.setAttribute("sandbox", "allow-scripts");
    144    iframe[loadType.desc == "a srcdoc" ? "srcdoc" : "src"] = loadType.uri;
    145    return iframe;
    146  }
    147  if (this.loadInFlag == eLoadInPopup) {
    148    return window.open(loadType.uri);
    149  }
    150  this.currentTest.step(function() {
    151    assert_unreached("Unknown load type flag: " + loadInFlags);
    152  });
    153  return null;
    154 }
    155 
    156 LoadTarget.prototype.close = function(domTarget) {
    157  if (this.loadInFlag == eLoadInUnsandboxedIframe ||
    158      this.loadInFlag == eLoadInSandboxedIframe) {
    159    domTarget.remove();
    160    return;
    161  }
    162  if (this.loadInFlag == eLoadInPopup) {
    163    domTarget.close();
    164    return;
    165  }
    166  this.currentTest.step(function() {
    167    assert_unreached("Unknown load type flag: " + loadInFlags);
    168  });
    169 }
    170 
    171 LoadTarget.prototype.load_and_get_result_for = function(loadType) {
    172  if (!(loadType.loadInFlags & this.loadInFlag)) {
    173    return Promise.reject("not applicable");
    174  }
    175  if (!(this.loadInFlag & eLoadInPopup) &&
    176      location.protocol == "https:" &&
    177      loadType.uri.substr(0,5) == "http:") {
    178    // Mixed content blocker will prevent this load
    179    return Promise.reject("not applicable");
    180  }
    181  this.currentTest = async_test("Test Window.isSecureContext in " + this.desc +
    182                                " loading " + loadType.desc)
    183  if (loadType.resultFrom == eResultFromExaminationSync) {
    184    let domTarget = this.open(loadType);
    185    let isFrame = domTarget instanceof HTMLIFrameElement;
    186    let result = !isFrame ?
    187          domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
    188    this.close(domTarget);
    189    return Promise.resolve({ result: result, isFrame: isFrame});
    190  }
    191  let target = this;
    192  if (loadType.resultFrom == eResultFromExaminationOnLoad) {
    193    return new Promise(function(resolve, reject) {
    194      function handleLoad(event) {
    195        clearTimeout(timer);
    196        let isFrame = domTarget instanceof HTMLIFrameElement;
    197        let result = !isFrame ?
    198              domTarget.isSecureContext : domTarget.contentWindow.isSecureContext;
    199        domTarget.removeEventListener("load", handleLoad);
    200        target.close(domTarget);
    201        resolve({ result: result, isFrame: isFrame});
    202      }
    203      let domTarget = target.open(loadType);
    204      domTarget.addEventListener("load", handleLoad, false);
    205 
    206      // Some browsers don't fire `load` events for `about:blank`. That's weird, but it also
    207      // isn't what we're testing here.
    208      let timer = setTimeout(handleLoad, 500);
    209    });
    210  }
    211  if (loadType.resultFrom == eResultFromPostMessage) {
    212    return new Promise(function(resolve, reject) {
    213      function handleMessage(event) {
    214        let isFrame = domTarget instanceof HTMLIFrameElement;
    215        window.removeEventListener("message", handleMessage);
    216        target.close(domTarget);
    217        resolve({ result: event.data, isFrame: isFrame});
    218      }
    219      window.addEventListener("message", handleMessage, false);
    220      let domTarget = target.open(loadType);
    221   });
    222  }
    223  return Promise.reject("unexpected 'result from' type");
    224 }
    225 
    226 
    227 let current_type_index = -1;
    228 let current_target_index = 0;
    229 
    230 function run_next_test() {
    231  current_type_index++;
    232  if (current_type_index >= loadTypes.length) {
    233    current_type_index = 0;
    234    current_target_index++;
    235    if (current_target_index >= loadTargets.length) {
    236      done();
    237      return; // all test permutations complete
    238    }
    239  }
    240  let loadTarget = loadTargets[current_target_index];
    241  let loadType = loadTypes[current_type_index];
    242  loadTarget.load_and_get_result_for(loadType).then(
    243    function(value) {
    244      run_next_test_soon();
    245      loadTarget.currentTest.step(function() {
    246        // If the new context is always non-secure, the assertion is straightforward.
    247        if (loadType.expectedSecureFlag == eSecureNo) {
    248          assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context");
    249        // If the new context is always secure if opened in a new window, and it's
    250        // been opened in a new window, the assertion is likewise straightforward.
    251        } else if (loadType.expectedSecureFlag == eSecureIfNewWindow && !value.isFrame) {
    252          assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a secure context regardless of its creator's state.");
    253        // Otherwise, we're either dealing with a context that's secure if and only
    254        // if its creator context (e.g. this window) is secure.
    255        } else if ((loadType.expectedSecureFlag == eSecureIfNewWindow && value.isFrame) ||
    256                   (loadType.expectedSecureFlag == eSecureIfCreatorSecure)) {
    257          if (!window.isSecureContext) {
    258            assert_false(value.result, loadType.desc + " in " + loadTarget.desc + " should not create a Secure Context when its creator is not a Secure Context.");
    259          } else {
    260            assert_true(value.result, loadType.desc + " in " + loadTarget.desc + " should create a Secure Context when its creator is a Secure Context");
    261          }
    262        } else {
    263          assert_unreached(loadType.desc + " - unknown expected secure flag: " + expectedSecureFlag);
    264        }
    265        loadTarget.currentTest.done();
    266      });
    267    },
    268    function(failReason) {
    269      run_next_test_soon();
    270      if (failReason == "not applicable") {
    271        return;
    272      }
    273      loadTarget.currentTest.step(function() {
    274        assert_unreached(loadType.desc + " - got unexpected rejected promise");
    275      });
    276    }
    277  );
    278 }
    279 
    280 function run_next_test_soon() {
    281  setTimeout(run_next_test, 0);
    282 }
    283 
    284 function begin() {
    285  test(function() {
    286    if (location.protocol == "http:") {
    287      assert_false(isSecureContext,
    288                   "http: creator should not be a Secure Context");
    289    } else if (location.protocol == "https:") {
    290      assert_true(isSecureContext,
    291                  "https: creator should be a Secure Context");
    292    } else {
    293      assert_unreached("Unknown location.protocol");
    294    }
    295  });
    296  run_next_test();
    297 }