tor-browser

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

lipfuzz.any.js (3872B)


      1 // META: global=window,worker,shadowrealm
      2 'use strict';
      3 
      4 class LipFuzzTransformer {
      5  constructor(substitutions) {
      6    this.substitutions = substitutions;
      7    this.partialChunk = '';
      8    this.lastIndex = undefined;
      9  }
     10 
     11  transform(chunk, controller) {
     12    chunk = this.partialChunk + chunk;
     13    this.partialChunk = '';
     14    // lastIndex is the index of the first character after the last substitution.
     15    this.lastIndex = 0;
     16    chunk = chunk.replace(/\{\{([a-zA-Z0-9_-]+)\}\}/g, this.replaceTag.bind(this));
     17    // Regular expression for an incomplete template at the end of a string.
     18    const partialAtEndRegexp = /\{(\{([a-zA-Z0-9_-]+(\})?)?)?$/g;
     19    // Avoid looking at any characters that have already been substituted.
     20    partialAtEndRegexp.lastIndex = this.lastIndex;
     21    this.lastIndex = undefined;
     22    const match = partialAtEndRegexp.exec(chunk);
     23    if (match) {
     24      this.partialChunk = chunk.substring(match.index);
     25      chunk = chunk.substring(0, match.index);
     26    }
     27    controller.enqueue(chunk);
     28  }
     29 
     30  flush(controller) {
     31    if (this.partialChunk.length > 0) {
     32      controller.enqueue(this.partialChunk);
     33    }
     34  }
     35 
     36  replaceTag(match, p1, offset) {
     37    let replacement = this.substitutions[p1];
     38    if (replacement === undefined) {
     39      replacement = '';
     40    }
     41    this.lastIndex = offset + replacement.length;
     42    return replacement;
     43  }
     44 }
     45 
     46 const substitutions = {
     47  in1: 'out1',
     48  in2: 'out2',
     49  quine: '{{quine}}',
     50  bogusPartial: '{{incompleteResult}'
     51 };
     52 
     53 const cases = [
     54  {
     55    input: [''],
     56    output: ['']
     57  },
     58  {
     59    input: [],
     60    output: []
     61  },
     62  {
     63    input: ['{{in1}}'],
     64    output: ['out1']
     65  },
     66  {
     67    input: ['z{{in1}}'],
     68    output: ['zout1']
     69  },
     70  {
     71    input: ['{{in1}}q'],
     72    output: ['out1q']
     73  },
     74  {
     75    input: ['{{in1}}{{in1}'],
     76    output: ['out1', '{{in1}']
     77  },
     78  {
     79    input: ['{{in1}}{{in1}', '}'],
     80    output: ['out1', 'out1']
     81  },
     82  {
     83    input: ['{{in1', '}}'],
     84    output: ['', 'out1']
     85  },
     86  {
     87    input: ['{{', 'in1}}'],
     88    output: ['', 'out1']
     89  },
     90  {
     91    input: ['{', '{in1}}'],
     92    output: ['', 'out1']
     93  },
     94  {
     95    input: ['{{', 'in1}'],
     96    output: ['', '', '{{in1}']
     97  },
     98  {
     99    input: ['{'],
    100    output: ['', '{']
    101  },
    102  {
    103    input: ['{', ''],
    104    output: ['', '', '{']
    105  },
    106  {
    107    input: ['{', '{', 'i', 'n', '1', '}', '}'],
    108    output: ['', '', '', '', '', '', 'out1']
    109  },
    110  {
    111    input: ['{{in1}}{{in2}}{{in1}}'],
    112    output: ['out1out2out1']
    113  },
    114  {
    115    input: ['{{wrong}}'],
    116    output: ['']
    117  },
    118  {
    119    input: ['{{wron', 'g}}'],
    120    output: ['', '']
    121  },
    122  {
    123    input: ['{{quine}}'],
    124    output: ['{{quine}}']
    125  },
    126  {
    127    input: ['{{bogusPartial}}'],
    128    output: ['{{incompleteResult}']
    129  },
    130  {
    131    input: ['{{bogusPartial}}}'],
    132    output: ['{{incompleteResult}}']
    133  }
    134 ];
    135 
    136 for (const testCase of cases) {
    137  const inputChunks = testCase.input;
    138  const outputChunks = testCase.output;
    139  promise_test(() => {
    140    const lft = new TransformStream(new LipFuzzTransformer(substitutions));
    141    const writer = lft.writable.getWriter();
    142    const promises = [];
    143    for (const inputChunk of inputChunks) {
    144      promises.push(writer.write(inputChunk));
    145    }
    146    promises.push(writer.close());
    147    const reader = lft.readable.getReader();
    148    let readerChain = Promise.resolve();
    149    for (const outputChunk of outputChunks) {
    150      readerChain = readerChain.then(() => {
    151        return reader.read().then(({ value, done }) => {
    152          assert_false(done, `done should be false when reading ${outputChunk}`);
    153          assert_equals(value, outputChunk, `value should match outputChunk`);
    154        });
    155      });
    156    }
    157    readerChain = readerChain.then(() => {
    158      return reader.read().then(({ done }) => assert_true(done, `done should be true`));
    159    });
    160    promises.push(readerChain);
    161    return Promise.all(promises);
    162  }, `testing "${inputChunks}" (length ${inputChunks.length})`);
    163 }