CVE-2023-29199: Improper Control of Dynamically-Managed Code Resources (CWE-913)
value: There exists a vulnerability in source code transformer (exception sanitization logic) of vm2 for versions up to 3.9.15, allowing attackers to bypass `handleException()` and leak unsanitized host exceptions which can be used to escape the sandbox and run arbitrary code in host context. A threat actor can bypass the sandbox protections to gain remote code execution rights on the host running the sandbox. This vulnerability was patched in the release of version `3.9.16` of `vm2`.

CWE description: The product does not properly restrict reading from or writing to dynamically-managed code resources such as variables, objects, classes, attributes, functions, or executable instructions or statements.
CVSS v3 base score: 10
Published: 2023-04-14T19:15Z
Repository: https://github.com/patriksimek/vm2
Language: JSON
Files changed: CHANGELOG.md, lib/transformer.js, package.json
Commit date: 2023-04-11 10:44:44 +0200
Commit message: Release 3.9.16

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3214085..0fbc31b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
+v3.9.16 (2023-04-11)
+--------------------
+[fix] Security fix (see https://github.com/patriksimek/vm2/issues/516).
+
 v3.9.15 (2023-04-06)
 --------------------
 [fix] Security fix (see https://github.com/patriksimek/vm2/issues/515).
 
 v3.9.14 (2023-02-05)
diff --git a/lib/transformer.js b/lib/transformer.js
index f20a248..9919453 100644
--- a/lib/transformer.js
+++ b/lib/transformer.js
@@ -116,43 +116,43 @@ function transformer(args, body, isAsync, isGenerator, filename) {
 				if (param.type === 'ObjectPattern') {
 					insertions.push({
 						__proto__: null,
 						pos: node.start,
 						order: TO_RIGHT,
-						code: `catch($tmpname){try{throw ${INTERNAL_STATE_NAME}.handleException($tmpname);}`
+						coder: () => `catch(${tmpname}){try{throw(${tmpname}=${INTERNAL_STATE_NAME}.handleException(${tmpname}));}`
 					});
 					insertions.push({
 						__proto__: null,
 						pos: node.body.end,
 						order: TO_LEFT,
-						code: `}`
+						coder: () => `}`
 					});
 				} else {
 					const name = assertType(param, 'Identifier').name;
 					const cBody = assertType(node.body, 'BlockStatement');
 					if (cBody.body.length > 0) {
 						insertions.push({
 							__proto__: null,
 							pos: cBody.body[0].start,
 							order: TO_LEFT,
-							code: `${name}=${INTERNAL_STATE_NAME}.handleException(${name});`
+							coder: () => `${name}=${INTERNAL_STATE_NAME}.handleException(${name});`
 						});
 					}
 				}
 			}
 		} else if (nodeType === 'WithStatement') {
 			insertions.push({
 				__proto__: null,
 				pos: node.object.start,
 				order: TO_LEFT,
-				code: INTERNAL_STATE_NAME + '.wrapWith('
+				coder: () => INTERNAL_STATE_NAME + '.wrapWith('
 			});
 			insertions.push({
 				__proto__: null,
 				pos: node.object.end,
 				order: TO_RIGHT,
-				code: ')'
+				coder: () => ')'
 			});
 		} else if (nodeType === 'Identifier') {
 			if (node.name === INTERNAL_STATE_NAME) {
 				if (internStateValiable === undefined || internStateValiable.start > node.start) {
 					internStateValiable = node;
@@ -163,11 +163,11 @@ function transformer(args, body, isAsync, isGenerator, filename) {
 		} else if (nodeType === 'ImportExpression') {
 			insertions.push({
 				__proto__: null,
 				pos: node.start,
 				order: TO_RIGHT,
-				code: INTERNAL_STATE_NAME + '.'
+				coder: () => INTERNAL_STATE_NAME + '.'
 			});
 		}
 	});
 
 	if (internStateValiable) {
@@ -184,11 +184,11 @@ function transformer(args, body, isAsync, isGenerator, filename) {
 
 	let ncode = '';
 	let curr = 0;
 	for (let i = 0; i < insertions.length; i++) {
 		const change = insertions[i];
-		ncode += code.substring(curr, change.pos) + change.code.replace(/\$tmpname/g, tmpname);
+		ncode += code.substring(curr, change.pos) + change.coder();
 		curr = change.pos;
 	}
 	ncode += code.substring(curr);
 
 	return {__proto__: null, code: ncode, hasAsync};
diff --git a/package.json b/package.json
index 232d8af..685d41b 100644
--- a/package.json
+++ b/package.json
@@ -11,11 +11,11 @@
     "jail",
     "vm",
     "alcatraz",
     "contextify"
   ],
-  "version": "3.9.15",
+  "version": "3.9.16",
   "main": "index.js",
   "sideEffects": false,
   "repository": "github:patriksimek/vm2",
   "license": "MIT",
   "dependencies": {

Vulnerable code: 						code: `catch($tmpname){try{throw ${INTERNAL_STATE_NAME}.handleException($tmpname);}`
						code: `}`
							code: `${name}=${INTERNAL_STATE_NAME}.handleException(${name});`
				code: INTERNAL_STATE_NAME + '.wrapWith('
				code: ')'
				code: INTERNAL_STATE_NAME + '.'
		ncode += code.substring(curr, change.pos) + change.code.replace(/\$tmpname/g, tmpname);
  "version": "3.9.15",
Fixed code: v3.9.16 (2023-04-11)
--------------------
[fix] Security fix (see https://github.com/patriksimek/vm2/issues/516).

						coder: () => `catch(${tmpname}){try{throw(${tmpname}=${INTERNAL_STATE_NAME}.handleException(${tmpname}));}`
						coder: () => `}`
							coder: () => `${name}=${INTERNAL_STATE_NAME}.handleException(${name});`
				coder: () => INTERNAL_STATE_NAME + '.wrapWith('
				coder: () => ')'
				coder: () => INTERNAL_STATE_NAME + '.'
		ncode += code.substring(curr, change.pos) + change.coder();
  "version": "3.9.16",