{"id":"CVE-2026-111759","kind":"vulnerability","aliases":["CVE-2026-111759","GHSA-r7fv-28h4-cvq7"],"related":[],"title":"markdown-it: Quadratic complexity in smartquotes with unmatched closing quotes","description":"### Impact\n\nWhen `typographer: true` is enabled, specially crafted Markdown can cause quadratic processing time in the smartquotes rule, potentially leading to denial of service in applications processing untrusted input.\n\nMany opening double quotes followed by unmatched closing single quotes cause repeated scans of the entire opener stack. The same issue occurs with the quote types reversed.\n\nThe default configuration (`typographer: false`) is not affected, but this option is enabled with high probability.\n\n### Reproduction\n\n```js\nimport MarkdownIt from 'markdown-it'\nimport { performance } from 'node:perf_hooks'\n\nconst md = new MarkdownIt({ typographer: true })\n\nfor (const k of [2000, 4000, 8000, 16000, 32000]) {\n  const input = '\"a '.repeat(k) + \"b' \".repeat(k)\n  const start = performance.now()\n  md.render(input)\n  console.log(input.length, (performance.now() - start).toFixed(1), 'ms')\n}\n```\n\nResult:\n\n|input size|time|\n|-|-|\n|12000|18ms|\n|24000|47ms|\n|48000|115ms|\n|96000|428ms|\n|192000|1586ms|\n\n### Patches\n\nFixed in **15.0.2** and **14.3.2**.\n\n### Workarounds\n\nDisable typography with `typographer: false`, or disable only the smartquotes rule:\n\n```js\nmd.disable('smartquotes')\n```","summary":"markdown-it is a JavaScript Markdown parser; with typographer enabled, crafted input with many unmatched quote characters makes the smartquotes rule run in quadratic time. An attacker who can submit Markdown to an application gets a denial of service by tying up CPU during rendering.","severity":"high","cvss":7.5,"epss":0.005979999899864197,"exploited":false,"exploited_at":null,"products":[{"name":"markdown-it","fixed":["15.0.2"],"affected":">= 15.0.0, < 15.0.2","ecosystem":"npm"},{"name":"markdown-it","fixed":["14.3.2"],"affected":"< 14.3.2","ecosystem":"npm"}],"ecosystems":["npm"],"tags":["cwe-400","cwe-407","markdown-it","node.js","package","typographer","untrusted-input"],"not_affected_if":["default-config","no-untrusted-markdown","typographer-disabled"],"how_to_check":"Run npm ls markdown-it to find the installed version and check whether typographer: true is set where markdown-it is constructed.\nSearch the code for 'typographer: true' or 'typographer:true'.","fix":"Upgrade markdown-it to 15.0.2 (for the 15.x line) or 14.3.2 (for the 14.x line).\nAs a workaround, set typographer to false or call md.disable('smartquotes').","status":"open","published_at":"2026-10-19T18:17:00+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","triaged_at":"2026-10-23T19:55:43+00:00","updated_at":"2026-10-23T21:57:18+00:00","category":"package","confidence":0.9,"triage":{"at":"2026-10-23T19:55:43Z","model":"claude-haiku-5-5","output":{"fix":"Upgrade markdown-it to 15.0.2 (for the 15.x line) or 14.3.2 (for the 14.x line).\nAs a workaround, set typographer to false or call md.disable('smartquotes').","summary":"markdown-it is a JavaScript Markdown parser; with typographer enabled, crafted input with many unmatched quote characters makes the smartquotes rule run in quadratic time. An attacker who can submit Markdown to an application gets a denial of service by tying up CPU during rendering.","category":"package","who_cares":["node.js","markdown-it","typographer","untrusted-input"],"confidence":0.9,"analyst_note":"","how_to_check":"Run npm ls markdown-it to find the installed version and check whether typographer: true is set where markdown-it is constructed.\nSearch the code for 'typographer: true' or 'typographer:true'.","needs_analyst":false,"not_affected_if":["typographer-disabled","default-config","no-untrusted-markdown"],"severity_if_unknown":"high"},"cost_usd":0.000427,"input_tokens":2482,"output_tokens":358,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-111759':14A,22A '-2026':13A,21A '-400':30B '-407':32B '1':207C '115ms':218C '12000':213C '14.3.2':228C '15.0.2':226C '1586ms':222C '16000':187C '18ms':214C '192000':221C '2000':184C '24000':215C '28h4':18A '32000':188C '4000':185C '428ms':220C '47ms':216C '48000':217C '8000':186C '96000':219C 'affect':151C 'applic':76B,113C 'attack':69B 'b':194C 'caus':98C,127C 'charact':59B 'close':10A,124C 'complex':5A 'configur':146C 'console.log':202C 'const':174C,181C,189C,197C 'cpu':85B 'craft':53B,95C 'cve':12A,20A 'cvq7':19A 'cwe':29B,31B 'default':145C 'denial':79B,109C 'disabl':230C,236C 'doubl':119C 'enabl':52B,93C,156C 'entir':132C 'fals':148C,234C 'fix':224C 'follow':121C 'get':77B 'ghsa':16A 'ghsa-r7fv-28h4-cvq7':15A 'high':158C 'hook':173C 'impact':88C 'import':162C,168C 'input':41B,54B,116C,190C,201C,210C 'input.length':203C 'issu':137C 'it':25A,28A 'javascript':47B 'js':161C,241C 'k':182C,193C,196C 'lead':107C 'make':60B 'mani':56B,117C 'markdown':2A,24A,27A,34B,43B,48B,73B,96C,166C 'markdown-it':1A,23A,26A,33B,42B,165C 'markdownit':163C,177C 'md':175C 'md.disable':242C 'md.render':200C 'ms':208C 'new':176C 'node':171C 'node.js':36B 'occur':138C 'open':118C,133C 'option':154C 'packag':37B 'parser':49B 'patch':223C 'perf':172C 'perform':169C 'performance.now':199C,204C 'potenti':106C 'probabl':159C 'process':100C,114C 'quadrat':4A,66B,99C 'quot':11A,58B,120C,126C,141C 'r7fv':17A 'render':87B 'repeat':128C,192C,195C 'reproduct':160C 'result':209C 'revers':143C 'rule':63B,105C,240C 'run':64B 'scan':129C 'servic':81B,111C 'singl':125C 'size':211C 'smartquot':7A,62B,104C,239C,243C 'special':94C 'stack':134C 'start':198C,205C 'submit':72B 'tie':83B 'time':67B,101C,212C 'tofix':206C 'true':91C,179C 'type':142C 'typograph':38B,51B,90C,147C,178C,233C 'typographi':231C 'unmatch':9A,57B,123C 'untrust':40B,115C 'untrusted-input':39B 'workaround':229C","nicknames":[],"mitigations":[],"who_affected":null,"tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-19T18:17:00+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-r7fv-28h4-cvq7","url":"https://github.com/advisories/GHSA-r7fv-28h4-cvq7","severity":"high","published_at":"2026-10-20T14:34:50+00:00","modified_at":"2026-10-20T14:34:52+00:00","first_seen_at":"2026-10-23T19:11:32+00:00","fetched_at":"2026-10-23T19:11:32+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-111759","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-111759","severity":null,"published_at":"2026-10-19T18:17:00+00:00","modified_at":"2026-10-19T21:13:39+00:00","first_seen_at":"2026-10-23T21:57:18+00:00","fetched_at":"2026-10-23T22:28:57+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}