CVE-2020-7772 (published 2020-11-15T15:15Z)

This affects the package doc-path before 2.1.2.

Severity: HIGH
CVSS v2 base score: 10.0 (AV:N/AC:L/Au:N/C:C/I:C/A:C)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: NVD-CWE-noinfo
cwe_name: Insufficient Information
description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/mrodrig/doc-path
commit_details: repo_url: https://github.com/mrodrig/doc-path
author: Mike Rodrigues
author_date: 2020-10-05 22:27:23-04:00
msg: Fix issue presented in #14
repository: repo_url: https://github.com/mrodrig/doc-path
repo_name: mrodrig/doc-path
description: A document path library for Node.JS
repo_language: TypeScript
owner: mrodrig
file_changes: filename: .travis.yml
change_type: ModificationType.MODIFY
diff: @@ -5,10 +5,7 @@ language: node_js
 node_js:
   - "14"
   - "12"
-  - "11"
   - "10"
-  - "9"
-  - "8"
 sudo: false
 before_script:
   - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter

programming_language: Shell
filename: package-lock.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
     "name": "doc-path",
-    "version": "2.1.1",
+    "version": "2.1.2",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -2,7 +2,7 @@
     "author": "mrodrig",
     "name": "doc-path",
     "description": "A document path library for Node",
-    "version": "2.1.1",
+    "version": "2.1.2",
     "repository": {
         "type": "git",
         "url": "http://github.com/mrodrig/doc-path.git"
@@ -37,7 +37,7 @@
         "should": "13.2.3"
     },
     "engines": {
-        "node": ">=6.0"
+        "node": ">=10"
     },
     "license": "MIT"
 }

programming_language: JavaScript
filename: path.js
change_type: ModificationType.MODIFY
diff: @@ -30,29 +30,40 @@ function evaluatePath(document, keyPath) {
 }
 
 function setPath(document, keyPath, value) {
+    if (!document) {
+        throw new Error('No document was provided.');
+    } else if (!keyPath) {
+        throw new Error('No keyPath was provided.');
+    }
+
+    // If this is clearly a prototype pollution attempt, then refuse to modify the path
+    if (keyPath.startsWith('__proto__') || keyPath.startsWith('constructor')) {
+        return document;
+    }
+
+    return _setPath(document, keyPath, value);
+}
+
+function _setPath(document, keyPath, value) {
     if (!document) {
         throw new Error('No document was provided.');
     }
 
     let {indexOfDot, currentKey, remainingKeyPath} = computeStateInformation(keyPath);
 
-    // if (currentKey === '__proto__' || currentKey === 'prototype' && Object.prototype.hasOwnProperty.call(document, currentKey)) {
-    if (currentKey === '__proto__') {
-        // Refuse to modify anything on __proto__, return the document
-        return document;
-    } else if (indexOfDot >= 0) {
+    if (indexOfDot >= 0) {
         // If there is a '.' in the keyPath, recur on the subdoc and ...
         if (!document[currentKey] && Array.isArray(document)) {
             // If this is an array and there are multiple levels of keys to iterate over, recur.
-            return document.forEach((doc) => setPath(doc, keyPath, value));
+            return document.forEach((doc) => _setPath(doc, keyPath, value));
         } else if (!document[currentKey]) {
             // If the currentKey doesn't exist yet, populate it
             document[currentKey] = {};
         }
-        setPath(document[currentKey], remainingKeyPath, value);
+        _setPath(document[currentKey], remainingKeyPath, value);
     } else if (Array.isArray(document)) {
         // If this "document" is actually an array, then we can loop over each of the values and set the path
-        return document.forEach((doc) => setPath(doc, remainingKeyPath, value));
+        return document.forEach((doc) => _setPath(doc, remainingKeyPath, value));
     } else {
         // Otherwise, we can set the path directly
         document[keyPath] = value;

programming_language: TypeScript
filename: tests.js
change_type: ModificationType.MODIFY
diff: @@ -218,11 +218,20 @@ describe('doc-path Module', function() {
 
         it('should protect against prototype pollution via __proto__', (done) => {
             doc = {};
-            path.setPath(doc, '__proto__.polluted', 'yes');
+            path.setPath(doc, '__proto__.polluted', 'prototype-polluted');
             assert.equal(doc.__proto__.polluted, undefined);
             assert.equal(doc.polluted, undefined);
             assert.equal({}.polluted, undefined);
             done();
         });
+
+        it('should protect against prototype pollution via constructor', (done) => {
+            doc = {};
+            path.setPath(doc, 'constructor', 'prototype-polluted');
+            assert.equal(doc.constructor, Object);
+            path.setPath(doc, 'constructor.prototype.test', 'prototype-polluted');
+            assert.equal(doc.test, undefined);
+            done();
+        });
     });
 });

programming_language: TypeScript