{"id":"CVE-2026-48988","aliases":["GHSA-6v5v-wf23-fmfq"],"url":"https://o3.security/vulnerability/CVE-2026-48988","summary":"markdown-it: Quadratic complexity DoS in smartquotes rule via replaceAt string operations","details":"### Summary\n\nA quadratic time complexity vulnerability exists in markdown-it's smartquotes rule (enabled via the `typographer: true` option). An attacker can craft a markdown input consisting of consecutive quotation marks that causes the parser to consume excessive CPU time, leading to denial of service.\n\n### Details\n\nThe vulnerability is in the `replaceAt()` helper function used by the smartquotes rule in `lib/rules_core/smartquotes.mjs`:\n\n```javascript\nfunction replaceAt (str, index, ch) {\n  return str.slice(0, index) + ch + str.slice(index + 1)\n}\n```\n\nWhen markdown-it processes a text token containing many quotation marks (either `\"` or `'`) with `typographer: true`, the smartquotes rule iterates through each quote character and calls `replaceAt()` to substitute it with a typographic (curly) quote. Each call to `replaceAt()` creates three new string slices and concatenates them, which is an O(n) operation where n is the length of the string.\n\nSince this is called once per quote character in the token, and there are n quote characters, the total time complexity becomes O(n^2).\n\nThe root cause is that the smartquotes rule modifies `token.content` in place using string slicing rather than building the result incrementally. The `process_inlines()` function (line 14) processes each quote in the text token, and for matching quote pairs, calls `replaceAt()` on both the opening and closing token's content (lines 151-152). When the entire input is a single text token of quote characters, this results in quadratic behavior.\n\n### PoC\n\n```javascript\nconst md = require('markdown-it');\nconst instance = md({ typographer: true });\n\n// 160,000 consecutive double-quote characters\nconst payload = '\"'.repeat(160000);\n\nconsole.time('render');\ninstance.render(payload);\nconsole.timeEnd('render');\n// Output: render: ~21000ms (21 seconds)\n\n// Compare with typographer disabled:\nconst safe = md({ typographer: false });\nconsole.time('render-safe');\nsafe.render(payload);\nconsole.timeEnd('render-safe');\n// Output: render-safe: ~8ms\n```\n\nMeasured timing on a modern system:\n- 10,000 quotes: ~19ms\n- 20,000 quotes: ~51ms\n- 40,000 quotes: ~212ms\n- 80,000 quotes: ~5,430ms\n- 160,000 quotes: ~21,198ms\n\nThe scaling is clearly superlinear (quadratic), with the 80K->160K step showing a ~3.9x increase for a 2x input increase, consistent with O(n^2).\n\n### Impact\n\nApplications that render user-supplied markdown with `typographer: true` are vulnerable to denial of service. An attacker can submit a relatively small payload (160KB of quote characters) that causes the server to spend over 21 seconds processing a single request. Repeated submissions can exhaust server CPU resources and prevent legitimate users from being served.\n\nThe impact is mitigated by the fact that the `typographer` option defaults to `false` and must be explicitly enabled. However, the typographer feature is commonly enabled in production applications that want smart typography, and the markdown-it documentation prominently suggests enabling it.\n\nA suggested fix would be to replace the `replaceAt()` approach with an array-based or StringBuilder-style approach that collects all replacements and applies them in a single pass, reducing the time complexity to O(n).","published":"2026-06-17T20:54:06.641Z","modified":"2026-10-28T02:30:36.304447051Z","cvss":{"score":5.3,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L"},"epss":{"score":0.00379,"percentile":0.31222,"asOf":"2026-08-20"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"markdown-it","fixedVersion":"14.2.0"}],"fix":{"url":"https://github.com/markdown-it/markdown-it/commit/9ce2087562c45d1e5ddd9f76b990f4b3fbe040e5","label":"markdown-it/markdown-it@9ce2087"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/48xxx/CVE-2026-48988.json"},{"type":"ADVISORY","url":"https://github.com/markdown-it/markdown-it/security/advisories/GHSA-6v5v-wf23-fmfq"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-48988"},{"type":"FIX","url":"https://github.com/markdown-it/markdown-it/commit/9ce2087562c45d1e5ddd9f76b990f4b3fbe040e5"},{"type":"PACKAGE","url":"https://github.com/markdown-it/markdown-it"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:36.304447051Z"}}