Lodash _.merge Prototype Pollution in Recent Versions
This article examines how the Lodash JavaScript library handles
prototype pollution vulnerabilities within its deep-merging utility,
_.merge. Historically a frequent target for prototype
pollution attacks, _.merge has undergone significant
patching in modern releases. The following breakdown explains the nature
of the original vulnerability, the mitigation mechanisms introduced in
recent Lodash versions, and the security implications for modern
JavaScript applications.
The Historic Prototype Pollution Flaw
Prototype pollution occurs when an attacker can manipulate object
properties to inject or modify attributes on JavaScript's base
Object.prototype. Because almost all objects inherit from
Object.prototype, any property added there becomes globally
accessible across the runtime environment.
In older versions of Lodash (prior to version 4.17.12),
_.merge recursively copied properties from a source object
to a target object without verifying property names. An attacker
supplying untrusted JSON data could pass objects containing sensitive
keys such as __proto__ or
constructor.prototype:
const maliciousPayload = JSON.parse('{"__proto__": {"isAdmin": true}}');
_.merge({}, maliciousPayload);
// In vulnerable versions:
console.log({}.isAdmin); // trueThis allowed attackers to bypass authorization checks, alter application logic, or cause denial of service (DoS) crashes.
How Recent Versions Neutralize the Threat
In recent versions of Lodash (starting broadly from version 4.17.15
and finalized in 4.17.21), prototype pollution via _.merge
is neutralized through defensive checks implemented in internal merge
routines, specifically baseMergeDeep.
Key Filtering
When traversing object keys during a merge operation, Lodash now
explicitly checks for dangerous property names. If an object property
key is __proto__, constructor, or
prototype, the merge function skips the operation for that
key.
Prototype Shielding
Because modern Lodash intercepts __proto__ and
constructor.prototype keys, it will not assign nested
values to Object.prototype. Merging a malicious payload now
produces safe, contained behavior:
const _ = require('lodash');
const payload = JSON.parse('{"__proto__": {"polluted": "yes"}}');
const target = {};
_.merge(target, payload);
// In modern versions:
console.log(target.polluted); // undefined
console.log({}.polluted); // undefinedThe payload's attributes are effectively dropped or treated as own properties without modifying the prototype chain.
Current Implications and Best Practices
While recent versions of Lodash actively defend against prototype
pollution within _.merge, developers should still observe
standard defensive programming practices:
- Keep Dependencies Updated: Ensure the installed
Lodash version is at least
4.17.21, as intermediate releases resolved edge cases aroundconstructorprototype manipulation and alternative merge utilities like_.defaultsDeep. - Validate Untrusted Input: Relying solely on library-level sanitization increases risk. Incoming payloads should be schema-validated (using libraries such as Zod or Joi) to reject unexpected keys before reaching business logic.
- Harden the Runtime: For high-security environments,
running
Object.freeze(Object.prototype)at application startup prevents modifications to the base prototype across the entire Node.js or browser process.