BTC
ETH
SOL
BNB
GOLD
XRP
DOGE
ADA
Back to home
Security

[HIGH] Security Advisory: defu: Prototype pollution via `__proto__` key in defaults argument (defu)

Defu, a popular deep-merge utility in the Nuxt and UnJS ecosystems, ships with a prototype pollution vulnerability in versions up to 6.1.4.

Defu, a popular deep-merge utility in the Nuxt and UnJS ecosystems, ships with a prototype pollution vulnerability in versions up to 6.1.4. Attackers can inject a __proto__ key into untrusted input passed as the first argument to defu(), overriding critical defaults like admin privileges or security settings. If your app merges JSON from requests, databases, or config files without sanitization, update to 6.1.5 now. This flaw affects npm’s defu package, which pulls over 15 million weekly downloads as of late 2024.

Prototype pollution lets attackers taint Object.prototype, injecting properties that every object in your app inherits. In defu’s case, a payload like {"__proto__":{"isAdmin":true}} slips through during merge. Here’s the exploit in action:

import { defu } from 'defu'

const userInput = JSON.parse('{"__proto__":{"isAdmin":true}}')
const config = defu(userInput, { isAdmin: false })
console.log(config.isAdmin) // true — attacker wins

Your merged config now carries the polluted trait. In a real app, this flips booleans for auth bypass, enables debug modes, or sets unsafe paths. Why it matters: defu underpins config handling in Nuxt 3, Nitro server engine, and tools like Nuxt UI. Nuxt alone boasts 60,000+ GitHub stars and powers production sites for companies like Louis Vuitton and NASA. A single vulnerable merge in a user-editable dashboard or API endpoint cascades to server-wide compromise.

Root Cause: Object.assign’s Trap

Defu’s internal _defu function copies defaults with Object.assign({}, defaults). This invokes the __proto__ setter on the target object, swapping its prototype chain for attacker data. Later, a for...in loop skips __proto__ explicitly but misses inherited props from the now-tainted prototype. Those props merge into the output unchecked.

JavaScript’s prototype mess strikes again. Object.assign dates back to ES6 but carries this footgun: it treats __proto__ as a regular property. Spread syntax ({...defaults}) dodges this by using internal [[DefineOwnProperty]] methods, which ignore setters. The fix in 6.1.5 swaps exactly that—no regressions reported.

Fix, Mitigation, and Why You Should Care

Upgrade to defu 6.1.4 or later. Run npm update defu or pin "defu": "^6.1.5" in package.json. Test merges post-update; the change is isolated to internals.

npm install defu@^6.1.5

Mitigate regardless: Never feed raw user input to defu or any merger. Use libraries like safe-object-merge or manual recursion with hasOwnProperty checks. Strip __proto__, constructor.prototype, and polluted keys upfront—tools like proto-strip automate this.

This vuln, reported by @BlackHatExploitation on GitHub, echoes a decade of JS prototype attacks: lodash (2018, 4M+ affected), minimist (CVE-2022-25883), and flat (CVE-2022-25883). Defu’s maintainers patched in hours, a win for the ecosystem. But it underscores a harsh truth: JS runtime quirks make “simple” utils minefields. In crypto or finance apps—where Njalla clients operate—one polluted config could leak keys or authorize fraudulent trades.

Audit your stack: grep for defu and trace first args. If pulling from MongoDB user docs or Vercel envs, assume tainted. Broader lesson: Defaults exist for a reason. Bypassing them via proto pollution isn’t clever—it’s a vector pros exploit. With Node’s event loop, one bad merge propagates fast. Stay vigilant; patch fast.

April 4, 2026 · 3 min · 2 views · Source: GitHub Security

Related