VYPR
Critical severityNVD Advisory· Published May 3, 2021· Updated Aug 3, 2024

CVE-2021-28860

CVE-2021-28860

Description

In Node.js mixme, prior to v0.5.1, an attacker can add or alter properties of an object via '__proto__' through the mutate() and merge() functions. The polluted attribute will be directly assigned to every object in the program. This will put the availability of the program at risk causing a potential denial of service (DoS).

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Prototype pollution in Node.js mixme prior to v0.5.1 allows attackers to cause denial of service via __proto__ in mutate() and merge().

Vulnerability

In Node.js mixme library versions prior to v0.5.1, the mutate() and merge() functions do not filter the __proto__ property key. When merging or mutating objects, if an attacker-controlled object contains a __proto__ property, it will be assigned to the target object's prototype, polluting all objects in the application. This is a classic prototype pollution vulnerability. The affected versions are all before v0.5.1. [1][2][4]

Exploitation

An attacker needs to supply a crafted object with a __proto__ property to the mutate() or merge() functions. This can be achieved if the application merges user-supplied data (e.g., from JSON input, query parameters, or configuration) without sanitization. No authentication is required if the vulnerable function is exposed to untrusted input. The attacker simply includes "__proto__": { "someProperty": "maliciousValue" } in the input. The library then assigns that property to the prototype of the target object, affecting all objects that inherit from that prototype. [1][2]

Impact

Successful exploitation results in prototype pollution, which can lead to denial of service (DoS) by overriding properties that affect application behavior (e.g., setting toString or hasOwnProperty to non-functions, or adding properties that cause infinite loops or crashes). The attacker can also potentially alter the behavior of the application by polluting properties used in security checks or logic. The impact is primarily on availability, but could also affect integrity if polluted properties are used in critical decisions. [1]

Mitigation

The fix was released in version v0.5.1, which adds a check to skip the __proto__ key in the mutate() function (see commit cfd5fbf). Users should upgrade to v0.5.1 or later. If upgrading is not possible, avoid passing untrusted data to mutate() or merge() without sanitizing the input to remove __proto__ keys. The library is open source and the fix is available on GitHub. [4]

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.

PackageAffected versionsPatched versions
mixmenpm
< 0.5.10.5.1

Affected products

3

Patches

1
cfd5fbfc3236

fix: dont pollute object proto #1

https://github.com/adaltas/node-mixmeDavid WormsApr 25, 2021via ghsa
8 files changed · +34 2
  • CHANGELOG.md+5 0 modified
    @@ -1,6 +1,11 @@
     
     # Changelog
     
    +## Trunk
    +
    +* fix: dont pollute object proto #1
    +* chore: latest dependencies
    +
     ## Version 0.5.0
     
     * feat: support object with null prototype
    
  • dist/mixme.cjs.js+5 1 modified
    @@ -19,7 +19,7 @@ function _typeof(obj) {
     }
     
     // Generated by CoffeeScript 2.5.1
    -var _snake_case;
    +var _snake_case; exports.clone = void 0; exports.compare = void 0; exports.is_object = void 0; exports.is_object_literal = void 0; exports.merge = void 0; exports.mutate = void 0; exports.snake_case = void 0;
     
     exports.merge = function merge() {
       return exports.mutate.apply(void 0, [{}].concat(Array.prototype.slice.call(arguments)));
    @@ -50,6 +50,10 @@ exports.mutate = function mutate() {
           }
     
           for (name in source) {
    +        if (name === '__proto__') {
    +          continue;
    +        }
    +
             target[name] = exports.mutate(target[name], source[name]);
           }
         } else if (Array.isArray(source)) {
    
  • dist/mixme.esm.js+4 0 modified
    @@ -46,6 +46,10 @@ _mutate = function mutate() {
           }
     
           for (name in source) {
    +        if (name === '__proto__') {
    +          continue;
    +        }
    +
             target[name] = _mutate(target[name], source[name]);
           }
         } else if (Array.isArray(source)) {
    
  • dist/mixme.umd.js+5 1 modified
    @@ -21,7 +21,7 @@
       }
     
       // Generated by CoffeeScript 2.5.1
    -  var _snake_case;
    +  var _snake_case; exports.clone = void 0; exports.compare = void 0; exports.is_object = void 0; exports.is_object_literal = void 0; exports.merge = void 0; exports.mutate = void 0; exports.snake_case = void 0;
     
       exports.merge = function merge() {
         return exports.mutate.apply(void 0, [{}].concat(Array.prototype.slice.call(arguments)));
    @@ -52,6 +52,10 @@
             }
     
             for (name in source) {
    +          if (name === '__proto__') {
    +            continue;
    +          }
    +
               target[name] = exports.mutate(target[name], source[name]);
             }
           } else if (Array.isArray(source)) {
    
  • lib/index.js+3 0 modified
    @@ -27,6 +27,9 @@ mutate = function() {
             target = {};
           }
           for (name in source) {
    +        if (name === '__proto__') {
    +          continue;
    +        }
             target[name] = mutate(target[name], source[name]);
           }
         } else if (Array.isArray(source)) {
    
  • src/index.coffee+1 0 modified
    @@ -19,6 +19,7 @@ mutate = ->
         if is_object_literal source
           target = {} unless is_object_literal target
           for name of source
    +        continue if name is '__proto__'
             target[name] = mutate target[name], source[name]
         else if Array.isArray source
           target = for v in source
    
  • test/merge.coffee+5 0 modified
    @@ -25,3 +25,8 @@ describe 'mixme.merge', ->
         .should.eql a: 1, b: 2, c: 0
         obj2
         .should.eql a: 1, c: 3, d: 4
    +
    +  it 'dont merge proto', ->
    +    merge {}, JSON.parse '{"__proto__": {"polluted": "ohno"}}'
    +    obj = Object.create {}
    +    should(obj.polluted).be.Undefined()
    
  • test/mutate.coffee+6 0 modified
    @@ -23,6 +23,12 @@ describe 'mutate', ->
           {...obj1}
           .should.eql { a: 'a value', b: 'b new', c: { d: 'd new', f: 'f value'}}
     
    +    it 'dont merge proto', ->
    +      src = {}
    +      mutate src, JSON.parse '{"__proto__": {"polluted": "ohno"}}'
    +      obj = Object.create {}
    +      should(obj.polluted).be.Undefined()
    +
       describe '2nd arg not object', ->
         
         it 'object with string', ->
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

9

News mentions

0

No linked articles in our index yet.