flawopen.com/Prototype Pollution/JSON.parse()
No. JSON.parse() creates an ordinary own property literally named __proto__ and never invokes the inherited setter, so parsing alone is safe. Pollution happens in the next step — when that parsed object is merged, cloned, or copied key-by-key onto another object.
const evil = JSON.parse( '{"__proto__":{"isAdmin":true}}' ); // Parsing was safe. This is not: deepMerge(config, evil); // Because the merge does: // target['__proto__'] ← READ returns // the real Object.prototype, then // writes isAdmin into it. ({}).isAdmin; // → true
// 1. Strip the key during parsing const data = JSON.parse(input, (k, v) => k === '__proto__' ? undefined : v ); // 2. Or read explicit fields only — // never copy the whole object const cfg = { theme: data.theme, locale: data.locale, }; // 3. Or validate with a schema library // that returns a new, known-shape // object rather than the input.
There are two different operations that look identical in source code. A plain assignment (obj.x = v) performs a set, which walks the prototype chain looking for a setter — and __proto__ has one. JSON.parse() instead defines own properties directly on the new object, bypassing the accessor.
You can confirm this: after const o = JSON.parse('{"__proto__":{"a":1}}'), the object o has an own key __proto__ holding a plain object, and Object.prototype.a remains undefined. The prototype chain is untouched.
The practical risk is that a parsed object carries a live payload waiting for careless downstream handling. Request bodies parsed by a web framework, configuration files, cached JSON documents and message-queue payloads all become polluted-object carriers the moment any generic copy routine touches them.
The useful review question is therefore not "where do we parse JSON?" but "where do we copy untrusted keys onto an existing object?" — deep merges, object-path setters, and for...in loops that assign.
It is cheap and removes the payload at the boundary, which is a reasonable default for untrusted input. It is not a substitute for fixing an unguarded deep merge, since pollution can arrive from sources other than JSON.
Body parsing produces the object; it does not decide what you do with it. Some query-string parsers additionally build nested objects from bracket syntax, which is its own source of attacker-controlled nested keys. Check what your framework's parser does with a[__proto__][b]=c.
No. Serialising reads properties and produces a string; it cannot modify a prototype.