|  | 
| 3 | 3 | <head> | 
| 4 | 4 |   <script src="/resources/testharness.js"></script> | 
| 5 | 5 |   <script src="/resources/testharnessreport.js"></script> | 
|  | 6 | +  <script src="./support/util.js"></script> | 
| 6 | 7 | </head> | 
| 7 | 8 | <body> | 
| 8 | 9 | <script> | 
| 9 |  | -function is_same_sanitizer_name(a, b) { | 
| 10 |  | -  return a.name === b.name && a.namespace === b.namespace; | 
| 11 |  | -} | 
| 12 |  | - | 
| 13 |  | -// https://pr-preview.s3.amazonaws.com/otherdaniel/purification/pull/296.html#sanitizerconfig-valid | 
| 14 |  | -function assert_config_is_valid(config) { | 
| 15 |  | -  // The config has either an elements or a removeElements key, but not both. | 
| 16 |  | -  assert_false( | 
| 17 |  | -    "elements" in config && "removeElements" in config, | 
| 18 |  | -    "Either elements or a removeElements, but not both", | 
| 19 |  | -  ); | 
| 20 |  | -  assert_true( | 
| 21 |  | -    "elements" in config || "removeElements" in config, | 
| 22 |  | -    "Either elements or a removeElements", | 
| 23 |  | -  ); | 
| 24 |  | - | 
| 25 |  | -  // The config has either an attributes or a removeAttributes key, but not both. | 
| 26 |  | -  assert_false( | 
| 27 |  | -    "attributes" in config && "removeAttributes" in config, | 
| 28 |  | -    "Either attributes or a removeAttributes, but not both", | 
| 29 |  | -  ); | 
| 30 |  | -  assert_true( | 
| 31 |  | -    "attributes" in config || "removeAttributes" in config, | 
| 32 |  | -    "Either attributes or removeAttributes", | 
| 33 |  | -  ); | 
| 34 |  | - | 
| 35 |  | -  // If both config[elements] and config[replaceWithChildrenElements] exist, then the difference of config[elements] and config[replaceWithChildrenElements] is empty. | 
| 36 |  | -  if (config.elements && config.replaceWithChildrenElements) { | 
| 37 |  | -    for (let element of config.elements) { | 
| 38 |  | -      assert_false( | 
| 39 |  | -        config.replaceWithChildrenElements.some((replaceElement) => | 
| 40 |  | -          is_same_sanitizer_name(element, replaceElement), | 
| 41 |  | -        ), | 
| 42 |  | -        `replaceWithChildrenElements should not contain ${element.name}`, | 
| 43 |  | -      ); | 
| 44 |  | -    } | 
| 45 |  | -  } | 
| 46 |  | - | 
| 47 |  | -  // If both config[removeElements] and config[replaceWithChildrenElements] exist, then the difference of config[removeElements] and config[replaceWithChildrenElements] is empty. | 
| 48 |  | -  if (config.removeElements && config.replaceWithChildrenElements) { | 
| 49 |  | -    for (let removeElement of config.removeElements) { | 
| 50 |  | -      assert_false( | 
| 51 |  | -        config.replaceWithChildrenElements.some((replaceElement) => | 
| 52 |  | -          is_same_sanitizer_name(removeElement, replaceElement), | 
| 53 |  | -        ), | 
| 54 |  | -        `replaceWithChildrenElements should not contain ${removeElement.name}`, | 
| 55 |  | -      ); | 
| 56 |  | -    } | 
| 57 |  | -  } | 
| 58 |  | - | 
| 59 |  | -  // If config[attributes] exists: | 
| 60 |  | -  if (config.attributes) { | 
| 61 |  | -  } else { | 
| 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 dataAttributes | 
| 89 |  | -  // XXX comments | 
| 90 |  | -  // XXX duplications | 
| 91 |  | -  // XXX other consistency checks | 
| 92 |  | - | 
| 93 |  | -  function assert_attrs(key, config, expected, prefix = "config") { | 
| 94 |  | -    // XXX we allow specifying only a subset for expected. | 
| 95 |  | -    if (!(key in expected)) { | 
| 96 |  | -      return; | 
| 97 |  | -    } | 
| 98 |  | - | 
| 99 |  | -    if (expected[key] === undefined) { | 
| 100 |  | -      assert_false(key in config, `Unexpected '${key}' in ${prefix}`); | 
| 101 |  | -      return; | 
| 102 |  | -    } | 
| 103 |  | - | 
| 104 |  | -    assert_true(key in config, `Missing '${key}' from ${prefix}`); | 
| 105 |  | -    assert_equals(config[key]?.length, expected[key].length, `${prefix}.${key}.length`); | 
| 106 |  | -    for (let i = 0; i < expected[key].length; i++) { | 
| 107 |  | -      let attribute = expected[key][i]; | 
| 108 |  | -      if (typeof attribute === "string") { | 
| 109 |  | -        assert_object_equals( | 
| 110 |  | -          config[key][i], | 
| 111 |  | -          { name: attribute, namespace: null }, | 
| 112 |  | -          `${prefix}.${key}[${i}] should match`, | 
| 113 |  | -        ); | 
| 114 |  | -      } else { | 
| 115 |  | -        assert_object_equals( | 
| 116 |  | -          config[key][i], | 
| 117 |  | -          attribute, | 
| 118 |  | -          `${prefix}.${key}[${i}] should match`, | 
| 119 |  | -        ); | 
| 120 |  | -      } | 
| 121 |  | -    } | 
| 122 |  | -  } | 
| 123 |  | - | 
| 124 |  | -  assert_attrs("attributes", config, expected); | 
| 125 |  | -  assert_attrs("removeAttributes", config, expected); | 
| 126 |  | - | 
| 127 |  | -  function assert_elems(key) { | 
| 128 |  | -    if (!(key in expected)) { | 
| 129 |  | -      return; | 
| 130 |  | -    } | 
| 131 |  | - | 
| 132 |  | -    if (expected[key] === undefined) { | 
| 133 |  | -      assert_false(key in config, `Unexpected '${key}' in config`); | 
| 134 |  | -      return; | 
| 135 |  | -    } | 
| 136 |  | - | 
| 137 |  | -    assert_true(key in config, `Missing '${key}' from config`); | 
| 138 |  | -    assert_equals(config[key]?.length, expected[key].length, `${key}.length`); | 
| 139 |  | - | 
| 140 |  | -    const XHTML_NS = "http://www.w3.org/1999/xhtml"; | 
| 141 |  | - | 
| 142 |  | -    for (let i = 0; i < expected[key].length; i++) { | 
| 143 |  | -      let element = expected[key][i]; | 
| 144 |  | -      // To make writing tests a bit easier we also support the shorthand string syntax. | 
| 145 |  | -      if (typeof element === "string") { | 
| 146 |  | -        let extra = key === "elements" ? { removeAttributes: [] } : { }; | 
| 147 |  | -        assert_object_equals( | 
| 148 |  | -          config[key][i], | 
| 149 |  | -          { name: element, namespace: XHTML_NS, ...extra }, | 
| 150 |  | -          `${key}[${i}] should match`, | 
| 151 |  | -        ); | 
| 152 |  | -      } else { | 
| 153 |  | -        if (key === "elements") { | 
| 154 |  | -          assert_equals(config[key][i].name, element.name, `${key}[${i}].name should match`); | 
| 155 |  | -          let ns = "namespace" in element ? element.namespace : XHTML_NS; | 
| 156 |  | -          assert_equals(config[key][i].namespace, ns, `${key}[${i}].namespace should match`); | 
| 157 |  | - | 
| 158 |  | -          assert_attrs("attributes", config[key][i], element, `config.elements[${i}]`); | 
| 159 |  | -          assert_attrs("removeAttributes", config[key][i], element, `config.elements[${i}]`); | 
| 160 |  | -        } else { | 
| 161 |  | -          assert_object_equals(config[key][i], element, `${key}[${i}] should match`); | 
| 162 |  | -        } | 
| 163 |  | -      } | 
| 164 |  | -    } | 
| 165 |  | -  } | 
| 166 |  | - | 
| 167 |  | -  assert_elems("elements"); | 
| 168 |  | -  assert_elems("removeElements"); | 
| 169 |  | -  assert_elems("replaceWithChildrenElements"); | 
| 170 |  | -} | 
| 171 |  | - | 
| 172 | 10 | const NS = "http://example.org/"; | 
| 173 | 11 | 
 | 
| 174 | 12 | test(() => { | 
|  | 
| 339 | 177 | 
 | 
| 340 | 178 |   assert_true(s.removeAttribute("dir")); | 
| 341 | 179 |   assert_config(s.get(), { | 
| 342 |  | -    removeAttributes: ["title", "dir"], | 
|  | 180 | +    removeAttributes: ["dir", "title"], | 
| 343 | 181 |     elements: [{ name: "div", attributes: ["class"], removeAttributes: ["id"] }], | 
| 344 | 182 |   }); | 
| 345 | 183 | 
 | 
| 346 | 184 |   assert_true(s.removeAttribute("id")); | 
| 347 | 185 |   assert_config(s.get(), { | 
| 348 |  | -    removeAttributes: ["title", "dir", "id"], | 
|  | 186 | +    removeAttributes: ["dir", "id", "title"], | 
| 349 | 187 |     elements: [{ name: "div", attributes: ["class"], removeAttributes: [] }], | 
| 350 | 188 |   }); | 
| 351 | 189 | 
 | 
| 352 | 190 |   assert_true(s.removeAttribute("class")); | 
| 353 | 191 |   assert_config(s.get(), { | 
| 354 |  | -    removeAttributes: ["title", "dir", "id", "class"], | 
|  | 192 | +    removeAttributes: ["class", "dir", "id", "title"], | 
| 355 | 193 |     elements: [{ name: "div", attributes: [], removeAttributes: [] }], | 
| 356 | 194 |   }); | 
| 357 | 195 | }, "sanitizer.removeAttribute() with global removeAttributes and elements"); | 
|  | 
| 364 | 202 | 
 | 
| 365 | 203 |   assert_false(s.removeElement("span")); | 
| 366 | 204 |   assert_config(s.get(), { | 
| 367 |  | -    elements: ["p", { name: "p", namespace: NS }], | 
|  | 205 | +    elements: [{ name: "p", namespace: NS }, "p"], | 
| 368 | 206 |     replaceWithChildrenElements: ["b"], | 
| 369 | 207 |   }); | 
| 370 | 208 | 
 | 
| 371 | 209 |   assert_true(s.removeElement("b")); | 
| 372 | 210 |   assert_config(s.get(), { | 
| 373 |  | -    elements: ["p", { name: "p", namespace: NS }], | 
|  | 211 | +    elements: [{ name: "p", namespace: NS }, "p"], | 
| 374 | 212 |     replaceWithChildrenElements: [], | 
| 375 | 213 |   }); | 
| 376 | 214 | 
 | 
|  | 
| 395 | 233 | 
 | 
| 396 | 234 |   assert_false(s.removeElement("p")); | 
| 397 | 235 |   assert_config(s.get(), { | 
| 398 |  | -    removeElements: ["p", { name: "p", namespace: NS }], | 
|  | 236 | +    removeElements: [{ name: "p", namespace: NS }, "p"], | 
| 399 | 237 |     replaceWithChildrenElements: ["b"], | 
| 400 | 238 |   }); | 
| 401 | 239 | 
 | 
| 402 | 240 |   assert_false(s.removeElement({ name: "p", namespace: NS })); | 
| 403 | 241 |   assert_config(s.get(), { | 
| 404 |  | -    removeElements: ["p", { name: "p", namespace: NS }], | 
|  | 242 | +    removeElements: [{ name: "p", namespace: NS }, "p"], | 
| 405 | 243 |     replaceWithChildrenElements: ["b"], | 
| 406 | 244 |   }); | 
| 407 | 245 | 
 | 
| 408 | 246 |   assert_true(s.removeElement("span")); | 
| 409 | 247 |   assert_config(s.get(), { | 
| 410 |  | -    removeElements: ["p", { name: "p", namespace: NS }, "span"], | 
|  | 248 | +    removeElements: [{ name: "p", namespace: NS }, "p", "span"], | 
| 411 | 249 |     replaceWithChildrenElements: ["b"], | 
| 412 | 250 |   }); | 
| 413 | 251 | 
 | 
| 414 | 252 |   assert_true(s.removeElement("b")); | 
| 415 | 253 |   assert_config(s.get(), { | 
| 416 |  | -    removeElements: ["p", { name: "p", namespace: NS }, "span", "b"], | 
|  | 254 | +    removeElements: [{ name: "p", namespace: NS },  "b",  "p", "span"], | 
| 417 | 255 |     replaceWithChildrenElements: [], | 
| 418 | 256 |   }); | 
| 419 | 257 | }, "sanitizer.removeElement() with global removeElements"); | 
|  | 
| 438 | 276 | 
 | 
| 439 | 277 |   assert_true(s.replaceElementWithChildren("b")); | 
| 440 | 278 |   assert_config(s.get(), { | 
| 441 |  | -    replaceWithChildrenElements: ["a", "span", "b"], | 
|  | 279 | +    replaceWithChildrenElements: ["a", "b", "span"], | 
| 442 | 280 |     elements: [], | 
| 443 | 281 |   }); | 
| 444 | 282 | }, "sanitizer.replaceElementWithChildren() with global elements"); | 
|  | 
| 463 | 301 | 
 | 
| 464 | 302 |   assert_true(s.replaceElementWithChildren("b")); | 
| 465 | 303 |   assert_config(s.get(), { | 
| 466 |  | -    replaceWithChildrenElements: ["a", "span", "b"], | 
|  | 304 | +    replaceWithChildrenElements: ["a", "b", "span"], | 
| 467 | 305 |     removeElements: [], | 
| 468 | 306 |   }); | 
| 469 | 307 | }, "sanitizer.replaceElementWithChildren() with global removeElements"); | 
|  | 
| 482 | 320 | 
 | 
| 483 | 321 |   assert_true(s.allowElement({name: "a", namespace: NS})); | 
| 484 | 322 |   assert_config(s.get(), { | 
| 485 |  | -    elements: ["a", {name: "a", namespace: NS}], | 
|  | 323 | +    elements: [{name: "a", namespace: NS}, "a"], | 
| 486 | 324 |     replaceWithChildrenElements: ["b"] | 
| 487 | 325 |   }); | 
| 488 | 326 | 
 | 
| 489 | 327 |   assert_true(s.allowElement("b")); | 
| 490 | 328 |   assert_config(s.get(), { | 
| 491 |  | -    elements: ["a", {name: "a", namespace: NS}, "b"], | 
|  | 329 | +    elements: [{name: "a", namespace: NS}, "a", "b"], | 
| 492 | 330 |     replaceWithChildrenElements: [] | 
| 493 | 331 |   }); | 
| 494 | 332 | }, "sanitizer.allowElement() with global elements"); | 
|  | 
0 commit comments