From 5b502bf62bcb49a12b18691acc66ec7474e15ae3 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 02:31:12 +0100 Subject: [PATCH 01/44] added broadway --- package.json | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index d49ce51..106c33b 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,28 @@ -{ "name" : "sandbox" -, "description": "A nifty javascript sandbox for node.js" -, "homepage" : "http://gf3.github.com/sandbox/" -, "author" : "Gianni Chiappetta (http://gf3.ca)" -, "contributors": - [ "Dominic Tarr (http://cyber-hobo.blogspot.com)" - ] -, "version" : "0.8.1" -, "main" : "./lib/sandbox" -, "directories" : { "lib" : "./lib" } -, "engines" : [ "node >=0.3.0-pre" ] -, "repository" : - { "type" : "git" - , "url" : "https://gf3@github.com/gf3/sandbox.git" - } -, "license" : - { "type" : "Public Domain" - , "url" : "http://github.com/gf3/sandbox/raw/master/UNLICENSE" +{ + "name": "sandbox", + "description": "A nifty javascript sandbox for node.js", + "homepage": "http://gf3.github.com/sandbox/", + "author": "Gianni Chiappetta (http://gf3.ca)", + "contributors": [ + "Dominic Tarr (http://cyber-hobo.blogspot.com)" + ], + "version": "0.8.1", + "main": "./lib/sandbox", + "directories": { + "lib": "./lib" + }, + "engines": [ + "node >=0.3.0-pre" + ], + "repository": { + "type": "git", + "url": "https://gf3@github.com/gf3/sandbox.git" + }, + "license": { + "type": "Public Domain", + "url": "http://github.com/gf3/sandbox/raw/master/UNLICENSE" + }, + "dependencies": { + "broadway": "~0.1.2" } } - From ef10cca9dd67f3fa8c9d04f3f3590ddaa463a489 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 02:31:59 +0100 Subject: [PATCH 02/44] used broadway for the child --- lib/shovel.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/shovel.js b/lib/shovel.js index 943f2ed..cf7cca0 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -3,6 +3,7 @@ /* ------------------------------ INIT ------------------------------ */ var util = require( 'util' ) + , broadway = require('broadway') , code , console , result @@ -15,16 +16,19 @@ if ( ! ( Script = process.binding( 'evals').NodeScript ) ) /* ------------------------------ Sandbox ------------------------------ */ // Sandbox methods -console = [] -sandbox = - { console: + +var options= JSON.parse(process.argv[3]) +options.sandbox = sandbox = {}; +var app = new broadway.App(); +options.forEach(function(o) {app.use(o.plugin, o.options)}) +/* { console: { log: function() { var i, l for ( i = 0, l = arguments.length; i < l; i++ ) console.push( util.inspect( arguments[i] ) ) } } } -sandbox.print = sandbox.console.log +sandbox.print = sandbox.console.log*/ // Get code code = '' From d6a827e1a353cd282e0dc45b82a0137017f95d43 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 02:33:03 +0100 Subject: [PATCH 03/44] added argv handling for passing plugins+configuration in a json --- lib/sandbox.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/sandbox.js b/lib/sandbox.js index 486dd52..bb87f58 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -6,6 +6,7 @@ var fs = require( 'fs' ) , path = require( 'path' ) , spawn = require( 'child_process' ).spawn +var Child = require('../../lib/intercom').EventChild /*------------------------- Sandbox -------------------------*/ function Sandbox( options ) { ( this.options = options || {} ).__proto__ = Sandbox.options @@ -14,7 +15,8 @@ function Sandbox( options ) { // Any vars in da house? var timer , stdout = '' - , child = spawn( this.options.node, [this.options.shovel] ) +// , child = spawn( this.options.node, [this.options.shovel, JSON.stringify(this.options)] ) + , child = Child( this.options.node, [this.options.shovel, JSON.stringify(this.options)] ) , output = function( data ) { if ( !!data ) stdout += data @@ -43,6 +45,7 @@ Sandbox.options = { timeout: 500 , node: 'node' , shovel: path.join( __dirname, 'shovel.js' ) + , plugins: [{file: path.join( __dirname, 'plugins/console.js' ), options:{}] } // Info From 4ac22f50d43c40481f87f0f4d0ab3737b7e203cf Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 02:35:07 +0100 Subject: [PATCH 04/44] moved console as a plugin --- lib/plugins/console.js | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 lib/plugins/console.js diff --git a/lib/plugins/console.js b/lib/plugins/console.js new file mode 100644 index 0000000..4604541 --- /dev/null +++ b/lib/plugins/console.js @@ -0,0 +1,20 @@ +var console = [] + +// `exports.attach` gets called by broadway on `app.use` +exports.attach = function (options) { + + options.sandbox.console = { log: function() { + var i, l + for ( i = 0, l = arguments.length; i < l; i++ ) + console.push( util.inspect( arguments[i] ) ) + } + } +}; + +// `exports.init` gets called by broadway on `app.init`. +exports.init = function (done) { + + // This plugin doesn't require any initialization step. + return done(); + +}; From 968a2413e3627eafe77ce8c62af032252b2e1539 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 16:18:31 +0100 Subject: [PATCH 05/44] added intercom --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 106c33b..78a59c2 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "url": "http://github.com/gf3/sandbox/raw/master/UNLICENSE" }, "dependencies": { - "broadway": "~0.1.2" + "broadway": "~0.1.2", + "intercom": "~0.2.4" } } From 20361e94fd808d0663065a69dfeb69b2e9541c2b Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 19:47:50 +0100 Subject: [PATCH 06/44] commented some parts of the sample for testing --- example/example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/example/example.js b/example/example.js index 090c1dd..99a40f2 100644 --- a/example/example.js +++ b/example/example.js @@ -5,7 +5,7 @@ var Sandbox = require("../lib/sandbox") s.run( "1 + 1", function( output ) { console.log( "Example 1: " + output.result + "\n" ) }) - +/* // Example 2 - Something slightly more complex s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( output ) { console.log( "Example 2: " + output.result + "\n" ) @@ -24,5 +24,5 @@ s.run( "process.platform", function( output ) { // Example 5 - Infinite loop s.run( "while (true) {}", function( output ) { console.log( "Example 5: " + output.result + "\n" ) -}) +})*/ From b597f42467a8820a461c64e0fd74a2b7607ad261 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 19:56:03 +0100 Subject: [PATCH 07/44] not stable yet, but at least starts and logs --- lib/plugins/console.js | 21 +++++++++---- lib/sandbox.js | 48 +++++++++++++++++++++--------- lib/shovel.js | 67 ++++++++++++++++++++---------------------- 3 files changed, 81 insertions(+), 55 deletions(-) diff --git a/lib/plugins/console.js b/lib/plugins/console.js index 4604541..f9ef0fd 100644 --- a/lib/plugins/console.js +++ b/lib/plugins/console.js @@ -1,18 +1,27 @@ var console = [] +export.name = console; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (options) { - - options.sandbox.console = { log: function() { - var i, l - for ( i = 0, l = arguments.length; i < l; i++ ) - console.push( util.inspect( arguments[i] ) ) - } +console.log("attach console") + if(this.child) {//I'm in the parent ("sandbox") + + this.child.on('child::console', function(level, args) { + args.unshift('Sandbox> '); + console[level].apply(args); + }) + + } else if(process.parent) {//I'm in the child ("shovel") + + function log(level) { + return process.parent.emit('child::console') + } } }; // `exports.init` gets called by broadway on `app.init`. exports.init = function (done) { +console.log("init console") // This plugin doesn't require any initialization step. return done(); diff --git a/lib/sandbox.js b/lib/sandbox.js index bb87f58..f5abf5b 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -4,48 +4,68 @@ /*------------------------- INIT -------------------------*/ var fs = require( 'fs' ) , path = require( 'path' ) - , spawn = require( 'child_process' ).spawn + , broadway = require('broadway') +// , spawn = require( 'child_process' ).spawn + +var Child = require('intercom').EventChild -var Child = require('../../lib/intercom').EventChild /*------------------------- Sandbox -------------------------*/ function Sandbox( options ) { ( this.options = options || {} ).__proto__ = Sandbox.options + var app = new broadway.App(); + this.run = function( code, hollaback ) { // Any vars in da house? var timer , stdout = '' // , child = spawn( this.options.node, [this.options.shovel, JSON.stringify(this.options)] ) - , child = Child( this.options.node, [this.options.shovel, JSON.stringify(this.options)] ) + , child = app.child = Child( this.options.shovel, this.options ) , output = function( data ) { if ( !!data ) stdout += data } // Listen - child.stdout.on( 'data', output ) + //child.on( 'stdout', console.log/*output*/ ) child.on( 'exit', function( code ) { clearTimeout( timer ) hollaback.call( this, JSON.parse( stdout ) ) }) - + child.on('stdout', function(txt) { + console.log('child stdout: ' + txt); + }); + + child.on('stderr', function(txt) { + console.log('child stderr: ' + txt); + }); + child.start() +// console.log('child =>',child) + child.ready(function(err){ + if(err) + throw err; + // Go - child.stdin.write( code ) - child.stdin.end() - timer = setTimeout( function() { - child.stdout.removeListener( 'output', output ) - stdout = JSON.stringify( { result: 'TimeoutError', console: [] } ) - child.kill( 'SIGKILL' ) - }, this.options.timeout ) +// child.child.stdin.write( code ) +// child.child.stdin.end() +console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}]) + child.emit("sandbox::start", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}], code) + + timer = setTimeout( function() { +// child.stdout.removeListener( 'output', output ) + stdout = JSON.stringify( { result: 'TimeoutError', console: [] } ) + child.kill( 'SIGKILL' ) + }, this.options.timeout ) + + }); } } // Options Sandbox.options = { timeout: 500 - , node: 'node' , shovel: path.join( __dirname, 'shovel.js' ) - , plugins: [{file: path.join( __dirname, 'plugins/console.js' ), options:{}] + , plugins: [{file: path.join( __dirname, 'plugins/console.js' ), options:{}}] } // Info diff --git a/lib/shovel.js b/lib/shovel.js index cf7cca0..6860aae 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -4,6 +4,7 @@ /* ------------------------------ INIT ------------------------------ */ var util = require( 'util' ) , broadway = require('broadway') + , path = require( 'path' ) , code , console , result @@ -17,41 +18,37 @@ if ( ! ( Script = process.binding( 'evals').NodeScript ) ) /* ------------------------------ Sandbox ------------------------------ */ // Sandbox methods -var options= JSON.parse(process.argv[3]) -options.sandbox = sandbox = {}; -var app = new broadway.App(); -options.forEach(function(o) {app.use(o.plugin, o.options)}) -/* { console: - { log: function() { var i, l - for ( i = 0, l = arguments.length; i < l; i++ ) - console.push( util.inspect( arguments[i] ) ) - } - } - } -sandbox.print = sandbox.console.log*/ +var console={} +console.log = function (d) { + var args = Array.prototype.map.call(arguments, function(arg){return util.inspect(arg)}) + process.stdout.write(args.join(' ') + '\n'); +}; +//console.log ("child process:" , process) +process.parent.on("sandbox::start", start) -// Get code -code = '' -stdin = process.openStdin() -stdin.on( 'data', function( data ) { - code += data -}) -stdin.on( 'end', run ) +function start(options) { + options= options || []; +// var options= [{file: path.join( __dirname, 'plugins/console.js' ), options:{}}]//JSON.parse(process.argv[3]) +console.log("child options:", options) + var app = new broadway.App(); + app.sandbox = sandbox = {}; -// Run code -function run() { - result = (function() { - try { - return Script.runInNewContext( this.toString(), sandbox ) - } - catch (e) { - return e.name + ': ' + e.message - } - }).call( code ) - - process.stdout.on( 'drain', function() { - process.exit(0) - }) - process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) ) -} + options.forEach(function(o) {app.use(o.plugin, o.options)}) + // Run code + function run() { + result = (function() { + try { + return Script.runInNewContext( this.toString(), sandbox ) + } + catch (e) { + return e.name + ': ' + e.message + } + }).call( code ) + + process.stdout.on( 'drain', function() { + process.exit(0) + }) + process.stdout.write( JSON.stringify( { result: util.inspect( result ) } ) ) + } +} From e229933b1c5292c1288c4afb7987c832c7340a55 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 7 Dec 2011 20:06:14 +0100 Subject: [PATCH 08/44] improved logs --- lib/sandbox.js | 10 ++++++++-- lib/shovel.js | 7 ++++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/sandbox.js b/lib/sandbox.js index f5abf5b..cfc4747 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -33,12 +33,18 @@ function Sandbox( options ) { hollaback.call( this, JSON.parse( stdout ) ) }) child.on('stdout', function(txt) { - console.log('child stdout: ' + txt); + console.log('child stdout> ', ""+txt); }); child.on('stderr', function(txt) { - console.log('child stderr: ' + txt); + console.log('child stderr> ', ""+txt); }); + + child.on("sandbox::parent", function(result) { + console.log('Sandbox result:', result) + hollaback(result) + }) + child.start() // console.log('child =>',child) child.ready(function(err){ diff --git a/lib/shovel.js b/lib/shovel.js index 6860aae..8438ce4 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -28,8 +28,8 @@ process.parent.on("sandbox::start", start) function start(options) { options= options || []; -// var options= [{file: path.join( __dirname, 'plugins/console.js' ), options:{}}]//JSON.parse(process.argv[3]) -console.log("child options:", options) +console.log("options:", options) + var app = new broadway.App(); app.sandbox = sandbox = {}; @@ -45,10 +45,11 @@ console.log("child options:", options) return e.name + ': ' + e.message } }).call( code ) + process.parent.emit("sandbox::result", result); process.stdout.on( 'drain', function() { process.exit(0) }) - process.stdout.write( JSON.stringify( { result: util.inspect( result ) } ) ) +// process.stdout.write( JSON.stringify( { result: util.inspect( result ) } ) ) } } From 9c11a38d072412b34598bffd2f4a235edea9b65e Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 8 Dec 2011 02:03:34 +0100 Subject: [PATCH 09/44] added some colors --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 78a59c2..aed5860 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ }, "dependencies": { "broadway": "~0.1.2", - "intercom": "~0.2.4" + "intercom": "~0.2.4", + "colors": "~0.5.1" } } From c8c493a16a7e9dd60752118a4e9d16c2957d3d52 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 8 Dec 2011 02:04:12 +0100 Subject: [PATCH 10/44] works! --- lib/plugins/console.js | 12 +++++++---- lib/sandbox.js | 48 +++++++++++++++++++++--------------------- lib/shovel.js | 32 ++++++++++++++++------------ 3 files changed, 50 insertions(+), 42 deletions(-) diff --git a/lib/plugins/console.js b/lib/plugins/console.js index f9ef0fd..1bad283 100644 --- a/lib/plugins/console.js +++ b/lib/plugins/console.js @@ -1,6 +1,4 @@ -var console = [] - -export.name = console; +exports.name = console; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (options) { console.log("attach console") @@ -14,8 +12,14 @@ console.log("attach console") } else if(process.parent) {//I'm in the child ("shovel") function log(level) { - return process.parent.emit('child::console') + return function() {process.parent.emit('child::console', level, arguments)} } + + this.sandbox.console = { + log: log('log'), + debug: log('debug'), + error: log('error')} + } }; diff --git a/lib/sandbox.js b/lib/sandbox.js index cfc4747..91cad56 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -2,10 +2,10 @@ // Gianni Chiappetta - gf3.ca - 2010 /*------------------------- INIT -------------------------*/ +require('colors') var fs = require( 'fs' ) , path = require( 'path' ) , broadway = require('broadway') -// , spawn = require( 'child_process' ).spawn var Child = require('intercom').EventChild @@ -14,56 +14,56 @@ function Sandbox( options ) { ( this.options = options || {} ).__proto__ = Sandbox.options var app = new broadway.App(); + (this.options.plugins || []).forEach(function(o) {app.use(require(o.plugin), o.options)}) + console.log('sandbox plugins loaded : '.bold, app) this.run = function( code, hollaback ) { // Any vars in da house? - var timer - , stdout = '' -// , child = spawn( this.options.node, [this.options.shovel, JSON.stringify(this.options)] ) - , child = app.child = Child( this.options.shovel, this.options ) - , output = function( data ) { - if ( !!data ) - stdout += data - } + var child = app.child = Child( this.options.shovel, this.options ) // Listen - //child.on( 'stdout', console.log/*output*/ ) child.on( 'exit', function( code ) { clearTimeout( timer ) - hollaback.call( this, JSON.parse( stdout ) ) }) + child.on('stdout', function(txt) { - console.log('child stdout> ', ""+txt); + console.log('child stdout> '.bold.blue, ""+txt); }); child.on('stderr', function(txt) { - console.log('child stderr> ', ""+txt); + console.log('child stderr> '.bold.yellow, ""+txt); }); - child.on("sandbox::parent", function(result) { - console.log('Sandbox result:', result) - hollaback(result) + child.on("sandbox::error", function(err) { + console.log('Sandbox error:'.bold.red, err) + child.emit("sandbox::exit") + hollaback(err) }) - child.start() -// console.log('child =>',child) - child.ready(function(err){ + child.on("sandbox::return", function(result) { + console.log('Sandbox result:'.bold.green, result) + child.emit("sandbox::exit") + hollaback(null, result) + }) + + child.ready(function(err) { if(err) throw err; // Go -// child.child.stdin.write( code ) -// child.child.stdin.end() -console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}]) +//console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}]) child.emit("sandbox::start", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}], code) timer = setTimeout( function() { -// child.stdout.removeListener( 'output', output ) + stdout = JSON.stringify( { result: 'TimeoutError', console: [] } ) child.kill( 'SIGKILL' ) }, this.options.timeout ) }); + + child.start() + } } @@ -71,7 +71,7 @@ console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), o Sandbox.options = { timeout: 500 , shovel: path.join( __dirname, 'shovel.js' ) - , plugins: [{file: path.join( __dirname, 'plugins/console.js' ), options:{}}] + , plugins: [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}] } // Info diff --git a/lib/shovel.js b/lib/shovel.js index 8438ce4..0354346 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -5,7 +5,6 @@ var util = require( 'util' ) , broadway = require('broadway') , path = require( 'path' ) - , code , console , result , sandbox @@ -19,37 +18,42 @@ if ( ! ( Script = process.binding( 'evals').NodeScript ) ) // Sandbox methods var console={} -console.log = function (d) { +console.debug = console.log = console.error = function (d) { var args = Array.prototype.map.call(arguments, function(arg){return util.inspect(arg)}) process.stdout.write(args.join(' ') + '\n'); }; -//console.log ("child process:" , process) + process.parent.on("sandbox::start", start) +process.parent.on("child::exit", function(result) { + process.exit(0) +}) -function start(options) { - options= options || []; +function start(options, code) { + options = options || []; console.log("options:", options) - var app = new broadway.App(); - app.sandbox = sandbox = {}; - - options.forEach(function(o) {app.use(o.plugin, o.options)}) + var app = new broadway.App() + app.sandbox = sandbox = {} + options.forEach(function(o) {app.use(require(o.plugin), o.options)}) +console.log('shovel plugins loaded : ', app) // Run code + run(); + function run() { result = (function() { try { return Script.runInNewContext( this.toString(), sandbox ) } catch (e) { - return e.name + ': ' + e.message + process.parent.emit("sandbox::error", e) + return undefined } }).call( code ) - process.parent.emit("sandbox::result", result); - process.stdout.on( 'drain', function() { - process.exit(0) - }) + process.parent.emit("sandbox::return", result); + + // process.stdout.write( JSON.stringify( { result: util.inspect( result ) } ) ) } } From 2286497843f36ab2289d482ee0af51c52d52475c Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 8 Dec 2011 10:29:01 +0100 Subject: [PATCH 11/44] changed result api, so changed example accordingly --- example/example.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/example/example.js b/example/example.js index 99a40f2..b352dc7 100644 --- a/example/example.js +++ b/example/example.js @@ -2,27 +2,31 @@ var Sandbox = require("../lib/sandbox") , s = new Sandbox() // Example 1 - Standard JS -s.run( "1 + 1", function( output ) { - console.log( "Example 1: " + output.result + "\n" ) +s.run( "1 + 1", function( err, result ) { + console.log( "Example 1: " + result + "\n" ) }) -/* + // Example 2 - Something slightly more complex -s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( output ) { - console.log( "Example 2: " + output.result + "\n" ) +s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { + console.log( "Example 2: " + result + "\n" ) }) // Example 3 - Syntax error -s.run( "lol)hai", function( output ) { - console.log( "Example 3: " + output.result + "\n" ) +s.run( "lol)hai", function( err, result ) { + console.log( "Example 3: " + result + "\n" ) }); // Example 4 - Restricted code -s.run( "process.platform", function( output ) { - console.log( "Example 4: " + output.result + "\n" ) +s.run( "process.platform", function( err, result ) { + console.log( "Example 4: " + result + "\n" ) +}) + +s.run( "console.log('hello, world')", function( err, result ) { + console.log( "Example 4: " + result + "\n" ) }) // Example 5 - Infinite loop -s.run( "while (true) {}", function( output ) { - console.log( "Example 5: " + output.result + "\n" ) -})*/ +s.run( "while (true) {}", function( err, result ) { + console.log( "Example 5: " + result + "\n" ) +}) From b46c7479f76d5c13c706c98ef1e6b5053f04ddda Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 8 Dec 2011 11:36:06 +0100 Subject: [PATCH 12/44] removed timeout from core, moved it as a plugin --- lib/plugins/timeout.js | 31 +++++++++++++++++++++++++++++++ lib/sandbox.js | 8 -------- lib/shovel.js | 6 +++--- 3 files changed, 34 insertions(+), 11 deletions(-) create mode 100644 lib/plugins/timeout.js diff --git a/lib/plugins/timeout.js b/lib/plugins/timeout.js new file mode 100644 index 0000000..a23c0fb --- /dev/null +++ b/lib/plugins/timeout.js @@ -0,0 +1,31 @@ +// `exports.attach` gets called by broadway on `app.use` +exports.attach = function (options) { + options = options || {} + options.timeout = options.timeout || 500 + + if(this.child) {//I'm in the parent ("sandbox") + + var timer = setTimeout( function() { + this.child.localEmit("sandbox::error", + Error("ETIMEOUT - Sandbox has not finished within its " + options.timeout + "ms")) + + this.child.kill( 'SIGKILL' ) + }, options.timeout ) + + this.child.on( 'exit', function( code ) { + clearTimeout( timer ) + }) + + + } else if(process.parent) {//I'm in the child ("shovel") + //nothing to be done + } +}; + +// `exports.init` gets called by broadway on `app.init`. +exports.init = function (done) { + + // This plugin doesn't require any initialization step. + return done(); + +}; diff --git a/lib/sandbox.js b/lib/sandbox.js index 91cad56..a0af083 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -22,10 +22,6 @@ function Sandbox( options ) { var child = app.child = Child( this.options.shovel, this.options ) // Listen - child.on( 'exit', function( code ) { - clearTimeout( timer ) - }) - child.on('stdout', function(txt) { console.log('child stdout> '.bold.blue, ""+txt); }); @@ -54,11 +50,7 @@ function Sandbox( options ) { //console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}]) child.emit("sandbox::start", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}], code) - timer = setTimeout( function() { - stdout = JSON.stringify( { result: 'TimeoutError', console: [] } ) - child.kill( 'SIGKILL' ) - }, this.options.timeout ) }); diff --git a/lib/shovel.js b/lib/shovel.js index 0354346..27438e7 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -18,7 +18,7 @@ if ( ! ( Script = process.binding( 'evals').NodeScript ) ) // Sandbox methods var console={} -console.debug = console.log = console.error = function (d) { +console.debug = console.log = console.error = function () { var args = Array.prototype.map.call(arguments, function(arg){return util.inspect(arg)}) process.stdout.write(args.join(' ') + '\n'); }; @@ -30,13 +30,13 @@ process.parent.on("child::exit", function(result) { function start(options, code) { options = options || []; -console.log("options:", options) +//console.log("options:", options) var app = new broadway.App() app.sandbox = sandbox = {} options.forEach(function(o) {app.use(require(o.plugin), o.options)}) -console.log('shovel plugins loaded : ', app) +console.log('shovel plugins loaded : ', app.sandbox) // Run code run(); From cc6f4e8655bf6f0e047c0ab3aa46736ad158d309 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 12 Jan 2012 03:21:51 +0100 Subject: [PATCH 13/44] added futures --- package.json | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index aed5860..31e463c 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,10 @@ }, "dependencies": { "broadway": "~0.1.2", - "intercom": "~0.2.4", - "colors": "~0.5.1" + "intercom": "~0.3", + "colors": "~0.5.1", + "stackedy": "~0.1.6", + "futures": "~2.3.1" } } + From 61f8a2f821d6bd0d03c463baf905c5622afaee7c Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Thu, 12 Jan 2012 03:23:45 +0100 Subject: [PATCH 14/44] fixed a bunch of issues, added name to sandbox, tried to fix infinite loop vs timeout problem, improved event interface --- example/example.js | 65 +++++++++++++++++++----- lib/plugins/console.js | 57 +++++++++++---------- lib/plugins/timeout.js | 57 +++++++++++++-------- lib/sandbox.js | 76 +++++++++++++++++---------- lib/shovel.js | 113 ++++++++++++++++++++++++++++++++--------- 5 files changed, 257 insertions(+), 111 deletions(-) diff --git a/example/example.js b/example/example.js index b352dc7..c3eb6f1 100644 --- a/example/example.js +++ b/example/example.js @@ -1,32 +1,71 @@ var Sandbox = require("../lib/sandbox") - , s = new Sandbox() - + , s = new Sandbox({name: "Example 1"}) +/**/ // Example 1 - Standard JS s.run( "1 + 1", function( err, result ) { - console.log( "Example 1: " + result + "\n" ) + if(err) return console.log( (s.name +" error:").bold.red, err ) + console.log( s.name.bold.green, result ) }) +s = new Sandbox({name: "Example 2"}) // Example 2 - Something slightly more complex s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { - console.log( "Example 2: " + result + "\n" ) + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) }) -// Example 3 - Syntax error +s = new Sandbox({name: "Example 3"}) +// Example 3 - Plugin based api +s.run( "console.log('hello, world')", function( err, result ) { + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) +}) + +s = new Sandbox({name: "Example 4"}) +// Example 4 - Syntax error s.run( "lol)hai", function( err, result ) { - console.log( "Example 3: " + result + "\n" ) + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) }); -// Example 4 - Restricted code +s = new Sandbox({name: "Example 5"}) +// Example 5 - Restricted code s.run( "process.platform", function( err, result ) { - console.log( "Example 4: " + result + "\n" ) + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) }) -s.run( "console.log('hello, world')", function( err, result ) { - console.log( "Example 4: " + result + "\n" ) +s = new Sandbox({name: "Example 6"}) +// Example 6 - Something more complex +s.run( +"function example(name) { throw Error('this is a dummy error '+ name || 'you') }\ + (function toto() {example('Florian')})()", + function( err, result ) { + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) }) -// Example 5 - Infinite loop -s.run( "while (true) {}", function( err, result ) { - console.log( "Example 5: " + result + "\n" ) + +s = new Sandbox({name: "Example 7"}) +// Example 7 - Long loop +s.run( "for( var i=0; i<10000000; i++) {if(!(i%100000)) console.log('-',i/100000,'-')}", function( err, result ) { + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) +}) + + +s = new Sandbox({name: "Example 8"}) +// Example 8 - Using interval +s.run( "setInterval(function(){console.log('-hello-')}, 50)", function( err, result ) { + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) +}) +/* +s = new Sandbox({name: "Example 9"}) +// Example 9 - Infinite loop +s.run( "while (true) {console.log('...')}", function( err, result ) { + if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) + console.log( s.name.bold.green, result ) }) +*/ diff --git a/lib/plugins/console.js b/lib/plugins/console.js index 1bad283..c6a5d77 100644 --- a/lib/plugins/console.js +++ b/lib/plugins/console.js @@ -1,33 +1,36 @@ -exports.name = console; +require('colors') + +exports.name = 'console'; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (options) { -console.log("attach console") - if(this.child) {//I'm in the parent ("sandbox") - - this.child.on('child::console', function(level, args) { - args.unshift('Sandbox> '); - console[level].apply(args); - }) + // `exports.init` gets called by broadway on `app.init`. + exports.init = function (done) { + var self= this; - } else if(process.parent) {//I'm in the child ("shovel") - - function log(level) { - return function() {process.parent.emit('child::console', level, arguments)} - } - - this.sandbox.console = { - log: log('log'), - debug: log('debug'), - error: log('error')} + if(this.IAmParent) {//I'm in the parent ("sandbox") + this.child.on('child::console', function(level, args) { + args = args || []; + args.unshift((self.options.name + '> ').bold); + console[level].apply(console, args); + }) + + } else if(this.IAmChild) {//I'm in the child ("shovel") + function log(level) { + return function() { + var args = Array.prototype.slice.call(arguments); //make it a real array + process.parent.emit('child::console', level, args) + } + } + this.sandbox.console = { + log: log('log'), + debug: log('debug'), + error: log('error') + } + + } + // This plugin doesn't require any initialization step. + return done() } -}; - -// `exports.init` gets called by broadway on `app.init`. -exports.init = function (done) { -console.log("init console") - - // This plugin doesn't require any initialization step. - return done(); +} -}; diff --git a/lib/plugins/timeout.js b/lib/plugins/timeout.js index a23c0fb..112d7dd 100644 --- a/lib/plugins/timeout.js +++ b/lib/plugins/timeout.js @@ -1,31 +1,46 @@ // `exports.attach` gets called by broadway on `app.use` +require('colors'); +exports.name = 'timeout' exports.attach = function (options) { options = options || {} - options.timeout = options.timeout || 500 + options.timeout = options.timeout || 50 - if(this.child) {//I'm in the parent ("sandbox") + // `exports.init` gets called by broadway on `app.init`. + // this is + exports.init = function (done) { + var self = this; - var timer = setTimeout( function() { - this.child.localEmit("sandbox::error", - Error("ETIMEOUT - Sandbox has not finished within its " + options.timeout + "ms")) + if(this.IAmParent) {//I'm in the parent ("sandbox") + var returned = false; + + self.child.on("sandbox::run", function(){ + var start = Date.now() + + setTimeout(function onTimeout() { + if(returned) + return; + console.log('---------STTTOOOOOOOOOOPPPPP----------'.bold.red) + self.child.emit('exit') + var err = Error("ETIMEOUT - Execution is taking too much time") + err.refinedStack= err.message + '\n' +'' + self.emit("sandbox::return", err) + }, options.timeout) - this.child.kill( 'SIGKILL' ) - }, options.timeout ) + self.child.on("sandbox::return", function() { + returned = true; + console.log((self.name +' execution took').bold.blue, Date.now() - start, 'ms') + }) + + }); + } else if(this.IAmChild) {//I'm in the child ("shovel") + //nothing to be done + self.on("runner::run", function(runner){ + runner.return.setTimeout(options.timeout) + }) - this.child.on( 'exit', function( code ) { - clearTimeout( timer ) - }) - - - } else if(process.parent) {//I'm in the child ("shovel") - //nothing to be done + } + // This plugin doesn't require any initialization step. + return done(); } }; -// `exports.init` gets called by broadway on `app.init`. -exports.init = function (done) { - - // This plugin doesn't require any initialization step. - return done(); - -}; diff --git a/lib/sandbox.js b/lib/sandbox.js index a0af083..b0e8df9 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -11,59 +11,83 @@ var Child = require('intercom').EventChild /*------------------------- Sandbox -------------------------*/ function Sandbox( options ) { - ( this.options = options || {} ).__proto__ = Sandbox.options + this.options = (options || {}); + var self = this; + //this.options.__proto__ = Sandbox.options; + + Object.keys(Sandbox.options).forEach(function(key){ + self.options[key] = self.options[key] || Sandbox.options[key]; + }) + + var app = new broadway.App(); - (this.options.plugins || []).forEach(function(o) {app.use(require(o.plugin), o.options)}) - console.log('sandbox plugins loaded : '.bold, app) + app.IAmParent = true; + + app.console = console; + app.options = this.options; + this.name = app.name = this.options.name; - this.run = function( code, hollaback ) { + app.options.plugins.forEach(function(plugin) { + var toUse = plugin.plugin || require(plugin.path) + if(toUse) + app.use(toUse, plugin.options || {}) + }) + + + this.run = function run ( code, hollaback ) { // Any vars in da house? var child = app.child = Child( this.options.shovel, this.options ) + app.IAmParent = true; + app.init() + //console.log('sandbox plugins loaded :\n'.bold, Object.keys(app.plugins)) // Listen child.on('stdout', function(txt) { - console.log('child stdout> '.bold.blue, ""+txt); - }); + console.log((self.options.name + ' stdout> ').bold.blue, ""+txt) + }) child.on('stderr', function(txt) { - console.log('child stderr> '.bold.yellow, ""+txt); - }); + console.log((self.options.name + ' stderr> ').bold.yellow, ""+txt) + }) - child.on("sandbox::error", function(err) { - console.log('Sandbox error:'.bold.red, err) - child.emit("sandbox::exit") + /*child.on("sandbox::error", function(err) { + //console.log('Sandbox error:'.bold.red, err.refinedStack ) + child.emit("shovel::exit") hollaback(err) - }) + })*/ - child.on("sandbox::return", function(result) { - console.log('Sandbox result:'.bold.green, result) - child.emit("sandbox::exit") - hollaback(null, result) + child.on("sandbox::return", function(err, result) { + //console.log('Sandbox result:'.bold.green, result) +// console.log("child", child) + child.emit("shovel::exit") + hollaback(err, result) + child.stop() }) + child.ready(function(err) { if(err) throw err; - - // Go -//console.log("options", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}]) - child.emit("sandbox::start", [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}], code) - - - - }); + // Go + child.emit("shovel::start", self.options, code) + }) child.start() + this.stop = child.stop.bind(child) } } // Options Sandbox.options = - { timeout: 500 + { name: 'Sandbox' +// , timeout: 50 , shovel: path.join( __dirname, 'shovel.js' ) - , plugins: [{plugin: path.join( __dirname, 'plugins/console.js' ), options:{}}] + , plugins: [ + {path: path.join( __dirname, 'plugins/console.js' ), options:{}}, + {path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} + ] } // Info diff --git a/lib/shovel.js b/lib/shovel.js index 27438e7..16c214a 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -2,9 +2,12 @@ // Gianni Chiappetta - gf3.ca - 2010 /* ------------------------------ INIT ------------------------------ */ +require('colors') var util = require( 'util' ) , broadway = require('broadway') , path = require( 'path' ) + , stackedy = require('stackedy') + , Futures = require('futures') , console , result , sandbox @@ -19,41 +22,103 @@ if ( ! ( Script = process.binding( 'evals').NodeScript ) ) var console={} console.debug = console.log = console.error = function () { - var args = Array.prototype.map.call(arguments, function(arg){return util.inspect(arg)}) - process.stdout.write(args.join(' ') + '\n'); + var args = Array.prototype.map.call(arguments, function(arg){ + return typeof arg === "string" ? + arg : + (typeof arg === "undefined"? + "undefined" : + util.inspect(arg)) + }) + process.stdout.write(args.join(' ') + '\n') }; -process.parent.on("sandbox::start", start) -process.parent.on("child::exit", function(result) { - process.exit(0) +process.parent.on("shovel::start", start) +process.parent.on("shovel::exit", function(result) { + process.nextTick(function() { + process.exit(0) + }); }) function start(options, code) { - options = options || []; -//console.log("options:", options) + options = options || {name:'Sandbox', plugins: []}; +// console.log("options:", options) var app = new broadway.App() app.sandbox = sandbox = {} + app.IAmChild = true + app.console = console + app.options = options + options.plugins.forEach(function(plugin) { + var toUse = plugin.plugin || require(plugin.path) + if(toUse) + app.use(toUse, plugin.options || {}) + }) + app.init() + +// console.log('shovel plugins loaded :\n'.bold, Object.keys(app.plugins)) +// console.log('used sandbox : ', app.sandbox) + - options.forEach(function(o) {app.use(require(o.plugin), o.options)}) -console.log('shovel plugins loaded : ', app.sandbox) - // Run code - run(); + //app.onAny(function(){console.log('-> '.bold, this.event.bold, arguments)}) + //process.parent.onAny(function(){console.log('-> '.bold.yellow, this.event.bold.yellow, arguments)}) - function run() { - result = (function() { - try { - return Script.runInNewContext( this.toString(), sandbox ) - } - catch (e) { - process.parent.emit("sandbox::error", e) - return undefined - } - }).call( code ) + process.parent.ready(function() { + process.parent.on("shovel::*", function relaySandboxEvent(){ + if(this.eventSource === process.parent) + return; + this.eventSource = process.parent + app.emit.apply(app, [this.event].concat(Array.prototype.slice.call(arguments))) + delete this.eventSource; + }) + app.on("sandbox::*", function relayRunnerEvent(){ + if(this.eventSource === process.parent) + return; + this.eventSource = app + process.parent.emit.apply(process.parent, [this.event].concat(Array.prototype.slice.call(arguments))) + delete this.eventSource; + }) - process.parent.emit("sandbox::return", result); + // Run code + app.runner = stackedy(code, { filename : '' }).run(sandbox, {global: sandbox, stoppable: true}) + app.runner.return = Futures.future(app) + app.emit("sandbox::run") + app.emit("runner::run", app.runner) + + app.runner.on('error', function onError (err, c) { + app.emit("runner::error", app.runner, err, c) + app.runner.return.deliver(refineStack(err, c)) + }).on('result', function onResult (result) { + app.emit("runner::result", app.runner, result) + app.runner.return.deliver(null, result) + }) + app.runner.return.when(function onReturn(err, result) { + app.emit("sandbox::return", err, result) + app.runner.stop() + }) -// process.stdout.write( JSON.stringify( { result: util.inspect( result ) } ) ) - } + }) + + return this } + +function refineStack(err, c) { + var cur = c.current || {filename:'', start:{}}, + message = err.message, + stack = message + + '\n in ' + (cur.filename || '') + + (cur.functionName ? ' at ' + cur.functionName +'()' : '') + + ((cur && cur.start && cur.start.line !== undefined && cur.start.col !== undefined)? + ' at ' + cur.start.line + ':' + cur.start.col : ''); + + c.stack.forEach(function (cur) { + cur = cur || {filename:'', start:{}}; + stack +='\n in ' + cur.filename + '.' + + (cur.functionName ? cur.functionName +'()' : '') + + ((cur && cur.start && cur.start.line !== undefined && cur.start.col !== undefined )? + ' at ' + cur.start.line + ':' + cur.start.col : '') + }) + err.refinedStack = stack; + return err; +} + From f72dcce48b5cd242d0f0451d27fc34d0309f276c Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 18 Jan 2012 11:25:46 +0100 Subject: [PATCH 15/44] now asynchronous code works, but doesn't stop yet --- example/example.js | 81 +++++++++++++++++++++++------------------- lib/plugins/timeout.js | 26 +++++++++----- lib/sandbox.js | 23 ++++++++---- lib/shovel.js | 66 +++++++++++++++++++++++++--------- 4 files changed, 128 insertions(+), 68 deletions(-) diff --git a/example/example.js b/example/example.js index c3eb6f1..729deb0 100644 --- a/example/example.js +++ b/example/example.js @@ -1,71 +1,80 @@ var Sandbox = require("../lib/sandbox") - , s = new Sandbox({name: "Example 1"}) -/**/ + , s; + // Example 1 - Standard JS +s = new Sandbox({name: "Example 1"}) s.run( "1 + 1", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err ) - console.log( s.name.bold.green, result ) -}) + if(err) return console.log( (this.name +" error:").bold.red, err ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 2"}) // Example 2 - Something slightly more complex s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 3"}) // Example 3 - Plugin based api s.run( "console.log('hello, world')", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 4"}) // Example 4 - Syntax error s.run( "lol)hai", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}); + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 5"}) // Example 5 - Restricted code s.run( "process.platform", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 6"}) // Example 6 - Something more complex s.run( "function example(name) { throw Error('this is a dummy error '+ name || 'you') }\ - (function toto() {example('Florian')})()", - function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) - + (function toto() {example('Florian')})()", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 7"}) // Example 7 - Long loop s.run( "for( var i=0; i<10000000; i++) {if(!(i%100000)) console.log('-',i/100000,'-')}", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) - + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) s = new Sandbox({name: "Example 8"}) // Example 8 - Using interval -s.run( "setInterval(function(){console.log('-hello-')}, 50)", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) -/* +s.run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) + s = new Sandbox({name: "Example 9"}) // Example 9 - Infinite loop s.run( "while (true) {console.log('...')}", function( err, result ) { - if(err) return console.log( (s.name +" error:").bold.red, err.refinedStack ) - console.log( s.name.bold.green, result ) -}) -*/ + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) + +s = new Sandbox({name: "Example 10"}) +s.run( "exports.times=0; while (true) {exports.times++}", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) + +s = new Sandbox({name: "Example 11"}) +s.run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) diff --git a/lib/plugins/timeout.js b/lib/plugins/timeout.js index 112d7dd..c33737c 100644 --- a/lib/plugins/timeout.js +++ b/lib/plugins/timeout.js @@ -3,8 +3,8 @@ require('colors'); exports.name = 'timeout' exports.attach = function (options) { options = options || {} - options.timeout = options.timeout || 50 - + options.timeout = options.timeout || 250 +// console.log('timeout :', options.timeout, 'ms') // `exports.init` gets called by broadway on `app.init`. // this is exports.init = function (done) { @@ -19,16 +19,26 @@ exports.attach = function (options) { setTimeout(function onTimeout() { if(returned) return; - console.log('---------STTTOOOOOOOOOOPPPPP----------'.bold.red) - self.child.emit('exit') - var err = Error("ETIMEOUT - Execution is taking too much time") - err.refinedStack= err.message + '\n' +'' - self.emit("sandbox::return", err) +// console.log('---------STTTOOOOOOOOOOPPPPP----------'.bold.red) + var err = Error("ETIMEDOUT - Your code is taking too much time") + err.refinedStack = err.message ; + err.code = require('constants').ETIMEDOUT + + self.emit("shovel::kill") + self.child.localEmit("sandbox::return", err) + setTimeout(function() { + self.emit("sandbox::kill", 'SIGKILL') + },50) + +// process.nextTick(function(){self.child.emit('exit')}) }, options.timeout) self.child.on("sandbox::return", function() { returned = true; - console.log((self.name +' execution took').bold.blue, Date.now() - start, 'ms') + var time = Date.now() - start; + self.emit("sandbox::time", time) + self.child.emit("sandbox::timeout", time) + console.log((self.name +' execution took').bold.cyan, time, 'ms') }) }); diff --git a/lib/sandbox.js b/lib/sandbox.js index b0e8df9..a0bba66 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -1,6 +1,6 @@ // sandbox.js - Rudimentary JS sandbox // Gianni Chiappetta - gf3.ca - 2010 - +process.title = 'sandbox' /*------------------------- INIT -------------------------*/ require('colors') var fs = require( 'fs' ) @@ -38,6 +38,7 @@ function Sandbox( options ) { this.run = function run ( code, hollaback ) { // Any vars in da house? var child = app.child = Child( this.options.shovel, this.options ) +// console.log('child', child) app.IAmParent = true; app.init() //console.log('sandbox plugins loaded :\n'.bold, Object.keys(app.plugins)) @@ -58,14 +59,13 @@ function Sandbox( options ) { })*/ child.on("sandbox::return", function(err, result) { - //console.log('Sandbox result:'.bold.green, result) + console.log('Sandbox result:'.bold.green, result) // console.log("child", child) - child.emit("shovel::exit") +// child.emit("shovel::exit") hollaback(err, result) - child.stop() + //child.stop() }) - child.ready(function(err) { if(err) throw err; @@ -73,7 +73,14 @@ function Sandbox( options ) { child.emit("shovel::start", self.options, code) }) + app.on("sandbox::kill", function(signal) { + signal = signal || 'sigterm' + if(child && child.child && child.child.kill) + child.child.kill() + }) + child.start() +// console.log('child'.bold.yellow, child) this.stop = child.stop.bind(child) } @@ -85,8 +92,10 @@ Sandbox.options = // , timeout: 50 , shovel: path.join( __dirname, 'shovel.js' ) , plugins: [ - {path: path.join( __dirname, 'plugins/console.js' ), options:{}}, - {path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} + {path: path.join( __dirname, 'plugins/console.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/cpulimit.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/module.js' ), options:{}} ] } diff --git a/lib/shovel.js b/lib/shovel.js index 16c214a..c1ccca6 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -1,6 +1,6 @@ // shovel.js - Do the heavy lifting in this sandbox // Gianni Chiappetta - gf3.ca - 2010 - +process.title = 'shovel' /* ------------------------------ INIT ------------------------------ */ require('colors') var util = require( 'util' ) @@ -33,18 +33,19 @@ console.debug = console.log = console.error = function () { }; process.parent.on("shovel::start", start) -process.parent.on("shovel::exit", function(result) { - process.nextTick(function() { - process.exit(0) - }); -}) function start(options, code) { options = options || {name:'Sandbox', plugins: []}; // console.log("options:", options) var app = new broadway.App() - app.sandbox = sandbox = {} + app.sandbox = options.sandbox = {} /*= sandbox = { + t:setTimeout, + setTimeout: setTimeout, + setInterval: setInterval, + clearTimeout: clearTimeout, + clearInterval: clearInterval}*/ + app.IAmChild = true app.console = console app.options = options @@ -54,7 +55,7 @@ function start(options, code) { app.use(toUse, plugin.options || {}) }) app.init() - + // console.log('shovel plugins loaded :\n'.bold, Object.keys(app.plugins)) // console.log('used sandbox : ', app.sandbox) @@ -79,24 +80,55 @@ function start(options, code) { }) // Run code - app.runner = stackedy(code, { filename : '' }).run(sandbox, {global: sandbox, stoppable: true}) + var stack = stackedy(code, { filename : '' }) + app.extracode = [] + app.emit("shovel::code::extra", function pushExtraCode(filename, code, opts) { + if(typeof filename != 'string') + throw Error('Invalid extra code filename (bad plugin?) : <'+filename+'> is not a String') + if(typeof filename != 'code') + throw Error('Invalid extra code loaded (bad plugin?) : <'+code+'> is not a String') + opts = opts || {} + opts.filename = filename + stack.include(code, opts) + }) + app.runner = stack.run(app.sandbox) + + app.runner.on('stop', function() { + console.log('runner has stopped') + app.emit("runner::stopped", app.sandbox) + }) app.runner.return = Futures.future(app) - app.emit("sandbox::run") + app.emit("sandbox::run", app.runner.source) + app.emit("runner::run", app.runner) - + + app.runner.on('error', function onError (err, c) { - app.emit("runner::error", app.runner, err, c) - app.runner.return.deliver(refineStack(err, c)) - }).on('result', function onResult (result) { - app.emit("runner::result", app.runner, result) - app.runner.return.deliver(null, result) + app.emit("runner::error", app.runner, err, c) + app.runner.return.deliver(refineStack(err, c)) + }).on('result', function onResult (result) { + app.emit("runner::result", app.runner, result) + app.sandbox.exports; + app.runner.return.deliver(null, app.sandbox.exports) }) app.runner.return.when(function onReturn(err, result) { + result = result || app.sandbox.exports; app.emit("sandbox::return", err, result) - app.runner.stop() + //app.runner.stop() }) + + app.on("shovel::exit", function() { + app.emit("runner::kill") + process.nextTick(function() { + process.exit(0) + }); + }) + + app.on("runner::kill", function() { + app.runner.stop() + }) }) return this From 8cb48e0bd6d2ae155d8f069ef5194990073e06f1 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 18 Jan 2012 12:01:07 +0100 Subject: [PATCH 16/44] added cpulimit/ module system --- lib/plugins/cpulimit.js | 57 +++++++++++++++++++++++++++++++++++++++++ lib/plugins/module.js | 39 ++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 lib/plugins/cpulimit.js create mode 100644 lib/plugins/module.js diff --git a/lib/plugins/cpulimit.js b/lib/plugins/cpulimit.js new file mode 100644 index 0000000..85f6874 --- /dev/null +++ b/lib/plugins/cpulimit.js @@ -0,0 +1,57 @@ +var posix = require('posix') + , proc = require('getrusage') + +require('colors') + +exports.name = 'cpulimit'; +// `exports.attach` gets called by broadway on `app.use` +exports.attach = function (options) { + var defaults = {soft: 2, hard: 2}; + options = options || defaults + Object.keys(defaults).forEach(function(key){ + options[key] = options[key] || defaults[key]; + }) + + if(! isFinite(options.soft) || ! isFinite(options.hard)) + throw Error("Both hard and soft CPU limits should be set, and they should be finite Numbers") + + // `exports.init` gets called by broadway on `app.init`. + exports.init = function (done) { + var self = this; + + if(this.IAmParent) {//I'm in the parent ("sandbox") + +// self.child.on('exit', function() {console.log(self.name.cyan, 'child exit', arguments)}) + } else if(this.IAmChild) {//I'm in the child ("shovel") + var start = proc.getcputime() +// console.log('current cpu usage :'.bold.yellow, start); + + function onKill() { + console.log('final cpu usage :'.bold.red, proc.getcputime()) + var err = Error("ECANCELED - You code is eating too much CPU !") + err.refinedStack = err.message + err.code = require('constants').ETIMEDOUT + + self.emit("sandbox::result", err) + throw err; +/* setTimeout(function() { + process.exit(1); + },20)*/ + } + + this.on('sandbox::return') + process.on('SIGXCPU', onKill) + process.on('SIGKILL', onKill) + process.on('SIGSTOP', onKill) + process.on('SIGILL', onKill) + process.on('SIGFPE', onKill) + process.on('SIGSEGV', onKill) + + posix.setrlimit('cpu', options) + //FIXME error reporting ? + } + // This plugin doesn't require any initialization step. + return done() + } +} + diff --git a/lib/plugins/module.js b/lib/plugins/module.js new file mode 100644 index 0000000..a8ca687 --- /dev/null +++ b/lib/plugins/module.js @@ -0,0 +1,39 @@ +require('colors'); + + +var options = {}; + + +exports.init = function (done) { + + + return done(); +} + + +exports.name = 'commonjs'; +// `exports.attach` gets called by broadway on `app.use` +exports.attach = function (_options) { + options = _options || {} + options.whitelist = options.whitelist || ['underscore', 'async'] + // `exports.init` gets called by broadway on `app.init`. + exports.init = function (done) { + var self= this; + + if(this.IAmParent) {//I'm in the parent ("sandbox") + + + } else if(this.IAmChild) {//I'm in the child ("shovel") + this.sandbox.exports = {} + this.sandbox.module = {} + this.sandbox.require = function sandboxRequire(module) { + if(!!~ options.whitelist.indexOf(module)) // only whitelisted modules + return require(module) + throw Error ("Invalid module <"+module+">, valid modules are : "+ options.whitelist.join()) + } + } + // This plugin doesn't require any initialization step. + return done() + } +} + From 55ca5d6e9f2e0af5ee18ebe7d0d08fb9a462667e Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Wed, 18 Jan 2012 12:19:03 +0100 Subject: [PATCH 17/44] added cpulimit/ module system --- lib/shovel.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/shovel.js b/lib/shovel.js index c1ccca6..418b0d4 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -85,7 +85,7 @@ function start(options, code) { app.emit("shovel::code::extra", function pushExtraCode(filename, code, opts) { if(typeof filename != 'string') throw Error('Invalid extra code filename (bad plugin?) : <'+filename+'> is not a String') - if(typeof filename != 'code') + if(typeof code != 'string') throw Error('Invalid extra code loaded (bad plugin?) : <'+code+'> is not a String') opts = opts || {} opts.filename = filename @@ -109,12 +109,11 @@ function start(options, code) { }).on('result', function onResult (result) { app.emit("runner::result", app.runner, result) app.sandbox.exports; - app.runner.return.deliver(null, app.sandbox.exports) + app.runner.return.deliver(null, result, app.sandbox.exports) }) - app.runner.return.when(function onReturn(err, result) { - result = result || app.sandbox.exports; - app.emit("sandbox::return", err, result) + app.runner.return.when(function onReturn(err, result, exports) { + app.emit("sandbox::return", err, result, exports) //app.runner.stop() }) @@ -146,7 +145,7 @@ function refineStack(err, c) { c.stack.forEach(function (cur) { cur = cur || {filename:'', start:{}}; stack +='\n in ' + cur.filename + '.' + - (cur.functionName ? cur.functionName +'()' : '') + + (cur.functionName ? cur.functionName +'()' : '()') + ((cur && cur.start && cur.start.line !== undefined && cur.start.col !== undefined )? ' at ' + cur.start.line + ':' + cur.start.col : '') }) From 5524dca3d789401afc43cf8c380187063c8f52f5 Mon Sep 17 00:00:00 2001 From: Marsup Date: Fri, 20 Jan 2012 18:32:22 +0100 Subject: [PATCH 18/44] Unfinished request sandbox implementation --- example/example.js | 11 +++++++ lib/plugins/module.js | 14 +++++---- lib/plugins/request.js | 67 ++++++++++++++++++++++++++++++++++++++++++ lib/sandbox.js | 1 + package.json | 7 +++-- 5 files changed, 92 insertions(+), 8 deletions(-) create mode 100644 lib/plugins/request.js diff --git a/example/example.js b/example/example.js index 729deb0..d2a28e4 100644 --- a/example/example.js +++ b/example/example.js @@ -78,3 +78,14 @@ s.run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]) console.log( this.name.bold.green, result ) }.bind(s)) +s = new Sandbox({name: "Example 12"}) +s.run( "var request = require('request'); request('http://www.google.fr', function(err, response, body) {\ + console.log(response.statusCode);\ + });\ + request('http://www.google.com', function(err, response, body) {\ + console.log(response.statusCode);\ + });", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}.bind(s)) + diff --git a/lib/plugins/module.js b/lib/plugins/module.js index a8ca687..b066ad4 100644 --- a/lib/plugins/module.js +++ b/lib/plugins/module.js @@ -1,24 +1,23 @@ require('colors'); - var options = {}; - exports.init = function (done) { - - return done(); } - exports.name = 'commonjs'; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (_options) { options = _options || {} options.whitelist = options.whitelist || ['underscore', 'async'] + options.customModules = {}; // `exports.init` gets called by broadway on `app.init`. exports.init = function (done) { - var self= this; + var self = this; + this.on('shovel::module::*', function(module) { + options.customModules[this.event.slice(16)] = module; + }); if(this.IAmParent) {//I'm in the parent ("sandbox") @@ -27,6 +26,9 @@ exports.attach = function (_options) { this.sandbox.exports = {} this.sandbox.module = {} this.sandbox.require = function sandboxRequire(module) { + if(options.customModules[module]) { + return options.customModules[module]; + } if(!!~ options.whitelist.indexOf(module)) // only whitelisted modules return require(module) throw Error ("Invalid module <"+module+">, valid modules are : "+ options.whitelist.join()) diff --git a/lib/plugins/request.js b/lib/plugins/request.js new file mode 100644 index 0000000..db7c9a4 --- /dev/null +++ b/lib/plugins/request.js @@ -0,0 +1,67 @@ +var request = require('request'), + dns = require('dns'), + url = require('url'), + net = require('net'); + +var pluginOpts = { + blacklist: [] +}; + +var dnsCheck = function dnsCheck(uri, callback) { + if(typeof uri !== 'string') { + return callback(new Error('URI is not a string')); + } + + uri = url.parse(uri); + if(!uri.host) { + return callback(new Error('URI does not contain a host')); + } + + if(net.isIP(uri.host)) { + + } else { + dns.resolve(uri.host); + } +}; + +var lastRequestTime; +var requestProxy = function requestProxy(options, callback) { + var requestTime = new Date(); + if(typeof callback !== 'function') { + callback = function() {}; + } + + // Limit the pace of requests per second + if(!lastRequestTime) { + lastRequestTime = new Date(); + } else { + if(requestTime-lastRequestTime < 1000) { + return setTimeout(requestProxy.bind(this, options, callback), 1000-(requestTime-lastRequestTime)); + } else { + lastRequestTime = requestTime; + } + } + + request(options, function(err, res, body) { + if(err) { + return callback(err); + } + + if(body) { + return callback(null, { + statusCode: res.statusCode, + body: body + }, body); + } + }); +}; + +exports.name = 'request'; + +exports.attach = function(opts) { + pluginOpts = opts; + exports.init = function(done) { + var module = requestProxy; + this.emit('shovel::module::request', module); + }; +}; \ No newline at end of file diff --git a/lib/sandbox.js b/lib/sandbox.js index a0bba66..a2b3f9c 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -96,6 +96,7 @@ Sandbox.options = ,{path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} ,{path: path.join( __dirname, 'plugins/cpulimit.js' ), options:{}} ,{path: path.join( __dirname, 'plugins/module.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/request.js' ), options:{}} ] } diff --git a/package.json b/package.json index 31e463c..3df58ba 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,10 @@ "intercom": "~0.3", "colors": "~0.5.1", "stackedy": "~0.1.6", - "futures": "~2.3.1" + "futures": "~2.3.1", + "posix": "~0.0.6", + "request": "~2.9.3", + "async": "~0.1.15", + "underscore": "~1.3.0" } } - From dfb58fdcb12041787b750ac1a76e0a95709acbbb Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Mon, 23 Jan 2012 16:02:36 +0100 Subject: [PATCH 19/44] updated dependencies for using ** in eventemitter modules like broadway and intercom, added posix for having setrlimit --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 3df58ba..e86b1d8 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,12 @@ "url": "http://github.com/gf3/sandbox/raw/master/UNLICENSE" }, "dependencies": { - "broadway": "~0.1.2", + "broadway": "~0.1.8", "intercom": "~0.3", "colors": "~0.5.1", "stackedy": "~0.1.6", "futures": "~2.3.1", + "eventemitter2": "~0.4.3", "posix": "~0.0.6", "request": "~2.9.3", "async": "~0.1.15", From 02134b5dac3e2afe3159394fc4e2af92b8506f04 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Mon, 23 Jan 2012 16:08:31 +0100 Subject: [PATCH 20/44] improved event api between sansdbox<->shovel, with event relaying, so now we just have to work with 'app' most of the time to handle events. Also improved Sandbox object usage so now you can do things like 'Sandbox('my name').run(code, mycallback) : Sandbox is a factory too if not an object, like standard JavaScript Objects do --- lib/sandbox.js | 83 ++++++++++++++++++++++----------- lib/shovel.js | 123 ++++++++++++++++++++++++++++++++----------------- 2 files changed, 138 insertions(+), 68 deletions(-) diff --git a/lib/sandbox.js b/lib/sandbox.js index a2b3f9c..72e50bd 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -11,16 +11,17 @@ var Child = require('intercom').EventChild /*------------------------- Sandbox -------------------------*/ function Sandbox( options ) { + if(! (this instanceof Sandbox)) + return (new Sandbox(options)) + this.options = (options || {}); + if(typeof this.options === "string") + this.options = {name: this.options} var self = this; - //this.options.__proto__ = Sandbox.options; - Object.keys(Sandbox.options).forEach(function(key){ self.options[key] = self.options[key] || Sandbox.options[key]; }) - - var app = new broadway.App(); app.IAmParent = true; @@ -35,20 +36,58 @@ function Sandbox( options ) { }) - this.run = function run ( code, hollaback ) { + this.run = app.run = function run ( code, hollaback ) { // Any vars in da house? + hollaback = hollaback.bind(this); + var child = app.child = Child( this.options.shovel, this.options ) -// console.log('child', child) app.IAmParent = true; app.init() + +// app.onAny(function(){console.log('-> '.bold.green, this.event.bold)}) +// child.onAny(function(){console.log('-> '.bold.cyan, (this.event||'???').bold.yellow)}) + + // ---- child <-> app event relay ---- + + + child.ready(function(err) { + if(err) + throw err; + + app.on("shovel::**", function relaySandboxEvent(){ +//console.log('sandbox ------', this.event, '----> shovel') + if(this.eventSource === app) + return; + this.eventSource = app + child.emit.apply(child, [this.event].concat(Array.prototype.slice.call(arguments))) + delete this.eventSource; + }) + + child.on("sandbox::**", function relayShovelEventToSandbox(){ +//console.log('shovel ------', this.event, '----> sandbox') + if(this.eventSource === child) + return; + this.eventSource = child + app.emit.apply(app, [this.event].concat(Array.prototype.slice.call(arguments))) + delete this.eventSource; + }) + // Go + child.emit("shovel::start", app.options, code) + + }) + + // ----------------------------------- + //console.log('sandbox plugins loaded :\n'.bold, Object.keys(app.plugins)) // Listen child.on('stdout', function(txt) { + app.emit("sandbox::shovel::stdout", txt) console.log((self.options.name + ' stdout> ').bold.blue, ""+txt) }) child.on('stderr', function(txt) { + app.emit("sandbox::shovel::stderr", txt) console.log((self.options.name + ' stderr> ').bold.yellow, ""+txt) }) @@ -58,31 +97,28 @@ function Sandbox( options ) { hollaback(err) })*/ - child.on("sandbox::return", function(err, result) { - console.log('Sandbox result:'.bold.green, result) -// console.log("child", child) -// child.emit("shovel::exit") - hollaback(err, result) - //child.stop() + app.on("shovel::return", function(err, result) { + app.emit("sandbox::return", err, result) }) - child.ready(function(err) { - if(err) - throw err; - // Go - child.emit("shovel::start", self.options, code) - }) + app.on("sandbox::return", function(err, result) { hollaback(err, result) }); - app.on("sandbox::kill", function(signal) { + + app.on("shovel::kill", function(signal) { signal = signal || 'sigterm' if(child && child.child && child.child.kill) child.child.kill() }) + app.on("sandbox::kill", function(signal) { + app.emit("shovel::kill", signal) + }) + child.start() // console.log('child'.bold.yellow, child) - this.stop = child.stop.bind(child) + app.stop = child.stop.bind(child) + return app; } } @@ -100,13 +136,6 @@ Sandbox.options = ] } -// Info -fs.readFile( path.join( __dirname, '..', 'package.json' ), function( err, data ) { - if ( err ) - throw err - else - Sandbox.info = JSON.parse( data ) -}) /*------------------------- Export -------------------------*/ module.exports = Sandbox diff --git a/lib/shovel.js b/lib/shovel.js index 418b0d4..147ad00 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -35,52 +35,56 @@ console.debug = console.log = console.error = function () { process.parent.on("shovel::start", start) function start(options, code) { +//console.log('start'.bold.red) options = options || {name:'Sandbox', plugins: []}; // console.log("options:", options) var app = new broadway.App() - app.sandbox = options.sandbox = {} /*= sandbox = { - t:setTimeout, - setTimeout: setTimeout, - setInterval: setInterval, - clearTimeout: clearTimeout, - clearInterval: clearInterval}*/ + app.sandbox = options.sandbox = {} app.IAmChild = true - app.console = console + app.parent = process.parent +// app.console = console app.options = options options.plugins.forEach(function(plugin) { var toUse = plugin.plugin || require(plugin.path) if(toUse) app.use(toUse, plugin.options || {}) }) - app.init() // console.log('shovel plugins loaded :\n'.bold, Object.keys(app.plugins)) -// console.log('used sandbox : ', app.sandbox) - +// console.log('used sandbox : ', app.sandbox) - //app.onAny(function(){console.log('-> '.bold, this.event.bold, arguments)}) - //process.parent.onAny(function(){console.log('-> '.bold.yellow, this.event.bold.yellow, arguments)}) +// app.onAny(function(){console.log('-> '.bold, this.event.bold)}) +// process.parent.onAny(function(){console.log('-> '.bold.yellow, this.event.bold.yellow)}) - process.parent.ready(function() { - process.parent.on("shovel::*", function relaySandboxEvent(){ - if(this.eventSource === process.parent) + app.init() +console.log('app init'.bold.red) + app.parent.ready(function() { + // ---- parent <-> app event relay ---- + app.parent.on("shovel::**", function relaySandboxEvent(){ +//console.log('sandbox ------', this.event, '----> shovel') + if(this.eventSource === app.parent) return; - this.eventSource = process.parent + this.eventSource = app.parent app.emit.apply(app, [this.event].concat(Array.prototype.slice.call(arguments))) delete this.eventSource; }) - app.on("sandbox::*", function relayRunnerEvent(){ - if(this.eventSource === process.parent) + app.on("sandbox::**", function relayShovelEventToSandbox(){ + if(this.eventSource === app.parent) return; +//console.log('shovel ------', this.event, '----> sandbox') this.eventSource = app - process.parent.emit.apply(process.parent, [this.event].concat(Array.prototype.slice.call(arguments))) + app.parent.emit.apply(app.parent, [this.event].concat(Array.prototype.slice.call(arguments))) delete this.eventSource; }) - // Run code + // ----------------------------------- + + // Load user main code var stack = stackedy(code, { filename : '' }) + + // Load extra code app.extracode = [] app.emit("shovel::code::extra", function pushExtraCode(filename, code, opts) { if(typeof filename != 'string') @@ -91,45 +95,77 @@ function start(options, code) { opts.filename = filename stack.include(code, opts) }) - app.runner = stack.run(app.sandbox) - app.runner.on('stop', function() { - console.log('runner has stopped') - app.emit("runner::stopped", app.sandbox) - }) + // Run code +console.log('app run'.bold.red) + app.runner = stack.run(app.sandbox, {stopppable:true}) +console.log('app runnning'.bold.red) +// app.runner app.runner.return = Futures.future(app) - app.emit("sandbox::run", app.runner.source) - app.emit("runner::run", app.runner) - + app.emit("shovel::runner::run", app.runner) + app.emit("shovel::run", app.runner.source) - app.runner.on('error', function onError (err, c) { - app.emit("runner::error", app.runner, err, c) + app.runner + .on('error', function onError (err, c) { + app.emit("shovel::runner::error", app.runner, err, c) app.runner.return.deliver(refineStack(err, c)) - }).on('result', function onResult (result) { - app.emit("runner::result", app.runner, result) + }) + .on('result', function onResult (result) { + app.emit("shovel::runner::result", app.runner, result) app.sandbox.exports; app.runner.return.deliver(null, result, app.sandbox.exports) - }) + }) + .on('stop', function onStop() { + console.log('runner has stopped') + console.log('result ?'.bold.cyan, app.runner.nodes) + app.emit("shovel::runner::stopped", app.sandbox) + }) app.runner.return.when(function onReturn(err, result, exports) { - app.emit("sandbox::return", err, result, exports) - //app.runner.stop() + console.log('app return'.bold.red) + app.emit("shovel::runner::return", err, result, exports) }) - app.on("shovel::exit", function() { - app.emit("runner::kill") - process.nextTick(function() { + app.on("shovel::exit", function onShovelExit() { + app.emit("shovel::runner::kill") + //TODO maybe act on return ? + /*process.nextTick(function() { process.exit(0) - }); + })*/ }) - app.on("runner::kill", function() { + app.on("shovel::runner::kill", function onRunnerKill() { app.runner.stop() }) - }) + app.on("shovel::runner::stopped", function onRunnerStopped() { + app.emit("shovel::stopped") + /*process.nextTick(function() { + process.exit(0) + })*/ + }) + }) + + /*function status () {console.log('not running'.bold.red, app.runner)} + console.log('running'.bold.green, app.runner) + process.nextTick(function() { + console.log('first tick'.bold.yellow, app.runner) + process.nextTick(function() { + console.log('second tick'.bold.yellow, app.runner) + }) + })*/ + setTimeout(function getStatus() { + if(!app.runner) { + console.log('---probably running---\n'.bold.green) + } + else { + console.log('---probably stopped---\n'.bold.yellow, app && app.runner) + + } + app.runner.stop() + }, 10) return this } @@ -153,3 +189,8 @@ function refineStack(err, c) { return err; } +function resultFilter (source) { + src = source.split('\n'); + src.pop() +} + From c31ebe6e4ff9015c5bf275fb315a12757def6638 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Mon, 23 Jan 2012 16:09:58 +0100 Subject: [PATCH 21/44] updated default plugins for using the new events --- lib/plugins/console.js | 27 ++++++++++++++++----------- lib/plugins/cpulimit.js | 8 ++++---- lib/plugins/timeout.js | 29 ++++++++++++++++------------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/lib/plugins/console.js b/lib/plugins/console.js index c6a5d77..f7523e6 100644 --- a/lib/plugins/console.js +++ b/lib/plugins/console.js @@ -3,26 +3,32 @@ require('colors') exports.name = 'console'; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (options) { - // `exports.init` gets called by broadway on `app.init`. - exports.init = function (done) { - var self= this; + +} + +// `exports.init` gets called by broadway on `app.init`. +exports.init = function (done) { + var app = this; - if(this.IAmParent) {//I'm in the parent ("sandbox") - this.child.on('child::console', function(level, args) { + if(app.IAmParent) {//I'm in the parent ("sandbox") + app.on('sandbox::console', function(level, args) { args = args || []; - args.unshift((self.options.name + '> ').bold); + args.unshift((app.options.name + '> ').bold); console[level].apply(console, args); }) - } else if(this.IAmChild) {//I'm in the child ("shovel") + } else if(app.IAmChild) {//I'm in the child ("shovel") function log(level) { - return function() { + var logger = function () { + var args = Array.prototype.slice.call(arguments); //make it a real array - process.parent.emit('child::console', level, args) + app.emit('sandbox::console', level, args) } + logger.name = level + return logger; } - this.sandbox.console = { + app.sandbox.console = { log: log('log'), debug: log('debug'), error: log('error') @@ -32,5 +38,4 @@ exports.attach = function (options) { // This plugin doesn't require any initialization step. return done() } -} diff --git a/lib/plugins/cpulimit.js b/lib/plugins/cpulimit.js index 85f6874..c0769b5 100644 --- a/lib/plugins/cpulimit.js +++ b/lib/plugins/cpulimit.js @@ -17,11 +17,11 @@ exports.attach = function (options) { // `exports.init` gets called by broadway on `app.init`. exports.init = function (done) { - var self = this; + var app = this; if(this.IAmParent) {//I'm in the parent ("sandbox") -// self.child.on('exit', function() {console.log(self.name.cyan, 'child exit', arguments)}) +// app.on('exit', function() {console.log(self.name.cyan, 'child exit', arguments)}) } else if(this.IAmChild) {//I'm in the child ("shovel") var start = proc.getcputime() // console.log('current cpu usage :'.bold.yellow, start); @@ -32,14 +32,14 @@ exports.attach = function (options) { err.refinedStack = err.message err.code = require('constants').ETIMEDOUT - self.emit("sandbox::result", err) + app.emit("sandbox::result", err) throw err; /* setTimeout(function() { process.exit(1); },20)*/ } - this.on('sandbox::return') +// this.on('sandbox::return') process.on('SIGXCPU', onKill) process.on('SIGKILL', onKill) process.on('SIGSTOP', onKill) diff --git a/lib/plugins/timeout.js b/lib/plugins/timeout.js index c33737c..3e549c3 100644 --- a/lib/plugins/timeout.js +++ b/lib/plugins/timeout.js @@ -8,12 +8,12 @@ exports.attach = function (options) { // `exports.init` gets called by broadway on `app.init`. // this is exports.init = function (done) { - var self = this; + var app = this; if(this.IAmParent) {//I'm in the parent ("sandbox") var returned = false; - self.child.on("sandbox::run", function(){ + app.on("sandbox::run", function(){ var start = Date.now() setTimeout(function onTimeout() { @@ -24,30 +24,33 @@ exports.attach = function (options) { err.refinedStack = err.message ; err.code = require('constants').ETIMEDOUT - self.emit("shovel::kill") - self.child.localEmit("sandbox::return", err) + app.emit("shovel::kill") + app.child.localEmit("sandbox::return", err) + setTimeout(function() { - self.emit("sandbox::kill", 'SIGKILL') - },50) + app.emit("shovel::kill", 'SIGKILL') + }, 50) -// process.nextTick(function(){self.child.emit('exit')}) +// process.nextTick(function(){app.child.emit('exit')}) }, options.timeout) - self.child.on("sandbox::return", function() { + app.on("shovel::runner::stopped", function() { returned = true; var time = Date.now() - start; - self.emit("sandbox::time", time) - self.child.emit("sandbox::timeout", time) - console.log((self.name +' execution took').bold.cyan, time, 'ms') +// app.emit("shovel::runner::time", time) + app.emit("shovel::runner::timeout", time) + console.log((app.name +' execution took').bold.cyan, time, 'ms') }) }); } else if(this.IAmChild) {//I'm in the child ("shovel") //nothing to be done - self.on("runner::run", function(runner){ + app.on("shovel::runner::run", function(runner){ runner.return.setTimeout(options.timeout) }) - +// function logStatus (){console.log("shovel runner->".bold, app.runner)} +// logStatus(); +// setInterval(logStatus, 50) } // This plugin doesn't require any initialization step. return done(); From df3bd915a26ccefa06be03a8f471d68b77307a9b Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Mon, 23 Jan 2012 16:46:09 +0100 Subject: [PATCH 22/44] updated examples --- example/example.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/example/example.js b/example/example.js index d2a28e4..058fec2 100644 --- a/example/example.js +++ b/example/example.js @@ -2,40 +2,39 @@ var Sandbox = require("../lib/sandbox") , s; // Example 1 - Standard JS -s = new Sandbox({name: "Example 1"}) -s.run( "1 + 1", function( err, result ) { +Sandbox("Example 1").run( " 2 + 3;", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err ) console.log( this.name.bold.green, result ) -}.bind(s)) - +}) +/**/ s = new Sandbox({name: "Example 2"}) // Example 2 - Something slightly more complex s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 3"}) // Example 3 - Plugin based api s.run( "console.log('hello, world')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 4"}) // Example 4 - Syntax error s.run( "lol)hai", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 5"}) // Example 5 - Restricted code s.run( "process.platform", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 6"}) // Example 6 - Something more complex s.run( @@ -44,39 +43,40 @@ s.run( if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 7"}) // Example 7 - Long loop s.run( "for( var i=0; i<10000000; i++) {if(!(i%100000)) console.log('-',i/100000,'-')}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 8"}) // Example 8 - Using interval s.run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**//* s = new Sandbox({name: "Example 9"}) // Example 9 - Infinite loop s.run( "while (true) {console.log('...')}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 10"}) s.run( "exports.times=0; while (true) {exports.times++}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) - +/**/ s = new Sandbox({name: "Example 11"}) s.run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }.bind(s)) +/**/ s = new Sandbox({name: "Example 12"}) s.run( "var request = require('request'); request('http://www.google.fr', function(err, response, body) {\ From 09d3a223d91469fd774097c4618892e501489077 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Sat, 28 Jan 2012 02:16:24 +0100 Subject: [PATCH 23/44] yeah, now this is a really working sandbox supporting both asynchronous and synchronous code --- lib/plugins/cpulimit.js | 37 +++++++++++---- lib/plugins/module.js | 24 ++++++---- lib/plugins/request.js | 36 +++++++++------ lib/plugins/timeout.js | 31 +++++++------ lib/sandbox.js | 74 +++++++++++++++++++---------- lib/shovel.js | 100 +++++++++++++++++++++++++++++----------- 6 files changed, 203 insertions(+), 99 deletions(-) diff --git a/lib/plugins/cpulimit.js b/lib/plugins/cpulimit.js index c0769b5..1431841 100644 --- a/lib/plugins/cpulimit.js +++ b/lib/plugins/cpulimit.js @@ -3,10 +3,12 @@ var posix = require('posix') require('colors') +var defaults = {soft: 10, hard: 11}; + exports.name = 'cpulimit'; // `exports.attach` gets called by broadway on `app.use` exports.attach = function (options) { - var defaults = {soft: 2, hard: 2}; + options = options || defaults Object.keys(defaults).forEach(function(key){ options[key] = options[key] || defaults[key]; @@ -20,34 +22,49 @@ exports.attach = function (options) { var app = this; if(this.IAmParent) {//I'm in the parent ("sandbox") - + // app.on('exit', function() {console.log(self.name.cyan, 'child exit', arguments)}) } else if(this.IAmChild) {//I'm in the child ("shovel") + var start = proc.getcputime() -// console.log('current cpu usage :'.bold.yellow, start); + +// console.log('cpu time at start:'.bold, start) + +// console.log('original cpu limit:'.bold, posix.getrlimit('cpu')) + posix.setrlimit('cpu', options) + +// console.log('new cpu limit:'.bold, posix.getrlimit('cpu')) +// console.log('current cpu time :'.bold.yellow, start); + + /*setInterval(function(){ + console.log('cpu time :'.bold.yellow, proc.getcputime()) + }, 100)*/ function onKill() { console.log('final cpu usage :'.bold.red, proc.getcputime()) - var err = Error("ECANCELED - You code is eating too much CPU !") + var err = Error("ECANCELED - Your code is eating too much CPU !") err.refinedStack = err.message err.code = require('constants').ETIMEDOUT - app.emit("sandbox::result", err) - throw err; -/* setTimeout(function() { + app.emit("sandbox::limit::cpu", proc.getcputime(), posix.getrlimit('cpu') ) + app.emit("shovel::exit") + app.emit("sandbox::return", err) + + //throw err; + process.nextTick(process.nextTick.bind(function() { process.exit(1); - },20)*/ + })) } // this.on('sandbox::return') process.on('SIGXCPU', onKill) - process.on('SIGKILL', onKill) +/* process.on('SIGKILL', onKill) process.on('SIGSTOP', onKill) process.on('SIGILL', onKill) process.on('SIGFPE', onKill) process.on('SIGSEGV', onKill) + process.on('SIGTERM', onKill)*/ - posix.setrlimit('cpu', options) //FIXME error reporting ? } // This plugin doesn't require any initialization step. diff --git a/lib/plugins/module.js b/lib/plugins/module.js index b066ad4..52804d1 100644 --- a/lib/plugins/module.js +++ b/lib/plugins/module.js @@ -15,24 +15,28 @@ exports.attach = function (_options) { // `exports.init` gets called by broadway on `app.init`. exports.init = function (done) { var self = this; - this.on('shovel::module::*', function(module) { - options.customModules[this.event.slice(16)] = module; - }); - - if(this.IAmParent) {//I'm in the parent ("sandbox") + if(this.IAmParent) {//I'm in the parent ("sandbox") } else if(this.IAmChild) {//I'm in the child ("shovel") + var name; + this.on('shovel::module::*', function(module) { + name = this.event.slice('shovel::module::'.length) + options.customModules[name] = module; + console.log('Added custom module <', name.bold, '> to sandbox require') + }); this.sandbox.exports = {} - this.sandbox.module = {} + this.sandbox.module = {exports: this.sandbox.exports } this.sandbox.require = function sandboxRequire(module) { - if(options.customModules[module]) { - return options.customModules[module]; - } + + if(options.customModules[module]) + return options.customModules[module] + if(!!~ options.whitelist.indexOf(module)) // only whitelisted modules return require(module) + throw Error ("Invalid module <"+module+">, valid modules are : "+ options.whitelist.join()) - } + } } // This plugin doesn't require any initialization step. return done() diff --git a/lib/plugins/request.js b/lib/plugins/request.js index db7c9a4..952782d 100644 --- a/lib/plugins/request.js +++ b/lib/plugins/request.js @@ -1,13 +1,13 @@ -var request = require('request'), - dns = require('dns'), +var dns = require('dns'), url = require('url'), net = require('net'); var pluginOpts = { - blacklist: [] + blacklist: [], + proxy: 'http://proxy:3128' }; - -var dnsCheck = function dnsCheck(uri, callback) { +/* +function dnsCheck(uri, callback) { if(typeof uri !== 'string') { return callback(new Error('URI is not a string')); } @@ -24,24 +24,28 @@ var dnsCheck = function dnsCheck(uri, callback) { } }; -var lastRequestTime; -var requestProxy = function requestProxy(options, callback) { +var lastRequestTime;*/ + +function requestProxy(options, callback) { var requestTime = new Date(); if(typeof callback !== 'function') { callback = function() {}; } + callback = this.runner.addCallback(callback); // Limit the pace of requests per second - if(!lastRequestTime) { + /*if(!lastRequestTime) { lastRequestTime = new Date(); } else { if(requestTime-lastRequestTime < 1000) { - return setTimeout(requestProxy.bind(this, options, callback), 1000-(requestTime-lastRequestTime)); + return setTimeout(requestProxy.bind(this, options, callback), 1000-(requestTime-lastRequestTime)) } else { lastRequestTime = requestTime; } - } + }*/ + var request = require('request').defaults(pluginOpts); + request(options, function(err, res, body) { if(err) { return callback(err); @@ -53,15 +57,17 @@ var requestProxy = function requestProxy(options, callback) { body: body }, body); } - }); -}; + }) +} exports.name = 'request'; exports.attach = function(opts) { pluginOpts = opts; + exports.init = function(done) { - var module = requestProxy; - this.emit('shovel::module::request', module); + var code = requestProxy.bind(this) + this.emit('shovel::module::request', code); }; -}; \ No newline at end of file +}; + diff --git a/lib/plugins/timeout.js b/lib/plugins/timeout.js index 3e549c3..dcd169d 100644 --- a/lib/plugins/timeout.js +++ b/lib/plugins/timeout.js @@ -3,7 +3,7 @@ require('colors'); exports.name = 'timeout' exports.attach = function (options) { options = options || {} - options.timeout = options.timeout || 250 + options.timeout = options.timeout || 5000 // console.log('timeout :', options.timeout, 'ms') // `exports.init` gets called by broadway on `app.init`. // this is @@ -13,41 +13,46 @@ exports.attach = function (options) { if(this.IAmParent) {//I'm in the parent ("sandbox") var returned = false; - app.on("sandbox::run", function(){ + app.on("sandbox::shovel::run", function(){ var start = Date.now() - setTimeout(function onTimeout() { + var to = setTimeout(function onTimeout() { if(returned) return; // console.log('---------STTTOOOOOOOOOOPPPPP----------'.bold.red) - var err = Error("ETIMEDOUT - Your code is taking too much time") + var err = Error("ETIMEDOUT - Your code is taking too much time : more than " + options.timeout + " ms") err.refinedStack = err.message ; err.code = require('constants').ETIMEDOUT + + app.emit("sandbox::timeout", options.timeout ) + app.emit("shovel::exit") + app.emit("sandbox::return", err) - app.emit("shovel::kill") - app.child.localEmit("sandbox::return", err) - - setTimeout(function() { + process.nextTick(function() { app.emit("shovel::kill", 'SIGKILL') - }, 50) + }) // process.nextTick(function(){app.child.emit('exit')}) }, options.timeout) - app.on("shovel::runner::stopped", function() { + app.on("sandbox::shovel::stopped", function() { returned = true; var time = Date.now() - start; // app.emit("shovel::runner::time", time) - app.emit("shovel::runner::timeout", time) + clearTimeout(to) + app.emit("sandbox::executiontime", time) console.log((app.name +' execution took').bold.cyan, time, 'ms') }) + app.on("sandbox::return", function(){ + clearTimeout(to) + }) }); } else if(this.IAmChild) {//I'm in the child ("shovel") //nothing to be done - app.on("shovel::runner::run", function(runner){ + /*app.on("shovel::runner::run", function(runner){ runner.return.setTimeout(options.timeout) - }) + })*/ // function logStatus (){console.log("shovel runner->".bold, app.runner)} // logStatus(); // setInterval(logStatus, 50) diff --git a/lib/sandbox.js b/lib/sandbox.js index 72e50bd..89b2b20 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -35,17 +35,16 @@ function Sandbox( options ) { app.use(toUse, plugin.options || {}) }) - this.run = app.run = function run ( code, hollaback ) { // Any vars in da house? - hollaback = hollaback.bind(this); + hollaback = hollaback.bind(app); - var child = app.child = Child( this.options.shovel, this.options ) + var child = app.child = Child( this.options.shovel, this.options.intercom ) app.IAmParent = true; app.init() // app.onAny(function(){console.log('-> '.bold.green, this.event.bold)}) -// child.onAny(function(){console.log('-> '.bold.cyan, (this.event||'???').bold.yellow)}) +// child.onAny(function(){console.log('-> '.bold.cyan, (this.event||'???').bold)}) // ---- child <-> app event relay ---- @@ -59,7 +58,9 @@ function Sandbox( options ) { if(this.eventSource === app) return; this.eventSource = app - child.emit.apply(child, [this.event].concat(Array.prototype.slice.call(arguments))) + try{child.emit.apply(child, [this.event].concat(Array.prototype.slice.call(arguments)))} catch(e) { + console.warn(this.name.yellow, 'Event'.yellow, this.event.bold.yellow, 'could not been send to shovel as it was shutdown'.yellow) + } delete this.eventSource; }) @@ -72,6 +73,7 @@ function Sandbox( options ) { delete this.eventSource; }) // Go + child.child._channel.on('error', function(err){child.emit('warn', err)}) child.emit("shovel::start", app.options, code) }) @@ -80,7 +82,7 @@ function Sandbox( options ) { //console.log('sandbox plugins loaded :\n'.bold, Object.keys(app.plugins)) - // Listen + // Listen for any output child.on('stdout', function(txt) { app.emit("sandbox::shovel::stdout", txt) console.log((self.options.name + ' stdout> ').bold.blue, ""+txt) @@ -90,34 +92,58 @@ function Sandbox( options ) { app.emit("sandbox::shovel::stderr", txt) console.log((self.options.name + ' stderr> ').bold.yellow, ""+txt) }) + + function onExit(){ + app.emit("sandbox::shovel::stopped") + var err = Error("Sandbox process was shutdown") + err.refinedStack = "Sandbox was shutdown, probably because you have reached a sandbox limit (memory, CPU) with your code" + app.emit("sandbox::return", err) + } - /*child.on("sandbox::error", function(err) { - //console.log('Sandbox error:'.bold.red, err.refinedStack ) - child.emit("shovel::exit") - hollaback(err) - })*/ + child.on('exit', onExit) - app.on("shovel::return", function(err, result) { - app.emit("sandbox::return", err, result) + app.on("sandbox::shovel::return", function(err, result, exports) { + app.emit("sandbox::return", err, result, exports) }) - app.on("sandbox::return", function(err, result) { hollaback(err, result) }); - + app.on("sandbox::return", function(err, result) { + child.off('exit', onExit) + hollaback(err, result) + if(err) + app.emit("sandbox::error", err) + else + app.emit("sandbox::result", result) + }) app.on("shovel::kill", function(signal) { + signal = signal || 'sigterm' - if(child && child.child && child.child.kill) - child.child.kill() + if(child && child.child && child.child.kill) { + console.log(this.name.red, 'kill :'.red, signal) + try {child.child.kill(signal)} catch (e) { } + } + }) + + app.on("sandbox::stop", function(signal) { + child.stop(); }) app.on("sandbox::kill", function(signal) { app.emit("shovel::kill", signal) }) - + process.setMaxListeners(100) + process.on('exit', function(){child.stop()}) child.start() // console.log('child'.bold.yellow, child) app.stop = child.stop.bind(child) + app.debugEvents = function debug() { + app.onAny(function() { + if(this.event !== "sandbox::shovel::stdout" && this.event !== "sandbox::console") + console.log(this.name, this.event.cyan, arguments) + }) + return app; + } return app; } } @@ -125,14 +151,14 @@ function Sandbox( options ) { // Options Sandbox.options = { name: 'Sandbox' -// , timeout: 50 , shovel: path.join( __dirname, 'shovel.js' ) + , intercom: {forever : false, max: 1} , plugins: [ - {path: path.join( __dirname, 'plugins/console.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/cpulimit.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/module.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/request.js' ), options:{}} + {path: path.join( __dirname, 'plugins/console.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/cpulimit.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/module.js' ), options:{}} + ,{path: path.join( __dirname, 'plugins/request.js' ), options:{}} ] } diff --git a/lib/shovel.js b/lib/shovel.js index 147ad00..6da4f26 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -1,6 +1,7 @@ // shovel.js - Do the heavy lifting in this sandbox // Gianni Chiappetta - gf3.ca - 2010 process.title = 'shovel' +//require('posix').setrlimit('cpu', {soft:1, hard:1}) /* ------------------------------ INIT ------------------------------ */ require('colors') var util = require( 'util' ) @@ -55,11 +56,11 @@ function start(options, code) { // console.log('shovel plugins loaded :\n'.bold, Object.keys(app.plugins)) // console.log('used sandbox : ', app.sandbox) -// app.onAny(function(){console.log('-> '.bold, this.event.bold)}) -// process.parent.onAny(function(){console.log('-> '.bold.yellow, this.event.bold.yellow)}) +// app.onAny(function(){console.log('-> '.green, this.event.bold)}) +// process.parent.onAny(function(){console.log('-> '.cyan, this.event.bold)}) app.init() -console.log('app init'.bold.red) +//console.log('app init'.bold.red) app.parent.ready(function() { // ---- parent <-> app event relay ---- app.parent.on("shovel::**", function relaySandboxEvent(){ @@ -97,14 +98,14 @@ console.log('app init'.bold.red) }) // Run code -console.log('app run'.bold.red) +//console.log('app run'.bold.red) app.runner = stack.run(app.sandbox, {stopppable:true}) -console.log('app runnning'.bold.red) +//console.log('app runnning'.bold.red) // app.runner app.runner.return = Futures.future(app) app.emit("shovel::runner::run", app.runner) - app.emit("shovel::run", app.runner.source) + app.emit("sandbox::shovel::run", app.runner.source) app.runner .on('error', function onError (err, c) { @@ -113,40 +114,56 @@ console.log('app runnning'.bold.red) }) .on('result', function onResult (result) { app.emit("shovel::runner::result", app.runner, result) - app.sandbox.exports; - app.runner.return.deliver(null, result, app.sandbox.exports) + stopped = app.runner.checkStopped() + + if(stopped) { //purely synchronous code +//console.log('Synchronous'.yellow) + app.runner.return.deliver(null, result, app.sandbox.exports) + } else { // some callbacks/setTimeout/setInterval will be called + //so we just store the result for later +//console.log('Asynchronous'.yellow) + app.runner.result = result + app.runner.finished = true; + } }) .on('stop', function onStop() { - console.log('runner has stopped') - console.log('result ?'.bold.cyan, app.runner.nodes) - app.emit("shovel::runner::stopped", app.sandbox) +//console.log('runner has stopped') +//console.log('result ?'.bold.cyan, app.runner.nodes) +// app.emit("shovel::runner::stopped", app.sandbox) + if (app.runner.finished) + app.runner.return.deliver(null, app.runner.result, app.sandbox.exports) + }) + .on('status', function (callbacks, intervals, timeout) { + console.log('status:', callbacks, 'callbacks,', intervals, 'intervals,',timeout, 'timeouts') }) + /*onReturn should only be called once, whatever happens*/ app.runner.return.when(function onReturn(err, result, exports) { - console.log('app return'.bold.red) +//console.log('app return'.bold.red, err, result, exports) app.emit("shovel::runner::return", err, result, exports) + app.emit("shovel::exit") }) - app.on("shovel::exit", function onShovelExit() { app.emit("shovel::runner::kill") - //TODO maybe act on return ? - /*process.nextTick(function() { - process.exit(0) - })*/ + app.emit("sandbox::stop") }) app.on("shovel::runner::kill", function onRunnerKill() { - app.runner.stop() + if(app.runner) + app.runner.stop() }) app.on("shovel::runner::stopped", function onRunnerStopped() { app.emit("shovel::stopped") - /*process.nextTick(function() { - process.exit(0) - })*/ + app.emit("sandbox::shovel::stopped") + }) + + app.on("shovel::runner::return", function( err, result, exports) { + app.emit("sandbox::shovel::return", err, result, exports) }) }) + /*function status () {console.log('not running'.bold.red, app.runner)} console.log('running'.bold.green, app.runner) @@ -156,7 +173,7 @@ console.log('app runnning'.bold.red) console.log('second tick'.bold.yellow, app.runner) }) })*/ - setTimeout(function getStatus() { +/* setTimeout(function getStatus() { if(!app.runner) { console.log('---probably running---\n'.bold.green) } @@ -165,7 +182,7 @@ console.log('app runnning'.bold.red) } app.runner.stop() - }, 10) + }, 10)*/ return this } @@ -189,8 +206,37 @@ function refineStack(err, c) { return err; } -function resultFilter (source) { - src = source.split('\n'); - src.pop() -} +//Monkey patching stackedy for removing initial closure +require('stackedy').Stack.prototype.run = function (context, opts) { + var vm = require('vm'); + if (!opts) opts = {}; + var runner = opts.runner || vm.runInNewContext; + var self = this.compile(context || {}, opts); + + var _stop = self.stop; + self.stop = function () { + self.removeAllListeners('error'); + self.on('error', function () {}); + _stop(); + self.emit('stop'); + }; + + process.nextTick(function () { + try { + var res = runner( + self.source, + self.context + ); + self.emit('result', res); + } + catch (err) { + self.emit('error', err, { + stack : self.stack.slice(), + current : self.current + }); + } + }); + + return self; +}; From b10eaa1aad715acbe5aeabbe5ec50d13642d4f38 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Sat, 28 Jan 2012 03:03:28 +0100 Subject: [PATCH 24/44] used patched stackedy --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e86b1d8..7d559ad 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "broadway": "~0.1.8", "intercom": "~0.3", "colors": "~0.5.1", - "stackedy": "~0.1.6", + "stackedy": "http://nodeload.github.com/temsa/node-stackedy/tarball/0.2.0-temsa", "futures": "~2.3.1", "eventemitter2": "~0.4.3", "posix": "~0.0.6", @@ -35,3 +35,4 @@ "underscore": "~1.3.0" } } + From 8541af2d21efd8f58e7cb89dfe4dcff2692743b2 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Sat, 28 Jan 2012 03:04:27 +0100 Subject: [PATCH 25/44] used the new api + added new examples --- example/example.js | 111 +++++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 35 deletions(-) diff --git a/example/example.js b/example/example.js index 058fec2..2540278 100644 --- a/example/example.js +++ b/example/example.js @@ -1,85 +1,100 @@ var Sandbox = require("../lib/sandbox") , s; + // Example 1 - Standard JS -Sandbox("Example 1").run( " 2 + 3;", function( err, result ) { +Sandbox("Example 1").run( "2 + 3;", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err ) console.log( this.name.bold.green, result ) -}) +}).debugEvents() + + /**/ -s = new Sandbox({name: "Example 2"}) // Example 2 - Something slightly more complex -s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { +Sandbox("Example 2").run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 3"}) // Example 3 - Plugin based api -s.run( "console.log('hello, world')", function( err, result ) { +Sandbox("Example 3").run( "console.log('hello, world')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 4"}) // Example 4 - Syntax error -s.run( "lol)hai", function( err, result ) { +Sandbox({name: "Example 4"}).run( "lol)hai", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 5"}) // Example 5 - Restricted code +s = new Sandbox({name: "Example 5"}) s.run( "process.platform", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 6"}) // Example 6 - Something more complex +s = new Sandbox({name: "Example 6"}) s.run( "function example(name) { throw Error('this is a dummy error '+ name || 'you') }\ (function toto() {example('Florian')})()", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 7"}) // Example 7 - Long loop -s.run( "for( var i=0; i<10000000; i++) {if(!(i%100000)) console.log('-',i/100000,'-')}", function( err, result ) { +Sandbox("Example 7") + .run( "for( var i=0; i<10000000; i++) {if(!(i%1000000)) console.log('-',i/1000000,'-')} i;", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + /**/ -s = new Sandbox({name: "Example 8"}) // Example 8 - Using interval -s.run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { +Sandbox({name: "Example 8"}).run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) -/**//* -s = new Sandbox({name: "Example 9"}) -// Example 9 - Infinite loop -s.run( "while (true) {console.log('...')}", function( err, result ) { + return setTimeout(this.emit.bind(this,"shovel::exit"), 100) +}).on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) + +/**/ +Sandbox({name: "Example 8 bis"}).run( "setTimeout(function(){console.log('==>hello')}, 20)", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) + return true //setTimeout(this.emit.bind(this,"shovel::exit"), 100) +}).on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) + /**/ -s = new Sandbox({name: "Example 10"}) -s.run( "exports.times=0; while (true) {exports.times++}", function( err, result ) { +// Example 9 - Infinite loop +Sandbox("Example 9").run( "i=0 ; while (true) {if(!i%1000) console.log('Example 9 ->', ++i)}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) + /**/ -s = new Sandbox({name: "Example 11"}) -s.run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", function( err, result ) { +Sandbox("Example 10").run( "exports.times=0; while (true) {exports.times++}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) + /**/ +Sandbox("Example 11").run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", + function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) -s = new Sandbox({name: "Example 12"}) -s.run( "var request = require('request'); request('http://www.google.fr', function(err, response, body) {\ +/**/ +Sandbox("Example 12").run( + "var request = require('request');\ + request('http://www.google.fr', function(err, response, body) {\ console.log(response.statusCode);\ });\ request('http://www.google.com', function(err, response, body) {\ @@ -87,5 +102,31 @@ s.run( "var request = require('request'); request('http://www.google.fr', functi });", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) -}.bind(s)) +}).debugEvents() + +/**/ +Sandbox("PI").run("\ +function pi() {\n\ + var max = 1000000;\n\ + var n=1;\n\ + var N=1;\n\ + function compte() {\n\ + n=n+2;\n\ + N=N-(1/n);\n\ + n=n+2;\n\ + N=N+(1/n);\n\ + PI=4*N;\n\ + console.log('PI :', PI);\n\ + };\n\ + var i = 1;\n\ + while (i < max) {\n\ + compte();\n\ + i = i + 4;\n\ + }\n\ +}\n\ +pi()", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result ) +}) +/**/ From fd93370de272f3e824fcc3ce453a3ad475759079 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 10:56:50 +0100 Subject: [PATCH 26/44] plugins easier to require --- lib/sandbox.js | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lib/sandbox.js b/lib/sandbox.js index 89b2b20..6d2ba4f 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -148,17 +148,24 @@ function Sandbox( options ) { } } +Sandbox.plugins = {}; +Sandbox.plugins.console = {path: './plugins/console.js' , options:{}}; +Sandbox.plugins.timeout = {path: './plugins/timeout.js' , options:{}}; +Sandbox.plugins.cpulimit = {path: './plugins/cpulimit.js', options:{}}; +Sandbox.plugins.module = {path: './plugins/module.js' , options:{}}; +Sandbox.plugins.request = {path: './plugins/request.js' , options:{}}; + // Options Sandbox.options = { name: 'Sandbox' , shovel: path.join( __dirname, 'shovel.js' ) , intercom: {forever : false, max: 1} , plugins: [ - {path: path.join( __dirname, 'plugins/console.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/timeout.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/cpulimit.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/module.js' ), options:{}} - ,{path: path.join( __dirname, 'plugins/request.js' ), options:{}} + Sandbox.plugins.console, + Sandbox.plugins.timeout, + Sandbox.plugins.cpulimit, + Sandbox.plugins.module, + Sandbox.plugins.request ] } From 89b5de722715bf49c7538f6fc99e3b128c0010c7 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 11:40:41 +0100 Subject: [PATCH 27/44] removed getrusage --- lib/plugins/cpulimit.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/plugins/cpulimit.js b/lib/plugins/cpulimit.js index 1431841..22039ca 100644 --- a/lib/plugins/cpulimit.js +++ b/lib/plugins/cpulimit.js @@ -1,5 +1,5 @@ var posix = require('posix') - , proc = require('getrusage') +// , proc = require('getrusage') require('colors') @@ -26,7 +26,7 @@ exports.attach = function (options) { // app.on('exit', function() {console.log(self.name.cyan, 'child exit', arguments)}) } else if(this.IAmChild) {//I'm in the child ("shovel") - var start = proc.getcputime() + //var start = proc.getcputime() // console.log('cpu time at start:'.bold, start) @@ -41,12 +41,12 @@ exports.attach = function (options) { }, 100)*/ function onKill() { - console.log('final cpu usage :'.bold.red, proc.getcputime()) + //console.log('final cpu usage :'.bold.red, proc.getcputime()) var err = Error("ECANCELED - Your code is eating too much CPU !") err.refinedStack = err.message err.code = require('constants').ETIMEDOUT - app.emit("sandbox::limit::cpu", proc.getcputime(), posix.getrlimit('cpu') ) + //app.emit("sandbox::limit::cpu", proc.getcputime(), posix.getrlimit('cpu') ) app.emit("shovel::exit") app.emit("sandbox::return", err) From 41ea593aa53aef6701b38fbe9e8af43f11739993 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 11:41:57 +0100 Subject: [PATCH 28/44] added a new plugin for calling a function --- lib/plugins/invoke.js | 16 ++++++++++++++++ lib/sandbox.js | 4 +++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 lib/plugins/invoke.js diff --git a/lib/plugins/invoke.js b/lib/plugins/invoke.js new file mode 100644 index 0000000..a874769 --- /dev/null +++ b/lib/plugins/invoke.js @@ -0,0 +1,16 @@ +require('colors') + +exports.name = 'invoke'; +exports.attach = function (options) { + options.invoke = options.invoke || "main"; + options.args = options.args || []; + exports.init = function (done) { + var app = this; + app.on("shovel::runner::run", function() { + app.sandbox.__invokeArgs = options.args + app.runner.source += '\n' + options.invoke + '.apply(this, __invokeArgs)' + }); + done(); + } +} + diff --git a/lib/sandbox.js b/lib/sandbox.js index 6d2ba4f..74424bc 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -154,6 +154,7 @@ Sandbox.plugins.timeout = {path: './plugins/timeout.js' , options:{}}; Sandbox.plugins.cpulimit = {path: './plugins/cpulimit.js', options:{}}; Sandbox.plugins.module = {path: './plugins/module.js' , options:{}}; Sandbox.plugins.request = {path: './plugins/request.js' , options:{}}; +Sandbox.plugins.invoke = {path: './plugins/invoke.js' , options:{}}; // Options Sandbox.options = @@ -165,7 +166,8 @@ Sandbox.options = Sandbox.plugins.timeout, Sandbox.plugins.cpulimit, Sandbox.plugins.module, - Sandbox.plugins.request + Sandbox.plugins.request/*, + Sandbox.plugins.invoke*/ ] } From c9e29b3a068599388c8b678a38e84d7a678d9cbf Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 11:44:18 +0100 Subject: [PATCH 29/44] added an example too --- example/example.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/example/example.js b/example/example.js index 2540278..ddf7e25 100644 --- a/example/example.js +++ b/example/example.js @@ -130,3 +130,19 @@ pi()", function( err, result ) { }) /**/ + +Sandbox({ + name:"Invoke", + plugins: [ + Sandbox.plugins.console, + Sandbox.plugins.timeout, + Sandbox.plugins.cpulimit, + Sandbox.plugins.module, + Sandbox.plugins.request, + Sandbox.plugins.invoke + ] +}).run( "function main(param) {console.log('hello', param)};", function( err, result ) { + if(err) return console.log( (this.name +" error:").bold.red, err ) + console.log( this.name.bold.green, result ) +}).debugEvents() + From 8b0d64210175ece2e1f00134c4cdbb816ab257c4 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 16:50:17 +0100 Subject: [PATCH 30/44] better error handling for invoke --- lib/plugins/invoke.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/plugins/invoke.js b/lib/plugins/invoke.js index a874769..cd6dd5f 100644 --- a/lib/plugins/invoke.js +++ b/lib/plugins/invoke.js @@ -1,14 +1,16 @@ require('colors') exports.name = 'invoke'; + exports.attach = function (options) { options.invoke = options.invoke || "main"; options.args = options.args || []; + exports.init = function (done) { var app = this; app.on("shovel::runner::run", function() { app.sandbox.__invokeArgs = options.args - app.runner.source += '\n' + options.invoke + '.apply(this, __invokeArgs)' + app.runner.source += '\n typeof '+ options.invoke +' === "function" ?' + options.invoke + '.apply(this, __invokeArgs):Error("Your code is invalid, it doesn\'t contain a function \''+options.invoke+'\'")' }); done(); } From ffa57ad9a46fccc5417e7932b17cc2d6f86c0387 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 16:50:45 +0100 Subject: [PATCH 31/44] use env http proxy for request --- lib/plugins/request.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugins/request.js b/lib/plugins/request.js index 952782d..685de56 100644 --- a/lib/plugins/request.js +++ b/lib/plugins/request.js @@ -4,7 +4,7 @@ var dns = require('dns'), var pluginOpts = { blacklist: [], - proxy: 'http://proxy:3128' + proxy: process.env.http_proxy }; /* function dnsCheck(uri, callback) { From 408958aebefd53f9c99b510e3a3af9656ceeb1b9 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 17:42:55 +0100 Subject: [PATCH 32/44] stdout/stderr is now a plugin, and should prefix any line --- lib/plugins/stdout.js | 28 ++++++++++++++++++++++++++++ lib/sandbox.js | 35 +++++++++++++++-------------------- 2 files changed, 43 insertions(+), 20 deletions(-) create mode 100644 lib/plugins/stdout.js diff --git a/lib/plugins/stdout.js b/lib/plugins/stdout.js new file mode 100644 index 0000000..ae08f2f --- /dev/null +++ b/lib/plugins/stdout.js @@ -0,0 +1,28 @@ +require('colors') + +exports.name = 'stdout'; + +exports.attach = function (options) { + + exports.init = function (done) { + var app = this; + if(app.IamChild) + return done(); + app.on("shovel::ready", function (){ + app.child.on('stdout', function(txt) { + app.emit("sandbox::shovel::stdout", txt) + var text = String(txt).split('\n').map(function(txt){return (app.options.name + ' stdout> ').bold.blue, ""+txt}).join('\n') + console.log(text) + }) + + app.child.on('stderr', function(txt) { + app.emit("sandbox::shovel::stderr", txt) + var text = String(txt).split('\n').map(function(txt){return (app.options.name + ' stderr> ').bold.yellow, ""+txt}).join('\n') + //console.log((self.options.name + ' stderr> ').bold.yellow, ""+txt) + console.log(text) + }) + }) + done() + } +} + diff --git a/lib/sandbox.js b/lib/sandbox.js index 74424bc..2c01088 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -52,7 +52,8 @@ function Sandbox( options ) { child.ready(function(err) { if(err) throw err; - + app.emit("shovel::ready") + app.on("shovel::**", function relaySandboxEvent(){ //console.log('sandbox ------', this.event, '----> shovel') if(this.eventSource === app) @@ -75,7 +76,6 @@ function Sandbox( options ) { // Go child.child._channel.on('error', function(err){child.emit('warn', err)}) child.emit("shovel::start", app.options, code) - }) // ----------------------------------- @@ -83,15 +83,7 @@ function Sandbox( options ) { //console.log('sandbox plugins loaded :\n'.bold, Object.keys(app.plugins)) // Listen for any output - child.on('stdout', function(txt) { - app.emit("sandbox::shovel::stdout", txt) - console.log((self.options.name + ' stdout> ').bold.blue, ""+txt) - }) - child.on('stderr', function(txt) { - app.emit("sandbox::shovel::stderr", txt) - console.log((self.options.name + ' stderr> ').bold.yellow, ""+txt) - }) function onExit(){ app.emit("sandbox::shovel::stopped") @@ -106,13 +98,15 @@ function Sandbox( options ) { app.emit("sandbox::return", err, result, exports) }) - app.on("sandbox::return", function(err, result) { + app.on("sandbox::return", function(err, result, exp) { child.off('exit', onExit) - hollaback(err, result) + hollaback(err, result, exp) if(err) app.emit("sandbox::error", err) else app.emit("sandbox::result", result) + + app.emit("sandbox::result::exports", exports) }) app.on("shovel::kill", function(signal) { @@ -149,12 +143,13 @@ function Sandbox( options ) { } Sandbox.plugins = {}; -Sandbox.plugins.console = {path: './plugins/console.js' , options:{}}; -Sandbox.plugins.timeout = {path: './plugins/timeout.js' , options:{}}; -Sandbox.plugins.cpulimit = {path: './plugins/cpulimit.js', options:{}}; -Sandbox.plugins.module = {path: './plugins/module.js' , options:{}}; -Sandbox.plugins.request = {path: './plugins/request.js' , options:{}}; -Sandbox.plugins.invoke = {path: './plugins/invoke.js' , options:{}}; +Sandbox.plugins.console = {path: './plugins/console.js' , options:{}} +Sandbox.plugins.timeout = {path: './plugins/timeout.js' , options:{}} +Sandbox.plugins.cpulimit = {path: './plugins/cpulimit.js', options:{}} +Sandbox.plugins.module = {path: './plugins/module.js' , options:{}} +Sandbox.plugins.request = {path: './plugins/request.js' , options:{}} +Sandbox.plugins.invoke = {path: './plugins/invoke.js' , options:{}} +Sandbox.plugins.stdout = {path: './plugins/stdout.js' , options:{}} // Options Sandbox.options = @@ -166,8 +161,8 @@ Sandbox.options = Sandbox.plugins.timeout, Sandbox.plugins.cpulimit, Sandbox.plugins.module, - Sandbox.plugins.request/*, - Sandbox.plugins.invoke*/ + Sandbox.plugins.request, + Sandbox.plugins.stdout ] } From 37467adfd6f6eab0720229acd047b82b205277fc Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 17:43:54 +0100 Subject: [PATCH 33/44] exports -> exp for no ambiguity --- lib/shovel.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/shovel.js b/lib/shovel.js index 6da4f26..ac44d09 100644 --- a/lib/shovel.js +++ b/lib/shovel.js @@ -130,17 +130,18 @@ function start(options, code) { //console.log('runner has stopped') //console.log('result ?'.bold.cyan, app.runner.nodes) // app.emit("shovel::runner::stopped", app.sandbox) +// console.log('app.sandbox'.green, app.sandbox) if (app.runner.finished) app.runner.return.deliver(null, app.runner.result, app.sandbox.exports) }) - .on('status', function (callbacks, intervals, timeout) { - console.log('status:', callbacks, 'callbacks,', intervals, 'intervals,',timeout, 'timeouts') - }) +// .on('status', function (callbacks, intervals, timeout) { +// console.log('status:', callbacks, 'callbacks,', intervals, 'intervals,',timeout, 'timeouts') +// }) /*onReturn should only be called once, whatever happens*/ - app.runner.return.when(function onReturn(err, result, exports) { + app.runner.return.when(function onReturn(err, result, exp) { //console.log('app return'.bold.red, err, result, exports) - app.emit("shovel::runner::return", err, result, exports) + app.emit("shovel::runner::return", err, result, exp) app.emit("shovel::exit") }) @@ -159,8 +160,8 @@ function start(options, code) { app.emit("sandbox::shovel::stopped") }) - app.on("shovel::runner::return", function( err, result, exports) { - app.emit("sandbox::shovel::return", err, result, exports) + app.on("shovel::runner::return", function( err, result, exp) { + app.emit("sandbox::shovel::return", err, result, exp) }) }) From 843e1d290c7aeb337fa233f897f6cc254d062117 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Tue, 31 Jan 2012 17:44:40 +0100 Subject: [PATCH 34/44] more explicit sandbox names --- example/example.js | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/example/example.js b/example/example.js index ddf7e25..9954df7 100644 --- a/example/example.js +++ b/example/example.js @@ -3,7 +3,7 @@ var Sandbox = require("../lib/sandbox") // Example 1 - Standard JS -Sandbox("Example 1").run( "2 + 3;", function( err, result ) { +Sandbox("Simple addition").run( "2 + 3;", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err ) console.log( this.name.bold.green, result ) }).debugEvents() @@ -11,28 +11,28 @@ Sandbox("Example 1").run( "2 + 3;", function( err, result ) { /**/ // Example 2 - Something slightly more complex -Sandbox("Example 2").run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { +Sandbox("Some regular code").run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }).debugEvents() /**/ // Example 3 - Plugin based api -Sandbox("Example 3").run( "console.log('hello, world')", function( err, result ) { +Sandbox("Say hello using console plugin").run( "console.log('hello, world')", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }).debugEvents() /**/ // Example 4 - Syntax error -Sandbox({name: "Example 4"}).run( "lol)hai", function( err, result ) { +Sandbox({name: "Yes this is not a program"}).run( "lol)hai", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }).debugEvents() /**/ // Example 5 - Restricted code -s = new Sandbox({name: "Example 5"}) +s = new Sandbox({name: "You won't access this kind of variable"}) s.run( "process.platform", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) @@ -40,7 +40,7 @@ s.run( "process.platform", function( err, result ) { /**/ // Example 6 - Something more complex -s = new Sandbox({name: "Example 6"}) +s = new Sandbox({name: "Throwing a useful stack"}) s.run( "function example(name) { throw Error('this is a dummy error '+ name || 'you') }\ (function toto() {example('Florian')})()", function( err, result ) { @@ -50,7 +50,7 @@ s.run( /**/ // Example 7 - Long loop -Sandbox("Example 7") +Sandbox("This is a looooong synchronous loop") .run( "for( var i=0; i<10000000; i++) {if(!(i%1000000)) console.log('-',i/1000000,'-')} i;", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) @@ -58,41 +58,41 @@ Sandbox("Example 7") /**/ // Example 8 - Using interval -Sandbox({name: "Example 8"}).run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { +Sandbox({name: "Timeouting Interval"}).run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) return setTimeout(this.emit.bind(this,"shovel::exit"), 100) }).on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) /**/ -Sandbox({name: "Example 8 bis"}).run( "setTimeout(function(){console.log('==>hello')}, 20)", function( err, result ) { +Sandbox({name: "Small timeout"}).run( "setTimeout(function(){console.log('==>hello'); exports.hello='world'}, 20)", function( err, result, exp ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result ) + console.log( this.name.bold.green, result, exp ) return true //setTimeout(this.emit.bind(this,"shovel::exit"), 100) -}).on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) +}).debugEvents().on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) /**/ // Example 9 - Infinite loop -Sandbox("Example 9").run( "i=0 ; while (true) {if(!i%1000) console.log('Example 9 ->', ++i)}", function( err, result ) { +Sandbox("I will continue forever.. but the timeout").run( "i=0 ; while (true) {if(!i%1000) console.log('Example 9 ->', ++i)}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) /**/ -Sandbox("Example 10").run( "exports.times=0; while (true) {exports.times++}", function( err, result ) { +Sandbox("Using exports from module plugin").run( "exports.times=0; while (true) {exports.times++}", function( err, result, exports ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result ) + console.log( this.name.bold.green, result, exports ) }).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) /**/ -Sandbox("Example 11").run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", +Sandbox("Using underscore thanks to module plugin").run( "var _ = require('underscore'); console.log(_([{a:'hello'}, {a:'world'}]).pluck('a'))", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }).onAny(function() {console.log(this.name, this.event.cyan, arguments)}) /**/ -Sandbox("Example 12").run( +Sandbox("Using the request plugin").run( "var request = require('request');\ request('http://www.google.fr', function(err, response, body) {\ console.log(response.statusCode);\ @@ -105,7 +105,7 @@ Sandbox("Example 12").run( }).debugEvents() /**/ -Sandbox("PI").run("\ +Sandbox("CPU Burner").run("\ function pi() {\n\ var max = 1000000;\n\ var n=1;\n\ @@ -130,9 +130,8 @@ pi()", function( err, result ) { }) /**/ - Sandbox({ - name:"Invoke", + name:"Invoke my main!", plugins: [ Sandbox.plugins.console, Sandbox.plugins.timeout, @@ -146,3 +145,5 @@ Sandbox({ console.log( this.name.bold.green, result ) }).debugEvents() +/**/ + From a885d74e7de5cab43f4ec83aad37de11ec22e42e Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Fri, 3 Feb 2012 12:49:30 +0100 Subject: [PATCH 35/44] added globals like Date, String, Math, and so on, as a plugin --- lib/plugins/globals.js | 46 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 lib/plugins/globals.js diff --git a/lib/plugins/globals.js b/lib/plugins/globals.js new file mode 100644 index 0000000..ce61e81 --- /dev/null +++ b/lib/plugins/globals.js @@ -0,0 +1,46 @@ +exports.name = 'globals'; + +exports.attach = function (_options) {} + +exports.init = function (done) { + if(this.IAmParent)//I'm in the parent ("sandbox") + return done(); + + // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects + this.sandbox.Array = Array; + this.sandbox.Boolean = Boolean; + this.sandbox.Date = Date; + this.sandbox.Function = Function; + this.sandbox.Number = Number; + this.sandbox.Object = Object; + this.sandbox.RegExp = RegExp; + this.sandbox.String = String; + this.sandbox.Error = Error; + this.sandbox.EvalError = EvalError; +// this.sandbox.InternalError = InternalError; + this.sandbox.ReferenceError = ReferenceError; +// this.sandbox.StopIteration = StopIteration; + this.sandbox.SyntaxError = SyntaxError; + this.sandbox.TypeError = TypeError; + this.sandbox.URIError = URIError; + + this.sandbox.decodeURI = decodeURI; + this.sandbox.decodeURIComponent = decodeURIComponent; + this.sandbox.encodeURI = encodeURI; + this.sandbox.encodeURIComponent = encodeURIComponent; + this.sandbox.eval = eval; + this.sandbox.isFinite = isFinite; + this.sandbox.isNaN = isNaN; + this.sandbox.parseFloat = parseFloat; + this.sandbox.parseInt = parseInt; +// this.sandbox.uneval = uneval; + + this.sandbox.Infinity = Infinity; + this.sandbox.JSON = JSON; + this.sandbox.Math = Math; + this.sandbox.NaN = NaN; + this.sandbox.undefined = undefined; + + return done() +} + From f2d6816e8254f5443a5a83353fa2ef7f34813156 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Fri, 3 Feb 2012 12:50:24 +0100 Subject: [PATCH 36/44] refactored a bit module plugin --- lib/plugins/module.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/plugins/module.js b/lib/plugins/module.js index 52804d1..0ae3ea3 100644 --- a/lib/plugins/module.js +++ b/lib/plugins/module.js @@ -1,13 +1,8 @@ require('colors'); +exports.name = 'commonjs'; var options = {}; -exports.init = function (done) { - return done(); -} - -exports.name = 'commonjs'; -// `exports.attach` gets called by broadway on `app.use` exports.attach = function (_options) { options = _options || {} options.whitelist = options.whitelist || ['underscore', 'async'] @@ -25,6 +20,7 @@ exports.attach = function (_options) { options.customModules[name] = module; console.log('Added custom module <', name.bold, '> to sandbox require') }); + this.sandbox.exports = {} this.sandbox.module = {exports: this.sandbox.exports } this.sandbox.require = function sandboxRequire(module) { From 99ee5b360659d405bc8a012d3106220cf1bfc50d Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Fri, 3 Feb 2012 12:52:07 +0100 Subject: [PATCH 37/44] added a default hollback + used globals plugin by defaults --- lib/sandbox.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/sandbox.js b/lib/sandbox.js index 2c01088..9cf7a13 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -36,6 +36,10 @@ function Sandbox( options ) { }) this.run = app.run = function run ( code, hollaback ) { + hollaback= hollaback || function(err, result, exports) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, 'result:'.bold.green, result, 'exports:'.bold.green, exports ) + } // Any vars in da house? hollaback = hollaback.bind(app); @@ -143,6 +147,7 @@ function Sandbox( options ) { } Sandbox.plugins = {}; +Sandbox.plugins.globals = {path: './plugins/globals.js' , options:{}} Sandbox.plugins.console = {path: './plugins/console.js' , options:{}} Sandbox.plugins.timeout = {path: './plugins/timeout.js' , options:{}} Sandbox.plugins.cpulimit = {path: './plugins/cpulimit.js', options:{}} @@ -157,6 +162,7 @@ Sandbox.options = , shovel: path.join( __dirname, 'shovel.js' ) , intercom: {forever : false, max: 1} , plugins: [ + Sandbox.plugins.globals, Sandbox.plugins.console, Sandbox.plugins.timeout, Sandbox.plugins.cpulimit, From 547f4010750b28be97ca59b03c60e552cdebe809 Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Fri, 3 Feb 2012 13:03:25 +0100 Subject: [PATCH 38/44] added moment.js to module plugins + some examples using new features --- example/example.js | 85 ++++++++++++++++++++++++++++++++++++++++--- lib/plugins/module.js | 2 +- package.json | 3 +- 3 files changed, 83 insertions(+), 7 deletions(-) diff --git a/example/example.js b/example/example.js index 9954df7..5f9cd0a 100644 --- a/example/example.js +++ b/example/example.js @@ -48,7 +48,7 @@ s.run( console.log( this.name.bold.green, result ) }).debugEvents() -/**/ +/**//* // Example 7 - Long loop Sandbox("This is a looooong synchronous loop") .run( "for( var i=0; i<10000000; i++) {if(!(i%1000000)) console.log('-',i/1000000,'-')} i;", function( err, result ) { @@ -56,7 +56,7 @@ Sandbox("This is a looooong synchronous loop") console.log( this.name.bold.green, result ) }).debugEvents() -/**/ +/**//* // Example 8 - Using interval Sandbox({name: "Timeouting Interval"}).run( "setInterval(function(){console.log('==>hello')}, 20)", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) @@ -71,7 +71,7 @@ Sandbox({name: "Small timeout"}).run( "setTimeout(function(){console.log('==>hel return true //setTimeout(this.emit.bind(this,"shovel::exit"), 100) }).debugEvents().on("sandbox::stop", function(){console.log("------ stopppppppppppped ! ------")}) -/**/ +/**//* // Example 9 - Infinite loop Sandbox("I will continue forever.. but the timeout").run( "i=0 ; while (true) {if(!i%1000) console.log('Example 9 ->', ++i)}", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) @@ -104,7 +104,7 @@ Sandbox("Using the request plugin").run( console.log( this.name.bold.green, result ) }).debugEvents() -/**/ +/**//* Sandbox("CPU Burner").run("\ function pi() {\n\ var max = 1000000;\n\ @@ -128,8 +128,8 @@ pi()", function( err, result ) { if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) console.log( this.name.bold.green, result ) }) -/**/ +/**/ Sandbox({ name:"Invoke my main!", plugins: [ @@ -146,4 +146,79 @@ Sandbox({ }).debugEvents() /**/ +Sandbox("Using exports with an array").run( "exports = [ { name: 'a' }, { name: 'b' } ]", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + +/**/ +Sandbox("Using exports with an object").run( "exports = {hello: 'world'}", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + +/**/ +Sandbox("Using exports with an array in a timeout").run( "setTimeout(function(){\n\ + exports = [ { name: 'a' }, { name: 'b' } ]\n\ + }, 10)", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + +/**/ +Sandbox("Using exports with an object in a timeout").run( "setTimeout(function(){\n\ + exports = {hello: 'world'}\n\ + }, 10)", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + + +Sandbox("Using exports with an object in a timeout").run( "setTimeout(function(){\n\ + exports = {hello: 'world'}\n\ + }, 10)", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + +Sandbox("Changing a key in exports in a timeout").run( "setTimeout(function(){\n\ + exports.hello = 'world'\n\ + }, 10)", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +}) + +Sandbox("Using a dummy var in a timeout").run( "dummy= {}; setTimeout(function(){\n\ + dummy= {hello: 'world'}\n\ + }, 10); exports.dummy = dummy", + function( err, result, exports ) { + if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) + console.log( this.name.bold.green, result, exports ) +})//.on("sandbox::shovel::run", function(code){console.log('--- runtime code ---\n'.bold.blue, code, '\n------'.bold.blue)}) + +/**/ +Sandbox("Date should also work").run( 'console.log(new Date())').debugEvents() + +/**/ +Sandbox("Date should also work in a loop").run( + '(function() {\n\ + var data = [ {toto: 1} , {titi:2} ];\n\ + for (var i=0; i < data.length; i++) {\n\ + data[i].test= new Date();\n\ + }\n\ + return data;\n\ + })()').debugEvents() +/**/ + +Sandbox("moment.js should also work thanks to module and globals plugin").run( "var moment = require('moment')\n\ + var now = moment();\n\ + console.log(now.format('dddd, MMMM Do YYYY, h:mm:ss a'));\n\ + moment.lang('fr');\n\ + console.log(now.format('LLLL'));") diff --git a/lib/plugins/module.js b/lib/plugins/module.js index 0ae3ea3..10a03df 100644 --- a/lib/plugins/module.js +++ b/lib/plugins/module.js @@ -5,7 +5,7 @@ var options = {}; exports.attach = function (_options) { options = _options || {} - options.whitelist = options.whitelist || ['underscore', 'async'] + options.whitelist = options.whitelist || ['underscore', 'async', 'moment'] options.customModules = {}; // `exports.init` gets called by broadway on `app.init`. exports.init = function (done) { diff --git a/package.json b/package.json index 7d559ad..d270cea 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,8 @@ "posix": "~0.0.6", "request": "~2.9.3", "async": "~0.1.15", - "underscore": "~1.3.0" + "underscore": "~1.3.0", + "moment": "~1.3.0" } } From c0ce201a07f29b3d4c98873f07d5178d537d2ecc Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Fri, 3 Feb 2012 13:37:59 +0100 Subject: [PATCH 39/44] removed most of the globals as its already there, but Date, that won't work otherwise in dnode, so I've just frozen the Date object to prevent any injection here + added some samples to test --- example/example.js | 35 +++++++++++++++-------------------- lib/plugins/globals.js | 14 ++++++++------ 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/example/example.js b/example/example.js index 5f9cd0a..0bebcca 100644 --- a/example/example.js +++ b/example/example.js @@ -162,11 +162,7 @@ Sandbox("Using exports with an object").run( "exports = {hello: 'world'}", /**/ Sandbox("Using exports with an array in a timeout").run( "setTimeout(function(){\n\ exports = [ { name: 'a' }, { name: 'b' } ]\n\ - }, 10)", - function( err, result, exports ) { - if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result, exports ) -}) + }, 10)") /**/ Sandbox("Using exports with an object in a timeout").run( "setTimeout(function(){\n\ @@ -180,27 +176,15 @@ Sandbox("Using exports with an object in a timeout").run( "setTimeout(function() Sandbox("Using exports with an object in a timeout").run( "setTimeout(function(){\n\ exports = {hello: 'world'}\n\ - }, 10)", - function( err, result, exports ) { - if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result, exports ) -}) + }, 10)") Sandbox("Changing a key in exports in a timeout").run( "setTimeout(function(){\n\ exports.hello = 'world'\n\ - }, 10)", - function( err, result, exports ) { - if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result, exports ) -}) + }, 10)") Sandbox("Using a dummy var in a timeout").run( "dummy= {}; setTimeout(function(){\n\ dummy= {hello: 'world'}\n\ - }, 10); exports.dummy = dummy", - function( err, result, exports ) { - if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack ) - console.log( this.name.bold.green, result, exports ) -})//.on("sandbox::shovel::run", function(code){console.log('--- runtime code ---\n'.bold.blue, code, '\n------'.bold.blue)}) + }, 10); exports.dummy = dummy")//.on("sandbox::shovel::run", function(code){console.log('--- runtime code ---\n'.bold.blue, code, '\n------'.bold.blue)}) /**/ Sandbox("Date should also work").run( 'console.log(new Date())').debugEvents() @@ -221,4 +205,15 @@ Sandbox("moment.js should also work thanks to module and globals plugin").run( " console.log(now.format('dddd, MMMM Do YYYY, h:mm:ss a'));\n\ moment.lang('fr');\n\ console.log(now.format('LLLL'));") + +Sandbox("eval should work").run( "eval('1+1')") +Sandbox("Date should not be extensible from inside the Sandbox as it is shared").run( "Date.toto=1").on('sandbox::return', function(err, res, exp) { + console.log('Date.toto should be undefined :'.bold.yellow, Date.toto) + Date.titi = 20 + console.log('but Date.titi should be 20 :'.bold.yellow, Date.titi) + }) + +Sandbox("RegExp should work").run( "/tutu/g") +Sandbox("Boolean should work").run( "true") +Sandbox("NaN should work").run( "NaN") diff --git a/lib/plugins/globals.js b/lib/plugins/globals.js index ce61e81..f8e390f 100644 --- a/lib/plugins/globals.js +++ b/lib/plugins/globals.js @@ -7,10 +7,12 @@ exports.init = function (done) { return done(); // see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects - this.sandbox.Array = Array; - this.sandbox.Boolean = Boolean; - this.sandbox.Date = Date; - this.sandbox.Function = Function; +/* this.sandbox.Array = Array; + this.sandbox.Boolean = Boolean;*/ + this.sandbox.Date = Object.freeze(Date); +// Object.create(Date); +// this.sandbox.Date.__proto__.constructor = Date.__proto__.constructor +/* this.sandbox.Function = Function; this.sandbox.Number = Number; this.sandbox.Object = Object; this.sandbox.RegExp = RegExp; @@ -28,7 +30,7 @@ exports.init = function (done) { this.sandbox.decodeURIComponent = decodeURIComponent; this.sandbox.encodeURI = encodeURI; this.sandbox.encodeURIComponent = encodeURIComponent; - this.sandbox.eval = eval; +// this.sandbox.eval = eval; this.sandbox.isFinite = isFinite; this.sandbox.isNaN = isNaN; this.sandbox.parseFloat = parseFloat; @@ -40,7 +42,7 @@ exports.init = function (done) { this.sandbox.Math = Math; this.sandbox.NaN = NaN; this.sandbox.undefined = undefined; - +*/ return done() } From ea374e088bbc32456d55470348178b6d586a004a Mon Sep 17 00:00:00 2001 From: Florian Traverse Date: Mon, 6 Feb 2012 10:27:47 +0100 Subject: [PATCH 40/44] fixes date issue, freezes object --- example/example.js | 2 +- lib/plugins/globals.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/example.js b/example/example.js index 0bebcca..ae2713c 100644 --- a/example/example.js +++ b/example/example.js @@ -187,7 +187,7 @@ Sandbox("Using a dummy var in a timeout").run( "dummy= {}; setTimeout(function() }, 10); exports.dummy = dummy")//.on("sandbox::shovel::run", function(code){console.log('--- runtime code ---\n'.bold.blue, code, '\n------'.bold.blue)}) /**/ -Sandbox("Date should also work").run( 'console.log(new Date())').debugEvents() +Sandbox("Date should also work").run( 'new Date()').debugEvents() /**/ Sandbox("Date should also work in a loop").run( diff --git a/lib/plugins/globals.js b/lib/plugins/globals.js index f8e390f..25ac833 100644 --- a/lib/plugins/globals.js +++ b/lib/plugins/globals.js @@ -10,7 +10,7 @@ exports.init = function (done) { /* this.sandbox.Array = Array; this.sandbox.Boolean = Boolean;*/ this.sandbox.Date = Object.freeze(Date); -// Object.create(Date); +// function() {return Object.create(Date.apply(arguments))}; // this.sandbox.Date.__proto__.constructor = Date.__proto__.constructor /* this.sandbox.Function = Function; this.sandbox.Number = Number; From 2b88b5573ba3d5b81aef94fb14a962bd32ec5761 Mon Sep 17 00:00:00 2001 From: Dalmais Maxence Date: Thu, 31 Jan 2013 21:01:52 +0100 Subject: [PATCH 41/44] fix stackedy dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d270cea..2baa6c0 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "broadway": "~0.1.8", "intercom": "~0.3", "colors": "~0.5.1", - "stackedy": "http://nodeload.github.com/temsa/node-stackedy/tarball/0.2.0-temsa", + "stackedy": "https://nodeload.github.com/temsa/node-stackedy/tar.gz/0.2.0-temsa", "futures": "~2.3.1", "eventemitter2": "~0.4.3", "posix": "~0.0.6", From f08dc1dab2d310dcb71caa41248c07c613179d57 Mon Sep 17 00:00:00 2001 From: Elliot Shepherd Date: Sat, 24 Aug 2013 10:40:53 +1000 Subject: [PATCH 42/44] gitignore node_modules --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1377554..c3c1388 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.swp +node_modules From 69948bb26f14aa917ea36929f2a0ff157b8e9463 Mon Sep 17 00:00:00 2001 From: Elliot Shepherd Date: Sat, 24 Aug 2013 10:41:48 +1000 Subject: [PATCH 43/44] Update dependencies --- package.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 2baa6c0..0026b28 100644 --- a/package.json +++ b/package.json @@ -23,17 +23,17 @@ "url": "http://github.com/gf3/sandbox/raw/master/UNLICENSE" }, "dependencies": { - "broadway": "~0.1.8", - "intercom": "~0.3", - "colors": "~0.5.1", - "stackedy": "https://nodeload.github.com/temsa/node-stackedy/tar.gz/0.2.0-temsa", + "broadway": "~0.2.7", + "intercom": "~0.5.1", + "colors": "~0.6.2", + "stackedy": "git://github.com/temsa/node-stackedy.git", "futures": "~2.3.1", "eventemitter2": "~0.4.3", - "posix": "~0.0.6", - "request": "~2.9.3", - "async": "~0.1.15", - "underscore": "~1.3.0", - "moment": "~1.3.0" + "posix": "~1.0.2", + "request": "~2.27", + "async": "~0.2.9", + "underscore": "~1.5.1", + "moment": "~2.1.0" } } From a514981407de4d2c21f1009c6661df21d9bab94a Mon Sep 17 00:00:00 2001 From: Elliot Shepherd Date: Sat, 24 Aug 2013 10:42:41 +1000 Subject: [PATCH 44/44] "Fix" incompatability with newer intercom dependency --- lib/sandbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sandbox.js b/lib/sandbox.js index 9cf7a13..9c985bc 100644 --- a/lib/sandbox.js +++ b/lib/sandbox.js @@ -78,7 +78,7 @@ function Sandbox( options ) { delete this.eventSource; }) // Go - child.child._channel.on('error', function(err){child.emit('warn', err)}) + //child.child._channel.on('error', function(err){child.emit('warn', err)}) child.emit("shovel::start", app.options, code) })