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 deep-merge utility popular in the Nuxt.js and Vue ecosystems, exposes applications to prototype pollution in versions up to 6.1.4.

Defu, a deep-merge utility popular in the Nuxt.js and Vue ecosystems, exposes applications to prototype pollution in versions up to 6.1.4. Attackers can craft payloads with a __proto__ key passed as the first argument to defu(), overriding object prototypes and bypassing default safeguards. This matters because defu handles configuration merging in thousands of production apps—npm reports over 10 million weekly downloads—and a single tainted input from JSON bodies, databases, or files can flip critical flags like admin privileges.

Consider a server merging user-provided config. An attacker sends {"__proto__":{"isAdmin":true}}. The app calls defu(userInput, {isAdmin: false}), but the result sets isAdmin to true. Prototype pollution taints Object.prototype, so every subsequent object inherits the attacker’s properties. In real apps, this enables authentication bypass, arbitrary code execution via polluted functions, or denial-of-service by bloating prototypes. We’ve seen similar flaws in libraries like lodash and minimist lead to CVEs with real exploits; defu joins that list.

Root Cause

The bug hides in defu’s internal _defu function. It copies defaults using Object.assign({}, defaults). This invokes the __proto__ setter, letting attackers swap the target’s prototype with their payload. A downstream for...in loop skips __proto__ keys explicitly, but inherited properties from the new prototype sneak through. Defu version 6.1.4 and earlier ship this flaw.

import { defu } from 'defu'

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

Object.assign dates back to ES6, but its prototype pitfalls persist. Modern alternatives like spread syntax avoid setters entirely.

Fix and Mitigation

Upgrade to defu 6.1.5 or later. The patch swaps Object.assign({}, defaults) for {...defaults}, which uses internal [[DefineOwnProperty]] and skips setters. No prototype swap occurs. Test your merge logic post-upgrade—deep merges can behave subtly differently.

Beyond patching, scrub inputs before merging. Strip __proto__, constructor.prototype, and similar keys using libraries like proto-strip or a custom filter:

function sanitize(obj) {
  if (typeof obj !== 'object' || obj === null) return obj
  delete obj.__proto__
  delete obj.constructor?.prototype
  for (const key in obj) {
    obj[key] = sanitize(obj[key])
  }
  return obj
}

Defu’s npm stats underscore urgency: 12M+ weekly downloads, stars on 500+ GitHub repos, core to Nuxt 3 configs. Many devs pass raw JSON from APIs or env files without validation, assuming merge utils are safe. This vuln proves otherwise. Prototype pollution ranked high in 2023’s JS advisories—Snyk tracked 50+ cases—and exploits often chain to RCE.

Why care? In finance or crypto apps—where Njalla clients operate—one polluted config could expose wallets, flip trading perms, or leak keys. Skeptically, the vuln requires misuse: defu’s docs warn against untrusted first-arg inputs. But devs ignore warnings, and attackers probe anyway. Patch now, validate always. Credit goes to @BlackHatExploitation on GitHub for the report.

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

Related