Run untrusted code in a seperate process using VM2 module. With timeout and memory limit management


Keywords
sandbox, memory limit, vm, vm2, untrusted code
License
MIT
Install
npm install pitboss-ng@2.0.1

Documentation

Build Status Build Status

Pitboss

Pitboss-NG (next gen)

A module for running untrusted code

var Pitboss = require('pitboss-ng').Pitboss;

var untrustedCode = "var a = !true;\n a";

var sandbox = new Pitboss(untrustedCode, {
  memoryLimit: 32*1024, // 32 MB memory limit (default is 64 MB)
  timeout: 5*1000, // 5000 ms to perform tasks or die (default is 500 ms = 0.5 s)
  heartBeatTick: 100 // interval between memory-limit checks (default is 100 ms)
});

sandbox.run({
  context: {       // context is an object of variables/values accessible by the untrusted code
    'foo': 'bar',  // context must be JSON.stringify positive
    'key': 'value' //  = no RegExp, Date, circular references, Buffer or more crazy things
  },
  libraries: {
    myModule: path.join(__dirname, './my/own/module'),
    // will be available as global "myModule" variable for the untrusted code
    'crypto': 'crypto', // you can also require system/installed packages
    '_': 'underscore'   // require underscore the traditional way
  }
}, function callback (err, result) {
  // result is synchronous "return" of the last line in your untrusted code, here "a = !true", so false
  console.log('Result is:', result); // prints "Result is: false"
  sandbox.kill(); // don't forget to kill the sandbox, if you don't need it anymore
});

// OR other option: libraries can be an array of system modules
sandbox.run({
  context: {}, // no data-variables are passed to context
  libraries: ['console', 'lodash'] // we will be using global "lodash" & "console"
}, function callback (err, result) {
  // finished, kill the sandboxed process
  sandbox.kill();
});

Runs JS code and returns the last eval'd statement

var assert = require('chai').assert;
var Pitboss = require('pitboss-ng').Pitboss;

var code = "num = num % 5;\nnum;"

var sandbox = new Pitboss(code);

sandbox.run({context: {'num': 23}}, function (err, result) {
  assert.equal(3, result);
  sandbox.kill(); // sandbox is not needed anymore, so kill the sandboxed process
});

Allows you to pass you own libraries into sandboxed content

var assert = require('chai').assert;
var Pitboss = require('pitboss-ng').Pitboss;

var code = "num = num % 5;\n console.log('from sandbox: ' + num);\n num;"

var sandbox = new Pitboss(code);

sandbox.run({context: {'num': 23}, libraries: ['console']}, function (err, result) {
  // will print "from sandbox: 5"
  assert.equal(3, result);
  sandbox.kill(); // sandbox is not needed anymore, so end it
});

Handles processes that take too damn long

var assert = require('chai').assert;
var Pitboss = require('pitboss-ng').Pitboss;

var code = "while(true) { num % 3 };";

var sandbox = new Pitboss(code, {timeout: 2000});
sandbox.run({context: {'num': 23}}, function (err, result) {
  assert.equal("Timedout", err);
  sandbox.kill();
});

Doesn't choke under pressure (or shitty code)

var assert = require('chai').assert;
var Pitboss = require('pitboss-ng').Pitboss;

var code = "Not a JavaScript at all!";

var sandbox = new Pitboss(code, {timeout: 2000});

sandbox.run({context: {num: 23}}, function (err, result) {
  assert.include(err, "VM Syntax Error");
  assert.include(err, "Unexpected identifier");
  sandbox.kill();
});

Doesn't handle this! But 'ulimit' or 'pidusage' does!

var assert = require('chai').assert;
var Pitboss = require('pitboss-ng').Pitboss;

var code = "var str = ''; while (true) { str = str + 'Memory is a finite resource!'; }";

var sandbox = new Pitboss(code, {timeout: 10000});

sandbox.run({context: {num: 23}}, function (err, result) {
  assert.equal("Process failed", err);
  sandbox.kill();
});

And since Pitboss-NG forks each process, ulimit kills only the runner