Apr 6, 2026·5 min read·29 visits
A flaw in defu < 6.1.5 allows prototype pollution via Object.assign() when merging unsanitized user input containing __proto__ keys, enabling attackers to alter default application properties.
The `defu` library, a popular utility for recursively assigning default properties, contains a prototype pollution vulnerability prior to version 6.1.5. Applications passing unsanitized user input as the primary argument to the `defu()` function permit attackers to override object properties via crafted JSON payloads, leading to configuration injection or potential application logic bypass.
The defu library provides recursive object merging functionality for JavaScript and Node.js applications. Prior to version 6.1.5, the library suffered from a prototype pollution vulnerability. The flaw exists in the internal cloning mechanism used to process default configurations.
The vulnerability, classified as CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes), requires specific conditions to trigger. The target application must accept unsanitized user input, typically derived from parsed JSON payloads or untrusted database records, and pass it directly as the first argument to the defu() function.
Exploitation allows attackers to manipulate the prototype chain of objects within the Node.js or browser environment. By overriding default configurations, attackers can manipulate downstream logic. This often leads to property injection, authorization bypass, or in specific configurations, remote code execution primitives.
The root cause of the vulnerability resides in the _defu internal function responsible for preparing objects for the recursive merge. The library utilized the Object.assign({}, defaults) construct to clone the defaults object prior to initiating the merging logic.
In the ECMAScript specification, Object.assign employs the [[Set]] internal method for property assignment. When the source object contains a key explicitly named __proto__, the runtime invokes the setter for Object.prototype.__proto__ on the target object. Because the target is an empty object literal {} that inherits from Object.prototype, this action successfully modifies the prototype of the new object to match the attacker-supplied value.
The defu library implemented an explicit guard inside its recursive for...in loop to skip __proto__ and constructor keys. The flaw bypassed this defense because the vulnerable Object.assign call occurred during the initial cloning phase, before the loop executed. The cloned object inherited the malicious prototype properties prematurely.
The patch applied in version 6.1.5 rectifies the prototype setter invocation by modifying how the initial object clone is instantiated. The maintainers replaced the Object.assign({}, defaults) call with the ECMAScript object spread operator { ...defaults }.
The spread operator utilizes the [[DefineOwnProperty]] internal method rather than [[Set]]. When [[DefineOwnProperty]] processes a __proto__ key, it creates the key as a literal data property on the target object instead of resolving and executing the prototype setter. This prevents the prototype chain from being altered.
Additionally, the patch alters the iteration strategy. The for...in loop, which traverses both own and inherited enumerable properties, was replaced with Object.keys(). This enforces iteration exclusively over the object's own enumerable properties, ensuring that any properties inherited from a polluted global prototype are ignored during the merge phase.
// Relevant changes from commit 3942bfbbcaa72084bd4284846c83bd61ed7c8b29
- const object = Object.assign({}, defaults);
+ const object = { ...defaults };
- for (const key in baseObject) {
+ for (const key of Object.keys(baseObject as Record<string, any>)) {
if (key === "__proto__" || key === "constructor") {
continue;
}Exploiting this vulnerability requires the attacker to supply a crafted JSON payload containing a __proto__ key to an endpoint that passes the resulting parsed object to defu. Standard JSON parsing (JSON.parse) without a custom reviver function will instantiate an object containing a literal __proto__ property.
When the vulnerable application passes this parsed object to defu(userInput, { isAdmin: false }), the Object.assign({}, defaults) routine triggers. The runtime executes the setter, pointing the target object's internal [[Prototype]] to the attacker's supplied object structure.
Properties defined within the attacker's __proto__ object manifest on the resulting configuration object. This directly overrides the intended default values provided by the developer. The proof-of-concept below demonstrates the structural manipulation required to achieve property injection.
import { defu } from 'defu'
// Attacker-controlled input (e.g., from a parsed JSON request body)
const userInput = JSON.parse('{"__proto__":{"isAdmin":true}}')
// Vulnerable call: passing untrusted input as the first argument
const config = defu(userInput, { isAdmin: false })
console.log(config.isAdmin)
// Result: true — The attacker has successfully overridden the server's default configurationThe impact of prototype pollution depends heavily on the specific implementation details of the host application. The vulnerability carries a CVSS 3.1 score of 7.5 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N), indicating a severe impact on system integrity with no authentication required.
The immediate consequence is property injection. Attackers control the keys and values merged into configuration objects, creating an opportunity to bypass business logic. Modifying boolean flags, such as administrative access checks or feature toggles, is a common objective.
In environments using vulnerable template engines or executing system commands, prototype pollution escalates to Remote Code Execution (RCE). If the polluted properties flow into execution sinks (e.g., child_process functions reading from an environment object), attackers can achieve arbitrary command execution on the host system.
The definitive remediation is to upgrade the defu dependency to version 6.1.5 or later. This addresses the vulnerability at the source by utilizing safe cloning mechanisms and restricted property iteration.
Developers must implement strict input validation for all user-supplied data prior to processing. Utilizing robust schema validation libraries, such as Zod or Joi, ensures that dangerous internal keys like __proto__, constructor, and prototype are stripped before data reaches object merging utilities.
In high-security execution environments, administrators should consider locking down the prototype chain globally. Invoking Object.freeze(Object.prototype) prevents unauthorized modifications to the global object prototype, acting as an effective defense-in-depth measure against broad classes of prototype pollution vulnerabilities.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
defu unjs | < 6.1.5 | 6.1.5 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1321 |
| CVSS Score | 7.5 |
| Attack Vector | Network |
| Impact | High Integrity |
| Exploit Status | Proof of Concept |
| KEV Status | Not Listed |
The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype.
A critical vulnerability in Dgraph Alpha allows unauthenticated network clients to delete and replace database stores. The public gRPC interface on port 9080 processes external snapshot streams without enforcing authentication or authorization, triggering immediate database destruction via the storage engine's initialization process.
A security vulnerability in Copier versions 9.5.0 through 9.15.1 allows unauthenticated remote code execution via crafted HTTP requests or local paths containing traversal sequences. The trust-evaluation mechanism compares target repository paths or URLs against trusted prefixes using unnormalized string comparison, while the subsequent fetching mechanism normalizes the path before cloning. Attackers can exploit this asymmetry to bypass security warning prompts and execute arbitrary commands under the local user context.
GeoLens before version 1.2.4 contains multiple critical-tier security vulnerabilities including improper authorization in metadata access, tile cache scope leakage, dataset title enumeration, weak default credentials, and denial of service via STAC POST search.
CVE-2026-55694 is a chained Information Disclosure and Insecure Direct Object Reference (IDOR) vulnerability in Snipe-IT prior to version 8.6.3. The vulnerability allows authenticated, restricted users to completely bypass randomized file-naming security controls, leak the obfuscated filenames of signed End User License Agreements (EULAs), and subsequently download these confidential documents across tenant boundaries.
Snipe-IT is an IT asset/license management system. Prior to 8.6.3, any activated account can request /maintenances/{id} and read maintenance records for assets in the same company without asset or maintenance permission. app/Http/Controllers/MaintenancesController.php show() renders the record without authorize(), while company-scoped route-model binding only prevents access to other companies. Disclosed fields include asset tags, suppliers, purchase costs, notes, and dates. This issue is fixed in version 8.6.3.
A Stored DOM-based Cross-Site Scripting (DOM XSS) vulnerability exists in Snipe-IT versions prior to 8.6.2. The vulnerability occurs when a stored manufacturer or supplier name is converted to CamelCase and rendered within the 'data-selected-count-id' attribute of a table. Client-side JavaScript retrieves this decoded attribute and performs unsafe string concatenation, passing it directly into jQuery's '.after()' method, enabling authenticated attackers to execute arbitrary JavaScript in the victim's session.