diff options
Diffstat (limited to 'testing/web-platform/tests/sanitizer-api')
12 files changed, 688 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/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..560e9cd635 --- /dev/null +++ b/testing/web-platform/tests/sanitizer-api/element-set-sanitized-html.https.html @@ -0,0 +1,111 @@ +<!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_true(node1 instanceof Node && node1.isEqualNode(node2), + `Node[${node1.innerHTML}] == Node[${node2.innerHTML}]`); + } + + 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>", new Sanitizer()); + 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, new Sanitizer()); + 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 => { + let s = new Sanitizer(testcase.config_input); + element.setHTML(testcase.value, {sanitizer: s }); + assert_node_equals(buildNode(element.localName, testcase.result), element); + }, "Sanitizer: Element.setHTML with config: " + testcase.message); + } + + [ + undefined, + {}, + { sanitizer: new Sanitizer() }, + { sanitizer: undefined }, + { avocado: new Sanitizer() }, + ].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: null }, + { sanitizer: "avocado" }, + { sanitizer: { allowElements: [ "a", "b", "c" ] } }, + ].forEach((options, index) => { + test(t => { + assert_throws_js(TypeError, _ => { + document.createElement("div").setHTML("bla", options); + }); + }, `Sanitizer: Element.setHTML invalid options dictionary #${index}`); + }); + + test(t => { + const sanitizer = new Sanitizer({allowElements: ["b"]}); + const element = document.createElement("div"); + + // WebIDL magic: An IDL dictionary is mapped to a JS object. Thus, a plain + // Sanitizer instance will be accepted as an options dictionary. However, + // it will then try to read the .sanitizer property of the Sanitizer, and + // since that doesn't usually exist will treat it as an empty dictionary. + // + // Ref: https://webidl.spec.whatwg.org/#es-dictionary + + // Sanitizer instance in the dictionary: Config is applied. + element.setHTML("<em>celery</em>", {sanitizer: sanitizer}); + assert_equals(element.innerHTML, "celery"); + + // Same Sanitizer instance, passed directly: Is like an empty dictionary + // and config is not applied. + element.setHTML("<em>celery</em>", sanitizer); + assert_equals(element.innerHTML, "<em>celery</em>"); + + // Sanitizer-ception: Set the Sanitizer as the .sanitizer property on itself. + // Now the config is applied. It's magic. Just not the good kind of magic. + sanitizer.sanitizer = sanitizer; + element.setHTML("<em>celery</em>", sanitizer); + assert_equals(element.innerHTML, "celery"); + }, "Sanitizer: Element.setHTML with sanitizer instance."); +</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..4faa156ead --- /dev/null +++ b/testing/web-platform/tests/sanitizer-api/sanitizer-config.https.html @@ -0,0 +1,90 @@ +<!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 => { + const div = document.createElement("div"); + div.setHTML(probe_string, {sanitizer: sanitizer}); + return probe_string == div.innerHTML; + }; + + const should_stay_the_same = { + allowElements: [ "div", "p" ], + blockElements: [ "test" ], + dropElements: [ "test" ], + allowAttributes: { "id": ["*"]}, + dropAttributes: { "bla": ["blubb"]}, + }; + const should_modify = { + allowElements: [ "div", "span" ], + blockElements: [ "div" ], + dropElements: [ "p" ], + allowAttributes: { "id": ["p"]}, + dropAttributes: { "id": ["div"]}, + }; + + 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..49949927de --- /dev/null +++ b/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.html @@ -0,0 +1,52 @@ +<!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. (E.g. null => "null") + null, undefined, 123 + ]; + const elems_invalid = [ + "", [], ["*"], ["p"] + ]; + + // 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 ["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}`); + } +</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..a703f42f49 --- /dev/null +++ b/testing/web-platform/tests/sanitizer-api/sanitizer-unknown.https.html @@ -0,0 +1,40 @@ +<!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: new Sanitizer({allowElements: ["hello", "world"]}) }); + assert_equals(d.innerHTML, ""); +}, "Unknown element names get blocked without allowUnknownMarkup."); + +test(t => { + d = document.createElement("div") + d.setHTML("<hello><world>", + { sanitizer: new Sanitizer({allowUnknownMarkup: true, + allowElements: ["hello", "world"]}) }); + assert_equals(d.innerHTML, "<hello><world></world></hello>"); +}, "Unknown element names pass with allowUnknownMarkup."); + +test(t => { + d = document.createElement("div") + d.setHTML("<b hello='1' world>", { sanitizer: + new Sanitizer({allowAttributes: {"hello": ["*"], "world": ["*"]}}) }); + assert_equals(d.innerHTML, "<b></b>"); +}, "Unknown attributes names get blocked without allowUnknownMarkup."); + +test(t => { + d = document.createElement("div") + d.setHTML("<b hello='1' world>", { sanitizer: + new Sanitizer({allowUnknownMarkup: true, + allowAttributes: {"hello": ["*"], "world": ["*"]}}) }); + assert_equals(d.innerHTML, `<b hello="1" world=""></b>`); +}, "Unknown attribute names pass with allowUnknownMarkup."); +</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..1a5989fb73 --- /dev/null +++ b/testing/web-platform/tests/sanitizer-api/support/testcases.sub.js @@ -0,0 +1,88 @@ +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: "<p>text</p>", 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: {dropElements: []}, value: "test", result: "test", message: "empty dropElements list"}, + {config_input: {dropElements: ["div"]}, value: "<div>test</div><p>bla", result: "<p>bla</p>", message: "test html without close tag with dropElements list ['div']"}, + {config_input: {}, value: "<custom-element>test</custom-element>bla", result: "bla", message: "default behavior for custom elements"}, + {config_input: {allowCustomElements: true}, value: "<custom-element>test</custom-element>bla", result: "testbla", message: "allow custom elements"}, + {config_input: {allowCustomElements: true, allowElements: ["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: {allowCustomElements: false}, value: "<custom-element>test</custom-element>bla", result: "bla", message: "disallow custom elements"}, + {config_input: {dropElements: ["custom-element"], allowCustomElements: true}, value: "<custom-element>test</custom-element>bla", result: "bla", message: "allow custom elements with drop list contains [\"custom-element\"]"}, + {config_input: {dropElements: ["script"]}, value: "<script>alert('i am a test')<\/script>", result: "", message: "test script with [\"script\"] as dropElements list"}, + {config_input: {dropElements: ["test-element", "i"]}, value: "<div>balabala<i>test</i></div><test-element>t</test-element>", result: "<div>balabala</div>", message: "dropElements list [\"test-element\", \"i\"]}"}, + {config_input: {dropElements: ["dl", "p"]}, value: "<div>balabala<i>i</i><p>t</p></div>", result: "<div>balabala<i>i</i></div>", message: "dropElements list [\"dl\", \"p\"]}"}, + {config_input: {allowElements: ["p"]}, value: "<div>test<div>p</div>tt<p>div</p></div>", result: "testptt<p>div</p>", message: "allowElements list [\"p\"]"}, + {config_input: {dropElements: ["div"], allowElements: ["div"]}, value: "<div>test</div><p>bla", result: "bla", message: "allowElements list has no influence to dropElements"}, + {config_input: {dropAttributes: {"style": ["p"]}}, value: "<p style='color: black'>Click.</p><div style='color: white'>div</div>", result: "<p>Click.</p><div style=\"color: white\">div</div>", message: "dropAttributes list {\"style\": [\"p\"]} with style attribute"}, + {config_input: {dropAttributes: {"*": ["a"]}}, value: "<a id='a' style='color: black'>Click.</a><div style='color: white'>div</div>", result: "<a>Click.</a><div style=\"color: white\">div</div>", message: "dropAttributes list {\"*\": [\"a\"]} with style attribute"}, + {config_input: {dropAttributes: {}}, value: "<p id='test'>Click.</p>", result: "<p id=\"test\">Click.</p>", message: "empty dropAttributes list with id attribute"}, + {config_input: {dropAttributes: {"id": ["*"]}}, value: "<p id='test'>Click.</p>", result: "<p>Click.</p>", message: "dropAttributes list {\"id\": [\"*\"]} with id attribute"}, + {config_input: {dropAttributes: {"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: "dropAttributes list {\"data-attribute-with-dashes\": [\"*\"]} with dom dataset js access"}, + {config_input: {allowAttributes: {"id": ["div"]}}, value: "<p id='p'>P</p><div id='div'>DIV</div>", result: "<p>P</p><div id=\"div\">DIV</div>", message: "allowAttributes list {\"id\": [\"div\"]} with id attribute"}, + {config_input: {allowAttributes: {"id": ["*"]}}, value: "<p id='test' onclick='a= 123'>Click.</p>", result: "<p id=\"test\">Click.</p>", message: "allowAttributes 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: {dropAttributes: {"style": ["*"]}, allowAttributes: {"style": ["*"]}}, value: "<p style='color: black'>Click.</p>", result: "<p>Click.</p>", message: "allowAttributes list has no influence to dropAttributes"}, + {config_input: {allowElements: ["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: {allowComments: 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; allowComments"}, + {config_input: {allowComments: 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; !allowComments"}, + {config_input: {}, value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>", result: "<p>commentintext</p>", message: "HTML with comments deeper in the tree"}, + {config_input: {allowComments: 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, allowComments"}, + {config_input: {allowComments: false}, value: "<p>comment<!-- hello -->in<!-- </p> -->text</p>", result: "<p>commentintext</p>", message: "HTML with comments deeper in the tree, !allowComments"}, + {config_input: {allowElements: ["svg"]}, value: "<svg></svg>", result: "", message: "Unknown HTML names (HTMLUnknownElement instances) should not match elements parsed as non-HTML namespaces."}, + {config_input: {allowElements: ["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: {dropElements: ["I", "DL"]}, value: "<div>balabala<dl>test</dl></div>", result: "<div>balabala<dl>test</dl></div>", message: "dropElements list [\"I\", \"DL\"]}"}, + {config_input: {dropElements: ["i", "dl"]}, value: "<div>balabala<dl>test</dl></div>", result: "<div>balabala</div>", message: "dropElements list [\"i\", \"dl\"]}"}, + {config_input: {dropElements: ["i", "dl"]}, value: "<DIV>balabala<DL>test</DL></DIV>", result: "<div>balabala</div>", message: "dropElements list [\"i\", \"dl\"]} with uppercase HTML"}, + {config_input: {dropAttributes: {"ID": ["*"]}}, value: "<p id=\"test\">Click.</p>", result: "<p id=\"test\">Click.</p>", message: "dropAttributes list {\"ID\": [\"*\"]} with id attribute"}, + {config_input: {dropAttributes: {"ID": ["*"]}}, value: "<p ID=\"test\">Click.</p>", result: "<p id=\"test\">Click.</p>", message: "dropAttributes list {\"ID\": [\"*\"]} with ID attribute"}, + {config_input: {dropAttributes: {"id": ["*"]}}, value: "<p ID=\"test\">Click.</p>", result: "<p>Click.</p>", message: "dropAttributes list {\"id\": [\"*\"]} with ID attribute"}, + + // allowUnknownMarkup for elements (with and without) + {config_input: {dropElements: [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: "dropElements with unknown elements and without allowUnknownMarkup"}, + {config_input: {blockElements: [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: "blockElements with unknown elements and without allowUnknownMarkup"}, + {config_input: {allowElements: ["p", "test"]}, value: "<div>test<div>p</div>tt<p>div</p></div><test>test</test>", result: "testptt<p>div</p>", message: "allowElements with unknown elements and without allowUnknownMarkup"}, + {config_input: {dropElements: [123, [], "test", "i", "custom-element"], allowUnknownMarkup: true}, value: "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>", result: "<div>balabala</div>", message: "dropElements with unknown elements and with allowUnknownMarkup"}, + {config_input: {blockElements: [123, [], "test", "i", "custom-element"], allowUnknownMarkup: true}, value: "<div>balabala<i>test</i></div><test>t</test><custom-element>custom-element</custom-element>", result: "<div>balabalatest</div>t", message: "blockElements with unknown elements and with allowUnknownMarkup"}, + {config_input: {allowElements: ["p", "test"], allowUnknownMarkup: true}, value: "<div>test<div>p</div>tt<p>div</p><test>test</test></div>", result: "testptt<p>div</p><test>test</test>", message: "allowElements with unknown elements and with allowUnknownMarkup"}, + + // allowUnknownMarkup for attributes (with and without) + {config_input: {allowAttributes: {"hello": ["*"], "world": ["b"]}}, value: "<div hello='1' world='2'><b hello='3' world='4'>", result: "<div><b></b></div>", message: "allowAttributes unknown attributes and without allowUnknownMarkup"}, + {config_input: {allowAttributes: {"hello": ["*"], "world": ["b"]}, allowUnknownMarkup: true}, value: "<div hello='1' world='2'><b hello='3' world='4'>", result: "<div hello=\"1\"><b hello=\"3\" world=\"4\"></b></div>", message: "allowAttributes unknown attributes and with allowUnknownMarkup"}, + {config_input: {dropAttributes: {"hello": ["*"], "world": ["b"]}}, value: "<div hello='1' world='2'><b hello='3' world='4'>", result: "<div><b></b></div>", message: "dropAttributes unknown attributes and without allowUnknownMarkup"}, + {config_input: {dropAttributes: {"hello": ["*"], "world": ["b"]}, allowUnknownMarkup: true}, value: "<div hello='1' world='2'><b hello='3' world='4'>", result: "<div><b></b></div>", message: "dropAttributes unknown attributes and with allowUnknownMarkup"}, +]; |