tor-browser

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

sanitizer-config.tentative.html (17722B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <script src="/resources/testharness.js"></script>
      5 <script src="/resources/testharnessreport.js"></script>
      6 </head>
      7 <body>
      8 <script>
      9 test(t => {
     10  let s = new Sanitizer();
     11  assert_true(s instanceof Sanitizer);
     12 }, "Sanitizer constructor without config.");
     13 
     14 test(t => {
     15  let s = new Sanitizer({});
     16  assert_true(s instanceof Sanitizer);
     17 }, "Sanitizer constructor with empty config.");
     18 
     19 test(t => {
     20  let s = new Sanitizer(null);
     21  assert_true(s instanceof Sanitizer);
     22 }, "Sanitizer constructor with null as config.");
     23 
     24 test(t => {
     25  let s = new Sanitizer(undefined);
     26  assert_true(s instanceof Sanitizer);
     27 }, "Sanitizer constructor with undefined as config.");
     28 
     29 test(t => {
     30  let s = new Sanitizer({testConfig: [1,2,3], attr: ["test", "i", "am"]});
     31  assert_true(s instanceof Sanitizer);
     32 }, "Sanitizer constructor with config ignore unknown values.");
     33 
     34 test(t => {
     35  assert_false(new Sanitizer().get().comments);
     36  assert_true(new Sanitizer({}).get().comments);
     37  assert_true(new Sanitizer({comments: true}).get().comments);
     38  assert_false(new Sanitizer({comments: false}).get().comments);
     39 
     40  let s = new Sanitizer();
     41  s.setComments(true);
     42  assert_true(s.get().comments);
     43  s.setComments(false);
     44  assert_false(s.get().comments);
     45  s.setComments("abc");
     46  assert_true(s.get().comments);
     47 }, "SanitizerConfig comments field.");
     48 
     49 test(t => {
     50  assert_false(new Sanitizer().get().dataAttributes);
     51  assert_true(new Sanitizer({ attributes: [] }).get().dataAttributes);
     52  assert_false('dataAttributes' in new Sanitizer({}).get());
     53  assert_false('dataAttributes' in new Sanitizer({ removeAttributes: [] }).get());
     54  assert_true(new Sanitizer({ attributes: [], dataAttributes: true}).get().dataAttributes);
     55  assert_false(new Sanitizer({ attributes: [], dataAttributes: false}).get().dataAttributes);
     56 
     57  let s = new Sanitizer();
     58  s.setDataAttributes(true);
     59  assert_true(s.get().dataAttributes);
     60  s.setDataAttributes(false);
     61  assert_false(s.get().dataAttributes);
     62  s.setDataAttributes("abc");
     63  assert_true(s.get().dataAttributes);
     64 }, "SanitizerConfig dataAttributes field.");
     65 
     66 function assert_object_equals(a, b, description) {
     67  assert_equals(JSON.stringify(a), JSON.stringify(b), description);
     68 }
     69 
     70 function test_normalization(key, value, expected) {
     71  test(t => {
     72    let config = Object.fromEntries([[key, [value]]]);
     73    let s = new Sanitizer(config);
     74    assert_equals(s.get()[key].length, 1);
     75    assert_object_equals(s.get()[key][0], expected);
     76  }, `SanitizerConfig, normalization: ${key}: [${JSON.stringify(value)}]`);
     77 }
     78 
     79 for (key of ["elements", "removeElements", "replaceWithChildrenElements"]) {
     80  // The canonical form of elements always includes an empty removeAttributes list.
     81  let extra = key == "elements" ? {removeAttributes: []} : {};
     82 
     83  test_normalization(key,
     84    "div",
     85    {name: "div", namespace: "http://www.w3.org/1999/xhtml", ...extra});
     86  test_normalization(key,
     87    {name: "b"},
     88    {name: "b", namespace: "http://www.w3.org/1999/xhtml", ...extra});
     89  test_normalization(key,
     90    {name: "b", namespace: null},
     91    {name: "b", namespace: null, ...extra});
     92  test_normalization(key,
     93    {name: "b", namespace: ""},
     94    {name: "b", namespace: null, ...extra});
     95  test_normalization(key,
     96    {name: "p", namespace: "http://www.w3.org/1999/xhtml"},
     97    {name: "p", namespace: "http://www.w3.org/1999/xhtml", ...extra});
     98  test_normalization(key,
     99    {name: "bla", namespace: "http://fantasy.org/namespace"},
    100    {name: "bla", namespace: "http://fantasy.org/namespace", ...extra});
    101 }
    102 for (key of ["attributes", "removeAttributes"]) {
    103  test_normalization(key,
    104    "href",
    105    {name: "href", namespace: null});
    106  test_normalization(key,
    107    {name: "href", namespace: null},
    108    {name: "href", namespace: null});
    109  // https://wicg.github.io/sanitizer-api/#canonicalize-a-sanitizer-name, step 5
    110  test_normalization(key,
    111    {name: "href", namespace: ""},
    112    {name: "href", namespace: null});
    113  test_normalization(key,
    114    {name: "href", namespace: "https://www.w3.org/1999/xlink"},
    115    {name: "href", namespace: "https://www.w3.org/1999/xlink"});
    116 }
    117 
    118 test(t => {
    119  let s = new Sanitizer({elements: ["div", "p"]});
    120  assert_equals(s.get().elements.length, 2);
    121  s.allowElement("bla");
    122  assert_equals(s.get().elements.length, 3);
    123  s.removeElement({name: "div"});
    124  assert_equals(s.get().elements.length, 2);
    125  s.replaceElementWithChildren({name: "p", namespace: "http://www.w3.org/1999/xhtml"});
    126  assert_equals(s.get().elements.length, 1);
    127  assert_object_equals(s.get().elements[0],
    128                       {name: "bla", namespace: "http://www.w3.org/1999/xhtml", removeAttributes: []});
    129 }, "Test elements addition.");
    130 
    131 test(t => {
    132  let s = new Sanitizer({removeElements: ["div", "p"]});
    133  assert_equals(s.get().removeElements.length, 2);
    134  s.removeElement("bla");
    135  assert_equals(s.get().removeElements.length, 3);
    136  s.replaceElementWithChildren({name: "div"});
    137  assert_equals(s.get().removeElements.length, 2);
    138  s.allowElement({name: "p", namespace: "http://www.w3.org/1999/xhtml"});
    139  assert_equals(s.get().removeElements.length, 1);
    140  assert_object_equals(s.get().removeElements[0],
    141                       {name: "bla", namespace: "http://www.w3.org/1999/xhtml"});
    142 }, "Test elements removal.");
    143 
    144 test(t => {
    145  let s = new Sanitizer({replaceWithChildrenElements: ["div", "p"]});
    146  assert_equals(s.get().replaceWithChildrenElements.length, 2);
    147  s.replaceElementWithChildren("bla");
    148  assert_equals(s.get().replaceWithChildrenElements.length, 3);
    149  s.allowElement({name: "div"});
    150  assert_equals(s.get().replaceWithChildrenElements.length, 2);
    151  s.removeElement({name: "p", namespace: "http://www.w3.org/1999/xhtml"});
    152  assert_equals(s.get().replaceWithChildrenElements.length, 1);
    153  assert_object_equals(s.get().replaceWithChildrenElements[0],
    154                       {name: "bla", namespace: "http://www.w3.org/1999/xhtml"});
    155 }, "Test elements replacewithchildren.");
    156 
    157 test(t => {
    158  let s = new Sanitizer({attributes: ["href", "src"]});
    159  assert_equals(s.get().attributes.length, 2);
    160  s.allowAttribute("id");
    161  assert_equals(s.get().attributes.length, 3);
    162  s.removeAttribute({name: "href", namespace: "https://www.w3.org/1999/xlink" });
    163  assert_equals(s.get().attributes.length, 3);
    164  s.removeAttribute({name: "href"});
    165  assert_equals(s.get().attributes.length, 2);
    166  s.removeAttribute({name: "src", namespace: null});
    167  assert_equals(s.get().attributes.length, 1);
    168  assert_object_equals(s.get().attributes[0],
    169                       {name: "id", namespace: null});
    170 }, "Test attribute addition.");
    171 
    172 test(t => {
    173  let s = new Sanitizer({removeAttributes: ["href", "src"]});
    174  assert_equals(s.get().removeAttributes.length, 2);
    175  s.removeAttribute("id");
    176  assert_equals(s.get().removeAttributes.length, 3);
    177  s.allowAttribute({name: "href", namespace: "https://www.w3.org/1999/xlink" });
    178  assert_equals(s.get().removeAttributes.length, 3);
    179  s.allowAttribute({name: "href"});
    180  assert_equals(s.get().removeAttributes.length, 2);
    181  s.allowAttribute({name: "src", namespace: null});
    182  assert_equals(s.get().removeAttributes.length, 1);
    183  assert_object_equals(s.get().removeAttributes[0],
    184                       {name: "id", namespace: null});
    185 }, "Test attribute removal.");
    186 
    187 test(t => {
    188  let s = new Sanitizer({elements: [{name: "div", attributes: ["href", "src"]}]});
    189  assert_equals(s.get().elements.length, 1);
    190  assert_true("attributes" in s.get().elements[0]);
    191  assert_false("removeAttributes" in s.get().elements[0]);
    192  assert_equals(s.get().elements[0].attributes.length, 2);
    193 
    194  s.allowElement({name: "div", namespace: "http://www.w3.org/1999/xhtml",
    195                  attributes: ["class"]});
    196  assert_equals(s.get().elements[0].attributes.length, 1);
    197  assert_object_equals(s.get().elements[0].attributes[0],
    198                       { name: "class", namespace: null });
    199 }, "Test attribute-per-element sets (i.e. overwrites).");
    200 
    201 test(t => {
    202  let s = new Sanitizer({elements: [{name: "div", removeAttributes: ["href", "src"]}]});
    203  assert_equals(s.get().elements.length, 1);
    204  assert_false("attributes" in s.get().elements[0]);
    205  assert_true("removeAttributes" in s.get().elements[0]);
    206  assert_equals(s.get().elements[0].removeAttributes.length, 2);
    207 
    208  s.allowElement({name: "div", namespace: "http://www.w3.org/1999/xhtml",
    209                  removeAttributes: ["class"]});
    210  assert_equals(s.get().elements[0].removeAttributes.length, 1);
    211  assert_object_equals(s.get().elements[0].removeAttributes[0],
    212                       { name: "class", namespace: null });
    213 }, "Test removeAttribute-per-element sets (i.e. overwrites).");
    214 
    215 // Tests for valid/invalid config parameter combinations.
    216 // 1. The config has either an elements or a removeElements key, but not both.
    217 test(() => {
    218  assert_throws_js(TypeError, () => {
    219    new Sanitizer({ elements: [], removeElements: [] });
    220  });
    221 }, "Both elements and removeElements should not be allowed.");
    222 
    223 // 2. The config has either an attributes or a removeAttributes key, but not both.
    224 test(() => {
    225  assert_throws_js(TypeError, () => {
    226    new Sanitizer({ attributes: [], removeAttributes: [] });
    227  });
    228 }, "Both attributes and removeAttributes should not be allowed.");
    229 
    230 // 3. Assert: All SanitizerElementNamespaceWithAttributes, SanitizerElementNamespace, and SanitizerAttributeNamespace items in config are canonical, meaning they have been run through canonicalize a sanitizer element or canonicalize a sanitizer attribute, as appropriate.
    231 // This is tested in the sanitizer-config test file.
    232 
    233 // 4. None of config[elements], config[removeElements], config[replaceWithChildrenElements], config[attributes], or config[removeAttributes], if they exist, has duplicates.
    234 const DUPLICATE_NAMES = [
    235  ["", ""],
    236  ["abc", "abc"],
    237  ["data-xyz", "data-xyz"],
    238  ["abc", {name: "abc"}],
    239  [{name: "abc", namespace: "xyz"}, {name: "abc", namespace: "xyz"}],
    240  [{name: "abc", namespace: ""}, {name: "abc", namespace: null}]
    241 ];
    242 
    243 // NOTE: Elements and attributes have different default namespaces.
    244 const DUPLICATE_ELEMENT_NAMES = DUPLICATE_NAMES.concat([
    245  ["abc", {name: "abc", namespace: "http://www.w3.org/1999/xhtml"}],
    246  [{name: "abc"}, {name: "abc", namespace: "http://www.w3.org/1999/xhtml"}]
    247 ]);
    248 
    249 const DUPLICATE_ATTRIBUTE_NAMES = DUPLICATE_NAMES.concat([
    250  ["abc", {name: "abc", namespace: null}],
    251  [{name: "abc"}, {name: "abc", namespace: null}]
    252 ]);
    253 
    254 test(() => {
    255  for (let names of DUPLICATE_ELEMENT_NAMES) {
    256    assert_throws_js(TypeError, () => {
    257      new Sanitizer({ elements: names });
    258    });
    259  }
    260 }, "config[elements] should not allow duplicates");
    261 
    262 test(() => {
    263  for (let names of DUPLICATE_ELEMENT_NAMES) {
    264    assert_throws_js(TypeError, () => {
    265      new Sanitizer({ removeElements: names });
    266    });
    267  }
    268 }, "config[removeElements] should not allow duplicates");
    269 
    270 test(() => {
    271  for (let names of DUPLICATE_ELEMENT_NAMES) {
    272    assert_throws_js(TypeError, () => {
    273      new Sanitizer({ replaceWithChildrenElements: names });
    274    });
    275  }
    276 }, "config[replaceWithChildrenElements] should not allow duplicates");
    277 
    278 test(() => {
    279  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    280    assert_throws_js(TypeError, () => {
    281      new Sanitizer({ attributes: names });
    282    });
    283  }
    284 }, "config[attributes] should not allow duplicates");
    285 
    286 test(() => {
    287  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    288    assert_throws_js(TypeError, () => {
    289      new Sanitizer({ removeAttributes: names });
    290    });
    291  }
    292 }, "config[removeAttributes] should not allow duplicates");
    293 
    294 // 5. If both config[elements] and config[replaceWithChildrenElements] exist, then the intersection of config[elements] and config[replaceWithChildrenElements] is empty.
    295 test(() => {
    296  for (let [a, b] of DUPLICATE_ELEMENT_NAMES) {
    297    assert_throws_js(TypeError, () => {
    298      new Sanitizer({
    299        elements: [a],
    300        replaceWithChildrenElements: [b],
    301      });
    302    });
    303  }
    304 }, "config[elements] and config[replaceWithChildrenElements] should not intersect");
    305 
    306 // 6. If both config[removeElements] and config[replaceWithChildrenElements] exist, then the intersection of config[removeElements] and config[replaceWithChildrenElements] is empty.
    307 test(() => {
    308  for (let [a, b] of DUPLICATE_ELEMENT_NAMES) {
    309    assert_throws_js(TypeError, () => {
    310      new Sanitizer({
    311        removeElements: [a],
    312        replaceWithChildrenElements: [b],
    313      });
    314    });
    315  }
    316 }, "config[removeElements] and config[replaceWithChildrenElements] should not intersect");
    317 
    318 // 7. If config[attributes] exists:
    319 // 7.1. If config[elements] exists:
    320 // 7.1.1. For each element of config[elements]:
    321 // 7.1.1.1. Neither element[attributes] nor element[removeAttributes], if they exist, has duplicates.
    322 test(() => {
    323  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    324    assert_throws_js(TypeError, () => {
    325      new Sanitizer({
    326        attributes: [],
    327        elements: [{ name: "div", attributes: names }],
    328      });
    329    });
    330  }
    331 }, "Duplicates in element[attributes] with config[attributes] should not be allowed.");
    332 
    333 test(() => {
    334  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    335    assert_throws_js(TypeError, () => {
    336      new Sanitizer({
    337        attributes: [],
    338        elements: [{ name: "div", removeAttributes: names }],
    339      });
    340    });
    341  }
    342 }, "Duplicates in element[removeAttributes] with config[attributes] should not be allowed.");
    343 
    344 // 7.1.1.2. The intersection of config[attributes] and element[attributes] with default « » is empty.
    345 test(() => {
    346  for (let [a, b] of DUPLICATE_ATTRIBUTE_NAMES) {
    347    assert_throws_js(TypeError, () => {
    348      new Sanitizer({
    349        attributes: [a],
    350        elements: [{ name: "div", attributes: [b] }],
    351      });
    352    });
    353  }
    354 }, "config[attributes] and element[attributes] should not intersect.");
    355 
    356 // 7.1.1.3. element[removeAttributes] with default « » is a subset of config[attributes].
    357 test(() => {
    358  assert_throws_js(TypeError, () => {
    359    new Sanitizer({
    360      attributes: ["class"],
    361      elements: [{ name: "div", removeAttributes: ["title"] }],
    362    });
    363  });
    364 }, "element[removeAttributes] should be a subset of config[attributes]");
    365 
    366 // 7.1.1.4. If dataAttributes exists and dataAttributes is true:
    367 // 7.1.1.4.1. element[attributes] does not contain a custom data attribute.
    368 test(() => {
    369  assert_throws_js(TypeError, () => {
    370    new Sanitizer({
    371      attributes: [],
    372      dataAttributes: true,
    373      elements: [{ name: "div", attributes: ["data-foo"] }],
    374    });
    375  });
    376 
    377  assert_throws_js(TypeError, () => {
    378    new Sanitizer({
    379      attributes: [],
    380      dataAttributes: true,
    381      elements: [{ name: "div", attributes: [{ name: "data-bar", namespace: null }] }],
    382    });
    383  });
    384 }, "element[attributes] with a data attribute must not co-exist with config[dataAttributes] set to true.");
    385 
    386 // 7.2. If dataAttributes is true:
    387 // 7.2.1. config[attributes] does not contain a custom data attribute.
    388 test(() => {
    389  assert_throws_js(TypeError, () => {
    390    new Sanitizer({
    391      attributes: ["data-bar"],
    392      dataAttributes: true,
    393    });
    394  });
    395 
    396  assert_throws_js(TypeError, () => {
    397    new Sanitizer({
    398      attributes: [{ name: "data-foo", namespace: null }],
    399      dataAttributes: true,
    400    });
    401  });
    402 }, "config[attributes] with a data attribute must not co-exist with config[dataAttributes] set to true.");
    403 
    404 // 8. If config[removeAttributes] exists:
    405 // 8.1. If config[elements] exists, then for each element of config[elements]:
    406 // 8.1.1. Not both element[attributes] and element[removeAttributes] exist.
    407 test(() => {
    408  assert_throws_js(TypeError, () => {
    409    new Sanitizer({
    410      removeAttributes: [],
    411      elements: [{ name: "div", attributes: [], removeAttributes: [] }],
    412    });
    413  });
    414 }, "element[attributes] and element[removeAttributes] should not both exist with config[removeAttributes].");
    415 
    416 // 8.1.2. Neither element[attributes] nor element[removeAttributes], if they exist, has duplicates.
    417 test(() => {
    418  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    419    assert_throws_js(TypeError, () => {
    420      new Sanitizer({
    421        removeAttributes: [],
    422        elements: [{ name: "div", attributes: names }],
    423      });
    424    });
    425  }
    426 }, "Duplicates in element[attributes] with config[removeAttributes] should not be allowed.");
    427 
    428 test(() => {
    429  for (let names of DUPLICATE_ATTRIBUTE_NAMES) {
    430    assert_throws_js(TypeError, () => {
    431      new Sanitizer({
    432        removeAttributes: [],
    433        elements: [{ name: "div", removeAttributes: names }],
    434      });
    435    });
    436  }
    437 }, "Duplicates in element[removeAttributes] with config[removeAttributes] should not be allowed.");
    438 
    439 // 8.1.3. The intersection of config[removeAttributes] and element[attributes] with default « » is empty.
    440 test(() => {
    441  for (let [a, b] of DUPLICATE_ATTRIBUTE_NAMES) {
    442    assert_throws_js(TypeError, () => {
    443      new Sanitizer({
    444        removeAttributes: [a],
    445        elements: [{ name: "div", attributes: [b] }],
    446      });
    447    });
    448  }
    449 }, "config[removeAttributes] and element[attributes] should not intersect.");
    450 
    451 // 8.1.4. The intersection of config[removeAttributes] and element[removeAttributes] with default « » is empty.
    452 test(() => {
    453  for (let [a, b] of DUPLICATE_ATTRIBUTE_NAMES) {
    454    assert_throws_js(TypeError, () => {
    455      new Sanitizer({
    456        removeAttributes: [a],
    457        elements: [{ name: "div", removeAttributes: [b] }],
    458      });
    459    });
    460  }
    461 }, "config[removeAttributes] and element[removeAttributes] should not intersect.");
    462 
    463 // 8.2. config[dataAttributes] does not exist.
    464 test(() => {
    465  assert_throws_js(TypeError, () => {
    466    new Sanitizer({ removeAttributes: [], dataAttributes: true });
    467  });
    468 
    469  assert_throws_js(TypeError, () => {
    470    new Sanitizer({ removeAttributes: [], dataAttributes: false });
    471  });
    472 }, "Can not use config[dataAttributes] and config[removeAttributes] together.");
    473 </script>
    474 </body>
    475 </html>