GitHub Security Advisory: GHSA-6v9c-7cg6-27q7
Package: marked (NPM)
Severity: HIGH (CVSS 7.5)
CWE: CWE-400: Uncontrolled Resource Consumption, CWE-674: Uncontrolled Recursion, CWE-835: Loop with Unreachable Exit Condition ('Infinite Loop')
Vulnerable versions: >= 18.0.0, <= 18.0.1
Patched in: 18.0.2

Summary: Marked Vulnerable to OOM Denial of Service via Infinite Recursion in marked Tokenizer

Details:
### Summary
A critical Denial of Service (DoS) vulnerability exists in `marked@18.0.0`. By providing a specific 3-byte input sequence a tab, a vertical tab, and a newline (`\x09\x0b\n`)—an unauthenticated attacker can trigger an infinite recursion loop during parsing. This leads to unbounded memory allocation, causing the host Node.js application to crash via Memory Exhaustion (OOM). 

### Details
The vulnerability originates in how `marked`'s block tokenizer handles unexpected whitespace characters. 

1. **Tab Character (`\x09`) Consumption**: The `space()` tokenizer matches standard whitespace using the regex `/^(?:[ \t]*(?:\n|$))+/`. When parsing the malicious payload (`\x09\x0b\n`), this rule successfully consumes the initial tab character (`\x09`).
2. **Vertical Tab (`\x0b`) Bypass**: The remaining input is now `\x0b\n`. The newline block rule explicitly looks for spaces or standard tabs (`[ \t]`) followed by a newline. Because the vertical tab is a legacy ASCII character not accounted for in this rule, it fails to match.
3. **Fallback to Text Tokenizer**: None of the standard block tokenizers (blockquote, code, heading, etc.) match `\x0b\n`. As a result, the parser falls through to the `text` tokenizer (`/^[^\n]+/`), which matches any character except a newline.
4. **Infinite Recursion**: Inside `blockTokens()`, the `text` tokenizer creates a text token and subsequently calls `inlineTokens()` on the exact same content. Inside `inlineTokens()`, the text rule again matches `\x0b\n` and recursively calls `inlineTokens()`. This creates an inescapable cycle: `blockTokens() → text token → inlineTokens() → text rule matches → inlineTokens() → ...`

With each recursive call allocating new token objects and concatenating strings, memory grows indefinitely until the Node.js heap limit is reached.

**Vulnerable Code in `lib/marked.esm.js` (Lexer class, `blockTokens()`):**
```javascript
// The text tokenizer triggers infinite recursion
if(r=this.tokenizer.text(e)) {
    e=e.substring(r.raw.length);
    let s=t.at(-1);
    s?.type==="text"?(s.raw+=(s.raw.endsWith("\n")?"":"\n")+r.raw, s.text+="\n"+r.text, this.inlineQueue.pop(), this.inlineQueue.at(-1).src=s.text):t.push(r);
    // ↑ This calls inlineTokens() internally via the text tokenizer, causing the OOM loop
    continue;
}
```

### PoC
This vulnerability can be reproduced using any standard Node.js environment with `marked@18.0.0` installed.

1. Create a file named `poc.js` with the following content:
```javascript
const marked = require('marked');

// The vulnerable 3-byte pattern: tab + vertical tab + newline
const vulnerableInput = '\x09\x0b\n';

console.log('Attempting to parse malicious payload...');
try {
    marked.parse(vulnerableInput);
} catch(e) {
    console.log('Error:', e.message);
}
```
2. Run the script: `node poc.js`
3. **Result:** The process will hang briefly as memory spikes, ultimately crashing with: `FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of