CVE-2026-63376: toml Prototype Pollution via __proto__ Key-Path Desynchronization
The toml npm package lets an attacker corrupt Object.prototype for the entire Node.js process by feeding toml.parse() a crafted TOML document that routes a table path through a scalar value and into…

The problem
toml.parse() in lib/compiler.js resolves table paths by walking the live object graph in deepRef(). When a path segment is __proto__, the walker follows it right through a scalar value (e.g. a number) into Number.prototype and then Object.prototype.
The duplicate-key guard that should block this traversal is defeated by a type mismatch: setPath stores currentPath as an array while addTableArray stores it as a string. When the guard later constructs a dot-joined lookup key it coerces the array via Array.toString(), producing comma-separated segments ("a,b.y") instead of dot-separated ones ("a.b.y").
The Set lookup misses, the guard never fires, and traversal continues into Object.prototype.
Proof of concept
A working proof-of-concept for CVE-2026-63376 in toml, with the exact payload below.
const toml = require("toml"); // npm install toml@4.1.1
delete Object.prototype.polluted;
toml.parse(`
[a.b]
y = 1
[a.b.y.__proto__.__proto__]
polluted = "yes"
`);
console.log(({}).polluted); // -> "yes" (Object.prototype is now corrupted)
// Alternate route via table-array prefix clearing:
toml.parse(`
aa = 1
[[a]]
[aa.__proto__.__proto__]
polluted = "yes"
`);
console.log(({}).polluted); // -> "yes"Two root causes combine (CWE-1321). First, deepRef() calls ctx = ctx[key] for every path segment with no reserved-key check, so __proto__ is treated as a normal traversable property. Tables the compiler creates use Object.create(null), but scalar values stored in those tables carry normal prototypes, giving the attacker a path into Object.prototype through any scalar.
Second, the guard that should detect reuse of an already-assigned scalar path is stored in a Set under comma-joined keys (because currentPath is an array that gets coerced by Array.toString()) while deepRef builds dot-joined lookup strings. The Set never matches, so the guard never raises an error.
The fix in commits def6ab5 and dfaff662 adds an explicit denylist check for __proto__, constructor, and prototype at each step of deepRef, and normalises currentPath to always be a string so the tracking keys and the traversal keys use the same format.
The fix
Upgrade to toml 4.1.2 or later. No configuration workaround exists for earlier versions; the only safe option is to avoid passing untrusted TOML input to toml.parse() until the upgrade is applied.
Reported by Duy Bui.
Related research
- high · 7.5CVE-2026-77465CVE-2026-77465: toml Uncontrolled Recursion (DoS)
- high · 8.3CVE-2026-82404CVE-2026-82404: @toon-format/toon Prototype Pollution via Untrusted Decode
- high · 8.5CVE-2026-73654CVE-2026-73654: @trigger.dev/core Prototype Pollution via Run Metadata Operations
- critical · 9.1CVE-2026-53609CVE-2026-53609: ApostropheCMS Server-Side Prototype Pollution via $pullAll Leading to Authorization Bypass