Skip to content
Heist/ feed
Back to feed
HighFix available

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

Type: advisorySources: OSV

### Summary A link reference definition whose label or title is never closed is O(n²) in markdown-it 15.0.2 with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. It is a regression introduced by PR #1004 in 14.0.0: 13.0.2 processed a 313 KB input in 125 ms, while every version since takes about 11 seconds on the same input. At the sizes below, 200 KB takes 3.6 seconds, 400 KB takes 18.1 seconds, and 800 KB takes 74.0 seconds, which is roughly 4–5× per doubling. No options are required to trigger it. ### Details src/rules_block/reference.ts appends continuation lines with str += lineContent at lines 74, 83, 100, 133, and 150, then keeps indexing the accumulated string via str.charCodeAt(pos) in the label and whitespace loops and str.slice(...) in parseLinkTitle. V8 represents += as a cons-string, and the next charCodeAt or slice flattens it, copying everything collected so far, so one definition spanning n lines costs O(n²). Isolation confirms the rule is responsible: block-only parsing with inline disabled is still quadratic, md.disable('reference') makes the same input linear, and the same text without the leading [ is linear. The fix is to track positions in state.src instead of building str, or to collect line slices in an array and parse acro

Products
markdown-it
Affected versions
Not specified
Fixed versions
14.3.3, 15.0.3
CVEs
CVE-2026-111758
Confirmed fixed CVEs
Not specified
Updated in this feed
31 Oct 2026, 13:25 UTC · Revision 1

Source evidence

markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)

core-osv · Original source

Published 20 Oct 2026, 20:28 UTC

### Summary A link reference definition whose label or title is never closed is O(n²) in markdown-it 15.0.2 with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. It is a regression introduced by PR #1004 in 14.0.0: 13.0.2 processed a 313 KB input in 125 ms, while every version since takes about 11 seconds on the same input. At the sizes below, 200 KB takes 3.6 seconds, 400 KB takes 18.1 seconds, and 800 KB takes 74.0 seconds, which is roughly 4–5× per doubling. No options are required to trigger it. ### Details src/rules_block/reference.ts appends continuation lines with str += lineContent at lines 74, 83, 100, 133, and 150, then keeps indexing the accumulated string via str.charCodeAt(pos) in the label and whitespace loops and str.slice(...) in parseLinkTitle. V8 represents += as a cons-string, and the next charCodeAt or slice flattens it, copying everything collected so far, so one definition spanning n lines costs O(n²). Isolation confirms the rule is responsible: block-only parsing with inline disabled is still quadratic, md.disable('reference') makes the same input linear, and the same text without the leading [ is linear. The fix is to track positions in state.src instead of building str, or to collect line slices in an array and parse acro

osv:GHSA-gfr2-rv44-r23c

markdown-it

Fixed versions: 14.3.3

markdown-it

Fixed versions: 15.0.3

Complete source record
{
  "id": "9faaccbbfba1e455983db785e7145d49ffceb3748d7b82d5c89fe0e5ea12076e",
  "url": "https://osv.dev/vulnerability/GHSA-gfr2-rv44-r23c",
  "method": "structured",
  "payload": {
    "kind": "advisory",
    "title": "markdown-it: Quadratic-time rendering of a single multi-line link reference definition with default options (regression since 14.0)",
    "summary": "### Summary A link reference definition whose label or title is never closed is O(n²) in markdown-it 15.0.2 with default options. Continuation lines are pulled lazily with str += lineContent in src/rules_block/reference.ts, and the subsequent charCodeAt and slice calls flatten the V8 cons-string, copying everything collected so far; this runs before the rule decides the definition is invalid. It is a regression introduced by PR #1004 in 14.0.0: 13.0.2 processed a 313 KB input in 125 ms, while every version since takes about 11 seconds on the same input. At the sizes below, 200 KB takes 3.6 seconds, 400 KB takes 18.1 seconds, and 800 KB takes 74.0 seconds, which is roughly 4–5× per doubling. No options are required to trigger it. ### Details src/rules_block/reference.ts appends continuation lines with str += lineContent at lines 74, 83, 100, 133, and 150, then keeps indexing the accumulated string via str.charCodeAt(pos) in the label and whitespace loops and str.slice(...) in parseLinkTitle. V8 represents += as a cons-string, and the next charCodeAt or slice flattens it, copying everything collected so far, so one definition spanning n lines costs O(n²). Isolation confirms the rule is responsible: block-only parsing with inline disabled is still quadratic, md.disable('reference') makes the same input linear, and the same text without the leading [ is linear. The fix is to track positions in state.src instead of building str, or to collect line slices in an array and parse acro",
    "evidence": "osv:GHSA-gfr2-rv44-r23c",
    "products": [
      {
        "name": {
          "value": "markdown-it",
          "evidence": "/affected/0/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "14.3.3",
            "evidence": "/affected/0/ranges"
          }
        ],
        "affected_versions": []
      },
      {
        "name": {
          "value": "markdown-it",
          "evidence": "/affected/1/package/name"
        },
        "prerequisites": [],
        "fixed_versions": [
          {
            "value": "15.0.3",
            "evidence": "/affected/1/ranges"
          }
        ],
        "affected_versions": []
      }
    ],
    "severity": "high",
    "structured": {
      "id": "GHSA-gfr2-rv44-r23c",
      "aliases": [
        "CVE-2026-111758",
        "GHSA-gfr2-rv44-r23c"
      ],
      "metrics": [
        {
          "type": "CVSS_V3",
          "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
        }
      ],
      "affected": [
        {
          "ranges": [
            {
              "type": "SEMVER",
              "events": [
                {
                  "introduced": "14.1.0"
                },
                {
                  "fixed": "14.3.3"
                }
              ]
            }
          ],
          "package": {
            "name": "markdown-it",
            "purl": "pkg:npm/markdown-it",
            "ecosystem": "npm"
          },
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-gfr2-rv44-r23c/GHSA-gfr2-rv44-r23c.json"
          }
        },
        {
          "ranges": [
            {
              "type": "SEMVER",
              "events": [
                {
                  "introduced": "15.0.0"
                },
                {
                  "fixed": "15.0.3"
                }
              ]
            }
          ],
          "package": {
            "name": "markdown-it",
            "purl": "pkg:npm/markdown-it",
            "ecosystem": "npm"
          },
          "database_specific": {
            "source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/10/GHSA-gfr2-rv44-r23c/GHSA-gfr2-rv44-r23c.json"
          }
        }
      ],
      "modified": "2026-10-20T20:45:05.953469039Z",
      "provider": "osv",
      "references": [
        "https://github.com/markdown-it/markdown-it/security/advisories/GHSA-gfr2-rv44-r23c",
        "https://github.com/markdown-it/markdown-it/commit/48fbc465812c440020c663f040ab22deaf8f1eca",
        "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"
      ],
      "provider_metadata": {
        "cwe_ids": [
          "CWE-400",
          "CWE-407"
        ],
        "severity": "HIGH",
        "github_reviewed": true,
        "nvd_published_at": null,
        "github_reviewed_at": "2026-10-20T20:28:44Z"
      }
    },
    "related_cves": [
      {
        "value": "CVE-2026-111758",
        "evidence": "/aliases"
      }
    ],
    "security_relevant": true,
    "severity_evidence": "/database_specific/severity"
  },
  "source_id": "core-osv",
  "document_id": 390694,
  "published_at": "2026-10-20T20:28:44+00:00"
}