-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Require does not work as expected within vm #2503
Comments
Each VM context gets its own set of global objects, including the Array object. In other words:
If you want to modify the Array object from the current context, pass it to the new context:
But note the following caveat:
Hope that helps. |
Hey @bnoordhuis thanks for the post, and your time. I really appreciate it. All of what you said in your comment I completely understand, and makes total sense. I think the bug goes a little deeper. It has more to do with the behavior of the imported Here I change the value of var vm = require('vm')
var code = [
'Array.marker = true',
].join('\n')
vm.runInNewContext(code)
console.log(Array.marker) // => undefined [expected] However when I bring var vm = require('vm')
var code = [
'Array.marker = true',
"require(\'./global.js\')",
'console.log(Array.marker)' // => true [unexpected]
].join('\n')
vm.runInNewContext(code, {
require: require,
console: console
})
console.log(Array.marker) // => false [unexpected] The real question is how can instantiate a new instance of |
The only trick I've found to get require to work the way I want within the vm code is if I create a file with the contents of the code, and require that file. usage.js Array.marker = true
require('./global.js')
console.log(Array.marker) // => false [expected] Then I can require the file and the global scope within the code works as expected. var vm = require('vm')
var code = [
"require('usage.js')"
].join('\n')
vm.runInNewContext(code, {
require: require,
console: console
})
console.log(Array.marker) // => false [unexpected] However this still leaks out Is there any way to load a string as-if it's being required? |
Here's a proof-of-concept, that takes the code and will write it to a file and the vm requires the newly created file. var path = require('path')
var os = require('os')
var crypto = require('crypto')
var fs = require('fs')
var vm = require('vm')
var globalPath = path.resolve('./global.js')
var code = [
"Array.marker = true",
"require('"+globalPath+"')",
"console.log(Array.marker)", // => true [expected]
].join('\n')
var getHash = function (content) {
var digest = crypto.createHash('md5')
return digest.update(content).digest('hex')
}
function encapsulate (code, fileName) {
// hash the name to ensure files are saved in secure location
// prevent ../../filename.js
var hashName = getHash(fileName) + '.js'
var filePath = path.join(os.tmpdir(), hashName)
fs.writeFileSync(filePath, code)
code = [
"require('" + filePath + "')"
].join('\n')
var value = vm.runInNewContext(code, {
require: require,
console: console
})
fs.unlinkSync(filePath)
return value
}
encapsulate(code, 'hi.js')
console.log(Array.marker) // => false [unexpected] |
That's the expected behavior and it makes sense when you realize that VM contexts are used to implement iframes in Chromium. Calling a function runs it in the context that created it. |
@bnoordhuis What your telling me is great info and context. Yes I'm not alone in wanting to use
var vm = require('vm')
var ret = vm.createScript('typeof require', 'sandbox.vm').runInNewContext({require:require}) Passing in require like this is really bad because it leaks variables. There has to be a better alternative. I agree with @isaacs it shouldn't be default, however perhaps an optional flag for the vm to have it's own instance of require. Unless theres a drop in replacement for require that would work, I've been looking into felixge/node-sandboxed-module and I believe it does the opposite of what we need. |
I admit I don't really understand what you mean by "it leaks variables" or what you are trying to accomplish but I think the short answer is "you can't do that", require() is too deeply tied into the node run-time for that. Contexts created with the vm module are not node contexts, they're just JS contexts. |
If you want to pass in a specific function called @bnoordhuis I presume that "leaks variables" means that it exposes internals to the untrusted code in the sandbox. |
Yes by "leaking variables" I mean that when I pass var vm = require('vm')
var code = [
'Array.marker = true',
"require(\'./global.js\')", // (changes Array.marker to false)
'console.log(Array.marker)' // => true [unexpected]
].join('\n')
vm.runInNewContext(code, {
require: require,
console: console
})
console.log(Array.marker) // => false [unexpected] In my opinion this is unexpected behavior. 👎 😿 🌋 🚒 I really don't know how to write a |
Perhaps this pertains to nodejs/node-v0.x-archive#9211? |
adds support for the validation keywords defined for strings in https://datatracker.ietf.org/doc/html/draft-bhutton-json-schema-validation-00#section-6.3 originally I tried to create `/RegExp literals/` but this didn't work for the test harness with `joi` due to nodejs/node#2503 (comment) and a reliance on `pattern instanceof RegExp` check that `joi` makes.
The code below uses the native node
vm
library, which allows you to evaluate javascript strings in different contexts.The particular code within
example.js
there's a javascript string that adds a property.marker
with the valuetrue
to theArray
global variable, then requires a fileglobal.js
(seen below), then logsArray.marker
. The code below logstrue
.Below is the contents of
global.js
, which is a simple module that changes the value ofArray.marker
tofalse
.What should be happening here is that the code within the
vm
should be settingArray.marker
totrue
then theglobal.js
module should be changing it the value tofalse
and it should logfalse
.If you go ahead and run the contents of the javascript string outside of
vm
, in it's own file, you will get the expected result,Array.marker
will be equal tofalse
.The question is: Why doesn't
Array.marker
get updated to the correct value (true
)? How can I allow the value ofArray.marker
to get updated from within theglobal.js
module?Is this an issue with the native node.js
vm
module? Or is this not supposed to be possible? Or are my settings off?The text was updated successfully, but these errors were encountered: