tor-browser

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

test_unicodeCharacters.js (5706B)


      1 /**
      2 * Any copyright is dedicated to the Public Domain.
      3 * http://creativecommons.org/publicdomain/zero/1.0/
      4 */
      5 
      6 const interpretChar = (chars, index) => {
      7  return chars.charCodeAt(index).toString(16).padStart(4, "0");
      8 };
      9 
     10 const hexEncode = str => {
     11  let result = "";
     12  const len = str.length;
     13  for (let i = 0; i < len; ++i) {
     14    result += interpretChar(str, i);
     15  }
     16  return result;
     17 };
     18 
     19 const collectCorrupted = (expected, actual) => {
     20  const len = Math.min(expected.length, actual.length);
     21  let notEquals = [];
     22  for (let i = 0; i < len; ++i) {
     23    if (expected[i] !== actual[i]) {
     24      notEquals.push([hexEncode(expected[i]), hexEncode(actual[i])]);
     25    }
     26  }
     27  return notEquals;
     28 };
     29 
     30 const sanitizeOutputWithSurrogates = (testValue, prefix = "") => {
     31  let utf8What = prefix;
     32  for (let i = 0; i < testValue.length; ++i) {
     33    const valueChar = testValue.charCodeAt(i);
     34    const isPlanar = 0xd800 <= valueChar && valueChar <= 0xdfff;
     35    utf8What += isPlanar ? "\\u" + interpretChar(testValue, i) : testValue[i];
     36  }
     37  return utf8What;
     38 };
     39 
     40 const getEncodingSample = () => {
     41  const expectedSample =
     42    "3681207208613504e0a5028800b945551988c60050008027ebc2808c00d38e806e03d8210ac906722b85499be9d00000";
     43 
     44  let result = "";
     45  const len = expectedSample.length;
     46  for (let i = 0; i < len; i += 4) {
     47    result += String.fromCharCode(parseInt(expectedSample.slice(i, i + 4), 16));
     48  }
     49  return result;
     50 };
     51 
     52 const getSeparatedBasePlane = () => {
     53  let result = "";
     54  for (let i = 0xffff; i >= 0; --i) {
     55    result += String.fromCharCode(i) + "\n";
     56  }
     57  return result;
     58 };
     59 
     60 const getJoinedBasePlane = () => {
     61  let result = "";
     62  for (let i = 0; i <= 0xffff; ++i) {
     63    result += String.fromCharCode(i);
     64  }
     65  return result;
     66 };
     67 
     68 const getSurrogateCombinations = () => {
     69  const upperLead = String.fromCharCode(0xdbff);
     70  const lowerTrail = String.fromCharCode(0xdc00);
     71 
     72  const regularSlot = ["w", "abcdefghijklmnopqrst", "aaaaaaaaaaaaaaaaaaaa", ""];
     73  const surrogateSlot = [lowerTrail, upperLead];
     74 
     75  let samples = [];
     76  for (const leadSnippet of regularSlot) {
     77    for (const firstSlot of surrogateSlot) {
     78      for (const trailSnippet of regularSlot) {
     79        for (const secondSlot of surrogateSlot) {
     80          samples.push(leadSnippet + firstSlot + secondSlot + trailSnippet);
     81        }
     82        samples.push(leadSnippet + firstSlot + trailSnippet);
     83      }
     84    }
     85  }
     86 
     87  return samples;
     88 };
     89 
     90 const fetchFrom = async (itemKey, sample, meanwhile) => {
     91  const principal = getPrincipal("http://example.com/", {});
     92 
     93  let request = clearOrigin(principal);
     94  await requestFinished(request);
     95 
     96  const storage = getLocalStorage(principal);
     97 
     98  await storage.setItem(itemKey, sample);
     99 
    100  await meanwhile(principal);
    101 
    102  return storage.getItem(itemKey);
    103 };
    104 
    105 /**
    106 * Value fetched from existing snapshot based on
    107 * existing in-memory datastore in the parent process
    108 * without any communication between content/parent
    109 */
    110 const fetchFromExistingSnapshotExistingDatastore = async (itemKey, sample) => {
    111  return fetchFrom(itemKey, sample, async () => {});
    112 };
    113 
    114 /**
    115 * Value fetched from newly created snapshot based on
    116 * existing in-memory datastore in the parent process
    117 */
    118 const fetchFromNewSnapshotExistingDatastore = async (itemKey, sample) => {
    119  return fetchFrom(itemKey, sample, async () => {
    120    await returnToEventLoop();
    121  });
    122 };
    123 
    124 /**
    125 * Value fetched from newly created snapshot based on newly created
    126 * in-memory datastore based on database in the parent process
    127 */
    128 const fetchFromNewSnapshotNewDatastore = async (itemKey, sample) => {
    129  return fetchFrom(itemKey, sample, async principal => {
    130    let request = resetClient(principal);
    131    await requestFinished(request);
    132  });
    133 };
    134 
    135 add_task(async function testSteps() {
    136  /* This test is based on bug 1681300 */
    137  Services.prefs.setBoolPref(
    138    "dom.storage.enable_unsupported_legacy_implementation",
    139    false
    140  );
    141  Services.prefs.setBoolPref("dom.storage.snapshot_reusing", false);
    142 
    143  const reportWhat = (testKey, testValue) => {
    144    if (testKey.length + testValue.length > 82) {
    145      return testKey;
    146    }
    147    return sanitizeOutputWithSurrogates(testValue, /* prefix */ testKey + ":");
    148  };
    149 
    150  const testFetchMode = async (testType, storeAndLookup) => {
    151    const testPairs = [
    152      { testEmptyValue: [""] },
    153      { testSampleKey: [getEncodingSample()] },
    154      { testSeparatedKey: [getSeparatedBasePlane()] },
    155      { testJoinedKey: [getJoinedBasePlane()] },
    156      { testCombinations: getSurrogateCombinations() },
    157    ];
    158 
    159    for (const testPair of testPairs) {
    160      for (const [testKey, expectedValues] of Object.entries(testPair)) {
    161        for (const expected of expectedValues) {
    162          const actual = await storeAndLookup(testKey, expected);
    163          const testInfo = reportWhat(testKey, expected);
    164          is(
    165            null != actual,
    166            true,
    167            testType + ": Value not null for " + testInfo
    168          );
    169          is(
    170            expected.length,
    171            actual.length,
    172            testType + ": Returned size for " + testInfo
    173          );
    174 
    175          const notEquals = collectCorrupted(expected, actual);
    176          for (let i = 0; i < notEquals.length; ++i) {
    177            is(
    178              notEquals[i][0],
    179              notEquals[i][1],
    180              testType + ": Unequal character at " + i + " for " + testInfo
    181            );
    182          }
    183        }
    184      }
    185    }
    186  };
    187 
    188  await testFetchMode(
    189    "ExistingSnapshotExistingDatastore",
    190    fetchFromExistingSnapshotExistingDatastore
    191  );
    192 
    193  await testFetchMode(
    194    "NewSnapshotExistingDatastore",
    195    fetchFromNewSnapshotExistingDatastore
    196  );
    197 
    198  await testFetchMode(
    199    "NewSnapshotNewDatastore",
    200    fetchFromNewSnapshotNewDatastore
    201  );
    202 });