summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/sanitizer-api
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/sanitizer-api')
-rw-r--r--testing/web-platform/tests/sanitizer-api/META.yml5
-rw-r--r--testing/web-platform/tests/sanitizer-api/WEB_FEATURES.yml3
-rw-r--r--testing/web-platform/tests/sanitizer-api/element-set-sanitized-html.https.html89
-rw-r--r--testing/web-platform/tests/sanitizer-api/idlharness.https.window.js12
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-config.https.html93
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-insecure-context.html17
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-names.https.html149
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-query-config.https.html79
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-sanitize.https.tentative.html76
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-sanitizeFor.https.tentative.html101
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-secure-context.https.html17
-rw-r--r--testing/web-platform/tests/sanitizer-api/sanitizer-unknown.https.html38
-rw-r--r--testing/web-platform/tests/sanitizer-api/support/testcases.sub.js538
13 files changed, 1217 insertions, 0 deletions
diff --git a/testing/web-platform/tests/sanitizer-api/META.yml b/testing/web-platform/tests/sanitizer-api/META.yml
new file mode 100644
index 0000000000..7ac32665e1
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/META.yml
@@ -0,0 +1,5 @@
+spec: https://wicg.github.io/sanitizer-api/
+suggested_reviewers:
+ - ivanlish
+ - mozfreddyb
+ - otherdaniel
diff --git a/testing/web-platform/tests/sanitizer-api/WEB_FEATURES.yml b/testing/web-platform/tests/sanitizer-api/WEB_FEATURES.yml
new file mode 100644
index 0000000000..428aeee63a
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/WEB_FEATURES.yml
@@ -0,0 +1,3 @@
+features:
+- name: sanitizer
+ files: "**"
diff --git a/testing/web-platform/tests/sanitizer-api/element-set-sanitized-html.https.html b/testing/web-platform/tests/sanitizer-api/element-set-sanitized-html.https.html
new file mode 100644
index 0000000000..8fabf1ce29
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/element-set-sanitized-html.https.html
@@ -0,0 +1,89 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testcases.sub.js"></script>
+</head>
+<body>
+<script>
+ function buildNode(element_name, markup) {
+ const e = document.createElement(element_name);
+ e.innerHTML = markup;
+ return e;
+ }
+
+ function assert_node_equals(node1, node2) {
+ assert_equals(node2.innerHTML, node1.innerHTML);
+ assert_true(node1.isEqualNode(node2),
+ `Node[${node1.innerHTML}] == Node[${node2.innerHTML}]`);
+ // TODO(https://github.com/WICG/sanitizer-api/issues/202)
+ /*
+ if (node1 instanceof HTMLTemplateElement) {
+ assert_true(node1.content.isEqualNode(node2.content), "<template> content is equal");
+ }
+ */
+ }
+
+ for (const context of ["script", "iframe", "object", "div"]) {
+ const should_fail = context != "div";
+ test(t => {
+ let elem = document.createElement(context);
+ let probe_fn = _ => elem.setHTML("<div>Hello!</div>");
+ if (should_fail) {
+ assert_throws_js(TypeError, probe_fn);
+ } else {
+ probe_fn();
+ }
+ assert_equals(should_fail, !elem.firstChild);
+ }, `${context}.setHTML should ${should_fail ? "fail" : "pass"}.`);
+ }
+
+ for (const context of ["div", "template", "table"]) {
+ const elem1 = document.createElement(context);
+ const elem2 = document.createElement(context);
+ for (const probe of ["<em>Hello</em>", "<td>data</td>"]) {
+ elem1.setHTML(probe, {});
+ elem2.innerHTML = probe;
+ test(t => {
+ assert_node_equals(elem2, elem1);
+ }, `Sanitizer: <${context}>.setHTML("${probe}", ...) obeys parse context.`);
+ }
+ }
+
+ for (const testcase of testcases) {
+ const element = document.createElement("template");
+ test(t => {
+ element.setHTML(testcase.value, {sanitizer: testcase.config_input });
+ assert_node_equals(buildNode(element.localName, testcase.result), element);
+ }, "Sanitizer: Element.setHTML with config: " + testcase.message);
+ }
+
+ [
+ undefined,
+ {},
+ { sanitizer: {} },
+ { sanitizer: undefined },
+ { avocado: {} },
+ ].forEach((options, index) => {
+ test(t => {
+ const e = document.createElement("div");
+ e.setHTML("<em>bla</em><script>bla<" + "/script>", options);
+ assert_equals(e.innerHTML, "<em>bla</em>");
+ }, `Sanitizer: Element.setHTML options dictionary #${index} uses default.`);
+ });
+
+ [
+ "tomato",
+ { sanitizer: false },
+ { sanitizer: "avocado" },
+ ].forEach((options, index) => {
+ test(t => {
+ assert_throws_js(TypeError, _ => {
+ document.createElement("div").setHTML("bla", options);
+ });
+ }, `Sanitizer: Element.setHTML invalid options dictionary #${index}`);
+ });
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/idlharness.https.window.js b/testing/web-platform/tests/sanitizer-api/idlharness.https.window.js
new file mode 100644
index 0000000000..384317b8e5
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/idlharness.https.window.js
@@ -0,0 +1,12 @@
+// META: script=/resources/WebIDLParser.js
+// META: script=/resources/idlharness.js
+
+idl_test(
+ ['sanitizer-api.tentative'],
+ ['html'],
+ idl_array => {
+ idl_array.add_objects({
+ Sanitizer: ['new Sanitizer({})']
+ });
+ }
+);
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-config.https.html b/testing/web-platform/tests/sanitizer-api/sanitizer-config.https.html
new file mode 100644
index 0000000000..fc371b5bb9
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-config.https.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+
+<body>
+<script>
+ test(t => {
+ let s = new Sanitizer();
+ assert_true(s instanceof Sanitizer);
+ }, "SanitizerAPI creator without config.");
+
+ test(t => {
+ let s = new Sanitizer({});
+ assert_true(s instanceof Sanitizer);
+ }, "SanitizerAPI creator with empty config.");
+
+ test(t => {
+ let s = new Sanitizer(null);
+ assert_true(s instanceof Sanitizer);
+ }, "SanitizerAPI creator with null as config.");
+
+ test(t => {
+ let s = new Sanitizer(undefined);
+ assert_true(s instanceof Sanitizer);
+ }, "SanitizerAPI creator with undefined as config.");
+
+ test(t => {
+ let s = new Sanitizer({testConfig: [1,2,3], attr: ["test", "i", "am"]});
+ assert_true(s instanceof Sanitizer);
+ }, "SanitizerAPI creator with config ignore unknown values.");
+
+ // In-depth testing of sanitization is handled in other tests. Here we
+ // do presence testing for each of the config options and test 3 things:
+ // - One case where our test string is modified,
+ // - one where it's unaffected,
+ // - that a config can't be changed afterwards.
+ // (I.e., that the Sanitizer won't hold on to a reference of the options.)
+
+ // The probe determines whether the Sanitizer modifies the probe string.
+ const probe_string = "<div id=\"i\">balabala</div><p>test</p>";
+ const probe = sanitizer => {
+ let template = document.createElement("template");
+ template.innerHTML = probe_string;
+ let fragment = sanitizer.sanitize(template.content);
+ let div = document.createElement("div");
+ div.append(fragment);
+ return probe_string == div.innerHTML;
+ };
+
+ const should_stay_the_same = {
+ elements: [ "div", "p" ],
+ replaceWithChildrenElements: [ "test" ],
+ removeElements: [ "test" ],
+ attributes: ["id"],
+ removeAttributes: ["bla"],
+ };
+ const should_modify = {
+ elements: [ "div", "span" ],
+ replaceWithChildrenElements: [ "div" ],
+ removeElements: [ "p" ],
+ attributes: ["test"],
+ removeAttributes: ["id"],
+ };
+
+ assert_array_equals(Object.keys(should_stay_the_same), Object.keys(should_modify));
+ Object.keys(should_stay_the_same).forEach(option_key => {
+ test(t => {
+ const options = {};
+ options[option_key] = should_stay_the_same[option_key];
+ const s = new Sanitizer(options);
+ assert_true(s instanceof Sanitizer);
+ assert_true(probe(s));
+ }, `SanitizerAPI: ${option_key} stays is okay.`);
+
+ const options = {};
+ options[option_key] = should_modify[option_key];
+ const s = new Sanitizer(options);
+ test(t => {
+ assert_true(s instanceof Sanitizer);
+ assert_false(probe(s));
+ }, `SanitizerAPI: ${option_key} modify is okay.`);
+
+ options[option_key] = should_stay_the_same[option_key];
+ test(t => {
+ assert_false(probe(s));
+ }, `SanitizerAPI: ${option_key} config is not kept as reference.`);
+ });
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-insecure-context.html b/testing/web-platform/tests/sanitizer-api/sanitizer-insecure-context.html
new file mode 100644
index 0000000000..4b185fd3a7
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-insecure-context.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+ // Currently, the Sanitizer requires a secure context.
+ test(t => {
+ assert_false(globalThis.isSecureContext);
+ assert_equals("Sanitizer" in globalThis, globalThis.isSecureContext);
+ assert_equals("setHTML" in document.body, globalThis.isSecureContext);
+ }, "Sanitizer API in an insecure context.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.html b/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.html
new file mode 100644
index 0000000000..cd33bbc763
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.html
@@ -0,0 +1,149 @@
+<!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],
+ // TODO(https://github.com/WICG/sanitizer-api/issues/167)
+ 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],
+ // TODO(https://github.com/WICG/sanitizer-api/issues/167)
+ 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 }
+ ]}
+ ],
+ // TODO(https://github.com/WICG/sanitizer-api/issues/167)
+ 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 }
+ ],
+ // TODO(https://github.com/WICG/sanitizer-api/issues/167)
+ 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>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-query-config.https.html b/testing/web-platform/tests/sanitizer-api/sanitizer-query-config.https.html
new file mode 100644
index 0000000000..60cba2d618
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-query-config.https.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+ function assert_deep_equals(obj1, obj2) {
+ assert_equals(typeof obj1, typeof obj2);
+ if (typeof obj1 == "string") {
+ assert_equals(obj1, obj2);
+ } else if (typeof obj1 == "boolean") {
+ assert_true(obj1 == obj2);
+ } else if (Array.isArray(obj1)) {
+ assert_equals(obj1.length, obj2.length);
+ assert_array_equals(obj1.sort(), obj2.sort());
+ } else if (typeof obj1 == "object") {
+ assert_array_equals(Object.keys(obj1).sort(), Object.keys(obj2).sort());
+ for (const k of Object.keys(obj1))
+ assert_deep_equals(obj1[k], obj2[k]);
+ }
+ }
+
+ test(t => {
+ // Quick sanity test: Test a few default values.
+ assert_in_array("div", Sanitizer.getDefaultConfiguration().allowElements);
+ assert_false(Sanitizer.getDefaultConfiguration().allowElements.includes("script"));
+ assert_false(Sanitizer.getDefaultConfiguration().allowElements.includes("noscript"));
+
+ assert_true("span" in Sanitizer.getDefaultConfiguration().allowAttributes);
+ assert_false("onclick" in Sanitizer.getDefaultConfiguration().allowAttributes);
+
+ assert_false("dropElements" in Sanitizer.getDefaultConfiguration());
+ assert_false("blockElements" in Sanitizer.getDefaultConfiguration());
+ assert_false("dropAttributes" in Sanitizer.getDefaultConfiguration());
+ assert_false(Sanitizer.getDefaultConfiguration().allowCustomElements);
+ assert_false(Sanitizer.getDefaultConfiguration().allowUnknownMarkup);
+ }, "SanitizerAPI getDefaultConfiguration()");
+
+ test(t => {
+ assert_deep_equals(Sanitizer.getDefaultConfiguration(),
+ new Sanitizer().getConfiguration());
+ }, "SanitizerAPI getConfiguration() on default created Sanitizer");
+
+ test(t => {
+ const configs = [{
+ allowElements: ["div", "span", "helloworld"],
+ dropElements: ["xxx"],
+ allowAttributes: { "class": ["*"], "color": ["span", "div"],
+ "onclick": ["*"] },
+ allowCustomElements: true,
+ allowUnknownMarkup: true,
+ },{
+ blockElements: ["table", "tbody", "th", "td"],
+ }, {
+ allowCustomElements: false,
+ }, {
+ allowUnknownMarkup: false,
+ }];
+ for (const config of configs)
+ assert_deep_equals(config, new Sanitizer(config).getConfiguration());
+
+ // Also test a mixed case variant:
+ const config_0_mixed = {
+ allowElements: ["div", "sPAn", "HelloWorld"],
+ dropElements: ["XXX"],
+ allowAttributes: { "class": ["*"], "color": ["sPAn", "div"],
+ "onclick": ["*"] },
+ allowCustomElements: true,
+ allowUnknownMarkup: true,
+ };
+ assert_deep_equals(config_0_mixed,
+ new Sanitizer(config_0_mixed).getConfiguration());
+ }, "SanitizerAPI getConfiguration() reflects creation config.");
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-sanitize.https.tentative.html b/testing/web-platform/tests/sanitizer-api/sanitizer-sanitize.https.tentative.html
new file mode 100644
index 0000000000..82eaeb4832
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-sanitize.https.tentative.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testcases.sub.js"></script>
+</head>
+
+<body>
+<script>
+ function getString(fragment) {
+ d = document.createElement("div");
+ d.replaceChildren(...fragment.cloneNode(true).childNodes);
+ return d.innerHTML;
+ }
+
+ function getFragment(markup) {
+ const d = document.createElement("div");
+ d.innerHTML = markup;
+ let f = document.createDocumentFragment();
+ f.replaceChildren(...d.childNodes);
+ return f;
+ }
+ function getDoc(markup) {
+ return new DOMParser().parseFromString(markup, "text/html");
+ }
+ function assert_node_equals(node1, node2) {
+ assert_true(node1 instanceof Node && node1.isEqualNode(node2),
+ `Node[${getString(node1)}] == Node[${getString(node2)}]`);
+ }
+
+ test(t => {
+ let s = new Sanitizer();
+ assert_throws_js(TypeError, _ => s.sanitize());
+ }, "Sanitizer.sanitize() should throw an error.");
+
+ test(t => {
+ let s = new Sanitizer();
+ assert_throws_js(TypeError, _ => s.sanitize(null));
+ }, "Sanitizer.sanitize(null).");
+
+ const probe_string = `<a href="about:blank">hello</a><script>cons` +
+ `ole.log("world!");<` + `/script>`;
+ test(t => {
+ const sanitized = new Sanitizer().sanitize(getFragment(probe_string));
+ const expected = getFragment(probe_string.substr(0, 31));
+ assert_node_equals(expected, sanitized);
+ }, "Sanitizer.sanitze(DocumentFragment)");
+
+ test(t => {
+ const sanitized = new Sanitizer().sanitize(getDoc(probe_string));
+ const expected = getFragment(probe_string.substr(0, 31));
+ assert_node_equals(expected, sanitized);
+ }, "Sanitizer.sanitze(Document)");
+
+ testcases.forEach(c => test(t => {
+ let s = new Sanitizer(c.config_input);
+ var dom = new DOMParser().parseFromString("<!DOCTYPE html><body>" + c.value, "text/html");
+ fragment = s.sanitize(dom);
+ assert_true(fragment instanceof DocumentFragment);
+
+ let result = getString(fragment);
+ assert_equals(result, c.result);
+ }, "SanitizerAPI with config: " + c.message + ", sanitize from document function for <body>"));
+
+ testcases.forEach(c => test(t => {
+ let s = new Sanitizer(c.config_input);
+ let tpl = document.createElement("template");
+ tpl.innerHTML = c.value;
+ fragment = s.sanitize(tpl.content);
+ assert_true(fragment instanceof DocumentFragment);
+ assert_equals(getString(fragment), c.result);
+ }, "SanitizerAPI with config: " + c.message + ", sanitize from document fragment function for <template>"));
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-sanitizeFor.https.tentative.html b/testing/web-platform/tests/sanitizer-api/sanitizer-sanitizeFor.https.tentative.html
new file mode 100644
index 0000000000..77ae0abb6b
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-sanitizeFor.https.tentative.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="support/testcases.sub.js"></script>
+</head>
+
+<body>
+<script>
+ function buildNode(element_name, markup) {
+ const e = document.createElement(element_name);
+ e.innerHTML = markup;
+ return e;
+ }
+
+ function toString(node) {
+ const e = document.createElement("div");
+ e.append(node.cloneNode(true));
+ return e.innerHTML;
+ }
+
+ function assert_node_equals(node1, node2) {
+ assert_equals(node1 instanceof Node, node2 instanceof Node);
+ if (!(node1 instanceof Node)) return;
+
+ node1.normalize();
+ node2.normalize();
+ assert_true(node1.isEqualNode(node2),
+ `Node[${toString(node1)}] == Node[${toString(node2)}]`);
+ if (node1 instanceof HTMLTemplateElement) {
+ assert_node_equals(node1.content, node2.content);
+ }
+ }
+
+ test(t => {
+ let s = new Sanitizer();
+ assert_throws_js(TypeError, _ => s.sanitizeFor());
+ assert_throws_js(TypeError, _ => s.sanitizeFor(null));
+ }, "Sanitizer.sanitizeFor() should throw.");
+
+ test(t => {
+ let s = new Sanitizer();
+ assert_throws_js(TypeError, _ => s.sanitizeFor("xxx"));
+ }, "Sanitizer.sanitizeFor() with one argument should throw.");
+
+ for (const context of ["script", "iframe", "object", "div"]) {
+ const should_fail = context != "div";
+ test(t => {
+ let result = new Sanitizer().sanitizeFor(context, "<div>Hello!</div>");
+ if (should_fail) {
+ assert_equals(null, result);
+ } else {
+ assert_true(result instanceof HTMLElement);
+ }
+ }, `Sanitizer.sanitizeFor("${context}", ...) should ${should_fail ? "fail" : "pass"}.`);
+ }
+
+ async_test(t => {
+ let s = new Sanitizer();
+ s.sanitizeFor("div", "<img src='https://bla/'>");
+ t.step_timeout(_ => {
+ assert_equals(performance.getEntriesByName("https://bla/").length, 0);
+ t.done();
+ }, 1000);
+ }, "Sanitizer.sanitizeFor function shouldn't load the image.");
+
+ test(t => {
+ const probe = `<a href="about:blank">hello</a><script>con` +
+ `sole.log("world!");<` + `/script>`;
+ const expected = `<a href="about:blank">hello</a>`;
+ for (const element of ["div", "template", "span", "table", "td",
+ "pumuckl", "custom-element", "linearGradient",
+ "svg", "svg:img", "svg:linearGradient"]) {
+ assert_node_equals(
+ buildNode(element, expected),
+ new Sanitizer().sanitizeFor(element, probe));
+ }
+ }, `Sanitizer.sanitizeFor(element, ..)`);
+
+ for (const context of ["div", "template", "table"]) {
+ for (const probe of ["<em>Hello</em>", "<td>data</td>"]) {
+ test(t => {
+ assert_node_equals(
+ buildNode(context, probe),
+ new Sanitizer().sanitizeFor(context, probe));
+ }, `Sanitizer.sanitizeFor("${context}", "${probe}") obeys parse context.`);
+ }
+ }
+
+ for (const testcase of testcases) {
+ test(t => {
+ let s = new Sanitizer(testcase.config_input);
+ assert_node_equals(
+ buildNode("template", testcase.result),
+ s.sanitizeFor("template", testcase.value));
+ }, "Sanitizer.sanitizeFor with config: " + testcase.message);
+ }
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-secure-context.https.html b/testing/web-platform/tests/sanitizer-api/sanitizer-secure-context.https.html
new file mode 100644
index 0000000000..0e04e04d16
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-secure-context.https.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+ // Currently, the Sanitizer requires a secure context.
+ test(t => {
+ assert_true(globalThis.isSecureContext);
+ assert_equals("Sanitizer" in globalThis, globalThis.isSecureContext);
+ assert_equals("setHTML" in document.body, globalThis.isSecureContext);
+ }, "SanitizerAPI in a secure context.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-unknown.https.html b/testing/web-platform/tests/sanitizer-api/sanitizer-unknown.https.html
new file mode 100644
index 0000000000..7b9835cb6d
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/sanitizer-unknown.https.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+<script>
+test(t => {
+ d = document.createElement("div")
+ d.setHTML("<hello><world>",
+ { sanitizer: { elements: ["hello", "world"] } });
+ assert_equals(d.innerHTML, "");
+}, "Unknown element names get blocked without unknownMarkup.");
+
+test(t => {
+ d = document.createElement("div")
+ d.setHTML("<hello><world>",
+ { sanitizer: { unknownMarkup: true, elements: ["hello", "world"] } });
+ assert_equals(d.innerHTML, "<hello><world></world></hello>");
+}, "Unknown element names pass with unknownMarkup.");
+
+test(t => {
+ d = document.createElement("div")
+ d.setHTML("<b hello='1' world>",
+ { sanitizer: { attributes: ["name", "world"] } });
+ assert_equals(d.innerHTML, "<b></b>");
+}, "Unknown attributes names get blocked without unknownMarkup.");
+
+test(t => {
+ d = document.createElement("div")
+ d.setHTML("<b hello='1' world>",
+ { sanitizer: { unknownMarkup: true, attributes: ["name", "world"] } });
+ assert_equals(d.innerHTML, `<b hello="1" world=""></b>`);
+}, "Unknown attribute names pass with unknownMarkup.");
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/sanitizer-api/support/testcases.sub.js b/testing/web-platform/tests/sanitizer-api/support/testcases.sub.js
new file mode 100644
index 0000000000..7ce755ca06
--- /dev/null
+++ b/testing/web-platform/tests/sanitizer-api/support/testcases.sub.js
@@ -0,0 +1,538 @@
+const testcases = [
+ { config_input: {}, value: "test", result: "test", message: "string" },
+ {
+ config_input: {},
+ value: "<b>bla</b>",
+ result: "<b>bla</b>",
+ message: "html fragment",
+ },
+ { config_input: {}, value: "<a<embla", result: "", message: "broken html" },
+ {
+ config_input: {},
+ value: {},
+ result: "[object Object]",
+ message: "empty object",
+ },
+ { config_input: {}, value: 1, result: "1", message: "number" },
+ { config_input: {}, value: 000, result: "0", message: "zeros" },
+ { config_input: {}, value: 1 + 2, result: "3", message: "arithmetic" },
+ { config_input: {}, value: "", result: "", message: "empty string" },
+ {
+ config_input: {},
+ value: undefined,
+ result: "undefined",
+ message: "undefined",
+ },
+ {
+ config_input: {},
+ value: "<html><head></head><body>test</body></html>",
+ result: "test",
+ message: "document",
+ },
+ {
+ config_input: {},
+ value: "<div>test",
+ result: "<div>test</div>",
+ message: "html without close tag",
+ },
+ {
+ config_input: {},
+ value: "<script>alert('i am a test')</script>",
+ result: "",
+ message: "scripts for default configs",
+ },
+ {
+ config_input: {},
+ value: "hello<script>alert('i am a test')</script>",
+ result: "hello",
+ message: "script not as root",
+ },
+ {
+ config_input: {},
+ value: "<div><b>hello<script>alert('i am a test')</script>",
+ result: "<div><b>hello</b></div>",
+ message: "script deeper in the tree",
+ },
+ {
+ config_input: {},
+ value: "<p onclick='a= 123'>Click.</p>",
+ result: "<p>Click.</p>",
+ message: "onclick scripts",
+ },
+ {
+ config_input: {},
+ value: "<plaintext><p>text</p>",
+ result: "&lt;p&gt;text&lt;/p&gt;",
+ message: "plaintext",
+ },
+ {
+ config_input: {},
+ value: "<xmp>TEXT</xmp>",
+ result: "TEXT",
+ message: "xmp",
+ },
+ {
+ config_input: { test: 123 },
+ value: "test",
+ result: "test",
+ message: "invalid config_input",
+ },
+ {
+ config_input: { removeElements: [] },
+ value: "test",
+ result: "test",
+ message: "empty removeElements list",
+ },
+ {
+ config_input: { removeElements: ["div"] },
+ value: "<div>test</div><p>bla",
+ result: "<p>bla</p>",
+ message: "test html without close tag with removeElements list ['div']",
+ },
+ {
+ config_input: {},
+ value: "<custom-element>test</custom-element>bla",
+ result: "bla",
+ message: "default behavior for custom elements",
+ },
+ {
+ config_input: { customElements: true },
+ value: "<custom-element>test</custom-element>bla",
+ result: "testbla",
+ message: "allow custom elements",
+ },
+ {
+ config_input: {
+ customElements: true,
+ elements: ["custom-element"],
+ },
+ value: "<custom-element>test</custom-element>bla",
+ result: "<custom-element>test</custom-element>bla",
+ message: "allow custom elements with allow elements",
+ },
+ {
+ config_input: { customElements: false },
+ value: "<custom-element>test</custom-element>bla",
+ result: "bla",
+ message: "disallow custom elements",
+ },
+ {
+ config_input: {
+ removeElements: ["custom-element"],
+ customElements: true,
+ },
+ value: "<custom-element>test</custom-element>bla",
+ result: "bla",
+ message: 'allow custom elements with drop list contains ["custom-element"]',
+ },
+ {
+ config_input: { removeElements: ["script"] },
+ value: "<script>alert('i am a test')</script>",
+ result: "",
+ message: 'test script with ["script"] as removeElements list',
+ },
+ {
+ config_input: { removeElements: ["test-element", "i"] },
+ value: "<div>balabala<i>test</i></div><test-element>t</test-element>",
+ result: "<div>balabala</div>",
+ message: 'removeElements list ["test-element", "i"]}',
+ },
+ {
+ config_input: { removeElements: ["dl", "p"] },
+ value: "<div>balabala<i>i</i><p>t</p></div>",
+ result: "<div>balabala<i>i</i></div>",
+ message: 'removeElements list ["dl", "p"]}',
+ },
+ {
+ config_input: { elements: ["p"] },
+ value: "<div>test<div>p</div>tt<p>div</p></div>",
+ result: "testptt<p>div</p>",
+ message: 'elements list ["p"]',
+ },
+ {
+ config_input: { removeElements: ["div"], elements: ["div"] },
+ value: "<div>test</div><p>bla",
+ result: "bla",
+ message: "elements list has no influence to removeElements",
+ },
+ {
+ config_input: { removeAttributes: [] },
+ value: "<p id='test'>Click.</p>",
+ result: '<p id="test">Click.</p>',
+ message: "empty removeAttributes list with id attribute",
+ },
+ {
+ config_input: { removeAttributes: ["id"] },
+ value: "<p id='test'>Click.</p>",
+ result: "<p>Click.</p>",
+ message: 'removeAttributes list ["id"] with id attribute',
+ },
+ {
+ config_input: {
+ removeAttributes: ["data-attribute-with-dashes"],
+ },
+ value:
+ "<p id='p' data-attribute-with-dashes='123'>Click.</p><script>document.getElementById('p').dataset.attributeWithDashes=123;</script>",
+ result: '<p id="p">Click.</p>',
+ message:
+ 'removeAttributes list ["data-attribute-with-dashes"] with dom dataset js access',
+ },
+ {
+ config_input: {
+ elements: [
+ { name: "p", attributes: ["title"] },
+ { name: "div", attributes: ["id"] },
+ ],
+ },
+ value: "<p id='p' title='p'>P</p><div id='div' title='div'>DIV</div>",
+ result: '<p title="p">P</p><div id="div">DIV</div>',
+ message:
+ 'elements list with <p> attributes: ["title"] and div attributes: ["id"] lists',
+ },
+ {
+ config_input: {
+ elements: [
+ { name: "p", removeAttributes: ["title"] },
+ { name: "div", removeAttributes: ["id"] },
+ ],
+ },
+ value: "<p id='p' title='p'>P</p><div id='div' title='div'>DIV</div>",
+ result: '<p id="p">P</p><div title="div">DIV</div>',
+ message:
+ 'elements list with <p> removeAttributes: ["title"] and div removeAttributes: ["id"] lists',
+ },
+ {
+ config_input: {
+ elements: [{ name: "div", attributes: ["id"], removeAttributes: ["id"] }],
+ },
+ value: "<div id='div' title='div'>DIV</div>",
+ result: "<div>DIV</div>",
+ message:
+ 'elements list with <div> attributes: ["id"] and removeAttributes: ["id"] lists',
+ },
+ {
+ config_input: {
+ elements: [{ name: "div", attributes: ["id", "title"] }],
+ attributes: []
+ },
+ value: "<div id='div' title='div'>DIV</div>",
+ result: "<div>DIV</div>",
+ message:
+ 'elements list with <div> attributes: ["id", "title"] does not override empty attributes: [] list',
+ },
+ {
+ config_input: {
+ elements: [{ name: "div", attributes: ["id", "title"] }],
+ removeAttributes: ["id", "title"]
+ },
+ value: "<div id='div' title='div'>DIV</div>",
+ result: "<div>DIV</div>",
+ message:
+ 'elements list with <div> attributes: ["id", "title"] does not override removeAttributes: ["id", "title"] list',
+ },
+ {
+ config_input: {
+ elements: [{ name: "div", removeAttributes: ["id", "title"] }],
+ attributes: ["id", "title"]
+ },
+ value: "<div id='div' title='div'>DIV</div>",
+ result: "<div>DIV</div>",
+ message:
+ 'elements list with <div> removeAttributes: ["id", "title"] is effective even with attributes: ["id", "title"] list',
+ },
+ {
+ config_input: { attributes: ["id"] },
+ value: "<p id='test' onclick='a= 123'>Click.</p>",
+ result: '<p id="test">Click.</p>',
+ message: 'attributes list ["id"] with id attribute and onclick scripts',
+ },
+ // {config_input: {allowAttributes: {"*": ["a"]}}, value: "<a id='a' style='color: black'>Click.</a><div style='color: white'>div</div>", result: "<a id=\"a\" style=\"color: black\">Click.</a><div>div</div>", message: "allowAttributes list {\"*\": [\"a\"]} with style attribute"},
+ {
+ config_input: {
+ removeAttributes: ["style"],
+ attributes: ["style"],
+ },
+ value: "<p style='color: black'>Click.</p>",
+ result: "<p>Click.</p>",
+ message: "attributes list has no influence to removeAttributes list",
+ },
+ {
+ config_input: { elements: ["template", "div"] },
+ value: "<template><script>test</script><div>hello</div></template>",
+ result: "<template><div>hello</div></template>",
+ message: "Template element",
+ },
+ {
+ config_input: {},
+ value: "<a href='javascript:evil.com'>Click.</a>",
+ result: "<a>Click.</a>",
+ message: "HTMLAnchorElement with javascript protocal",
+ },
+ {
+ config_input: {},
+ value: "<a href=' javascript:evil.com'>Click.</a>",
+ result: "<a>Click.</a>",
+ message: "HTMLAnchorElement with javascript protocal start with space",
+ },
+ {
+ config_input: {},
+ value: "<a href='http:evil.com'>Click.</a>",
+ result: '<a href="http:evil.com">Click.</a>',
+ message: "HTMLAnchorElement",
+ },
+ {
+ config_input: {},
+ value: "<area href='javascript:evil.com'>Click.</area>",
+ result: "<area>Click.",
+ message: "HTMLAreaElement with javascript protocal",
+ },
+ {
+ config_input: {},
+ value: "<area href=' javascript:evil.com'>Click.</area>",
+ result: "<area>Click.",
+ message: "HTMLAreaElement with javascript protocal start with space",
+ },
+ {
+ config_input: {},
+ value: "<area href='http:evil.com'>Click.</area>",
+ result: '<area href="http:evil.com">Click.',
+ message: "HTMLAreaElement",
+ },
+ {
+ config_input: {},
+ value: "<form action='javascript:evil.com'>Click.</form>",
+ result: "<form>Click.</form>",
+ message: "HTMLFormElement with javascript action",
+ },
+ {
+ config_input: {},
+ value: "<form action=' javascript:evil.com'>Click.</form>",
+ result: "<form>Click.</form>",
+ message: "HTMLFormElement with javascript action start with space",
+ },
+ {
+ config_input: {},
+ value: "<form action='http:evil.com'>Click.</form>",
+ result: '<form action="http:evil.com">Click.</form>',
+ message: "HTMLFormElement",
+ },
+ {
+ config_input: {},
+ value: "<input formaction='javascript:evil.com'>Click.</input>",
+ result: "<input>Click.",
+ message: "HTMLInputElement with javascript formaction",
+ },
+ {
+ config_input: {},
+ value: "<input formaction=' javascript:evil.com'>Click.</input>",
+ result: "<input>Click.",
+ message: "HTMLInputElement with javascript formaction start with space",
+ },
+ {
+ config_input: {},
+ value: "<input formaction='http:evil.com'>Click.</input>",
+ result: '<input formaction="http:evil.com">Click.',
+ message: "HTMLInputElement",
+ },
+ {
+ config_input: {},
+ value: "<button formaction='javascript:evil.com'>Click.</button>",
+ result: "<button>Click.</button>",
+ message: "HTMLButtonElement with javascript formaction",
+ },
+ {
+ config_input: {},
+ value: "<button formaction=' javascript:evil.com'>Click.</button>",
+ result: "<button>Click.</button>",
+ message: "HTMLButtonElement with javascript formaction start with space",
+ },
+ {
+ config_input: {},
+ value: "<button formaction='http:evil.com'>Click.</button>",
+ result: '<button formaction="http:evil.com">Click.</button>',
+ message: "HTMLButtonElement",
+ },
+ {
+ config_input: {},
+ value:
+ "<p>Some text</p></body><!-- 1 --></html><!-- 2 --><p>Some more text</p>",
+ result: "<p>Some text</p><p>Some more text</p>",
+ message: "malformed HTML",
+ },
+ {
+ config_input: {},
+ value: "<p>Some text</p><!-- 1 --><!-- 2 --><p>Some more text</p>",
+ result: "<p>Some text</p><p>Some more text</p>",
+ message: "HTML with comments; comments not allowed",
+ },
+ {
+ config_input: { comments: true },
+ value: "<p>Some text</p><!-- 1 --><!-- 2 --><p>Some more text</p>",
+ result: "<p>Some text</p><!-- 1 --><!-- 2 --><p>Some more text</p>",
+ message: "HTML with comments; comments",
+ },
+ {
+ config_input: { comments: false },
+ value: "<p>Some text</p><!-- 1 --><!-- 2 --><p>Some more text</p>",
+ result: "<p>Some text</p><p>Some more text</p>",
+ message: "HTML with comments; !comments",
+ },
+ {
+ config_input: {},
+ value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>",
+ result: "<p>commentintext</p>",
+ message: "HTML with comments deeper in the tree",
+ },
+ {
+ config_input: { comments: true },
+ value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>",
+ result: "<p>comment<!-- hello -->in<!-- </p> -->text</p>",
+ message: "HTML with comments deeper in the tree, comments",
+ },
+ {
+ config_input: { comments: false },
+ value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>",
+ result: "<p>commentintext</p>",
+ message: "HTML with comments deeper in the tree, !comments",
+ },
+ {
+ config_input: { elements: ["svg"] },
+ value: "<svg></svg>",
+ result: "",
+ message:
+ "Unknown HTML names (HTMLUnknownElement instances) should not match elements parsed as non-HTML namespaces.",
+ },
+ {
+ config_input: { elements: ["div", "svg"] },
+ value: "<div><svg></svg></div>",
+ result: "<div></div>",
+ message:
+ "Unknown HTML names (HTMLUnknownElement instances) should not match elements parsed as non-HTML namespaces when nested.",
+ },
+
+ // Case normalization (actually: lack of)
+ {
+ config_input: { removeElements: ["I", "DL"] },
+ value: "<div>balabala<dl>test</dl></div>",
+ result: "<div>balabala<dl>test</dl></div>",
+ message: 'removeElements list ["I", "DL"]}',
+ },
+ {
+ config_input: { removeElements: ["i", "dl"] },
+ value: "<div>balabala<dl>test</dl></div>",
+ result: "<div>balabala</div>",
+ message: 'removeElements list ["i", "dl"]}',
+ },
+ {
+ config_input: { removeElements: ["i", "dl"] },
+ value: "<DIV>balabala<DL>test</DL></DIV>",
+ result: "<div>balabala</div>",
+ message: 'removeElements list ["i", "dl"]} with uppercase HTML',
+ },
+ {
+ config_input: { removeAttributes: ["ID"] },
+ value: '<p id="test">Click.</p>',
+ result: '<p id="test">Click.</p>',
+ message: 'removeAttributes list ["ID"] with id attribute',
+ },
+ {
+ config_input: { removeAttributes: ["ID"] },
+ value: '<p ID="test">Click.</p>',
+ result: '<p id="test">Click.</p>',
+ message: 'removeAttributes list ["ID"] with ID attribute',
+ },
+ {
+ config_input: { removeAttributes: ["id"] },
+ value: '<p ID="test">Click.</p>',
+ result: "<p>Click.</p>",
+ message: 'removeAttributes list ["id"] with ID attribute',
+ },
+
+ // unknownMarkup for elements (with and without)
+ {
+ config_input: { removeElements: [123, "test", "i", "custom-element"] },
+ value:
+ "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>",
+ result: "<div>balabala</div>",
+ message: "removeElements with unknown elements and without unknownMarkup",
+ },
+ {
+ config_input: {
+ replaceWithChildrenElements: [123, "test", "i", "custom-element"],
+ },
+ value:
+ "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>",
+ result: "<div>balabalatest</div>",
+ message:
+ "replaceWithChildrenElements with unknown elements and without unknownMarkup",
+ },
+ {
+ config_input: { elements: ["p", "test"] },
+ value: "<div>test<div>p</div>tt<p>div</p></div><test>test</test>",
+ result: "testptt<p>div</p>",
+ message: "elements with unknown elements and without unknownMarkup",
+ },
+ {
+ config_input: {
+ removeElements: [123, "test", "i", "custom-element"],
+ unknownMarkup: true,
+ },
+ value:
+ "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>",
+ result: "<div>balabala</div>",
+ message: "removeElements with unknown elements and with unknownMarkup",
+ },
+ {
+ config_input: {
+ replaceWithChildrenElements: [123, "test", "i", "custom-element"],
+ unknownMarkup: true,
+ },
+ value:
+ "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>",
+ result: "<div>balabalatest</div>t",
+ message:
+ "replaceWithChildrenElements with unknown elements and with unknownMarkup",
+ },
+ {
+ config_input: { elements: ["p", "test"], unknownMarkup: true },
+ value: "<div>test<div>p</div>tt<p>div</p><test>test</test></div>",
+ result: "testptt<p>div</p><test>test</test>",
+ message: "elements with unknown elements and with unknownMarkup",
+ },
+
+ // unknownMarkup for attributes (with and without)
+ {
+ config_input: {
+ attributes: ["hello", "world"],
+ },
+ value: "<div hello='1' world='2'><b hello='3' world='4'>",
+ result: "<div><b></b></div>",
+ message: "attributes: unknown attributes and without unknownMarkup",
+ },
+ {
+ config_input: {
+ attributes: ["hello", "world"],
+ unknownMarkup: true,
+ },
+ value: "<div hello='1' world='2'><b hello='3' world='4'>",
+ result: '<div hello="1" world="2"><b hello="3" world="4"></b></div>',
+ message: "attributes: unknown attributes and with unknownMarkup",
+ },
+ {
+ config_input: {
+ removeAttributes: ["hello", "world"],
+ },
+ value: "<div hello='1' world='2'><b hello='3' world='4'>",
+ result: "<div><b></b></div>",
+ message: "removeAttributes: unknown attributes and without unknownMarkup",
+ },
+ {
+ config_input: {
+ removeAttributes: ["hello", "world"],
+ unknownMarkup: true,
+ },
+ value: "<div hello='1' world='2'><b hello='3' world='4'>",
+ result: "<div><b></b></div>",
+ message: "removeAttributes unknown attributes and with allowUnknownMarkup",
+ },
+];