Created
November 25, 2025 10:10
-
-
Save mozfreddyb/9bb187bfb1416610219b96a75bf3c811 to your computer and use it in GitHub Desktop.
testing setHTML in obscure DOM trees by going 3 levels deep and looping through all elements
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!doctype html> | |
| <html> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Sanitizer Fuzzing</title> | |
| </head> | |
| <body> | |
| <h1>Testing...<span id="progress">0</span> of <span id="total">Unknown</span> (<span id="pct"></span>%)</h1> | |
| <div id="ctx">...</div> | |
| <script> | |
| // from whatwg spec | |
| let element_list = [ | |
| "a", | |
| "abbr", | |
| "address", | |
| "area", | |
| "article", | |
| "aside", | |
| "audio", | |
| "b", | |
| "base", | |
| "bdi", | |
| "bdo", | |
| "blockquote", | |
| "body", | |
| "br", | |
| "button", | |
| "canvas", | |
| "caption", | |
| "cite", | |
| "code", | |
| "col", | |
| "colgroup", | |
| "data", | |
| "datalist", | |
| "dd", | |
| "del", | |
| "details", | |
| "dfn", | |
| "dialog", | |
| "div", | |
| "dl", | |
| "dt", | |
| "em", | |
| "embed", | |
| "fieldset", | |
| "figcaption", | |
| "figure", | |
| "footer", | |
| "form", | |
| "h1", | |
| "h2", | |
| "h3", | |
| "h4", | |
| "h5", | |
| "h6", | |
| "head", | |
| "header", | |
| "hgroup", | |
| "hr", | |
| "html", | |
| "i", | |
| "iframe", | |
| "img", | |
| "input", | |
| "ins", | |
| "kbd", | |
| "label", | |
| "legend", | |
| "li", | |
| "link", | |
| "main", | |
| "map", | |
| "mark", | |
| "menu", | |
| "meta", | |
| "meter", | |
| "nav", | |
| "noscript", | |
| "object", | |
| "ol", | |
| "optgroup", | |
| "option", | |
| "output", | |
| "p", | |
| "picture", | |
| "pre", | |
| "progress", | |
| "q", | |
| "rp", | |
| "rt", | |
| "ruby", | |
| "s", | |
| "samp", | |
| "script", | |
| "search", | |
| "section", | |
| "select", | |
| "selectedcontent", | |
| "slot", | |
| "small", | |
| "source", | |
| "span", | |
| "strong", | |
| "style", | |
| "sub", | |
| "summary", | |
| "sup", | |
| "table", | |
| "tbody", | |
| "td", | |
| "template", | |
| "textarea", | |
| "tfoot", | |
| "th", | |
| "thead", | |
| "time", | |
| "title", | |
| "tr", | |
| "track", | |
| "u", | |
| "ul", | |
| "var", | |
| "video", | |
| "wbr" | |
| ]; | |
| // sourced from Sonar mxss cheat sheet | |
| let vectors = [ | |
| `<svg></p><style><a id="</style><img src=1 onerror=alert(1)>">`, | |
| `<form><math><mtext></form><form><mglyph><style></math><img src onerror=alert(1)>`, | |
| `<math><mtext><table><mglyph><style><!--</style><img title="--></mglyph><img	src=1	onerror=alert(1)>">`, | |
| '<math><mtext><table><mglyph><style><math><table id=”</table>”><img src onerror=alert(1)”>', | |
| `<form><math><mtext></form><form><mglyph><svg><mtext><style><path id="</style><img onerror=alert(1) src>">`, | |
| `<svg><xss><desc><noscript></noscript></desc><p></p><style><a title="</style><img src onerror=alert(1)>">`, | |
| `<svg><annotation-xml><foreignobject><style><!--</style><p id="--><img src='x' onerror='alert(1)'>">`, | |
| `<svg><a><foreignobject><a><table><a></table><style><!--</style></svg><a id="-><img src onerror=alert(1)>">.`, | |
| `<math><foo-test><mi><li><table><foo-test><li></li></foo-test>a<a><style><!--</style>a<foo-bar is="--><img src=x onerror=alert(1)>">`, | |
| `<math><foo-test><mi><li><table><foo-test><li></li></foo-test><a><style><! \\$$\{</style>}<foo-b id="><img src onerror='alert(1)'>">hmm...</foo-b></a></table></li></mi></foo-test></math>`, | |
| `<noscript><style></noscript><img src=x onerror=alert(1)>`, | |
| `<svg><style><img src=x onerror=alert(1)>`, | |
| `<math><p></p><style><!--</style><img src/onerror=alert(1)>--></style></math>`, | |
| `<noscript><p title="</noscript><img src=x onerror=alert(1)>">`, | |
| `<!--a foo=--!><img src=x onerror=alert(1)><!--<a>">`, | |
| `<![CDATA[<math><img src=x onerror=alert(1)>]]>` | |
| ] | |
| n = 506; | |
| var payload = `${"<div>".repeat(n)}<table id="outer"><caption id="outer"><svg><desc><table id="inner"><caption id="inner"></caption></table></desc><style><a title="</style><img src onerror=alert(1)>"></a></style></svg></caption></table>${"</div>".repeat(n)}`; | |
| vectors.push(payload); | |
| results = []; | |
| function myAlert() { | |
| let stack = new Error().stack; | |
| result = { | |
| stack, | |
| count, | |
| outerHTML: ctx.outerHTML, | |
| } | |
| results.push(result); | |
| } | |
| var original_alert = alert; | |
| window.alert = myAlert; | |
| document.alert = myAlert; | |
| let totalnum = Math.pow(element_list.length,3) * vectors.length; | |
| total.textContent = totalnum; | |
| var count = 0; | |
| var o = 0; | |
| var m = 0; | |
| var i = 0; | |
| let step = 1; | |
| function next(o, step) { | |
| if (step > element_list.length) { | |
| ctx.textContent = "Done."; | |
| let pretty = JSON.stringify(results, null, 2); | |
| ctx.innerHTML += `<pre>${pretty}</pre>`; | |
| return -1; | |
| } | |
| for (; o < step; o++) { | |
| let outer = element_list[o]; | |
| console.log("Start", outer); | |
| let outer_el = document.createElement(outer); | |
| ctx.appendChild(outer_el); | |
| for (var m=0; m < element_list.length; m++) { | |
| let middle = element_list[m]; | |
| let middle_el = document.createElement(middle); | |
| outer_el.appendChild(middle_el); | |
| for (var i=0; i < element_list.length; i++) { | |
| let inner = element_list[i]; | |
| let inner_el = document.createElement(inner); | |
| middle_el.appendChild(inner_el); | |
| for (let xss of vectors) { | |
| inner_el.setHTML(xss, {sanitizer: {}}); | |
| ++count; | |
| } | |
| } | |
| middle_el.textContent = ""; | |
| } | |
| outer_el.textContent = ""; | |
| progress.textContent = count; | |
| pct.textContent = Math.round(count / totalnum * 100); | |
| console.log("Done with", outer); | |
| } | |
| } | |
| // based on local testing, iteration at 3 levels deep takes about 11 seconds | |
| // continue the main loop every 15 seconds | |
| let interval = setInterval(outer_loop, 15); | |
| function outer_loop() { | |
| r = next(o++, step++) | |
| if (r === -1) { | |
| clearInterval(interval); | |
| } | |
| } | |
| </script> | |
| <button onclick="next(o++, step++)">Next</button> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment