deep-object-diff 1.1.0 - Prototype Pollution
Description
deep-object-diff 1.1.0 is vulnerable to prototype pollution; an attacker can inject arbitrary properties via __proto__ in JSON keys, leading to object property manipulation.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
deep-object-diff 1.1.0 is vulnerable to prototype pollution; an attacker can inject arbitrary properties via `__proto__` in JSON keys, leading to object property manipulation.
Root
Cause
The deep-object-diff library version 1.1.0 does not properly validate incoming JSON keys when computing diffs between objects. This allows the __proto__ property to be processed as a regular key, enabling prototype pollution [1][2]. The library's diff function and other related functions assign properties from the diff output without checking for special keys, which can lead to the modification of Object.prototype.
Exploitation
An attacker can exploit this vulnerability by supplying a crafted JSON object that includes a __proto__ key. For example, the diff or addedDiff functions will treat __proto__ as a normal property and copy it into the resulting diff object or, during the merge, pollute the global Object.prototype [1]. No authentication is required; the attacker only needs to control one of the objects being compared.
Impact
Successful exploitation allows the attacker to add or modify properties on the global Object.prototype, affecting all objects in the runtime. This can lead to unexpected behavior, privilege escalation, or denial of service, depending on how the application uses the polluted properties.
Mitigation
The vulnerability has been fixed in subsequent commits. Pull Request #87 introduced changes that use makeObjectWithoutPrototype() (which creates objects with a null prototype) to store the diff results, preventing __proto__ from being inherited [1][4]. Users should update to a patched version (e.g., 1.1.1 or later) to protect against this attack.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
deep-object-diffnpm | >= 1.1.6, < 1.1.9 | 1.1.9 |
Affected products
2- deep-object-diff/deep-object-diffdescription
Patches
1f55b858811f1Remove unnecessary reassignment of `lhs`/`rhs`
4 files changed · +24 −35
src/added.js+4 −6 modified@@ -4,20 +4,18 @@ const addedDiff = (lhs, rhs) => { if (lhs === rhs || !isObject(lhs) || !isObject(rhs)) return {}; - const l = lhs; - const r = rhs; - return Object.keys(r).reduce((acc, key) => { - if (hasOwnProperty(l, key)) { - const difference = addedDiff(l[key], r[key]); + return Object.keys(rhs).reduce((acc, key) => { + if (hasOwnProperty(lhs, key)) { + const difference = addedDiff(lhs[key], rhs[key]); if (isObject(difference) && isEmpty(difference)) return acc; acc[key] = difference; return acc; } - acc[key] = r[key]; + acc[key] = rhs[key]; return acc; }, makeObjectWithoutPrototype()); };
src/deleted.js+3 −6 modified@@ -3,12 +3,9 @@ import { isEmpty, isObject, hasOwnProperty, makeObjectWithoutPrototype } from '. const deletedDiff = (lhs, rhs) => { if (lhs === rhs || !isObject(lhs) || !isObject(rhs)) return {}; - const l = lhs; - const r = rhs; - - return Object.keys(l).reduce((acc, key) => { - if (hasOwnProperty(r, key)) { - const difference = deletedDiff(l[key], r[key]); + return Object.keys(lhs).reduce((acc, key) => { + if (hasOwnProperty(rhs, key)) { + const difference = deletedDiff(lhs[key], rhs[key]); if (isObject(difference) && isEmpty(difference)) return acc;
src/diff.js+10 −13 modified@@ -5,33 +5,30 @@ const diff = (lhs, rhs) => { if (!isObject(lhs) || !isObject(rhs)) return rhs; // return updated rhs - const l = lhs; - const r = rhs; - - const deletedValues = Object.keys(l).reduce((acc, key) => { - if (!hasOwnProperty(r, key)) { + const deletedValues = Object.keys(lhs).reduce((acc, key) => { + if (!hasOwnProperty(rhs, key)) { acc[key] = undefined; } return acc; }, makeObjectWithoutPrototype()); - if (isDate(l) || isDate(r)) { - if (l.valueOf() == r.valueOf()) return {}; - return r; + if (isDate(lhs) || isDate(rhs)) { + if (lhs.valueOf() == rhs.valueOf()) return {}; + return rhs; } - return Object.keys(r).reduce((acc, key) => { - if (!hasOwnProperty(l, key)){ - acc[key] = r[key]; // return added r key + return Object.keys(rhs).reduce((acc, key) => { + if (!hasOwnProperty(lhs, key)){ + acc[key] = rhs[key]; // return added r key return acc; } - const difference = diff(l[key], r[key]); + const difference = diff(lhs[key], rhs[key]); // If the difference is empty, and the lhs is an empty object or the rhs is not an empty object - if (isEmptyObject(difference) && !isDate(difference) && (isEmptyObject(l[key]) || !isEmptyObject(r[key]))) + if (isEmptyObject(difference) && !isDate(difference) && (isEmptyObject(lhs[key]) || !isEmptyObject(rhs[key]))) return acc; // return no diff acc[key] = difference // return updated key
src/updated.js+7 −10 modified@@ -5,20 +5,17 @@ const updatedDiff = (lhs, rhs) => { if (!isObject(lhs) || !isObject(rhs)) return rhs; - const l = lhs; - const r = rhs; - - if (isDate(l) || isDate(r)) { - if (l.valueOf() == r.valueOf()) return {}; - return r; + if (isDate(lhs) || isDate(rhs)) { + if (lhs.valueOf() == rhs.valueOf()) return {}; + return rhs; } - return Object.keys(r).reduce((acc, key) => { - if (hasOwnProperty(l, key)) { - const difference = updatedDiff(l[key], r[key]); + return Object.keys(rhs).reduce((acc, key) => { + if (hasOwnProperty(lhs, key)) { + const difference = updatedDiff(lhs[key], rhs[key]); // If the difference is empty, and the lhs is an empty object or the rhs is not an empty object - if (isEmptyObject(difference) && !isDate(difference) && (isEmptyObject(l[key]) || !isEmptyObject(r[key]))) + if (isEmptyObject(difference) && !isDate(difference) && (isEmptyObject(lhs[key]) || !isEmptyObject(rhs[key]))) return acc; // return no diff acc[key] = difference;
Vulnerability mechanics
Root cause
"The library does not sanitize object keys during diff operations, allowing a malicious `__proto__` key to pollute the prototype of the accumulator object created by `makeObjectWithoutPrototype()`."
Attack vector
An attacker supplies a crafted JSON object containing a `__proto__` key as either the `lhs` or `rhs` argument to a diff function (e.g., `diff()`, `updatedDiff()`, `addedDiff()`, or `deletedDiff()`). The library iterates over the object's keys with `Object.keys()` and copies values into an accumulator created by `makeObjectWithoutPrototype()`. If the attacker's object includes `__proto__`, that key is treated as a regular property and assigned onto the accumulator, which pollutes the prototype chain of all objects in the runtime. No authentication or special network position is required beyond the ability to pass a malicious object to the library.
Affected code
The vulnerability affects all four diff functions in the library: `src/diff.js`, `src/updated.js`, `src/added.js`, and `src/deleted.js`. Each function iterates over object keys using `Object.keys()` and assigns values into an accumulator without filtering out `__proto__` or other prototype-polluting keys. The `makeObjectWithoutPrototype()` helper is used for some accumulators but not consistently applied to all property assignments.
What the fix does
The patch removes the intermediate variable assignments (`const l = lhs; const r = rhs;`) and directly uses the original `lhs` and `rhs` parameters. While this change alone does not add explicit `__proto__` filtering, the commit message describes it as "Remove unnecessary reassignment of `lhs`/`rhs`". The advisory states that the underlying issue is the lack of validation on incoming JSON keys, meaning the patch is a partial cleanup; a complete fix would require either filtering out `__proto__` keys or using `Object.create(null)` accumulators consistently to prevent prototype pollution.
Preconditions
- inputThe attacker must be able to supply a JavaScript object (e.g., via JSON.parse) that contains a __proto__ key as either the lhs or rhs argument to a diff function.
- configThe application must use deep-object-diff version 1.1.0 or earlier and pass untrusted data to any of the exported diff functions.
Generated on May 23, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-653v-rqx9-j85pghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-41713ghsaADVISORY
- fluidattacks.com/advisories/heldensghsaWEB
- github.com/mattphillips/deep-object-diff/issues/85ghsaWEB
- github.com/mattphillips/deep-object-diff/issues/85ghsaWEB
- github.com/mattphillips/deep-object-diff/pull/87/commits/55f9c3c70cf0d54cb30291e949fb8682fa3c5d9fghsaWEB
- github.com/mattphillips/deep-object-diff/pull/87/commits/9576963b68b955e88610aa4f0c696a1aafc1119dghsaWEB
- fluidattacks.com/advisories/heldens/mitre
News mentions
0No linked articles in our index yet.