Permalink
Browse files

updated: Script, NPM, logging.

* Removed manual sandbox in favour of node's new Script sandbox.
* NPM-ification of package.
* Added logging via `console.log` and `print`.
* Changed output format, no longer returns string, now returns an
  object.
* Added more tests.
  • Loading branch information...
gf3 committed Oct 29, 2010
1 parent d548fa7 commit d0818e9a78669d6c9a20d866a6fee2558248c876
Showing with 166 additions and 66 deletions.
  1. +34 −8 README.md
  2. +25 −0 UNLICENSE
  3. +16 −17 example/example.js
  4. +16 −11 lib/sandbox.js
  5. +24 −25 lib/shovel.js
  6. +20 −0 package.json
  7. +31 −5 spec/sandbox.spec.js
View
@@ -1,34 +1,56 @@
# Node Sandbox
-A rudimentary javascript sandbox for use with node.js.
+A nifty javascript sandbox for use with node.js.
## Some features
- Can be used to execute untrusted code.
- Support for timeouts (e.g. prevent infinite loops)
- Handles errors gracefully
-- Restricted code (cannot access NodeJS methods)
+- Restricted code (cannot access node.js methods)
+- Supports `console.log` and `print` utility methods
## Example
Be sure to check out example/example.js
var s = new Sandbox()
s.run( '1 + 1 + " apples"', function( output ) {
- // output == "2 apples"
+ // output.result == "2 apples"
})
## Documentation
-Coming soon!
+Basic syntax: `sandbox_instance.run( output, hollaback )`
-Basic syntax: `sandbox_instance.run( code_string, hollaback_function )`
+`output` is an object with two properties: `result` and `console`. The `result`
+property is an inspected string of the return value of the code. The `console`
+property is an array of all console output.
+
+For example, given the following code:
+
+ function add( a, b ){
+ console.log( a )
+ console.log( b )
+ return a + b
+ }
+ add( 20, 22 )
+
+The resulting output object is:
+
+ { result: "42"
+ , console: [ "20", "22" ]
+ }
## Installation & Running
-Let's get it!
+Let's get it! The easiest way is through npm:
- git clone git://github.com/gf3/node-sandbox.git
+ npm install sandbox
+
+Or if you'd like to play with the code, see the examples, run the tests, what-the-fuck-ever...
+
+ git clone git://github.com/gf3/sandbox.git
And run some examples:
@@ -39,8 +61,12 @@ And run some examples:
npm install async_testing
node spec/sandbox.spec.js
+## License
+
+Sandbox is [UNLICENSED](http://unlicense.org/).
+
## Author
- Written by [Gianni Chiappetta](http://github.com/gf3) – [gf3.ca](http://gf3.ca)
-- Updates by [Dominic Tarr](http://github.com/dominictarr) – [cyber-hobo.blogspot.com](http://cyber-hobo.blogspot.com/)
+- Contributions by [Dominic Tarr](http://github.com/dominictarr) – [cyber-hobo.blogspot.com](http://cyber-hobo.blogspot.com/)
View
@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to <http://unlicense.org/>
+
View
@@ -1,29 +1,28 @@
-var sys = require("sys")
- , Sandbox = require("../lib/sandbox")
+var Sandbox = require("../lib/sandbox")
, s = new Sandbox()
- ;
// Example 1 - Standard JS
-s.run("1 + 1", function(output) {
- sys.puts("Example 1: " + output + "\n");
-});
+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) {
- sys.puts("Example 2: " + output + "\n");
-});
+s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( output ) {
+ console.log( "Example 2: " + output.result + "\n" )
+})
// Example 3 - Syntax error
-s.run("lol)hai", function(output) {
- sys.puts("Example 3: " + output + "\n");
+s.run( "lol)hai", function( output ) {
+ console.log( "Example 3: " + output.result + "\n" )
});
// Example 4 - Restricted code
-s.run("process.platform", function(output) {
- sys.puts("Example 4: " + output + "\n");
-});
+s.run( "process.platform", function( output ) {
+ console.log( "Example 4: " + output.result + "\n" )
+})
// Example 5 - Infinite loop
-s.run("while (true) {}", function(output) {
- sys.puts("Example 5: " + output + "\n");
-});
+s.run( "while (true) {}", function( output ) {
+ console.log( "Example 5: " + output.result + "\n" )
+})
+
View
@@ -2,11 +2,12 @@
// Gianni Chiappetta - gf3.ca - 2010
/*------------------------- INIT -------------------------*/
-var sys = require( 'sys' )
+var fs = require( 'fs' )
+ , path = require( 'path' )
, spawn = require( 'child_process' ).spawn
/*------------------------- Sandbox -------------------------*/
- function Sandbox( options ) {
+function Sandbox( options ) {
( this.options = options || {} ).__proto__ = Sandbox.options
this.run = function( code, hollaback ) {
@@ -20,18 +21,18 @@ var sys = require( 'sys' )
}
// Listen
- child.stdout.addListener( 'data', output )
- child.addListener( 'exit', function( code ) {
+ child.stdout.on( 'data', output )
+ child.on( 'exit', function( code ) {
clearTimeout( timer )
- hollaback.call( this, stdout )
+ hollaback.call( this, JSON.parse( stdout ) )
})
// Go
child.stdin.write( code )
child.stdin.end()
timer = setTimeout( function() {
child.stdout.removeListener( 'output', output )
- stdout = 'TimeoutError'
+ stdout = JSON.stringify( { result: 'TimeoutError', console: [] } )
child.kill( 'SIGKILL' )
}, this.options.timeout )
}
@@ -41,13 +42,17 @@ var sys = require( 'sys' )
Sandbox.options =
{ timeout: 500
, node: 'node'
- , shovel: (function() {
- var p = __filename.split('/').slice( 0, -1 )
- p.push( 'shovel.js' )
- return p.join('/')
- })()
+ , shovel: path.join( __dirname, 'shovel.js' )
}
+// 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
View
@@ -2,50 +2,49 @@
// Gianni Chiappetta - gf3.ca - 2010
/* ------------------------------ INIT ------------------------------ */
-var sys = require('sys')
- , code = ''
- , i
+var util = require( 'util' )
+ , code
+ , result
+ , console
, stdin
- , reserved =
- { 'require': undefined
- , '__filename': undefined
- , '__module': undefined
- , 'module': undefined
- , 'code': undefined
- , 'reserved': undefined
- , 'run': undefined
- , 'sys': undefined
- , 'i': undefined
- , 'stdin': undefined
- }
+ , Script = process.binding('evals').Script
+ , sandbox
/* ------------------------------ Sandbox ------------------------------ */
-// Generate list of reserved items
-for ( i in GLOBAL )
- reserved[i] = undefined
+// Sandbox methods
+console = []
+sandbox =
+ { 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
// Get code
+code = ''
stdin = process.openStdin()
-stdin.addListener( 'data', function( data ) {
+stdin.on( 'data', function( data ) {
code += data
})
-stdin.addListener( 'end', run )
+stdin.on( 'end', run )
// Run code
function run() {
- var output = (function() {
+ result = (function() {
try {
- with ( reserved )
- return eval( this.toString() )
+ return Script.runInNewContext( this.toString().replace( /\\([rn])/g, "\\\\$1" ), sandbox )
}
catch (e) {
return e.name + ': ' + e.message
}
}).call( code )
- process.stdout.addListener( 'drain', function() {
+ process.stdout.on( 'drain', function() {
process.exit(0)
})
- process.stdout.write( sys.inspect( output ) )
+ process.stdout.write( JSON.stringify( { result: util.inspect( result ), console: console } ) )
}
View
@@ -0,0 +1,20 @@
+{ "name" : "sandbox"
+, "description": "A nifty javascript sandbox for use with node.js"
+, "author" : "Gianni Chiappetta <gianni@runlevel6.org> (http://gf3.ca)"
+, "contributors":
+ [ "Dominic Tarr (http://cyber-hobo.blogspot.com)"
+ ]
+, "version" : "0.7.0"
+, "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"
+ }
+}
+
View
@@ -5,39 +5,65 @@ var Sandbox = require( '../lib/sandbox' )
/* ------------------------------ Tests ------------------------------ */
exports['it should execute basic javascript'] = function( test ) {
sb.run( '1 + 1', function( output ) {
- test.equal( output, '2' )
+ test.equal( output.result, '2' )
test.finish()
})
}
exports['it should gracefully handle syntax errors'] = function( test ) {
sb.run( 'hi )there', function( output ) {
- test.equal( output, "'SyntaxError: Unexpected token )'" )
+ test.equal( output.result, "'SyntaxError: Unexpected token )'" )
test.finish()
})
}
exports['it should effectively prevent code from accessing node'] = function( test ) {
sb.run( 'process.platform', function( output ) {
- test.equal( output, "'TypeError: Cannot read property \\'platform\\' of undefined'" )
+ test.equal( output.result, "'ReferenceError: process is not defined'" )
test.finish()
})
}
exports['it should effectively prevent code from circumventing the sandbox'] = function( test ) {
sb.run( "var sys=require('sys'); sys.puts('Up in your fridge')", function( output ) {
- test.equal( output, "'TypeError: undefined is not a function'" )
+ test.equal( output.result, "'ReferenceError: require is not defined'" )
test.finish()
})
}
exports['it should timeout on infinite loops'] = function( test ) {
sb.run( 'while ( true ) {}', function( output ) {
- test.equal( output, "TimeoutError" )
+ test.equal( output.result, "TimeoutError" )
test.finish()
})
}
+exports['it should allow console output via `console.log`'] = function( test ) {
+ sb.run( 'console.log(7); 42', function( output ) {
+ test.equal( output.result, "42" )
+ test.equal( output.console[0], "7" )
+ test.finish()
+ })
+}
+
+exports['it should allow console output via `print`'] = function( test ) {
+ sb.run( 'print(7); 42', function( output ) {
+ test.equal( output.result, "42" )
+ test.equal( output.console[0], "7" )
+ test.finish()
+ })
+}
+
+exports['it should maintain the order of sync. console output'] = function( test ) {
+ sb.run( 'console.log("first"); console.log("second"); 42', function( output ) {
+ test.equal( output.result, "42" )
+ test.equal( output.console[0], "'first'" )
+ test.equal( output.console[1], "'second'" )
+ test.finish()
+ })
+}
+
+/* ------------------------------ GO GO GO ------------------------------ */
if ( module == require.main )
require( 'async_testing' ).run( __filename, process.ARGV )

0 comments on commit d0818e9

Please sign in to comment.