tor-browser

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

test_plaintext_sniff.js (5700B)


      1 // Test the plaintext-or-binary sniffer
      2 
      3 "use strict";
      4 
      5 const { HttpServer } = ChromeUtils.importESModule(
      6  "resource://testing-common/httpd.sys.mjs"
      7 );
      8 
      9 // List of Content-Type headers to test.  For each header we have an array.
     10 // The first element in the array is the Content-Type header string.  The
     11 // second element in the array is a boolean indicating whether we allow
     12 // sniffing for that type.
     13 var contentTypeHeaderList = [
     14  ["text/plain", true],
     15  ["text/plain; charset=ISO-8859-1", true],
     16  ["text/plain; charset=iso-8859-1", true],
     17  ["text/plain; charset=UTF-8", true],
     18  ["text/plain; charset=unknown", false],
     19  ["text/plain; param", false],
     20  ["text/plain; charset=ISO-8859-1; param", false],
     21  ["text/plain; charset=iso-8859-1; param", false],
     22  ["text/plain; charset=UTF-8; param", false],
     23  ["text/plain; charset=utf-8", false],
     24  ["text/plain; charset=utf8", false],
     25  ["text/plain; charset=UTF8", false],
     26  ["text/plain; charset=iSo-8859-1", false],
     27 ];
     28 
     29 // List of response bodies to test.  For each response we have an array. The
     30 // first element in the array is the body string.  The second element in the
     31 // array is a boolean indicating whether that string should sniff as binary.
     32 var bodyList = [["Plaintext", false]];
     33 
     34 // List of possible BOMs
     35 var BOMList = [
     36  "\xFE\xFF", // UTF-16BE
     37  "\xFF\xFE", // UTF-16LE
     38  "\xEF\xBB\xBF", // UTF-8
     39  "\x00\x00\xFE\xFF", // UCS-4BE
     40  "\x00\x00\xFF\xFE", // UCS-4LE
     41 ];
     42 
     43 // Build up bodyList.  The things we treat as binary are ASCII codes 0-8,
     44 // 14-26, 28-31.  That is, the control char range, except for tab, newline,
     45 // vertical tab, form feed, carriage return, and ESC (this last being used by
     46 // Shift_JIS, apparently).
     47 function isBinaryChar(ch) {
     48  return (
     49    (0 <= ch && ch <= 8) || (14 <= ch && ch <= 26) || (28 <= ch && ch <= 31)
     50  );
     51 }
     52 
     53 // Test chars on their own
     54 var i;
     55 for (i = 0; i <= 127; ++i) {
     56  bodyList.push([String.fromCharCode(i), isBinaryChar(i)]);
     57 }
     58 
     59 // Test that having a BOM prevents plaintext sniffing
     60 var j;
     61 for (i = 0; i <= 127; ++i) {
     62  for (j = 0; j < BOMList.length; ++j) {
     63    bodyList.push([BOMList[j] + String.fromCharCode(i, i), false]);
     64  }
     65 }
     66 
     67 // Test that having a BOM requires at least 4 chars to kick in
     68 for (i = 0; i <= 127; ++i) {
     69  for (j = 0; j < BOMList.length; ++j) {
     70    bodyList.push([
     71      BOMList[j] + String.fromCharCode(i),
     72      BOMList[j].length == 2 && isBinaryChar(i),
     73    ]);
     74  }
     75 }
     76 
     77 function makeChan(headerIdx, bodyIdx) {
     78  var chan = NetUtil.newChannel({
     79    uri:
     80      "http://localhost:" +
     81      httpserv.identity.primaryPort +
     82      "/" +
     83      headerIdx +
     84      "/" +
     85      bodyIdx,
     86    loadUsingSystemPrincipal: true,
     87  }).QueryInterface(Ci.nsIHttpChannel);
     88 
     89  chan.loadFlags |= Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
     90 
     91  return chan;
     92 }
     93 
     94 function makeListener(headerIdx, bodyIdx) {
     95  var listener = {
     96    onStartRequest: function test_onStartR(request) {
     97      try {
     98        var chan = request.QueryInterface(Ci.nsIChannel);
     99 
    100        Assert.equal(chan.status, Cr.NS_OK);
    101 
    102        var type = chan.contentType;
    103 
    104        var expectedType =
    105          contentTypeHeaderList[headerIdx][1] && bodyList[bodyIdx][1]
    106            ? "application/x-vnd.mozilla.guess-from-ext"
    107            : "text/plain";
    108        if (expectedType != type) {
    109          do_throw(
    110            "Unexpected sniffed type '" +
    111              type +
    112              "'.  " +
    113              "Should be '" +
    114              expectedType +
    115              "'.  " +
    116              "Header is ['" +
    117              contentTypeHeaderList[headerIdx][0] +
    118              "', " +
    119              contentTypeHeaderList[headerIdx][1] +
    120              "].  " +
    121              "Body is ['" +
    122              bodyList[bodyIdx][0].toSource() +
    123              "', " +
    124              bodyList[bodyIdx][1] +
    125              "]."
    126          );
    127        }
    128        Assert.equal(expectedType, type);
    129      } catch (e) {
    130        do_throw("Unexpected exception: " + e);
    131      }
    132 
    133      throw Components.Exception("", Cr.NS_ERROR_ABORT);
    134    },
    135 
    136    onDataAvailable: function test_ODA() {
    137      do_throw("Should not get any data!");
    138    },
    139 
    140    onStopRequest: function test_onStopR() {
    141      // Advance to next test
    142      ++headerIdx;
    143      if (headerIdx == contentTypeHeaderList.length) {
    144        headerIdx = 0;
    145        ++bodyIdx;
    146      }
    147 
    148      if (bodyIdx == bodyList.length) {
    149        do_test_pending();
    150        httpserv.stop(do_test_finished);
    151      } else {
    152        doTest(headerIdx, bodyIdx);
    153      }
    154 
    155      do_test_finished();
    156    },
    157  };
    158 
    159  return listener;
    160 }
    161 
    162 function doTest(headerIdx, bodyIdx) {
    163  var chan = makeChan(headerIdx, bodyIdx);
    164 
    165  var listener = makeListener(headerIdx, bodyIdx);
    166 
    167  chan.asyncOpen(listener);
    168 
    169  do_test_pending();
    170 }
    171 
    172 function createResponse(headerIdx, bodyIdx, metadata, response) {
    173  response.setHeader(
    174    "Content-Type",
    175    contentTypeHeaderList[headerIdx][0],
    176    false
    177  );
    178  response.bodyOutputStream.write(
    179    bodyList[bodyIdx][0],
    180    bodyList[bodyIdx][0].length
    181  );
    182 }
    183 
    184 function makeHandler(headerIdx, bodyIdx) {
    185  var f = function handlerClosure(metadata, response) {
    186    return createResponse(headerIdx, bodyIdx, metadata, response);
    187  };
    188  return f;
    189 }
    190 
    191 var httpserv;
    192 function run_test() {
    193  // disable on Windows for now, because it seems to leak sockets and die.
    194  // Silly operating system!
    195  // This is a really nasty way to detect Windows.  I wish we could do better.
    196  if (mozinfo.os == "win") {
    197    //failing eslint no-empty test
    198  }
    199 
    200  httpserv = new HttpServer();
    201 
    202  for (i = 0; i < contentTypeHeaderList.length; ++i) {
    203    for (j = 0; j < bodyList.length; ++j) {
    204      httpserv.registerPathHandler("/" + i + "/" + j, makeHandler(i, j));
    205    }
    206  }
    207 
    208  httpserv.start(-1);
    209 
    210  doTest(0, 0);
    211 }