pitboss-ng
Version:
Run untrusted code in a seperate process using VM2 module. With timeout and memory limit management
251 lines (231 loc) • 6.54 kB
JavaScript
const { assert } = require('chai');
const { fork } = require('child_process');
describe('The forkable process', function() {
const referenceErrorMsg = 'VM Runtime Error: ReferenceError:';
const syntaxErrorMsg = 'VM Runtime Error: SyntaxError:';
const typeErrorMsg = 'VM Runtime Error: TypeError:';
beforeEach(function(done) {
this.runner = fork('./lib/forkable.js');
this.runner.once('message', (msg) => done(msg !== 'ready'));
});
afterEach(function() {
if (this.runner != null) {
if (typeof this.runner.kill === 'function') {
this.runner.kill();
}
}
this.runner = null;
});
describe('basic operation', function() {
beforeEach(function() {
this.code = `// EchoTron: returns the 'data' variable in a VM
if(typeof data === "undefined") {
var data = 1
};
data`;
});
it('run without errors', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.strictEqual(msg.result, 1);
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {},
});
});
});
describe('running code that assumes priviledge', function() {
beforeEach(function() {
this.code = `require('http');
123;`;
});
it('should fail on require', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, null);
assert.include(msg.error, 'require is not defined');
assert.include(msg.error, referenceErrorMsg);
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {},
});
});
});
describe('Running code that uses JSON global', function() {
beforeEach(function() {
this.code = 'JSON.stringify({});';
});
it('should work as expected', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, '{}');
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {},
});
});
});
describe('Running code that uses Buffer', function() {
beforeEach(function() {
this.code = `var buf = new Buffer('abc');
buf.toString();`;
});
it('should keep working', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, 'XYZ');
assert.equal(msg.result, 'abc');
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: 'XYZ',
context: {},
});
});
});
describe('Running shitty code', function() {
beforeEach(function() {
this.code = "This isn't even Javascript!!!!";
});
it('should return errors on running of bad syntax code', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, void 0);
assert.include(msg.error, syntaxErrorMsg);
assert.include(msg.error, 'Unexpected identifier');
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {},
});
});
});
describe('Running runtime error code', function() {
beforeEach(function() {
this.code = `var foo = [];
foo[data][123];`;
});
it('should happily suck up and relay the errors', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, void 0);
assert.include(msg.error, typeErrorMsg);
assert.include(msg.error, "Cannot read property '123' of undefined");
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {
data: 'foo',
},
});
});
});
describe('requiring libraries in context', function() {
describe('from array', function() {
beforeEach(function() {
this.code = `if(vm == undefined){
throw('vm is undefined');
}
null`;
});
it('should require and pass library to the context under variriable with module name', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, null);
assert.equal(msg.error, null);
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {
data: 'foo',
},
libraries: ['vm'],
});
});
});
describe('from object for specifiyng context variable name', function() {
beforeEach(function() {
this.code = `if(vmFooBar == undefined){
throw('vmFooBar is undefined');
}
null`;
});
it('should require and pass library to the context under variable with key name', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '123');
assert.equal(msg.result, null);
assert.equal(msg.error, null);
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '123',
context: {
data: 'foo',
},
libraries: {
vmFooBar: 'vm',
},
});
});
});
describe('from unintentional other type', function() {
beforeEach(function() {
this.code = `var a = 'result';
a`;
});
it('should raise and exception telling that it expects array or object', function(done) {
this.runner.on('message', function(msg) {
assert.equal(msg.id, '1234');
assert.equal(msg.result, void 0);
assert.equal(
msg.error,
'Pitboss error: Libraries must be defined by an array or by an object.'
);
done();
});
this.runner.send({
code: this.code,
});
this.runner.send({
id: '1234',
context: {
data: 'foo',
},
libraries: 'vm',
});
});
});
});
});