CVE-2019-10761: Uncontrolled Recursion (CWE-674)
value: This affects the package vm2 before 3.6.11. It is possible to trigger a RangeError exception from the host rather than the "sandboxed" context by reaching the stack call limit with an infinite recursion. The returned object is then used to reference the mainModule property of the host code running the script allowing it to spawn a child_process and execute arbitrary code.
CWE description: The product does not properly control the amount of recursion that takes place,  consuming excessive resources, such as allocated memory or the program stack.
CVSS v3 base score: 8.3
Published: 2022-07-13T09:15Z
Repository: https://github.com/patriksimek/vm2
Language: JavaScript
Files changed: lib/contextify.js
Commit date: 2019-04-08 01:52:30 +0200
Commit message: Fixes sandbox escape (#197)

Diff: diff --git a/lib/contextify.js b/lib/contextify.js
index 361fb8a..97acccf 100644
--- a/lib/contextify.js
+++ b/lib/contextify.js
@@ -325,19 +325,19 @@ Decontextify.object = (object, traps, deepTraps, flags, mock) => {
 	Decontextify.proxies.set(object, proxy);
 	Decontextified.set(proxy, object);
 	return proxy;
 };
 Decontextify.value = (value, traps, deepTraps, flags, mock) => {
-	if (Contextified.has(value)) {
-		// Contextified object has returned back from vm
-		return Contextified.get(value);
-	} else if (Decontextify.proxies.has(value)) {
-		// Decontextified proxy already exists, reuse
-		return Decontextify.proxies.get(value);
-	}
-
 	try {
+		if (Contextified.has(value)) {
+			// Contextified object has returned back from vm
+			return Contextified.get(value);
+		} else if (Decontextify.proxies.has(value)) {
+			// Decontextified proxy already exists, reuse
+			return Decontextify.proxies.get(value);
+		}
+
 		switch (typeof value) {
 			case 'object':
 				if (value === null) {
 					return null;
 				} else if (instanceOf(value, Number))         { return host.Number(value);
@@ -619,19 +619,19 @@ Contextify.object = (object, traps, deepTraps, flags, mock) => {
 	Contextify.proxies.set(object, proxy);
 	Contextified.set(proxy, object);
 	return proxy;
 };
 Contextify.value = (value, traps, deepTraps, flags, mock) => {
-	if (Decontextified.has(value)) {
-		// Decontextified object has returned back to vm
-		return Decontextified.get(value);
-	} else if (Contextify.proxies.has(value)) {
-		// Contextified proxy already exists, reuse
-		return Contextify.proxies.get(value);
-	}
-
 	try {
+		if (Decontextified.has(value)) {
+			// Decontextified object has returned back to vm
+			return Decontextified.get(value);
+		} else if (Contextify.proxies.has(value)) {
+			// Contextified proxy already exists, reuse
+			return Contextify.proxies.get(value);
+		}
+
 		switch (typeof value) {
 			case 'object':
 				if (value === null) {
 					return null;
 				} else if (instanceOf(value, host.Number))         { return host.Number(value);

Vulnerable code: 	if (Contextified.has(value)) {
		// Contextified object has returned back from vm
		return Contextified.get(value);
	} else if (Decontextify.proxies.has(value)) {
		// Decontextified proxy already exists, reuse
		return Decontextify.proxies.get(value);
	}

	if (Decontextified.has(value)) {
		// Decontextified object has returned back to vm
		return Decontextified.get(value);
	} else if (Contextify.proxies.has(value)) {
		// Contextified proxy already exists, reuse
		return Contextify.proxies.get(value);
	}

Fixed code: 		if (Contextified.has(value)) {
			// Contextified object has returned back from vm
			return Contextified.get(value);
		} else if (Decontextify.proxies.has(value)) {
			// Decontextified proxy already exists, reuse
			return Decontextify.proxies.get(value);
		}

		if (Decontextified.has(value)) {
			// Decontextified object has returned back to vm
			return Decontextified.get(value);
		} else if (Contextify.proxies.has(value)) {
			// Contextified proxy already exists, reuse
			return Contextify.proxies.get(value);
		}