CVE-2020-7708 (published 2020-08-18T15:15Z)

The package irrelon-path before 4.7.0; the package @irrelon/path before 4.7.0 are vulnerable to Prototype Pollution via the set, unSet, pushVal and pullVal functions.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
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: 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/Irrelon/irrelon-path
commit_details: repo_url: https://github.com/Irrelon/irrelon-path
author: robevans
author_date: 2020-08-18 15:10:45+01:00
msg: Fixed functions with prototype pollution vulnerability
repository: repo_url: https://github.com/Irrelon/irrelon-path
repo_name: Irrelon/irrelon-path
description: A powerful JSON path processor. Allows you to drill into JSON objects with a simple dot-delimited path format e.g. "obj.name"
repo_language: JavaScript
owner: Irrelon
file_changes: filename: Path.js
change_type: ModificationType.MODIFY
diff: @@ -451,6 +451,8 @@ var set = function set(obj, path, val) {
 
 
   if (isNonCompositePath(internalPath)) {
+    // Do not allow prototype pollution
+    if (internalPath === "__proto__") return obj;
     obj = decouple(obj, options);
     obj[options.transformKey(unEscape(internalPath))] = val;
     return obj;
@@ -459,7 +461,9 @@ var set = function set(obj, path, val) {
   var newObj = decouple(obj, options);
   var pathParts = split(internalPath);
   var pathPart = pathParts.shift();
-  var transformedPathPart = options.transformKey(pathPart);
+  var transformedPathPart = options.transformKey(pathPart); // Do not allow prototype pollution
+
+  if (transformedPathPart === "__proto__") return obj;
   var childPart = newObj[transformedPathPart];
 
   if ((0, _typeof2["default"])(childPart) !== "object") {
@@ -519,8 +523,12 @@ var unSet = function unSet(obj, path) {
   var newObj = decouple(obj, options); // Path has no dot-notation, set key/value
 
   if (isNonCompositePath(internalPath)) {
-    if (newObj.hasOwnProperty(unEscape(internalPath))) {
-      delete newObj[options.transformKey(unEscape(internalPath))];
+    var unescapedPath = unEscape(internalPath); // Do not allow prototype pollution
+
+    if (unescapedPath === "__proto__") return obj;
+
+    if (newObj.hasOwnProperty(unescapedPath)) {
+      delete newObj[options.transformKey(unescapedPath)];
       return newObj;
     }
 
@@ -530,7 +538,9 @@ var unSet = function unSet(obj, path) {
 
   var pathParts = split(internalPath);
   var pathPart = pathParts.shift();
-  var transformedPathPart = options.transformKey(unEscape(pathPart));
+  var transformedPathPart = options.transformKey(unEscape(pathPart)); // Do not allow prototype pollution
+
+  if (transformedPathPart === "__proto__") return obj;
   var childPart = newObj[transformedPathPart];
 
   if (!childPart) {
@@ -618,6 +628,7 @@ var pushVal = function pushVal(obj, path, val) {
   path = clean(path);
   var pathParts = split(path);
   var part = pathParts.shift();
+  if (part === "__proto__") return obj;
 
   if (pathParts.length) {
     // Generate the path part in the object if it does not already exist
@@ -671,6 +682,7 @@ var pullVal = function pullVal(obj, path, val) {
   path = clean(path);
   var pathParts = split(path);
   var part = pathParts.shift();
+  if (part === "__proto__") return obj;
 
   if (pathParts.length) {
     // Generate the path part in the object if it does not already exist

programming_language: JavaScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
 	"name": "@irrelon/path",
-	"version": "4.6.8",
+	"version": "4.7.0",
 	"description": "A powerful JSON path processor. Allows you to drill into and manipulate JSON objects with a simple dot-delimited path format e.g. \"obj.name\".",
 	"main": "./src/Path.js",
 	"scripts": {

programming_language: JavaScript
filename: Path.js
change_type: ModificationType.MODIFY
diff: @@ -400,6 +400,9 @@ const set = (obj, path, val, options = {}) => {
 	
 	// Path has no dot-notation, set key/value
 	if (isNonCompositePath(internalPath)) {
+		// Do not allow prototype pollution
+		if (internalPath === "__proto__") return obj;
+
 		obj = decouple(obj, options);
 		obj[options.transformKey(unEscape(internalPath))] = val;
 		return obj;
@@ -409,6 +412,10 @@ const set = (obj, path, val, options = {}) => {
 	const pathParts = split(internalPath);
 	const pathPart = pathParts.shift();
 	const transformedPathPart = options.transformKey(pathPart);
+
+	// Do not allow prototype pollution
+	if (transformedPathPart === "__proto__") return obj;
+
 	let childPart = newObj[transformedPathPart];
 	
 	if (typeof childPart !== "object") {
@@ -470,8 +477,13 @@ const unSet = (obj, path, options = {}, tracking = {}) => {
 	
 	// Path has no dot-notation, set key/value
 	if (isNonCompositePath(internalPath)) {
-		if (newObj.hasOwnProperty(unEscape(internalPath))) {
-			delete newObj[options.transformKey(unEscape(internalPath))];
+		const unescapedPath = unEscape(internalPath);
+
+		// Do not allow prototype pollution
+		if (unescapedPath === "__proto__") return obj;
+
+		if (newObj.hasOwnProperty(unescapedPath)) {
+			delete newObj[options.transformKey(unescapedPath)];
 			return newObj;
 		}
 		
@@ -479,10 +491,13 @@ const unSet = (obj, path, options = {}, tracking = {}) => {
 		return obj;
 	}
 	
-	
 	const pathParts = split(internalPath);
 	const pathPart = pathParts.shift();
 	const transformedPathPart = options.transformKey(unEscape(pathPart));
+
+	// Do not allow prototype pollution
+	if (transformedPathPart === "__proto__") return obj;
+
 	let childPart = newObj[transformedPathPart];
 	
 	if (!childPart) {
@@ -563,7 +578,9 @@ const pushVal = (obj, path, val, options = {}) => {
 	
 	const pathParts = split(path);
 	const part = pathParts.shift();
-	
+
+	if (part === "__proto__") return obj;
+
 	if (pathParts.length) {
 		// Generate the path part in the object if it does not already exist
 		obj[part] = decouple(obj[part], options) || {};
@@ -613,6 +630,8 @@ const pullVal = (obj, path, val, options = {strict: true}) => {
 	
 	const pathParts = split(path);
 	const part = pathParts.shift();
+
+	if (part === "__proto__") return obj;
 	
 	if (pathParts.length) {
 		// Generate the path part in the object if it does not already exist

programming_language: TypeScript