| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src="/resources/testharness.js"></script> | |
| <script src="/resources/testharnessreport.js"></script> | |
| </head> | |
| <body> | |
| <script> | |
| // Like assert_array_equals, but disregard element order. | |
| function assert_array_same(actual, expected) { | |
| assert_array_equals(actual.sort(), expected.sort()); | |
| } | |
| // Element names: | |
| const elems_valid = [ | |
| "p", "template", "span", "custom-elements", "svg", "svg:svg", "potato", | |
| // Arguments will be stringified, so anything that stringifies to a valid | |
| // name is also valid. (E.g. null => "null") | |
| null, undefined, 123 | |
| ]; | |
| const elems_invalid = [ | |
| "", "svg svg", "potato:svg", [], ["*"], ["p"] | |
| ]; | |
| // Attribute names: | |
| const attrs_valid = [ | |
| "href", "span", "xlink:href" | |
| ]; | |
| const attrs_invalid = [ | |
| "svg:href", "svg href", "xlink:span" | |
| ]; | |
| const all_elems = elems_valid.concat(elems_invalid); | |
| const all_attrs = attrs_valid.concat(attrs_invalid); | |
| for (const item of ["allowElements", "dropElements", "blockElements"]) { | |
| test(t => { | |
| const sanitizer = new Sanitizer({[item]: all_elems}); | |
| assert_array_same(sanitizer.getConfiguration()[item], | |
| elems_valid.map(x => "" + x)); | |
| }, `Element names in config item: ${item}`); | |
| } | |
| for (const item of ["allowAttributes", "dropAttributes"]) { | |
| test(t => { | |
| const sanitizer = new Sanitizer( | |
| {[item]: Object.fromEntries(all_attrs.map(x => [x, ["*"]]))}); | |
| assert_array_same(Object.keys(sanitizer.getConfiguration()[item]), | |
| attrs_valid.map(x => "" + x)); | |
| }, `Attribute names in config item: ${item}`); | |
| } | |
| // Quick sanity tests for namespaced elements. | |
| // Each test case is a duo or triplet: | |
| // - a Sanitizer config string for an element. | |
| // - an HTML probe string. | |
| // - the expected result. (If different from the probe.) | |
| [ | |
| [ "p", "<p>Hello</p>" ], | |
| [ "svg", "<svg>Hello</svg>", "Hello" ], | |
| [ "svg:svg", "<svg>Hello</svg>" ], | |
| [ "math", "<math>Hello</math>", "Hello" ], | |
| [ "svg:math", "<math>Hello</math>", "Hello" ], | |
| [ "math:math", "<math>Hello</math>" ], | |
| [ "potato:math", "<math>Hello</math>", "Hello" ], | |
| [ "potato:math", "<potato:math>Hello</potato:math>", "Hello" ], | |
| ].forEach(([elem, probe, expected], index) => { | |
| test(t => { | |
| const sanitizer = new Sanitizer({allowElements: [elem]}); | |
| assert_equals(sanitizer.sanitizeFor("template", probe).innerHTML, | |
| expected ?? probe); | |
| }, `Namespaced elements #${index}: allowElements: ["${elem}"]`); | |
| }); | |
| // Same for attributes: | |
| [ | |
| [ "style", "<p style=\"bla\"></p>" ], | |
| [ "href", "<p href=\"bla\"></p>" ], | |
| [ "xlink:href", "<p xlink:href=\"bla\"></p>" ], | |
| [ "potato:href", "<p potato:href='bla'></p>", "<p></p>" ], | |
| [ "xlink:href", "<p href='bla'></p>", "<p></p>" ], | |
| [ "href", "<p xlink:href='bla'></p>", "<p></p>" ], | |
| ].forEach(([attr, probe, expected], index) => { | |
| test(t => { | |
| const sanitizer = new Sanitizer({allowAttributes: {[attr]: ["*"]}}); | |
| assert_equals(sanitizer.sanitizeFor("template", probe).innerHTML, | |
| expected ?? probe); | |
| }, `Namespaced attributes #${index}: allowAttributes: {"${attr}": ["*"]}`); | |
| }); | |
| // Most element and attribute names are lower-cased, but "foreign content" | |
| // like SVG and MathML have some mixed-cased names. | |
| [ | |
| [ "svg:feBlend", "<feBlend></feBlend>" ], | |
| [ "svg:feColorMatrix", "<feColorMatrix></feColorMatrix>" ], | |
| [ "svg:textPath", "<textPath></textPath>" ], | |
| ].forEach(([elem, probe], index) => { | |
| const sanitize = (elem, probe) => { | |
| return new Sanitizer({allowElements: ["svg:svg", elem]}). | |
| sanitizeFor("template", `<svg>${probe}</svg`). | |
| content.firstElementChild.innerHTML; | |
| }; | |
| test(t => { | |
| assert_equals(sanitize(elem, probe), probe); | |
| }, `Mixed-case element names #${index}: "${elem}"`); | |
| test(t => { | |
| assert_equals(sanitize(elem.toLowerCase(), probe), probe); | |
| }, `Mixed-case element names #${index}: "${elem.toLowerCase()}"`); | |
| test(t => { | |
| assert_equals(sanitize(elem.toUpperCase(), probe), probe); | |
| }, `Mixed-case element names #${index}: "${elem.toUpperCase()}"`); | |
| test(t => { | |
| assert_equals(sanitize(elem, probe.toLowerCase()), probe); | |
| }, `Mixed-case element names #${index}: "${elem}" with lowercase probe.`); | |
| test(t => { | |
| const elems = [elem]; | |
| assert_array_equals( | |
| new Sanitizer({allowElements: elems}).getConfiguration().allowElements, | |
| elems); | |
| }, `Mixed case element names #${index}: "${elem}" is preserved in config.`); | |
| }); | |
| </script> | |
| </body> | |
| </html> |