tor-browser

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

util.js (6025B)


      1 function is_same_sanitizer_name(a, b) {
      2  return a.name === b.name && a.namespace === b.namespace;
      3 }
      4 
      5 // https://pr-preview.s3.amazonaws.com/otherdaniel/purification/pull/296.html#sanitizerconfig-valid
      6 function assert_config_is_valid(config) {
      7  // The config has either an elements or a removeElements key, but not both.
      8  assert_false(
      9    "elements" in config && "removeElements" in config,
     10    "Either elements or a removeElements, but not both",
     11  );
     12  assert_true(
     13    "elements" in config || "removeElements" in config,
     14    "Either elements or a removeElements",
     15  );
     16 
     17  // The config has either an attributes or a removeAttributes key, but not both.
     18  assert_false(
     19    "attributes" in config && "removeAttributes" in config,
     20    "Either attributes or a removeAttributes, but not both",
     21  );
     22  assert_true(
     23    "attributes" in config || "removeAttributes" in config,
     24    "Either attributes or removeAttributes",
     25  );
     26 
     27  // If both config[elements] and config[replaceWithChildrenElements] exist, then the difference of config[elements] and config[replaceWithChildrenElements] is empty.
     28  if (config.elements && config.replaceWithChildrenElements) {
     29    for (let element of config.elements) {
     30      assert_false(
     31        config.replaceWithChildrenElements.some((replaceElement) =>
     32          is_same_sanitizer_name(element, replaceElement),
     33        ),
     34        `replaceWithChildrenElements should not contain ${element.name}`,
     35      );
     36    }
     37  }
     38 
     39  // If both config[removeElements] and config[replaceWithChildrenElements] exist, then the difference of config[removeElements] and config[replaceWithChildrenElements] is empty.
     40  if (config.removeElements && config.replaceWithChildrenElements) {
     41    for (let removeElement of config.removeElements) {
     42      assert_false(
     43        config.replaceWithChildrenElements.some((replaceElement) =>
     44          is_same_sanitizer_name(removeElement, replaceElement),
     45        ),
     46        `replaceWithChildrenElements should not contain ${removeElement.name}`,
     47      );
     48    }
     49  }
     50 
     51  // If config[attributes] exists:
     52  if (config.attributes) {
     53  } else {
     54    if (config.elements) {
     55      for (let element of config.elements) {
     56        // Not both element[attributes] and element[removeAttributes] exist.
     57        assert_false("attributes" in element && "removeAttributes" in element,
     58                     `Element ${element.name} can't have both 'attributes' and 'removeAttributes'`);
     59      }
     60    }
     61 
     62    // config[dataAttributes] does not exist.
     63    assert_false("dataAttributes" in config, "dataAttributes does not exist");
     64  }
     65 }
     66 
     67 function assert_config(config, expected) {
     68  const PROPERTIES = [
     69    "attributes",
     70    "removeAttributes",
     71    "elements",
     72    "removeElements",
     73    "replaceWithChildrenElements",
     74    "comments",
     75    "dataAttributes",
     76  ];
     77 
     78  // Prevent some typos in the expected config.
     79  for (let key of Object.keys(expected)) {
     80    assert_in_array(key, PROPERTIES, "expected");
     81  }
     82  for (let key of Object.keys(config)) {
     83    assert_in_array(key, PROPERTIES, "config");
     84  }
     85 
     86  assert_config_is_valid(config);
     87 
     88  // XXX duplications
     89  // XXX other consistency checks
     90 
     91  function assert_attrs(key, config, expected, prefix = "config") {
     92    // XXX we allow specifying only a subset for expected.
     93    if (!(key in expected)) {
     94      return;
     95    }
     96 
     97    if (expected[key] === undefined) {
     98      assert_false(key in config, `Unexpected '${key}' in ${prefix}`);
     99      return;
    100    }
    101 
    102    assert_true(key in config, `Missing '${key}' from ${prefix}`);
    103    assert_equals(config[key]?.length, expected[key].length, `${prefix}.${key}.length`);
    104    for (let i = 0; i < expected[key].length; i++) {
    105      let attribute = expected[key][i];
    106      if (typeof attribute === "string") {
    107        assert_object_equals(
    108          config[key][i],
    109          { name: attribute, namespace: null },
    110          `${prefix}.${key}[${i}] should match`,
    111        );
    112      } else {
    113        assert_object_equals(
    114          config[key][i],
    115          attribute,
    116          `${prefix}.${key}[${i}] should match`,
    117        );
    118      }
    119    }
    120  }
    121 
    122  assert_attrs("attributes", config, expected);
    123  assert_attrs("removeAttributes", config, expected);
    124 
    125  function assert_elems(key) {
    126    if (!(key in expected)) {
    127      return;
    128    }
    129 
    130    if (expected[key] === undefined) {
    131      assert_false(key in config, `Unexpected '${key}' in config`);
    132      return;
    133    }
    134 
    135    assert_true(key in config, `Missing '${key}' from config`);
    136    assert_equals(config[key]?.length, expected[key].length, `${key}.length`);
    137 
    138    const XHTML_NS = "http://www.w3.org/1999/xhtml";
    139 
    140    for (let i = 0; i < expected[key].length; i++) {
    141      let element = expected[key][i];
    142      // To make writing tests a bit easier we also support the shorthand string syntax.
    143      if (typeof element === "string") {
    144        let extra = key === "elements" ? { removeAttributes: [] } : { };
    145        assert_object_equals(
    146          config[key][i],
    147          { name: element, namespace: XHTML_NS, ...extra },
    148          `${key}[${i}] should match`,
    149        );
    150      } else {
    151        if (key === "elements") {
    152          assert_equals(config[key][i].name, element.name, `${key}[${i}].name should match`);
    153          let ns = "namespace" in element ? element.namespace : XHTML_NS;
    154          assert_equals(config[key][i].namespace, ns, `${key}[${i}].namespace should match`);
    155 
    156          assert_attrs("attributes", config[key][i], element, `config.elements[${i}]`);
    157          assert_attrs("removeAttributes", config[key][i], element, `config.elements[${i}]`);
    158        } else {
    159          assert_object_equals(config[key][i], element, `${key}[${i}] should match`);
    160        }
    161      }
    162    }
    163  }
    164 
    165  assert_elems("elements");
    166  assert_elems("removeElements");
    167  assert_elems("replaceWithChildrenElements");
    168 
    169  if ("comments" in expected) {
    170    assert_equals(config.comments, expected.comments, "comments should match");
    171  }
    172 
    173  if ("dataAttributes" in expected) {
    174    assert_equals(config.dataAttributes, expected.dataAttributes, "dataAttributes should match");
    175  }
    176 }