sanitize-html discards the content of every element in nonTextTags. It delimits that region
using htmlparser2's close events, and ignores the isImplied flag htmlparser2 passes it. Closing a
still-open ancestor from inside a <noscript> makes htmlparser2 unwind its stack past the
noscript; the synthetic implied close it emits on the way ends the discard region early, and
everything after it is treated as ordinary body content.
A browser with scripting enabled parses <noscript> content as raw text, so the same markup
renders as inert text there. The sanitizer therefore emits elements the page itself would not have.
const sanitizeHtml = require("sanitize-html");
sanitizeHtml('<div><noscript></div><img src="http://evil.test/t.gif"></noscript>', {
allowedTags: ["div", "img"],
allowedAttributes: { img: ["src"] },
nonTextTags: ["script", "style", "textarea", "option", "noscript"],
});
// => '<div></div><img src="http://evil.test/t.gif" />'
Chrome, given the same input, renders no <img> — the whole payload is raw text inside the
unclosed <noscript>. Verified with Playwright, srcdoc into an iframe so the scripting flag is on.
A <![CDATA[ variant hides the opening tag from the parser entirely and has the same effect:
<p>Hi</p><![CDATA[ x > <noscript>]]><a href="…">Reset your password</a><img src="…beacon.gif"></noscript>
htmlparser2 runs a CDATA token to ]]>; Chrome ends a bogus comment at the first >, so the
<noscript> after it is real to the browser and invisible to the parser.
index.js:
:154 — nonTextTagsArray (the default list, ['script','style','textarea','option','xmp']).:638 — onclosetag: function (name, isImplied) — receives the flag.:644 — skipTextDepth-- — decrements without consulting isImplied.Do not end a discard region on an implied close. htmlparser2 already distinguishes them; the flag is passed and discarded. Something close to:
onclosetag: function (name, isImplied) {
if (skipText) {
if (isImplied) return; // an implied close must not end the region
skipTextDepth--;
…
{
"cwe_ids": [
"CWE-79"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-20T18:03:22Z",
"nvd_published_at": null,
"severity": "MODERATE"
}