CVE-2021-23438: Access of Resource Using Incompatible Type ('Type Confusion') (CWE-843)
value: This affects the package mpath before 0.8.4. A type confusion vulnerability can lead to a bypass of CVE-2018-16490. In particular, the condition ignoreProperties.indexOf(parts[i]) !== -1 returns -1 if parts[i] is ['__proto__']. This is because the method that has been called if the input is an array is Array.prototype.indexOf() and not String.prototype.indexOf(). They behave differently depending on the type of the input.
CWE description: The product allocates or initializes a resource such as a pointer, object, or variable using one type, but it later accesses that resource using a type that is incompatible with the original type.
CVSS v3 base score: 9.8
Published: 2021-09-01T19:15Z
Repository: https://github.com/aheckmann/mpath
Language: JavaScript
Files changed: lib/index.js
Commit date: 2021-09-01 11:12:24 -0400
Commit message: fix: throw error if `parts` contains an element that isn't a string or number

Fix #13

Diff: diff --git a/lib/index.js b/lib/index.js
index 338bf9b..c9e7e5f 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -62,10 +62,13 @@ exports.get = function(path, o, special, map) {
   var obj = o,
       part;
 
   for (var i = 0; i < parts.length; ++i) {
     part = parts[i];
+    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
+      throw new TypeError('Each segment of path to `get()` must be a string or number, got ' + typeof parts[i]);
+    }
 
     if (Array.isArray(obj) && !/^\d+$/.test(part)) {
       // reading a property from the array items
       var paths = parts.slice(i);
 
@@ -110,10 +113,13 @@ exports.has = function(path, o) {
   }
 
   var len = parts.length;
   var cur = o;
   for (var i = 0; i < len; ++i) {
+    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
+      throw new TypeError('Each segment of path to `has()` must be a string or number, got ' + typeof parts[i]);
+    }
     if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) {
       return false;
     }
     cur = cur[parts[i]];
   }
@@ -141,10 +147,13 @@ exports.unset = function(path, o) {
   var cur = o;
   for (var i = 0; i < len; ++i) {
     if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) {
       return false;
     }
+    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
+      throw new TypeError('Each segment of path to `unset()` must be a string or number, got ' + typeof parts[i]);
+    }
     // Disallow any updates to __proto__ or special properties.
     if (ignoreProperties.indexOf(parts[i]) !== -1) {
       return false;
     }
     if (i === len - 1) {
@@ -191,10 +200,13 @@ exports.set = function(path, val, o, special, map, _copying) {
   }
 
   if (null == o) return;
 
   for (var i = 0; i < parts.length; ++i) {
+    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
+      throw new TypeError('Each segment of path to `set()` must be a string or number, got ' + typeof parts[i]);
+    }
     // Silently ignore any updates to `__proto__`, these are potentially
     // dangerous if using mpath with unsanitized data.
     if (ignoreProperties.indexOf(parts[i]) !== -1) {
       return;
     }

Fixed code:     if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
      throw new TypeError('Each segment of path to `get()` must be a string or number, got ' + typeof parts[i]);
    }
    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
      throw new TypeError('Each segment of path to `has()` must be a string or number, got ' + typeof parts[i]);
    }
    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
      throw new TypeError('Each segment of path to `unset()` must be a string or number, got ' + typeof parts[i]);
    }
    if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') {
      throw new TypeError('Each segment of path to `set()` must be a string or number, got ' + typeof parts[i]);
    }