diff --git a/src/rules.ts b/src/rules.ts index cdbbeadb93..2c08276fc8 100644 --- a/src/rules.ts +++ b/src/rules.ts @@ -149,11 +149,11 @@ const _tag = 'address|article|aside|base|basefont|blockquote|body|caption' const _comment = /|$))/; const html = edit( '^ {0,3}(?:' // optional indentation -+ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)' // (1) ++ '<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n*|$)' // (1) + '|comment[^\\n]*(\\n+|$)' // (2) -+ '|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n+|$)' // (3) -+ '|[^\\n]*\\n+|$)' // (4) -+ '|[^\\n]*\\n+|$)' // (5) ++ '|<\\?[\\s\\S]*?(?:\\?>[^\\n]*\\n*|$)' // (3) ++ '|[^\\n]*\\n*|$)' // (4) ++ '|[^\\n]*\\n*|$)' // (5) + '|)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (6) + '|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) open tag + '|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)' // (7) closing tag @@ -169,7 +169,7 @@ const createParagraph = (listInterrupt: RegExp) => edit(_paragraph) .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs .replace('|table', '') .replace('blockquote', ' {0,3}>') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') + .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n') .replace('list', listInterrupt) .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // pars can be interrupted by type (6) html blocks @@ -218,7 +218,7 @@ const gfmTable = edit( .replace('heading', ' {0,3}#{1,6}(?:\\s|$)') .replace('blockquote', ' {0,3}>') .replace('code', '(?: {4}| {0,3}\t)[^\\n]') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') + .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]') // any bullet ends the table rows .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // tables can be interrupted by type (6) html blocks @@ -234,7 +234,7 @@ const blockGfm: Record = { .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs .replace('table', gfmTable) // interrupt paragraphs with table .replace('blockquote', ' {0,3}>') - .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n') + .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~~~)[^\\n]*\\n') .replace('list', ' {0,3}(?:[*+-]|1[.)])[ \\t]+[^ \\t\\n]') // only non-empty lists starting from 1 can interrupt .replace('html', ')|<(?:script|pre|style|textarea|!--)') .replace('tag', _tag) // pars can be interrupted by type (6) html blocks diff --git a/test/specs/redos/quadratic_html_block_close.cjs b/test/specs/redos/quadratic_html_block_close.cjs new file mode 100644 index 0000000000..b5c753f6df --- /dev/null +++ b/test/specs/redos/quadratic_html_block_close.cjs @@ -0,0 +1,4 @@ +module.exports = { + markdown: ''.repeat(50000), + html: ''.repeat(50000), +}; diff --git a/test/specs/redos/quadratic_tilde_paragraph_interrupt.cjs b/test/specs/redos/quadratic_tilde_paragraph_interrupt.cjs new file mode 100644 index 0000000000..b9e9c4c096 --- /dev/null +++ b/test/specs/redos/quadratic_tilde_paragraph_interrupt.cjs @@ -0,0 +1,4 @@ +module.exports = { + markdown: 'intro\n' + '~'.repeat(50000), + html: `

intro\n${'~'.repeat(50000)}

\n`, +};