Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pull #58

Open
wants to merge 44 commits into
base: master
Choose a base branch
from
Open

Pull #58

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5b502bf
added broadway
temsa Dec 7, 2011
ef10cca
used broadway for the child
temsa Dec 7, 2011
d6a827e
added argv handling for passing plugins+configuration in a json
temsa Dec 7, 2011
4ac22f5
moved console as a plugin
temsa Dec 7, 2011
968a241
added intercom
temsa Dec 7, 2011
20361e9
commented some parts of the sample for testing
temsa Dec 7, 2011
b597f42
not stable yet, but at least starts and logs
temsa Dec 7, 2011
e229933
improved logs
temsa Dec 7, 2011
9c11a38
added some colors
temsa Dec 8, 2011
c8c493a
works!
temsa Dec 8, 2011
2286497
changed result api, so changed example accordingly
temsa Dec 8, 2011
b46c747
removed timeout from core, moved it as a plugin
temsa Dec 8, 2011
cc6f4e8
added futures
temsa Jan 12, 2012
61f8a2f
fixed a bunch of issues, added name to sandbox, tried to fix infinite…
temsa Jan 12, 2012
f72dcce
now asynchronous code works, but doesn't stop yet
temsa Jan 18, 2012
8cb48e0
added cpulimit/ module system
temsa Jan 18, 2012
55ca5d6
added cpulimit/ module system
temsa Jan 18, 2012
5524dca
Unfinished request sandbox implementation
Marsup Jan 20, 2012
dfb58fd
updated dependencies for using ** in eventemitter modules like broadw…
temsa Jan 23, 2012
02134b5
improved event api between sansdbox<->shovel, with event relaying, so…
temsa Jan 23, 2012
c31ebe6
updated default plugins for using the new events
temsa Jan 23, 2012
df3bd91
updated examples
temsa Jan 23, 2012
09d3a22
yeah, now this is a really working sandbox supporting both asynchrono…
temsa Jan 28, 2012
b10eaa1
used patched stackedy
temsa Jan 28, 2012
8541af2
used the new api + added new examples
temsa Jan 28, 2012
fd93370
plugins easier to require
temsa Jan 31, 2012
89b5de7
removed getrusage
temsa Jan 31, 2012
41ea593
added a new plugin for calling a function
temsa Jan 31, 2012
c9e29b3
added an example too
temsa Jan 31, 2012
8b0d642
better error handling for invoke
temsa Jan 31, 2012
ffa57ad
use env http proxy for request
temsa Jan 31, 2012
408958a
stdout/stderr is now a plugin, and should prefix any line
temsa Jan 31, 2012
37467ad
exports -> exp for no ambiguity
temsa Jan 31, 2012
843e1d2
more explicit sandbox names
temsa Jan 31, 2012
a885d74
added globals like Date, String, Math, and so on, as a plugin
temsa Feb 3, 2012
f2d6816
refactored a bit module plugin
temsa Feb 3, 2012
99ee5b3
added a default hollback + used globals plugin by defaults
temsa Feb 3, 2012
547f401
added moment.js to module plugins + some examples using new features
temsa Feb 3, 2012
c0ce201
removed most of the globals as its already there, but Date, that won'…
temsa Feb 3, 2012
ea374e0
fixes date issue, freezes object
temsa Feb 6, 2012
2b88b55
fix stackedy dependency
maxired Jan 31, 2013
f08dc1d
gitignore node_modules
elliots Aug 24, 2013
69948bb
Update dependencies
elliots Aug 24, 2013
a514981
"Fix" incompatability with newer intercom dependency
elliots Aug 24, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.swp
node_modules
223 changes: 207 additions & 16 deletions example/example.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,219 @@
var Sandbox = require("../lib/sandbox")
, s = new Sandbox()
, s;


// Example 1 - Standard JS
s.run( "1 + 1", function( output ) {
console.log( "Example 1: " + output.result + "\n" )
})
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()


/**/
// Example 2 - Something slightly more complex
s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( output ) {
console.log( "Example 2: " + output.result + "\n" )
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("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: "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: "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 )
}).debugEvents()

/**/
// Example 6 - Something more complex
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 ) {
if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack )
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 ) {
if(err) return console.log( (this.name +" error:").bold.red, err.refinedStack )
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 )
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: "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, exp )
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 )
console.log( this.name.bold.green, result )
}).onAny(function() {console.log(this.name, this.event.cyan, arguments)})

/**/
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, exports )
}).onAny(function() {console.log(this.name, this.event.cyan, arguments)})

/**/
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("Using the request plugin").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 )
}).debugEvents()

/**//*
Sandbox("CPU Burner").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 )
})

// Example 3 - Syntax error
s.run( "lol)hai", function( output ) {
console.log( "Example 3: " + output.result + "\n" )
});
/**/
Sandbox({
name:"Invoke my main!",
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()

/**/
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 )
})

// Example 4 - Restricted code
s.run( "process.platform", function( output ) {
console.log( "Example 4: " + output.result + "\n" )
/**/
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 )
})

// Example 5 - Infinite loop
s.run( "while (true) {}", function( output ) {
console.log( "Example 5: " + output.result + "\n" )
/**/
Sandbox("Using exports with an array in a timeout").run( "setTimeout(function(){\n\
exports = [ { name: 'a' }, { name: 'b' } ]\n\
}, 10)")

/**/
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)")

Sandbox("Changing a key in exports in a timeout").run( "setTimeout(function(){\n\
exports.hello = 'world'\n\
}, 10)")

Sandbox("Using a dummy var in a timeout").run( "dummy= {}; setTimeout(function(){\n\
dummy= {hello: 'world'}\n\
}, 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( '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'));")

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")

41 changes: 41 additions & 0 deletions lib/plugins/console.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
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 app = this;

if(app.IAmParent) {//I'm in the parent ("sandbox")
app.on('sandbox::console', function(level, args) {
args = args || [];
args.unshift((app.options.name + '> ').bold);
console[level].apply(console, args);
})

} else if(app.IAmChild) {//I'm in the child ("shovel")
function log(level) {
var logger = function () {

var args = Array.prototype.slice.call(arguments); //make it a real array
app.emit('sandbox::console', level, args)
}
logger.name = level
return logger;
}

app.sandbox.console = {
log: log('log'),
debug: log('debug'),
error: log('error')
}

}
// This plugin doesn't require any initialization step.
return done()
}

74 changes: 74 additions & 0 deletions lib/plugins/cpulimit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
var posix = require('posix')
// , proc = require('getrusage')

require('colors')

var defaults = {soft: 10, hard: 11};

exports.name = 'cpulimit';
// `exports.attach` gets called by broadway on `app.use`
exports.attach = function (options) {

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 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('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 - 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("shovel::exit")
app.emit("sandbox::return", err)

//throw err;
process.nextTick(process.nextTick.bind(function() {
process.exit(1);
}))
}

// 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)
process.on('SIGTERM', onKill)*/

//FIXME error reporting ?
}
// This plugin doesn't require any initialization step.
return done()
}
}

Loading