tor-browser

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

file_main.html (11553B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 Tests for Mixed Content Blocker
      5 https://bugzilla.mozilla.org/show_bug.cgi?id=62178
      6 -->
      7 <head>
      8  <meta charset="utf-8">
      9  <title>Tests for Bug 62178</title>
     10  <script src="/tests/SimpleTest/EventUtils.js"></script>
     11 </head>
     12 <body>
     13 <div id="testContent"></div>
     14 
     15 <!-- types the Mixed Content Blocker can block
     16     /*
     17  switch (aContentType) {
     18  case nsIContentPolicy::TYPE_OBJECT:
     19  case nsIContentPolicy::TYPE_SCRIPT:
     20  case nsIContentPolicy::TYPE_STYLESHEET:
     21  case nsIContentPolicy::TYPE_SUBDOCUMENT:
     22  case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
     23 
     24  case nsIContentPolicy::TYPE_FONT: - NO TEST:
     25    Load events for external fonts are not detectable by javascript.
     26  case nsIContentPolicy::TYPE_WEBSOCKET: - NO TEST:
     27    websocket connections over https require an encrypted websocket protocol (wss:)
     28 
     29  case nsIContentPolicy::TYPE_IMAGE:
     30  case nsIContentPolicy::TYPE_IMAGESET:
     31  case nsIContentPolicy::TYPE_MEDIA:
     32  case nsIContentPolicy::TYPE_PING:
     33    our ping implementation is off by default and does not comply with the current spec (bug 786347)
     34  case nsIContentPolicy::TYPE_BEACON:
     35 
     36  }
     37     */
     38 -->
     39 
     40 <script>
     41  function ok(a, msg) {
     42    parent.postMessage({msg, check: true, status: !!a}, "http://mochi.test:8888");
     43  }
     44 
     45  function is(a, b, msg) {
     46    ok(a == b, msg);
     47  }
     48 
     49  function report(type, msg) {
     50    parent.postMessage({test: type, msg}, "http://mochi.test:8888");
     51  }
     52 
     53  function uniqueID() {
     54    return Math.floor(Math.random() * 100000)
     55  }
     56  function uniqueIDParam(id) {
     57    return "&uniqueID=" + id;
     58  }
     59 
     60  async function init() {
     61    var baseUrl = "http://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs";
     62    var checkLastRequestUrl = "https://example.com/tests/dom/security/test/mixedcontentblocker/file_server.sjs?lastRequest=true";
     63 
     64    //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
     65    var MAX_COUNT = 100;
     66    var TIMEOUT_INTERVAL = 100;
     67 
     68    var testContent = document.getElementById("testContent");
     69 
     70    async function checkLastRequest() {
     71      const response = await fetch(checkLastRequestUrl);
     72      return response.json();
     73    }
     74 
     75    /* Part 1: Mixed Script tests */
     76 
     77    // Test 1a: insecure object
     78    var object = document.createElement("object");
     79    var objectId = uniqueID();
     80    object.data = baseUrl + "?type=object" + uniqueIDParam(objectId);
     81    object.type = "image/png";
     82    object.width = "200";
     83    object.height = "200";
     84 
     85    testContent.appendChild(object);
     86 
     87    var objectCount = 0;
     88 
     89    function objectStatus(object) {
     90      // Expose our privileged bits on the object.  We will match the MIME type to the one
     91      // provided by file_server.sjs
     92      object = SpecialPowers.wrap(object);
     93      var typeIsSet = object.actualType && (object.actualType !== '');
     94      var isLoaded = typeIsSet && object.actualType === 'application/x-test-match';
     95      if (isLoaded) {
     96        //object loaded
     97        report("object", "insecure object loaded");
     98      }
     99      else {
    100        if(!typeIsSet && objectCount < MAX_COUNT) {
    101          objectCount++;
    102          setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
    103        }
    104        else {
    105          //After we have called setTimeout the maximum number of times, assume object is blocked
    106          report("object", "insecure object blocked");
    107        }
    108      }
    109    }
    110 
    111    // object does not have onload and onerror events. Hence we need a setTimeout to check the object's status
    112    setTimeout(objectStatus, TIMEOUT_INTERVAL, object);
    113 
    114    // Test 1b: insecure script
    115    var script = document.createElement("script");
    116    var scriptId = uniqueID();
    117    var scriptLoad = false;
    118    var scriptCount = 0;
    119    script.src = baseUrl + "?type=script" + uniqueIDParam(scriptId);
    120    script.onload = function(e) {
    121      report("script", "insecure script loaded");
    122      scriptLoad = true;
    123    }
    124    testContent.appendChild(script);
    125 
    126    function scriptStatus(script)
    127    {
    128      if(scriptLoad) {
    129        return;
    130      }
    131      if (scriptCount < MAX_COUNT) {
    132        scriptCount++;
    133        setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
    134      }
    135      else {
    136        //After we have called setTimeout the maximum number of times, assume script is blocked
    137        report("script", "insecure script blocked");
    138      }
    139    }
    140 
    141    // scripts blocked by Content Policy's do not have onerror events (see bug 789856).  Hence we need a setTimeout to check the script's status
    142    setTimeout(scriptStatus, TIMEOUT_INTERVAL, script);
    143 
    144 
    145    // Test 1c: insecure stylesheet
    146    var cssStyleSheet = document.createElement("link");
    147    var cssStyleSheetId = uniqueID();
    148    cssStyleSheet.rel = "stylesheet";
    149    cssStyleSheet.href = baseUrl + "?type=stylesheet" + uniqueIDParam(cssStyleSheetId);
    150    cssStyleSheet.type = "text/css";
    151    testContent.appendChild(cssStyleSheet);
    152 
    153    var styleCount = 0;
    154 
    155    function styleStatus(cssStyleSheet) {
    156      if( cssStyleSheet.sheet || cssStyleSheet.styleSheet || cssStyleSheet.innerHTML ) {
    157        report("stylesheet", "insecure stylesheet loaded");
    158      }
    159      else {
    160        if(styleCount < MAX_COUNT) {
    161          styleCount++;
    162          setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
    163        }
    164        else {
    165          //After we have called setTimeout the maximum number of times, assume stylesheet is blocked
    166          report("stylesheet", "insecure stylesheet blocked");
    167        }
    168      }
    169    }
    170 
    171    // link does not have onload and onerror events. Hence we need a setTimeout to check the link's status
    172    window.setTimeout(styleStatus, TIMEOUT_INTERVAL, cssStyleSheet);
    173 
    174    // Test 1d: insecure iframe
    175    var iframe = document.createElement("iframe");
    176    var iframeId = uniqueID();
    177    iframe.src = baseUrl + "?type=iframe" + uniqueIDParam(iframeId);
    178    iframe.onload = function() {
    179      report("iframe", "insecure iframe loaded");
    180    }
    181    iframe.onerror = function() {
    182      report("iframe", "insecure iframe blocked");
    183    };
    184    testContent.appendChild(iframe);
    185 
    186 
    187    // Test 1e: insecure xhr
    188    await new Promise((resolve) => {
    189      var xhr = new XMLHttpRequest;
    190      var xhrId = uniqueID();
    191      try {
    192        xhr.open("GET", baseUrl + "?type=xhr" + uniqueIDParam(xhrId), true);
    193        xhr.send();
    194        xhr.onloadend = function (oEvent) {
    195          if (xhr.status == 200) {
    196            report("xhr", "insecure xhr loaded");
    197            resolve();
    198          }
    199          else {
    200            report("xhr", "insecure xhr blocked");
    201            resolve();
    202          }
    203        }
    204      } catch(ex) {
    205         report("xhr", "insecure xhr blocked");
    206        resolve();
    207      }
    208    });
    209 
    210    /* Part 2: Mixed Display tests */
    211 
    212    // Shorthand for all image test variants
    213    async function imgHandlers(img, test, uniqueID) {
    214      await new Promise((resolve) => {
    215        img.onload = async () => {
    216          const lastRequest = await checkLastRequest();
    217          is(lastRequest.uniqueID, uniqueID, "UniqueID for the last request matches");
    218          let message = "insecure image loaded";
    219          if (lastRequest.scheme == "https") {
    220            message = "secure image loaded after upgrade";
    221          }
    222          report(test, message);
    223          resolve();
    224        }
    225        img.onerror = async () => {
    226          let message = "insecure image blocked";
    227          report(test, message);
    228          resolve();
    229        }
    230      });
    231    }
    232 
    233    // Test 2a: insecure image
    234    var img = document.createElement("img");
    235    var imgId = uniqueID();
    236    img.src = baseUrl + "?type=img" + uniqueIDParam(imgId);
    237    await imgHandlers(img, "image", imgId);
    238    // We don't need to append the image to the document. Doing so causes the image test to run twice.
    239 
    240    // Test 2b: insecure media
    241    var media = document.createElement("video");
    242    var mediaId = uniqueID();
    243    media.src = baseUrl + "?type=media" + uniqueIDParam(mediaId);
    244    media.width = "320";
    245    media.height = "200";
    246    media.type = "video/ogg";
    247    await new Promise(res => {
    248      media.onloadeddata = async () => {
    249        const lastRequest = await checkLastRequest();
    250        is(lastRequest.uniqueID, mediaId, "UniqueID for the last request matches");
    251        let message = "insecure media loaded";
    252        if (lastRequest.scheme == "https") {
    253          message = "secure media loaded after upgrade";
    254        }
    255        report("media", message);
    256        res();
    257      }
    258      media.onerror = function() {
    259        report("media", "insecure media blocked");
    260        res();
    261      }
    262    });
    263    // We don't need to append the video to the document. Doing so causes the image test to run twice.
    264 
    265    /* Part 3: Mixed Active Tests for Image srcset */
    266 
    267    // Test 3a: image with srcset
    268    var imgA = document.createElement("img");
    269    var imgAId = uniqueID();
    270    imgA.srcset = baseUrl + "?type=img&subtype=imageSrcset" + uniqueIDParam(imgAId);
    271    await imgHandlers(imgA, "imageSrcset", imgAId);
    272 
    273    // Test 3b: image with srcset, using fallback from src, should still use imageset policy
    274    var imgB = document.createElement("img");
    275    var imgBId = uniqueID();
    276    imgB.srcset = " ";
    277    imgB.src = baseUrl + "?type=img&subtype=imageSrcSetFallback" + uniqueIDParam(imgBId);
    278    await imgHandlers(imgB, "imageSrcsetFallback", imgBId);
    279 
    280    // Test 3c: image in <picture>
    281    var imgC = document.createElement("img");
    282    var pictureC = document.createElement("picture");
    283    var sourceC = document.createElement("source");
    284    var sourceCId = uniqueID();
    285    sourceC.srcset = baseUrl + "?type=img&subtype=imagePicture" + uniqueIDParam(sourceCId);
    286    pictureC.appendChild(sourceC);
    287    pictureC.appendChild(imgC);
    288    await imgHandlers(imgC, "imagePicture", sourceCId);
    289 
    290    // Test 3d: Loaded basic image switching to a <picture>, loading
    291    //          same source, should still redo the request with new
    292    //          policy.
    293    var imgD = document.createElement("img");
    294    var imgDId = uniqueID();
    295    var sourceDId = uniqueID();
    296    imgD.src = baseUrl + "?type=img&subtype=imageJoinPicture" + uniqueIDParam(imgDId);
    297    await new Promise(res => {
    298      imgD.onload = imgD.onerror = function() {
    299        // Whether or not it loads, we want to now append it to a picture and observe
    300        var pictureD = document.createElement("picture");
    301        var sourceD = document.createElement("source");
    302        sourceD.srcset = baseUrl + "?type=img&subtype=imageJoinPicture2" + uniqueIDParam(sourceDId);
    303        pictureD.appendChild(sourceD);
    304        pictureD.appendChild(imgD);
    305        res();
    306      }
    307    });
    308    await imgHandlers(imgD, "imageJoinPicture", sourceDId);
    309 
    310    // Test 3e: img load from <picture> source reverts to img.src as it
    311    //          is removed -- the new request should revert to mixed
    312    //          display policy
    313    var imgE = document.createElement("img");
    314    var pictureE = document.createElement("picture");
    315    var pictureEId = uniqueID();
    316    var sourceE = document.createElement("source");
    317    var sourceEId = uniqueID();
    318    sourceE.srcset = baseUrl + "?type=img&subtype=imageLeavePicture" + uniqueIDParam(sourceEId);
    319    pictureE.appendChild(sourceE);
    320    pictureE.appendChild(imgE);
    321    imgE.src = baseUrl + "?type=img&subtype=imageLeavePicture2" + uniqueIDParam(pictureEId);
    322    await new Promise(res => {
    323      imgE.onload = imgE.onerror = function() {
    324        // Whether or not it loads, remove it from the picture and observe
    325        pictureE.removeChild(imgE)
    326        res();
    327      }
    328    });
    329    await imgHandlers(imgE, "imageLeavePicture", pictureEId);
    330  }
    331 
    332  init();
    333 
    334 </script>
    335 </body>
    336 </html>