From cda5ff981ba132b822d995f47aa7b061262b496e Mon Sep 17 00:00:00 2001 From: TH Chen Date: Tue, 24 Feb 2026 10:01:28 -0500 Subject: [PATCH] fix: prevent ReDoS in inline link regex title group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The title separator in the link regex used [ \t]* which allowed the title group to be probed at every backtrack position of the greedy href group. On long single-line input containing [text]( patterns without a nearby closing ), this produced O(n²) per regex call and O(n³) in the full inline tokenizer. Change [ \t]* to [ \t]+|\n to require actual whitespace before the title. This matches CommonMark spec requirements and eliminates the backtracking cascade. Before: 18K input takes ~36 seconds (event loop blocked) After: 18K input takes ~45ms --- src/rules.ts | 2 +- test/specs/redos/cubic_link_title.cjs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 test/specs/redos/cubic_link_title.cjs diff --git a/src/rules.ts b/src/rules.ts index 39fa7befbf..fd77781ea3 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -380,7 +380,7 @@ const tag = edit( const _inlineLabel = /(?:\[(?:\\[\s\S]|[^\[\]\\])*\]|\\[\s\S]|`+[^`]*?`+(?!`)|[^\[\]\\`])*?/; -const link = edit(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]*(?:\n[ \t]*)?)(title))?\s*\)/) +const link = edit(/^!?\[(label)\]\(\s*(href)(?:(?:[ \t]+(?:\n[ \t]*)?|\n[ \t]*)(title))?\s*\)/) .replace('label', _inlineLabel) .replace('href', /<(?:\\.|[^\n<>\\])+>|[^ \t\n\x00-\x1f]*/) .replace('title', /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/) diff --git a/test/specs/redos/cubic_link_title.cjs b/test/specs/redos/cubic_link_title.cjs new file mode 100644 index 0000000000..3066d0238a --- /dev/null +++ b/test/specs/redos/cubic_link_title.cjs @@ -0,0 +1,4 @@ +module.exports = { + markdown: 'a[b](c'.repeat(1000), + html: `

${'a[b](c'.repeat(1000)}

\n`, +};