PT-2026-120329 · Npm · Sanitize-Html

تاريخ النشر

2026-10-20

·

تاريخ التحديث

2026-10-20

CVSS v4.0

5.3

متوسطة

المتجهAV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N

Summary

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.

Reproduction

js
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.

Where the behaviour is

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.

Suggested fix

Do not end a discard region on an implied close. htmlparser2 already distinguishes them; the flag is passed and discarded. Something close to:
js
onclosetag: function (name, isImplied) {
 if (skipText) {
  if (isImplied) return;  // an implied close must not end the region
  skipTextDepth--;
  …

إصلاح

XSS

وجدت خطأً في الوصف؟ لديك ما تضيفه؟ لا تتردد في مراسلتنا 👾

تصنيف نقاط الضعف

المعرّفات ذات الصلة

GHSA-X3Q4-9HXX-GX8M

المنتجات المتأثرة

Sanitize-Html