diff --git a/_sandbox_runner.js b/_sandbox_runner.js index 6781185..2679dc8 100644 --- a/_sandbox_runner.js +++ b/_sandbox_runner.js @@ -26,7 +26,8 @@ try { loaded_module.runInNewContext({ - console: console + console: console, + setTimeout: setTimeout }); } catch (ex) { process.send({ diff --git a/sandboxer.js b/sandboxer.js index a1f03e3..9dd4acc 100644 --- a/sandboxer.js +++ b/sandboxer.js @@ -37,8 +37,9 @@ * This is to prevent the downloaded source from getting into infinite loops and such. * XXX (not impl) */ - Sandbox.prototype.runSandboxed = function(code) { - var proc = cp.fork(__dirname + '/_sandbox_runner.js'); + Sandbox.prototype.runSandboxed = function(code, timeout) { + var proc = cp.fork(__dirname + '/_sandbox_runner.js'), + timer; // Connect to be notified of messages // Messages will be sanitised prior to usage. @@ -52,18 +53,27 @@ } }); - // XXX Setup a timeout so that long running code can be killed. - // Detect when the child has finished proc.addListener('exit', (function(statusCode) { - this.emit('finish', statusCode === 1, proc.errors); + this.emit('finish', proc.timedOut || (statusCode > 0), proc.errors); }).bind(this)); // The setup is finsihed so pipe in the source code to start things off. this.emit('start'); - return proc.send({ - "runCode": code - }); + proc.send( {runCode: code }); + + // set a timer to remind us to kill this proccess if it runs to long + if (timeout) { + timer = setTimeout(function() { + proc.timedOut = true; + proc.errors = proc.errors || []; + proc.errors.push('Timeout exceeded.'); + + proc.kill(); + }, timeout); + } + + return proc; }; diff --git a/tests/sandbox_spec.js b/tests/sandbox_spec.js index 07c9116..f4a6601 100644 --- a/tests/sandbox_spec.js +++ b/tests/sandbox_spec.js @@ -1,23 +1,22 @@ sandboxer = require(__dirname + '/../sandboxer.js'); - describe('Sandbox', function () { - var box, end_status; + var box, end_status, proc; - // { TEST Helpers + // { Helpers function waitForSub() { waitsFor(function() { return end_status.err !== undefined; }, 'sub script did not finish', // error message if timeout exceeds - 200 // milliseconds to wait + 100 // milliseconds to wait ); }; - function runCode(codeStr) { + function runCode(codeStr, timeout) { // run the code async - runs(function() { box.runSandboxed(codeStr); }); + runs(function() { proc = box.runSandboxed(codeStr, timeout); }); // wait for it to finish waitForSub(); }; @@ -31,7 +30,7 @@ describe('Sandbox', function () { } }); }; - // END TEST Helpers + // END Helpers beforeEach(function() { box = new sandboxer.Sandbox(); @@ -48,7 +47,7 @@ describe('Sandbox', function () { }); // TESTS - it("valid code", function () { + it("works with valid code", function () { runCode('var a = 1;'); assertRanWithError(false); }); @@ -59,5 +58,11 @@ describe('Sandbox', function () { assertRanWithError(true, "SyntaxError: Unexpected string"); }); + it("fails if the timeout is passed", function() { + // timeout set to happen after 0.1 seconds but the script will run for a full second. + runCode("setTimeout(function(){var a = 1;}, 1000);", 10); + assertRanWithError(true, "Timeout exceeded."); + }); + });