Skip to content

fix(deps): update dependency markdown-it@<=14.1.1 to ^14.3.3 [security] - #46

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-markdown-it-=14.1.1-vulnerability
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/npm-markdown-it-=14.1.1-vulnerability

Conversation

@renovate

@renovate renovate Bot commented Oct 25, 2026

Copy link
Copy Markdown

This PR contains the following updates:

Package Change Age Confidence
markdown-it@<=14.1.1 ^14.1.2 → ^14.3.3 age confidence

markdown-it: Quadratic complexity in smartquotes with unmatched closing quotes

CVE-2026-111759 / GHSA-r7fv-28h4-cvq7

More information

Details

Impact

When 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.

Many 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.

The default configuration (typographer: false) is not affected, but this option is enabled with high probability.

Reproduction
import MarkdownIt from 'markdown-it'
import { performance } from 'node:perf_hooks'

const md = new MarkdownIt({ typographer: true })

for (const k of [2000, 4000, 8000, 16000, 32000]) {
  const input = '"a '.repeat(k) + "b' ".repeat(k)
  const start = performance.now()
  md.render(input)
  console.log(input.length, (performance.now() - start).toFixed(1), 'ms')
}

Result:

input size time
12000 18ms
24000 47ms
48000 115ms
96000 428ms
192000 1586ms
Patches

Fixed in 15.0.2 and 14.3.2.

Workarounds

Disable typography with typographer: false, or disable only the smartquotes rule:

md.disable('smartquotes')

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


markdown-it: Quadratic-time denial of service in inline HTML parsing with html enabled

CVE-2026-111766 / GHSA-vfgh-jcmw-g37q

More information

Details

Summary

With html: true, markdown-it takes quadratic time to parse repeated unterminated HTML constructs inside a paragraph. This affects HTML comments (<!--), processing instructions (<?), declarations (<!A), and CDATA sections (<![CDATA[).

An attacker-controlled input of a few hundred kilobytes can block the Node.js event loop for seconds or tens of seconds. The default configuration has HTML disabled, but the commonmark preset enables it.

The issue was reported against the published markdown-it@15.0.2 package on Node.js 20 and reproduced on the current source with Node.js 24.16.0.

Details

src/rules_inline/text.ts treats < as a terminator, causing the inline parser to attempt the HTML rule at successive HTML construct openers.

With HTML enabled, src/rules_inline/html_inline.ts applies an anchored regular expression to the remaining input:

const match = state.src.slice(pos).match(HTML_TAG_RE)

The comment, processing instruction, declaration, and CDATA alternatives in src/common/html_re.ts scan the remaining input for their respective terminators: -->, ?>, >, and ]]>.

When the terminator is absent, matching fails. Parsing eventually reaches the next opener and scans an overlapping suffix again. Repeating this across the paragraph produces quadratic total work. For these inputs, the problem is repeated scanning across separate matches.

The payload must reach inline parsing. Prefixing it with x keeps it inside a paragraph. Starting a line directly with one of these constructs instead invokes HTML block parsing, which does not exhibit the same repeated inline scans for these payloads.

The existing unclosed <!-- test in test/markdown-it/pathological.test.mjs already contains a reproducer:

'</' + '<!--'.repeat(100000)

However, it runs with default options, where HTML is disabled.

A fix should avoid repeated searches for absent terminators across all four affected alternatives, for example by caching failed searches on the inline state. Fixing only the comment alternative leaves the other three cases affected. Regression coverage should exercise all four with html: true.

The original report was submitted by @​JarLob. It credits the GitHub Security Lab Taskflow Agent as part of the GitHub Secure Open Source Fund.

Proof of concept

Install the affected package in an empty directory:

npm install markdown-it@15.0.2

Save the following as poc.mjs and run node poc.mjs:

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)
})

Measurements from the original report on markdown-it@15.0.2, Node.js 20:

Construct 8,000 repetitions 64,000 repetitions
Comment 170 ms 8,799 ms
Processing instruction 26 ms 1,566 ms
Declaration 93 ms 5,628 ms
CDATA 119 ms 4,179 ms

An eightfold increase in repetitions produces approximately 35–60 times the execution time, consistent with quadratic growth becoming dominant.

For the comment payload at 64,000 repetitions:

  • HTML disabled: 25 ms.
  • Closed comments: 29 ms.
  • Payload at the start of an HTML block: 1 ms.
  • Plain text: 1 ms.

The existing pathological-test payload with 100,000 comment openers took 33 ms with HTML disabled and 21,030 ms with HTML enabled. A separate comment payload with 128,000 repetitions, approximately 500 KiB, took 32,847 ms.

Exact timings depend on the runtime and hardware.

Impact

This is an availability issue affecting applications that render attacker-controlled Markdown with html: true, including applications using the commonmark preset.

On Node.js, a synchronous render() call can occupy a CPU core and block the event loop for seconds or tens of seconds, delaying other requests handled by the same process. Repeated submissions can cause denial of service. Browser applications can similarly become unresponsive when rendering on the main thread.

Sanitizing the generated HTML afterwards does not prevent the issue because the expensive processing occurs during Markdown rendering.

The default configuration, with HTML disabled, is unaffected by these payloads. Disabling HTML or limiting input size can reduce exposure while a fix is prepared.

The reported affected release is 15.0.2; the full affected version range has not been established.

Severity

  • CVSS Score: 3.7 / 10 (Low)
  • Vector String: CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).


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

CVE-2026-111758 / GHSA-gfr2-rv44-r23c

More information

Details

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 across them. At minimum, stop scanning the label after 999 characters (CommonMark §6.3) before pulling more lines, and cap title continuation. Add a pathological test with a 100k-line unclosed title and label. Open PR #​1215 edits the same file but its label check runs after the label loop, so it does not bound either variant and the fix can land together with that PR.

The observed output from PoC was:
unclosed title n=100000 200009 B 3585.5 ms
unclosed title n=200000 400009 B 18107.1 ms (5.05x)
unclosed title n=400000 800009 B 73954.4 ms (4.08x)

Regression comparison at 313 KB: 13.0.2 → 125 ms, 14.1.0 → 11.4 s, 15.0.0 → 10.6 s, 15.0.2 → 10.7 s.

Proof of concept

How to execute the PoC:

  1. mkdir -p markdown-it-poc && cd markdown-it-poc
  2. npm init -y
  3. npm i markdown-it@15.0.2
  4. node poc_markdown_it.js

PoC below is:

// poc_markdown_it.js
// Requires: npm install markdown-it@15.0.2
const MarkdownIt = require('markdown-it');
const md = new MarkdownIt();

function bench(label, src) {
  const t0 = process.hrtime.bigint();
  md.render(src);
  const t1 = process.hrtime.bigint();
  console.log(
    `${label.padEnd(30)} ${String(src.length).padStart(9)} B ${(Number(t1 - t0) / 1e6).toFixed(1).padStart(9)} ms`
  );
}

// Unclosed title: one reference definition spread over many lines
for (const n of [100000, 200000, 400000]) {
  bench(`unclosed title n=${n}`, '[a]: /u "' + 'x\n'.repeat(n));
}

// Unclosed label
for (const n of [5000, 10000, 20000]) {
  bench(`unclosed label n=${n}`, '[' + 'a\n'.repeat(n));
}
Impact

This is an algorithmic-complexity denial of service (CWE-407). Any server that renders user Markdown with markdown-it is affected with default options, including comments, issues, chat, wikis, docs previews, and LLM chat UIs. A ~400 KB request body blocks the Node.js event loop for about 18 seconds and an ~800 KB body for about 74 seconds. This is the same class and attacker model as GHSA-253c, but it is reachable without any opt-in option, since the affected path is a core block rule.

Severity

  • CVSS Score: 7.5 / 10 (High)
  • Vector String: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

References

This data is provided by the GitHub Advisory Database (CC-BY 4.0).

⚠️ Renovate does not enforce Minimum Release Age for bump, lockfileUpdate, or rollback updates, so these are raised without a Minimum Release Age check. You will need to manually validate the Minimum Release Age for these package(s).


Release Notes

markdown-it/markdown-it (markdown-it@<=14.1.1)

v14.3.3

Compare Source

v14.3.2

Compare Source

v14.3.1

Compare Source

v14.3.0

Compare Source

Changed
  • Reworked build pipeline & tools.
  • Added source maps.
  • Bumped linkify-it to 5.0.2.
Fixed
  • Preserve backslash-space hard line breaks, matching CommonMark 6.7, #​1185.

Configuration

📅 Schedule: (in timezone Europe/Berlin)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Enabled.

♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

Signed-off-by: Renovate Bot <bot@renovateapp.com>
@coderabbitai

coderabbitai Bot commented Oct 25, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3fae79f9-a1bc-4388-8b4a-788a75545462

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

Thank you for following the naming conventions! 🙏

@github-actions github-actions Bot added the size:XS Size of the change, tests and generated files excluded. label Oct 25, 2026
@github-actions

Copy link
Copy Markdown
Contributor

It looks like this is your first pull request. 🎉 Thank you for your contribution! One of the project maintainers will triage and assign the pull request for review. We appreciate your patience. To safeguard the health of the project, please take a moment to read our code of conduct.

@github-actions github-actions Bot added the vouch:trusted PR author is a collaborator, a bot or on the VOUCHED list. label Oct 25, 2026
@pkg-pr-new

pkg-pr-new Bot commented Oct 25, 2026

Copy link
Copy Markdown

Open in StackBlitz

@visulima/tui

npm i https://pkg.pr.new/visulima/vis/@visulima/tui@46

@visulima/secret-scanner

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner@46

@visulima/task-runner

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner@46

@visulima/task-runner-client

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-client@46

@visulima/vis

npm i https://pkg.pr.new/visulima/vis/@visulima/vis@46

@visulima/vis-mcp

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-mcp@46

@visulima/tui-binding-darwin-arm64

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-darwin-arm64@46

@visulima/tui-binding-darwin-x64

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-darwin-x64@46

@visulima/tui-binding-linux-arm64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-linux-arm64-gnu@46

@visulima/tui-binding-linux-arm64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-linux-arm64-musl@46

@visulima/tui-binding-linux-x64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-linux-x64-gnu@46

@visulima/tui-binding-linux-x64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-linux-x64-musl@46

@visulima/tui-binding-win32-arm64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-win32-arm64-msvc@46

@visulima/tui-binding-win32-x64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/tui-binding-win32-x64-msvc@46

@visulima/task-runner-binding-darwin-arm64

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-darwin-arm64@46

@visulima/task-runner-binding-darwin-x64

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-darwin-x64@46

@visulima/task-runner-binding-linux-arm64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-linux-arm64-gnu@46

@visulima/task-runner-binding-linux-arm64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-linux-arm64-musl@46

@visulima/task-runner-binding-linux-x64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-linux-x64-gnu@46

@visulima/task-runner-binding-linux-x64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-linux-x64-musl@46

@visulima/task-runner-binding-win32-arm64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-win32-arm64-msvc@46

@visulima/task-runner-binding-win32-x64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/task-runner-binding-win32-x64-msvc@46

@visulima/secret-scanner-binding-darwin-arm64

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-darwin-arm64@46

@visulima/secret-scanner-binding-darwin-x64

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-darwin-x64@46

@visulima/secret-scanner-binding-linux-arm64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-linux-arm64-gnu@46

@visulima/secret-scanner-binding-linux-arm64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-linux-arm64-musl@46

@visulima/secret-scanner-binding-linux-x64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-linux-x64-gnu@46

@visulima/secret-scanner-binding-linux-x64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-linux-x64-musl@46

@visulima/secret-scanner-binding-win32-arm64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-win32-arm64-msvc@46

@visulima/secret-scanner-binding-win32-x64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/secret-scanner-binding-win32-x64-msvc@46

@visulima/vis-binding-darwin-arm64

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-darwin-arm64@46

@visulima/vis-binding-darwin-x64

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-darwin-x64@46

@visulima/vis-binding-linux-arm64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-linux-arm64-gnu@46

@visulima/vis-binding-linux-arm64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-linux-arm64-musl@46

@visulima/vis-binding-linux-x64-gnu

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-linux-x64-gnu@46

@visulima/vis-binding-linux-x64-musl

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-linux-x64-musl@46

@visulima/vis-binding-win32-arm64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-win32-arm64-msvc@46

@visulima/vis-binding-win32-x64-msvc

npm i https://pkg.pr.new/visulima/vis/@visulima/vis-binding-win32-x64-msvc@46

commit: 1129e01

@codspeed

codspeed Bot commented Oct 25, 2026

Copy link
Copy Markdown

Merging this PR will regress 1 benchmark

2 warnings

⚠️ 40 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 3 improved benchmarks
❌ 1 regressed benchmark
✅ 109 untouched benchmarks

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
❌ detectPm · bun workspace 97.9 µs 110.5 µs -11.39%
⚡ native Rust xxHash3 parallel (our addon) 534.2 µs 461.2 µs +15.82%
⚡ storeAction (1KB payload) 8.9 ms 7.8 ms +14.35%
⚡ cache.put (write) 1,094.8 µs 990.1 µs +10.56%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing renovate/npm-markdown-it-=14.1.1-vulnerability (1129e01) with main (ce36a5d)

Open in CodSpeed

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority-critical security size:XS Size of the change, tests and generated files excluded. vouch:trusted PR author is a collaborator, a bot or on the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants