CVE-2020-7639 (published 2020-04-06T13:15Z)

eivindfjeldstad-dot below 1.0.3 is vulnerable to Prototype Pollution.The function 'set' could be tricked into adding or modifying properties of 'Object.prototype' using a '__proto__' payload.

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

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/eivindfjeldstad/dot
commit_details: repo_url: https://github.com/eivindfjeldstad/dot
author: Eivind Fjeldstad
author_date: 2020-02-22 19:36:16+01:00
msg: Prevent prototype pollution
repository: repo_url: https://github.com/eivindfjeldstad/dot
repo_name: eivindfjeldstad/dot
description: Get and set object properties with dot notation
repo_language: JavaScript
owner: eivindfjeldstad
file_changes: filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -8,18 +8,21 @@
  * @api public
  */
 
-exports.set = function (obj, path, val) {
+exports.set = function(obj, path, val) {
   var segs = path.split('.');
   var attr = segs.pop();
   var src = obj;
 
   for (var i = 0; i < segs.length; i++) {
     var seg = segs[i];
+    if (!isSafe(obj, seg)) return src;
     obj[seg] = obj[seg] || {};
     obj = obj[seg];
   }
 
-  obj[attr] = val;
+  if (isSafe(obj, attr)) {
+    obj[attr] = val;
+  }
 
   return src;
 };
@@ -33,7 +36,7 @@ exports.set = function (obj, path, val) {
  * @api public
  */
 
-exports.get = function (obj, path) {
+exports.get = function(obj, path) {
   var segs = path.split('.');
   var attr = segs.pop();
 
@@ -55,19 +58,42 @@ exports.get = function (obj, path) {
  * @api public
  */
 
-exports.delete = function (obj, path) {
+exports.delete = function(obj, path) {
   var segs = path.split('.');
   var attr = segs.pop();
 
   for (var i = 0; i < segs.length; i++) {
     var seg = segs[i];
     if (!obj[seg]) return;
+    if (!isSafe(obj, seg)) return;
     obj = obj[seg];
   }
 
+  if (!isSafe(obj, attr)) return;
+
   if (Array.isArray(obj)) {
-    obj.splice(path, 1);
+    obj.splice(attr, 1);
   } else {
     delete obj[attr];
   }
 };
+
+function isSafe(obj, prop) {
+  if (isObject(obj)) {
+    return obj[prop] === undefined || hasOwnProperty(obj, prop);
+  }
+
+  if (Array.isArray(obj)) {
+    return !isNaN(parseInt(prop, 10));
+  }
+
+  return false;
+}
+
+function hasOwnProperty(obj, prop) {
+  return Object.prototype.hasOwnProperty.call(obj, prop);
+}
+
+function isObject(obj) {
+  return Object.prototype.toString.call(obj) === '[object Object]';
+}

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -3,7 +3,7 @@
   "publishConfig": {
     "access": "public"
   },
-  "version": "1.0.2",
+  "version": "1.0.3",
   "description": "Get and set object properties with dot notation",
   "main": "index.js",
   "scripts": {

programming_language: JavaScript
filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -1,15 +1,15 @@
 var assert = require('assert');
 var dot = require('..');
 
-var tests = module.exports = {
-  'test set': function () {
+var tests = (module.exports = {
+  'test set': function() {
     var obj = {};
     var ret = dot.set(obj, 'cool.aid', 'rocks');
     assert(obj.cool.aid === 'rocks');
     assert(obj === ret);
   },
 
-  'test get': function () {
+  'test get': function() {
     var obj = {};
     obj.cool = {};
     obj.cool.aid = 'rocks';
@@ -17,17 +17,44 @@ var tests = module.exports = {
     assert(value === 'rocks');
   },
 
-  'test delete': function () {
+  'test delete': function() {
     var obj = {};
     obj.cool = {};
     obj.cool.aid = 'rocks';
     obj.cool.hello = ['world'];
     dot.delete(obj, 'cool.aid');
     dot.delete(obj, 'cool.hello.0');
-    assert(!obj.cool.hasOwnProperty('aid'))
+    assert(!obj.cool.hasOwnProperty('aid'));
     assert(obj.cool.hello.length == 0);
+  },
+
+  'test prototype pollution': function() {
+    var obj = {};
+    obj.cool = {};
+    obj.cool.aid = 'rocks';
+    obj.cool.hello = ['world'];
+    dot.set(obj, '__proto__', 'test');
+    dot.set(obj, '__proto__.toString', 'test');
+    dot.set(obj, 'toString', 'test');
+    dot.set(obj, 'cool.hello.__proto__', 'test');
+    dot.set(obj, 'cool.hello.__proto__.toString', 'test');
+    dot.set(obj, 'cool.hello.toString', 'test');
+    assert(obj.__proto__ === {}.__proto__);
+    assert(obj.toString === Object.prototype.toString);
+    assert(obj.cool.hello.__proto__ === [].__proto__);
+    assert(obj.cool.hello.toString === Array.prototype.toString);
+    dot.delete(obj, '__proto__.toString', 'test');
+    dot.delete(obj, '__proto__', 'test');
+    dot.delete(obj, 'toString', 'test');
+    dot.delete(obj, 'cool.hello.__proto__.toString', 'test');
+    dot.delete(obj, 'cool.hello.__proto__', 'test');
+    dot.delete(obj, 'cool.hello.toString', 'test');
+    assert(obj.__proto__ === {}.__proto__);
+    assert(obj.toString === Object.prototype.toString);
+    assert(obj.cool.hello.__proto__ === [].__proto__);
+    assert(obj.cool.hello.toString === Array.prototype.toString);
   }
-}
+});
 
 for (var t in tests) {
   tests[t]();

programming_language: JavaScript