Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!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", "potato",
// Arguments will be stringified, so anything that stringifies to a valid
// name is also valid.
123
];
const elems_invalid = [
"", {name: ""},
];
// Attribute names:
const attrs_valid = [
"href", "span",
];
const attrs_invalid = [
];
const all_elems = elems_valid.concat(elems_invalid);
const all_attrs = attrs_valid.concat(attrs_invalid);
for (const item of ["elements", "removeElements", "replaceWithChildrenElements"]) {
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 ["attributes", "removeAttributes"]) {
test(t => {
const sanitizer = new Sanitizer({[item]: all_attrs});
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.)
const SVG_NS = "http://www.w3.org/2000/svg";
const MATHML_NS = "http://www.w3.org/1998/Math/MathML";
[
[ "p", "<p>Hello</p>" ],
[ "svg", "<svg>Hello</svg>", "Hello" ],
[ { name: "svg", namespace: SVG_NS }, "<svg>Hello</svg>" ],
[ "math", "<math>Hello</math>", "Hello" ],
[ { name: "math", namespace: SVG_NS }, "<math>Hello</math>", "Hello" ],
[ { name: "math", namespace: MATHML_NS }, "<math>Hello</math>" ],
].forEach(([elem, probe, expected], index) => {
test(t => {
const options = { elements: [elem],
unknownMarkup: true};
const template = document.createElement("template");
template.setHTML(probe, {sanitizer: options});
assert_equals(template.innerHTML, expected ?? probe);
}, `Namespaced elements #${index}: elements: [${JSON.stringify(elem)}]`);
});
// Same for attributes:
const XLINK_NS = "http://www.w3.org/1999/xlink";
[
[ { name: "style"}, "<p style=\"bla\"></p>" ],
[ { name: "href"}, "<p href=\"bla\"></p>" ],
[ { name: "xlink:href"}, "<p xlink:href=\"bla\"></p>" ],
[ { name: "href", namespace: XLINK_NS}, "<p xlink:href=\"bla\"></p>", "<p></p>" ],
[ { name: "href", namespace: XLINK_NS}, "<p href='bla'></p>", "<p></p>" ],
[ { name: "href"}, "<p xlink:href='bla'></p>", "<p></p>" ],
].forEach(([attr, probe, expected], index) => {
test(t => {
const options = {attributes: [attr],
unknownMarkup: true};
const template = document.createElement("template");
template.setHTML(probe, {sanitizer: options});
assert_equals(template.innerHTML, expected ?? probe);
}, `Namespaced attributes #${index}: attributes: [${JSON.stringify(attr)}]`);
});
// Test for namespaced attribute inside namespace element
test(t => {
const probe = `<svg><a xlink:href="bla"></a></svg>`;
const options = {
elements: [
{name: "svg", namespace: SVG_NS},
{name: "a", namespace: SVG_NS, attributes: [
{ name: "href", namespace: XLINK_NS }
]}
],
unknownMarkup: true};
const template = document.createElement("template");
template.setHTML(probe, {sanitizer: options});
assert_equals(template.innerHTML, probe);
}, "Namespaced attribute xlink:href inside SVG tree");
// Most element and attribute names are lower-cased, but "foreign content"
// like SVG and MathML have some mixed-cased names.
[
[ "feBlend", "<feBlend></feBlend>" ],
[ "feColorMatrix", "<feColorMatrix></feColorMatrix>" ],
[ "textPath", "<textPath></textPath>" ],
].forEach(([elem, probe], index) => {
const sanitize = (elem, probe) => {
const options = {elements: [
{ name: "svg", namespace: SVG_NS },
{ name: elem, namespace: SVG_NS }
],
unknownMarkup: true};
const template = document.createElement("template");
template.setHTML(`<svg>${probe}</svg>`, {sanitizer: options});
return template.content.firstElementChild.innerHTML;
};
test(t => {
assert_equals(sanitize(elem, probe), probe);
}, `Mixed-case element names #${index}: "svg:${elem}"`);
test(t => {
// Lowercase element names should be normalized to mixed-case.
assert_equals(sanitize(elem.toLowerCase(), probe), probe);
}, `Lower-case element names #${index}: "svg:${elem.toLowerCase()}"`);
test(t => {
assert_not_equals(sanitize(elem.toUpperCase(), probe), probe);
}, `Upper-case element names #${index}: "svg:${elem.toUpperCase()}"`);
test(t => {
const elems = ["svg:" + elem];
assert_array_equals(
new Sanitizer({elements: elems}).getConfiguration().allowElements,
elems);
}, `Mixed case element names #${index}: "${elem}" is preserved in config.`);
});
</script>
</body>
</html>