7

I can't seem to figure out how to set up a node sandbox, which can run untrusted code safely, and allows a user to interact with the program through api calls (sys in and out). I'm trying to set up a console in the browser for users to run their own code from the server.

Are there any node packages that support this, or do I need to write my own node VM? Thanks.

Edit: I want a user to be able to write readline() and have the program wait at the breakpoint for data to be transferred in. similarly console.log()'s output should redirect to the response of the input api call.

X88B88
  • 611
  • 1
  • 5
  • 7
  • Perhaps this: [Nodejs VM module](https://nodejs.org/api/vm.html)? Though if you want full sandbox safety, then you probably need to run a new nodejs process in an actual isolated VM. Also [How to run user-submitted scripts securely in a node.js sandbox?](https://stackoverflow.com/questions/7446729/how-to-run-user-submitted-scripts-securely-in-a-node-js-sandbox) – jfriend00 Aug 19 '17 at 05:35
  • I've seen these, but they don't seem to be able to accept input if a user wrote `readline()`. @jfriend00 check the edit – X88B88 Aug 19 '17 at 15:11
  • I don't understand what you're trying to do. Where do you expect stdin to come from? Are you expecting a local console? And where do you expect stdout to go? Your question mentions "interact with the program through API calls" which I assumed meant you were starting a server and some other process elsewhere would communicate with that server. – jfriend00 Aug 19 '17 at 20:17
  • Yeah I have a server running, with the user's code on it. When the user makes an api request on the client to run the progrem, I want a vm to run it. if there is a readline, the program should pause and wait, the response to the client would have any sys out data produced before the breakpoint, and should tell the client that input is expected. when the user enters input, program resumes until next input or program end. in either case, the api response would have the sys out. – X88B88 Aug 19 '17 at 20:40
  • I still don't get where you expect the input to come from for the `readline()`. If the user is remote, they have zero connection to stdin of any server process. So, if you want to feed a process something from the end user, somebody has to build an app or a page that fetches that input from the user and feeds it to the server. It would probably be the job of whatever page the user was interacting with when they first told it to run the program in the first place. There are many possible ways to do that, but I don't understand the overall nature of what you're doing to know what to recommend. – jfriend00 Aug 19 '17 at 20:46
  • I have already built a page that fetches from the user and feeds it to the server. I just need a way to have a program running safely on the server. I pretty much want to simulate stdin and pass in what i received from the client. – X88B88 Aug 19 '17 at 20:52
  • If you want a `stdin`, `stdout` environment for the code you are running, then you need to fire up a new node.js process and that's a good thing to do for isolation and safety anyway. The sandbox module referenced in my first comment does that and the `sandbox.child.stdin` property has is where you can feed input to after you've created the sandbox object. Now, as best I can tell the sandbox module does not prevent `fs` or `net` code in the untrusted code from messing with things on your server. You could perhaps tweak OS privilege of the child for additional safety. – jfriend00 Aug 19 '17 at 22:49
  • You can look at the sandbox code to see what is does here: https://github.com/gf3/sandbox/blob/master/lib/sandbox.js#L40. Note, it does a `spawn()` for the child process. – jfriend00 Aug 19 '17 at 22:52
  • 1
    Does this answer your question? [How to run untrusted code serverside?](https://stackoverflow.com/questions/10937870/how-to-run-untrusted-code-serverside) – Jerska Mar 27 '20 at 10:31

1 Answers1

3

You can use the vm2 module and run almost any code that comes with user input in a secure way.

You can even define whether the user-supplied code will have access to require native Node modules or other modules via relative path or even define whether a code coming from the user input can make an external call.

You can envelop and execute this "untrusted" code in a try/catch to observe catastrophic failures or even set a timeout so that this run does not overwhelm.

quick example

const {VM} = require('vm2');
const vm = new VM();

vm.run(`process.exit()`); // TypeError: process.exit is not a function

using "request" module "bultin" for access external resource

const {NodeVM} = require('vm2');
const vm = new NodeVM({
    require: {
        external: true // allow all modules or use Array for one e.g: ['request']
    }
});    

vm.run(`
    var request = require('request');
    request('http://www.google.com', function (error, response, body) {
        console.error(error);
        if (!error && response.statusCode == 200) {
            console.log(body) // Show the HTML for the Google homepage.
        }
    })
`, 'vm.js');

By default the entry is compiled into javascript but you can pass a function with your custom compiler.

Lauro Moraes
  • 1,358
  • 2
  • 14
  • 16