{"items":[{"id":"NVI-00010564","kind":"vulnerability","title":"markdown-it linkify: true has two quadratic paths, so a few hundred KB of markdown blocks the event loop for tens of seconds","revision":1,"unknowns":["Customer inventory, deployment, reachability and exploitation have not been inspected."],"lifecycle":"active","identifiers":["GHSA-253c-mchw-3w2r"],"plain_summary":"GitHub lists this reviewed advisory for markdown-it, markdown-it. Published version ranges and source prerequisites are shown below; this is not an inspection of a visitor’s application.","review_status":"source_listed","control_topics":[],"public_actions":[{"summary":"Identify the exact component and deployed version, examine the linked publisher’s conditions, and retain evidence of any mitigation.","source_ids":["sr-44a49ea0de15e343f0d7dce5e87917e8"],"action_type":"investigate","limitations":"Confirm current vendor support, compatibility and deployment evidence. This report has not tested a fix in your system.","current_supported_target":null}],"schema_version":"1.1.0","source_metrics":[],"why_it_matters":"Published affected context helps an owner decide what evidence to gather. This report has not examined your application.","related_sources":[{"id":"sr-44a49ea0de15e343f0d7dce5e87917e8","url":"https://github.com/advisories/GHSA-253c-mchw-3w2r","basis":"vendor_advisory","notes":"GitHub Advisory Database; CC-BY-4.0. Each advisory links its original attribution. Null normalizes fields.","title":"GHSA-253c-mchw-3w2r","publisher":"GitHub Advisory Database","observed_at":"2026-10-31T22:07:30.906Z"}],"affected_context":[{"certainty":"source_listed","source_ids":["sr-44a49ea0de15e343f0d7dce5e87917e8"],"technology":"markdown-it","known_exclusions":[],"conditions_summary":"## Summary\n\nTwo independent quadratic paths in the `linkify: true` handling. Both are in markdown-it's own code rather than in linkify-it, which stays linear on both payloads.\n\n`src/rules_core/linkify.ts` calls `arrayReplaceAt` once per linkified text token, and that rebuilds the whole `children` array each time. A paragraph of N soft-broken lines is one inline token with about 2N children, so you get N rebuilds over a 2N array. Schema-less emails are what reach it. A `http://` link gets consumed by the inline rule first and never arrives as a text token, so those stay linear.\n\n`src/rules_inline/linkify.ts` runs `state.pending.match(SCHEME_RE)` at every `://` in the source. `state.pending` only gets truncated once a link is actually produced, so an unregistered scheme leaves it growing and every `://` rescans the lot.\n\n## Proof of concept\n\nClean install of 15.0.0 from npm, `new MarkdownIt({ linkify: true }).render(payload)`, Node 24.\n\n| N | 5,000 | 10,000 | 20,000 | 40,000 |\n|---|---|---|---|---|\n| `'a@b.co\\n'.repeat(N)`, 34KB to 273KB | 1.1s | 4.7s | 19.6s | 89s |\n| `'a://'.repeat(N)`, 20KB to 156KB | 0.25s | 0.76s | 3.1s | 15.0s |\n\nDoubling the input roughly quadruples the time i","public_affected_ranges":["= 15.0.0"]},{"certainty":"source_listed","source_ids":["sr-44a49ea0de15e343f0d7dce5e87917e8"],"technology":"markdown-it","known_exclusions":[],"conditions_summary":"## Summary\n\nTwo independent quadratic paths in the `linkify: true` handling. Both are in markdown-it's own code rather than in linkify-it, which stays linear on both payloads.\n\n`src/rules_core/linkify.ts` calls `arrayReplaceAt` once per linkified text token, and that rebuilds the whole `children` array each time. A paragraph of N soft-broken lines is one inline token with about 2N children, so you get N rebuilds over a 2N array. Schema-less emails are what reach it. A `http://` link gets consumed by the inline rule first and never arrives as a text token, so those stay linear.\n\n`src/rules_inline/linkify.ts` runs `state.pending.match(SCHEME_RE)` at every `://` in the source. `state.pending` only gets truncated once a link is actually produced, so an unregistered scheme leaves it growing and every `://` rescans the lot.\n\n## Proof of concept\n\nClean install of 15.0.0 from npm, `new MarkdownIt({ linkify: true }).render(payload)`, Node 24.\n\n| N | 5,000 | 10,000 | 20,000 | 40,000 |\n|---|---|---|---|---|\n| `'a@b.co\\n'.repeat(N)`, 34KB to 273KB | 1.1s | 4.7s | 19.6s | 89s |\n| `'a://'.repeat(N)`, 20KB to 156KB | 0.25s | 0.76s | 3.1s | 15.0s |\n\nDoubling the input roughly quadruples the time i","public_affected_ranges":["< 14.3.1"]}],"null_published_at":"2026-10-31T22:27:09.622Z","source_updated_at":"2026-10-22T20:35:11.000Z","source_disclosed_at":"2026-09-29T17:57:56.000Z","is_synthetic_fixture":false,"null_last_reviewed_at":null,"public_change_summary":"First indexed by Null; source disclosure date is shown separately.","null_first_observed_at":"2026-10-31T22:07:30.906Z"}],"next_cursor":null,"snapshot_revision":"42594"}