CVE-2021-28860 (published 2021-05-03T12:15Z)

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).

Severity: MEDIUM
CVSS v2 base score: 6.4 (AV:N/AC:L/Au:N/C:N/I:P/A:P)
CVSS v3 base score: 9.1, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H)

Weakness information:
cwe_id: CWE-1321
cwe_name: Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
description: 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.
extended_description: 
				By adding or modifying attributes of an object prototype, it is possible to create attributes that exist on every object, or replace critical attributes with malicious ones. This can be problematic if the product depends on existence or non-existence of certain attributes, or uses pre-defined attributes of object prototype (such as hasOwnProperty, toString or valueOf).
				This weakness is usually exploited by using a special attribute of objects called proto,  constructor or prototype. Such attributes give access to the object prototype. This weakness is often found in code that assigns object attributes based on user input, or merges or clones objects recursively.
			
			

Fix information:
repo_url: https://github.com/adaltas/node-mixme
commit_details: repo_url: https://github.com/adaltas/node-mixme
author: David Worms
author_date: 2021-04-25 23:39:08+02:00
msg: fix: dont pollute object proto #1
repository: repo_url: https://github.com/adaltas/node-mixme
repo_name: adaltas/node-mixme
description: A library for recursive merging of Javascript objects
repo_language: TypeScript
owner: adaltas
file_changes: filename: CHANGELOG.md
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: Markdown
filename: mixme.cjs.js
change_type: ModificationType.MODIFY
diff: @@ -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)) {

programming_language: JavaScript
filename: mixme.esm.js
change_type: ModificationType.MODIFY
diff: @@ -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)) {

programming_language: JavaScript
filename: mixme.umd.js
change_type: ModificationType.MODIFY
diff: @@ -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)) {

programming_language: JavaScript
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -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)) {

programming_language: JavaScript
filename: index.coffee
change_type: ModificationType.MODIFY
diff: @@ -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

programming_language: CoffeeScript
filename: merge.coffee
change_type: ModificationType.MODIFY
diff: @@ -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()

programming_language: CoffeeScript
filename: mutate.coffee
change_type: ModificationType.MODIFY
diff: @@ -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', ->

programming_language: CoffeeScript