tor-browser

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

test_input_sanitization.html (15177B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=549475
      5 -->
      6 <head>
      7  <title>Test for Bug 549475</title>
      8  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      9  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     10 </head>
     11 <body>
     12 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=549475">Mozilla Bug 549475</a>
     13 <p id="display"></p>
     14 <pre id="test">
     15 <div id='content'>
     16  <form>
     17  </form>
     18 </div>
     19 <script type="application/javascript">
     20 
     21 SimpleTest.requestLongerTimeout(2);
     22 
     23 /**
     24 * This files tests the 'value sanitization algorithm' for the various input
     25 * types. Note that an input's value is affected by more than just its type's
     26 * value sanitization algorithm; e.g. some type=range has actions that the user
     27 * agent must perform to change the element's value to avoid underflow/overflow
     28 * and step mismatch (when possible). We specifically avoid triggering these
     29 * other actions here so that this test only tests the value sanitization
     30 * algorithm for the various input types.
     31 *
     32 * XXXjwatt splitting out testing of the value sanitization algorithm and
     33 * "other things" that affect .value makes it harder to know what we're testing
     34 * and what we've missed, because what's included in the value sanitization
     35 * algorithm and what's not is different from input type to input type. It
     36 * seems to me it would be better to have a test (maybe one per type) focused
     37 * on testing .value for permutations of all other inputs that can affect it.
     38 * The value sanitization algorithm is just an internal spec concept after all.
     39 */
     40 
     41 // We buffer up the results of sets of sub-tests, and avoid outputting log
     42 // entries for them all if they all pass.  Otherwise, we have an enormous amount
     43 // of test output.
     44 
     45 var delayedTests = [];
     46 var anyFailedDelayedTests = false;
     47 
     48 function delayed_is(actual, expected, description)
     49 {
     50  var result = actual == expected;
     51  delayedTests.push({ actual, expected, description });
     52  if (!result) {
     53    anyFailedDelayedTests = true;
     54  }
     55 }
     56 
     57 function flushDelayedTests(description)
     58 {
     59  if (anyFailedDelayedTests) {
     60    info("Outputting individual results for \"" + description + "\" due to failures in subtests");
     61    for (var test of delayedTests) {
     62      is(test.actual, test.expected, test.description);
     63    }
     64  } else {
     65    ok(true, description + " (" + delayedTests.length + " subtests)");
     66  }
     67  delayedTests = [];
     68  anyFailedDelayedTests = false;
     69 }
     70 
     71 // We are excluding "file" because it's too different from the other types.
     72 // And it has no sanitizing algorithm.
     73 var inputTypes =
     74 [
     75  "text", "password", "search", "tel", "hidden", "checkbox", "radio",
     76  "submit", "image", "reset", "button", "email", "url", "number", "date",
     77  "time", "range", "color", "month", "week", "datetime-local"
     78 ];
     79 
     80 var valueModeValue =
     81 [
     82  "text", "search", "url", "tel", "email", "password", "date", "datetime",
     83  "month", "week", "time", "datetime-local", "number", "range", "color",
     84 ];
     85 
     86 function sanitizeDate(aValue)
     87 {
     88  // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-date-string
     89  function getNumbersOfDaysInMonth(aMonth, aYear) {
     90    if (aMonth === 2) {
     91      return (aYear % 400 === 0 || (aYear % 100 != 0 && aYear % 4 === 0)) ? 29 : 28;
     92    }
     93    return (aMonth === 1 || aMonth === 3 || aMonth === 5 || aMonth === 7 ||
     94            aMonth === 8 || aMonth === 10 || aMonth === 12) ? 31 : 30;
     95  }
     96 
     97  var match = /^([0-9]{4,})-([0-9]{2})-([0-9]{2})$/.exec(aValue);
     98  if (!match) {
     99    return "";
    100  }
    101  var year = Number(match[1]);
    102  if (year === 0) {
    103    return "";
    104  }
    105  var month = Number(match[2]);
    106  if (month > 12 || month < 1) {
    107    return "";
    108  }
    109  var day = Number(match[3]);
    110  return 1 <= day && day <= getNumbersOfDaysInMonth(month, year) ? aValue : "";
    111 }
    112 
    113 function sanitizeTime(aValue)
    114 {
    115  // http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#valid-time-string
    116  var match = /^([0-9]{2}):([0-9]{2})(.*)$/.exec(aValue);
    117  if (!match) {
    118    return "";
    119  }
    120  var hours = match[1];
    121  if (hours < 0 || hours > 23) {
    122    return "";
    123  }
    124  var minutes = match[2];
    125  if (minutes < 0 || minutes > 59) {
    126    return "";
    127  }
    128  var other = match[3];
    129  if (other == "") {
    130    return aValue;
    131  }
    132  match = /^:([0-9]{2})(.*)$/.exec(other);
    133  if (!match) {
    134    return "";
    135  }
    136  var seconds = match[1];
    137  if (seconds < 0 || seconds > 59) {
    138    return "";
    139  }
    140  var other = match[2];
    141  if (other == "") {
    142    return aValue;
    143  }
    144  match = /^.([0-9]{1,3})$/.exec(other);
    145  if (!match) {
    146    return "";
    147  }
    148  return aValue;
    149 }
    150 
    151 function sanitizeDateTimeLocal(aValue)
    152 {
    153  // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-local-date-and-time-string
    154  if (aValue.length < 16) {
    155    return "";
    156  }
    157 
    158  var sepIndex = aValue.indexOf("T");
    159  if (sepIndex == -1) {
    160    sepIndex = aValue.indexOf(" ");
    161    if (sepIndex == -1) {
    162      return "";
    163    }
    164  }
    165 
    166  var [date, time] = aValue.split(aValue[sepIndex]);
    167  if (!sanitizeDate(date)) {
    168    return "";
    169  }
    170 
    171  if (!sanitizeTime(time)) {
    172    return "";
    173  }
    174 
    175  // Normalize datetime-local string.
    176  // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-normalised-local-date-and-time-string
    177  if (aValue[sepIndex] == " ") {
    178    aValue = date + "T" + time;
    179  }
    180 
    181  if ((aValue.length - sepIndex) == 6) {
    182    return aValue;
    183  }
    184 
    185  if ((aValue.length - sepIndex) > 9) {
    186    var milliseconds = aValue.substring(sepIndex + 10);
    187    if (Number(milliseconds) != 0) {
    188      return aValue;
    189    }
    190    aValue = aValue.slice(0, sepIndex + 9);
    191  }
    192 
    193  var seconds = aValue.substring(sepIndex + 7);
    194  if (Number(seconds) != 0) {
    195    return aValue;
    196  }
    197  aValue = aValue.slice(0, sepIndex + 6);
    198 
    199  return aValue;
    200 }
    201 
    202 function sanitizeValue(aType, aValue)
    203 {
    204  // http://www.whatwg.org/html/#value-sanitization-algorithm
    205  switch (aType) {
    206    case "text":
    207    case "password":
    208    case "search":
    209    case "tel":
    210      return aValue.replace(/[\n\r]/g, "");
    211    case "url":
    212    case "email":
    213      return aValue.replace(/[\n\r]/g, "").replace(/^[\u0020\u0009\t\u000a\u000c\u000d]+|[\u0020\u0009\t\u000a\u000c\u000d]+$/g, "");
    214    case "number":
    215      return isNaN(Number(aValue)) ? "" : aValue;
    216    case "range":
    217      var defaultMinimum = 0;
    218      var defaultMaximum = 100;
    219      var value = Number(aValue);
    220      if (isNaN(value)) {
    221        return ((defaultMaximum - defaultMinimum)/2).toString(); // "50"
    222      }
    223      if (value < defaultMinimum) {
    224        return defaultMinimum.toString();
    225      }
    226      if (value > defaultMaximum) {
    227        return defaultMaximum.toString();
    228      }
    229      return aValue;
    230    case "date":
    231      return sanitizeDate(aValue);
    232    case "time":
    233      return sanitizeTime(aValue);
    234    case "month":
    235      // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-month-string
    236      var match = /^([0-9]{4,})-([0-9]{2})$/.exec(aValue);
    237      if (!match) {
    238        return "";
    239      }
    240      var year = Number(match[1]);
    241      if (year === 0) {
    242        return "";
    243      }
    244      var month = Number(match[2]);
    245      if (month > 12 || month < 1) {
    246        return "";
    247      }
    248      return aValue;
    249    case "week": {
    250      // https://html.spec.whatwg.org/multipage/infrastructure.html#valid-week-string
    251      function isLeapYear(aYear) {
    252        return ((aYear % 4 == 0) && (aYear % 100 != 0)) || (aYear % 400 == 0);
    253      }
    254      function getDayofWeek(aYear, aMonth, aDay) { /* 0 = Sunday */
    255        // Tomohiko Sakamoto algorithm.
    256        var monthTable = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4];
    257        aYear -= Number(aMonth < 3);
    258 
    259        return (aYear + parseInt(aYear / 4) - parseInt(aYear / 100) +
    260                parseInt(aYear / 400) + monthTable[aMonth - 1] + aDay) % 7;
    261      }
    262      function getMaximumWeekInYear(aYear) {
    263        var day = getDayofWeek(aYear, 1, 1);
    264        return day == 4 || (day == 3 && isLeapYear(aYear)) ? 53 : 52;
    265      }
    266 
    267      var match = /^([0-9]{4,})-W([0-9]{2})$/.exec(aValue);
    268      if (!match) {
    269        return "";
    270      }
    271      var year = Number(match[1]);
    272      if (year === 0) {
    273        return "";
    274      }
    275      var week = Number(match[2]);
    276      if (week > 53 || month < 1) {
    277        return "";
    278      }
    279      return 1 <= week && week <= getMaximumWeekInYear(year) ? aValue : "";
    280    }
    281    case "datetime-local":
    282      return sanitizeDateTimeLocal(aValue);
    283    case "color":
    284      switch (aValue) {
    285        case "red": return "#ff0000";
    286        case "#0f0": return "#00ff00";
    287      }
    288      return /^#[0-9A-Fa-f]{6}$/.exec(aValue) ? aValue.toLowerCase() : "#000000";
    289    default:
    290      return aValue;
    291  }
    292 }
    293 
    294 function checkSanitizing(element, inputTypeDescription)
    295 {
    296  var testData =
    297  [
    298    // For text, password, search, tel, email:
    299    "\n\rfoo\n\r",
    300    "foo\n\rbar",
    301    "  foo  ",
    302    "  foo\n\r  bar  ",
    303    // For url:
    304    "\r\n foobar    \n\r",
    305    "\u000B foo \u000B",
    306    "\u000A foo \u000A",
    307    "\u000C foo \u000C",
    308    "\u000d foo \u000d",
    309    "\u0020 foo \u0020",
    310    " \u0009 foo \u0009 ",
    311    // For number and range:
    312    "42",
    313    "13.37",
    314    "1.234567898765432",
    315    "12foo",
    316    "1e2",
    317    "3E42",
    318    // For date:
    319    "1970-01-01",
    320    "1234-12-12",
    321    "1234567890-01-02",
    322    "2012-12-31",
    323    "2012-02-29",
    324    "2000-02-29",
    325    "1234",
    326    "1234-",
    327    "12345",
    328    "1234-01",
    329    "1234-012",
    330    "1234-01-",
    331    "12-12",
    332    "999-01-01",
    333    "1234-56-78-91",
    334    "1234-567-78",
    335    "1234--7-78",
    336    "abcd-12-12",
    337    "thisinotadate",
    338    "2012-13-01",
    339    "1234-12-42",
    340    " 2012-13-01",
    341    " 123-01-01",
    342    "2012- 3-01",
    343    "12- 10- 01",
    344    "  12-0-1",
    345    "2012-3-001",
    346    "2012-12-00",
    347    "2012-12-1r",
    348    "2012-11-31",
    349    "2011-02-29",
    350    "2100-02-29",
    351    "a2000-01-01",
    352    "2000a-01-0'",
    353    "20aa00-01-01",
    354    "2000a2000-01-01",
    355    "2000-1-1",
    356    "2000-1-01",
    357    "2000-01-1",
    358    "2000-01-01 ",
    359    "2000- 01-01",
    360    "-1970-01-01",
    361    "0000-00-00",
    362    "0001-00-00",
    363    "0000-01-01",
    364    "1234-12 12",
    365    "1234 12-12",
    366    "1234 12 12",
    367    // For time:
    368    "1",
    369    "10",
    370    "10:",
    371    "10:1",
    372    "21:21",
    373    ":21:21",
    374    "-21:21",
    375    " 21:21",
    376    "21-21",
    377    "21:21:",
    378    "21:211",
    379    "121:211",
    380    "21:21 ",
    381    "00:00",
    382    "-1:00",
    383    "24:00",
    384    "00:60",
    385    "01:01",
    386    "23:59",
    387    "99:99",
    388    "8:30",
    389    "19:2",
    390    "19:a2",
    391    "4c:19",
    392    "10:.1",
    393    "1.:10",
    394    "13:37:42",
    395    "13:37.42",
    396    "13:37:42 ",
    397    "13:37:42.",
    398    "13:37:61.",
    399    "13:37:00",
    400    "13:37:99",
    401    "13:37:b5",
    402    "13:37:-1",
    403    "13:37:.1",
    404    "13:37:1.",
    405    "13:37:42.001",
    406    "13:37:42.001",
    407    "13:37:42.abc",
    408    "13:37:42.00c",
    409    "13:37:42.a23",
    410    "13:37:42.12e",
    411    "13:37:42.1e1",
    412    "13:37:42.e11",
    413    "13:37:42.1",
    414    "13:37:42.99",
    415    "13:37:42.0",
    416    "13:37:42.00",
    417    "13:37:42.000",
    418    "13:37:42.-1",
    419    "13:37:42.1.1",
    420    "13:37:42.1,1",
    421    "13:37:42.",
    422    "foo12:12",
    423    "13:37:42.100000000000",
    424    // For color
    425    "#00ff00",
    426    "#000000",
    427    "red",
    428    "#0f0",
    429    "#FFFFAA",
    430    "FFAABB",
    431    "fFAaBb",
    432    "FFAAZZ",
    433    "ABCDEF",
    434    "#7654321",
    435    // For month
    436    "1970-01",
    437    "1234-12",
    438    "123456789-01",
    439    "2013-13",
    440    "0000-00",
    441    "2015-00",
    442    "0001-01",
    443    "1-1",
    444    "888-05",
    445    "2013-3",
    446    "2013-may",
    447    "2000-1a",
    448    "2013-03-13",
    449    "december",
    450    "abcdef",
    451    "12",
    452    "  2013-03",
    453    "2013 - 03",
    454    "2013 03",
    455    "2013/03",
    456    // For week
    457    "1970-W01",
    458    "1970-W53",
    459    "1964-W53",
    460    "1900-W10",
    461    "2004-W53",
    462    "2065-W53",
    463    "2099-W53",
    464    "2010-W53",
    465    "2016-W30",
    466    "1900-W3",
    467    "2016-w30",
    468    "2016-30",
    469    "16-W30",
    470    "2016-Week30",
    471    "2000-100",
    472    "0000-W01",
    473    "00-W01",
    474    "123456-W05",
    475    "1985-W100",
    476    "week",
    477    // For datetime-local
    478    "1970-01-01T00:00",
    479    "1970-01-01Z12:00",
    480    "1970-01-01 00:00:00",
    481    "1970-01-01T00:00:00.0",
    482    "1970-01-01T00:00:00.00",
    483    "1970-01-01T00:00:00.000",
    484    "1970-01-01 00:00:00.20",
    485    "1969-12-31 23:59",
    486    "1969-12-31 23:59:00",
    487    "1969-12-31 23:59:00.000",
    488    "1969-12-31 23:59:00.30",
    489    "123456-01-01T12:00",
    490    "123456-01-01T12:00:00",
    491    "123456-01-01T12:00:00.0",
    492    "123456-01-01T12:00:00.00",
    493    "123456-01-01T12:00:00.000",
    494    "123456-01-01T12:00:30",
    495    "123456-01-01T12:00:00.123",
    496    "10000-12-31 20:00",
    497    "10000-12-31 20:00:00",
    498    "10000-12-31 20:00:00.0",
    499    "10000-12-31 20:00:00.00",
    500    "10000-12-31 20:00:00.000",
    501    "10000-12-31 20:00:30",
    502    "10000-12-31 20:00:00.123",
    503    "2016-13-01T12:00",
    504    "2016-12-32T12:00",
    505    "2016-11-08 15:40:30.0",
    506    "2016-11-08T15:40:30.00",
    507    "2016-11-07T17:30:10",
    508    "2016-12-1T12:45",
    509    "2016-12-01T12:45:30.123456",
    510    "2016-12-01T24:00",
    511    "2016-12-01T12:88:30",
    512    "2016-12-01T12:30:99",
    513    "2016-12-01T12:30:100",
    514    "2016-12-01",
    515    "2016-12-01T",
    516    "2016-Dec-01T00:00",
    517    "12-05-2016T00:00",
    518    "datetime-local"
    519  ];
    520 
    521  for (value of testData) {
    522    element.setAttribute('value', value);
    523    delayed_is(element.value, sanitizeValue(type, value),
    524       "The value has not been correctly sanitized for type=" + type);
    525    delayed_is(element.getAttribute('value'), value,
    526       "The content value should not have been sanitized");
    527 
    528    if (type in valueModeValue) {
    529      element.setAttribute('value', 'tulip');
    530      element.value = value;
    531      delayed_is(element.value, sanitizeValue(type, value),
    532         "The value has not been correctly sanitized for type=" + type);
    533      delayed_is(element.getAttribute('value'), 'tulip',
    534         "The content value should not have been sanitized");
    535    }
    536 
    537    element.setAttribute('value', '');
    538    form.reset();
    539    element.type = 'checkbox'; // We know this type has no sanitizing algorithm.
    540    element.setAttribute('value', value);
    541    delayed_is(element.value, value, "The value should not have been sanitized");
    542    element.type = type;
    543    delayed_is(element.value, sanitizeValue(type, value),
    544       "The value has not been correctly sanitized for type=" + type);
    545    delayed_is(element.getAttribute('value'), value,
    546       "The content value should not have been sanitized");
    547 
    548    element.setAttribute('value', '');
    549    form.reset();
    550    element.setAttribute('value', value);
    551    form.reset();
    552    delayed_is(element.value, sanitizeValue(type, value),
    553       "The value has not been correctly sanitized for type=" + type);
    554    delayed_is(element.getAttribute('value'), value,
    555       "The content value should not have been sanitized");
    556 
    557    // Cleaning-up.
    558    element.setAttribute('value', '');
    559    form.reset();
    560  }
    561 
    562  flushDelayedTests(inputTypeDescription);
    563 }
    564 
    565 for (type of inputTypes) {
    566  var form = document.forms[0];
    567  var element = document.createElement("input");
    568  element.style.display = "none";
    569  element.type = type;
    570  form.appendChild(element);
    571 
    572  checkSanitizing(element, "type=" + type + ", no frame, no editor");
    573 
    574  element.style.display = "";
    575  checkSanitizing(element, "type=" + type + ", frame, no editor");
    576 
    577  element.focus();
    578  element.blur();
    579  checkSanitizing(element, "type=" + type + ", frame, editor");
    580 
    581  element.style.display = "none";
    582  checkSanitizing(element, "type=" + type + ", no frame, editor");
    583 
    584  form.removeChild(element);
    585 }
    586 
    587 </script>
    588 </pre>
    589 </body>
    590 </html>