Hey, not an urgent issue. Just a heads up 😀
Right now doing typeof can only return "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function". This means code can currently safely assume that anything that is not object or function is primitive and inert, no need to wrap the value to prevent access to outside of the VM.
|
default: // string, number, boolean, symbol |
|
return value; |
This assumption may not hold in the future depending on how the Record&Tuple proposal ends up being specified. It could mean that there is a value that returns "record" | "tuple" | "box" which then contains a direct reference to an object inside it.
One option to future proof against this is to explicitly add "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" to the switch and let those values pass, but don't pass on future unknown types by default until explicit support has been added.
case 'undefined':
return undefined;
case 'string':
case 'number':
case 'bigint':
case 'boolean':
case 'symbol':
return value;
default: // unexpected typeof - support not added yet
return null;
Example of vm escape via Box
// Current safe usage:
const {VM} = require('vm2');
const vm = new VM({sandbox: {
add: function(a, b) { return a + b }
}});
vm.run('add.constructor("process.exit()")()'); // ReferenceError [Error]: process is not defined
// Possible future unsafe usage:
const {VM} = require('vm2');
const vm = new VM({sandbox: {
utils: #{
add: Box(function(a, b) { return a + b })
}
}});
vm.run('Box.unbox(utils.add).constructor("process.exit()")()'); // process does exit
The exact details around what should happen when a box is passed to a new realm are still being discussed so this might not ever be an issue, but wanted to give you a heads up that these discussions are happening.
tc39/proposal-record-tuple#260
Hey, not an urgent issue. Just a heads up 😀
Right now doing
typeofcan only return"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function". This means code can currently safely assume that anything that is notobjectorfunctionis primitive and inert, no need to wrap the value to prevent access to outside of the VM.vm2/lib/contextify.js
Lines 563 to 564 in 4198060
This assumption may not hold in the future depending on how the Record&Tuple proposal ends up being specified. It could mean that there is a value that returns
"record" | "tuple" | "box"which then contains a direct reference to an object inside it.One option to future proof against this is to explicitly add
"string" | "number" | "bigint" | "boolean" | "symbol" | "undefined"to theswitchand let those values pass, but don't pass on future unknown types by default until explicit support has been added.Example of vm escape via Box
The exact details around what should happen when a box is passed to a new realm are still being discussed so this might not ever be an issue, but wanted to give you a heads up that these discussions are happening.
tc39/proposal-record-tuple#260