sandbox 0.8.6 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,64 +0,0 @@
1
- var Sandbox = require("../lib/sandbox")
2
- , s = new Sandbox()
3
-
4
- // Example 1 - Standard JS
5
- s.run( "1 + 1", function( output ) {
6
- console.log( "Example 1: " + output.result + "\n" )
7
- })
8
-
9
- // Example 2 - Something slightly more complex
10
- s.run( "(function(name) { return 'Hi there, ' + name + '!'; })('Fabio')", function( output ) {
11
- console.log( "Example 2: " + output.result + "\n" )
12
- })
13
-
14
- // Example 3 - Syntax error
15
- s.run( "lol)hai", function( output ) {
16
- console.log( "Example 3: " + output.result + "\n" )
17
- });
18
-
19
- // Example 4 - Restricted code
20
- s.run( "process.platform", function( output ) {
21
- console.log( "Example 4: " + output.result + "\n" )
22
- })
23
-
24
- // Example 5 - Infinite loop
25
- s.run( "while (true) {}", function( output ) {
26
- console.log( "Example 5: " + output.result + "\n" )
27
- })
28
-
29
- // Example 6 - Caller Attack Failure
30
- s.run( "(function foo() {return foo.caller.caller;})()", function( output ) {
31
- console.log( "Example 6: " + output.result + "\n" )
32
- })
33
-
34
- // Example 7 - Argument Attack Failure
35
- s.run( "(function foo() {return [].slice.call(foo.caller.arguments);})()", function( output ) {
36
- console.log( "Example 7: " + output.result + "\n" )
37
- })
38
-
39
- // Example 8 - Type Coersion Attack Failure
40
- s.run( "(function foo() {return {toJSON:function x(){return x.caller.caller.name}}})()", function( output ) {
41
- console.log( "Example 8: " + output.result + "\n" )
42
- })
43
-
44
- // Example 9 - Global Attack Failure
45
- s.run( "x=1;(function() {return this})().console.log.constructor('return this')()", function( output ) {
46
- console.log( "Example 9: " + output.result + "\n" )
47
- })
48
-
49
- // Example 10 - Console Log
50
- s.run( "var x = 5; console.log(x * x); x", function( output ) {
51
- console.log( "Example 10: " + output.console + "\n" )
52
- })
53
-
54
- // Example 11 - IPC Messaging
55
- s.run( "onmessage = function(message){ if (message === 'hello from outside') { postMessage('hello from inside'); };", function(output){
56
-
57
- })
58
- s.on('message', function(message){
59
- console.log("Example 11: received message sent from inside the sandbox '" + message + "'\n")
60
- });
61
- var test_message = "hello from outside";
62
- console.log("Example 11: sending message into the sandbox '" + test_message + "'");
63
- s.postMessage(test_message);
64
-
package/lib/sandbox.js DELETED
@@ -1,124 +0,0 @@
1
- //-----------------------------------------------------------------------------
2
- // Init
3
- //-----------------------------------------------------------------------------
4
-
5
- var fs = require('fs');
6
- var path = require('path');
7
- var spawn = require('child_process').spawn;
8
- var util = require('util');
9
- var EventEmitter = require('events').EventEmitter;
10
-
11
- //-----------------------------------------------------------------------------
12
- // Constructor
13
- //-----------------------------------------------------------------------------
14
-
15
- function Sandbox(options) {
16
- var self = this;
17
-
18
- // message_queue is used to store messages that are meant to be sent
19
- // to the sandbox before the sandbox is ready to process them
20
- self._ready = false;
21
- self._message_queue = [];
22
-
23
- self.options = {
24
- timeout: 500,
25
- node: 'node',
26
- shovel: path.join(__dirname, 'shovel.js')
27
- };
28
-
29
- self.info = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json')));
30
- }
31
-
32
- // Make the Sandbox class an event emitter to handle messages
33
- util.inherits(Sandbox, EventEmitter);
34
-
35
-
36
- //-----------------------------------------------------------------------------
37
- // Instance Methods
38
- //-----------------------------------------------------------------------------
39
-
40
- Sandbox.prototype.run = function(code, hollaback) {
41
- var self = this;
42
- var timer;
43
- var stdout = '';
44
- self.child = spawn(this.options.node, [this.options.shovel], { stdio: ['pipe', 'pipe', 'pipe', 'ipc'] });
45
- var output = function(data) {
46
- if (!!data) {
47
- stdout += data;
48
- }
49
- };
50
-
51
- if (typeof hollaback == 'undefined') {
52
- hollaback = console.log;
53
- } else {
54
- hollaback = hollaback.bind(this);
55
- }
56
-
57
- // Listen
58
- self.child.stdout.on('data', output);
59
-
60
- // Pass messages out from child process
61
- // These messages can be handled by Sandbox.on('message', function(message){...});
62
- self.child.on('message', function(message){
63
- if (message === '__sandbox_inner_ready__') {
64
-
65
- self.emit('ready');
66
- self._ready = true;
67
-
68
- // Process the _message_queue
69
- while(self._message_queue.length > 0) {
70
- self.postMessage(self._message_queue.shift());
71
- }
72
-
73
- } else {
74
- self.emit('message', message);
75
- }
76
- });
77
-
78
- self.child.on('exit', function(code) {
79
- clearTimeout(timer);
80
- setImmediate(function(){
81
- if (!stdout) {
82
- hollaback({ result: 'Error', console: [] });
83
- } else {
84
- var ret;
85
- try {
86
- ret = JSON.parse(stdout);
87
- } catch (e) {
88
- ret = { result: 'JSON Error (data was "'+stdout+'")', console: [] }
89
- }
90
- hollaback(ret);
91
- }
92
- });
93
- });
94
-
95
- // Go
96
- self.child.stdin.write(code);
97
- self.child.stdin.end();
98
-
99
- timer = setTimeout(function() {
100
- self.child.stdout.removeListener('output', output);
101
- stdout = JSON.stringify({ result: 'TimeoutError', console: [] });
102
- self.child.kill('SIGKILL');
103
- }, self.options.timeout);
104
- };
105
-
106
- // Send a message to the code running inside the sandbox
107
- // This message will be passed to the sandboxed
108
- // code's `onmessage` function, if defined.
109
- // Messages posted before the sandbox is ready will be queued
110
- Sandbox.prototype.postMessage = function(message) {
111
- var self = this;
112
-
113
- if (self._ready) {
114
- self.child.send(message);
115
- } else {
116
- self._message_queue.push(message);
117
- }
118
- };
119
-
120
- //-----------------------------------------------------------------------------
121
- // Export
122
- //-----------------------------------------------------------------------------
123
-
124
- module.exports = Sandbox;
package/lib/shovel.js DELETED
@@ -1,113 +0,0 @@
1
- var util = require('util');
2
- var vm = require('vm');
3
-
4
- //-----------------------------------------------------------------------------
5
- // Sandbox
6
- //-----------------------------------------------------------------------------
7
-
8
- var code = '';
9
- var stdin = process.openStdin();
10
- var result;
11
- var console = [];
12
-
13
- // Get code
14
- stdin.on('data', function(data) {
15
- code += data;
16
- });
17
- stdin.on('end', run);
18
-
19
- function getSafeRunner() {
20
- var global = this;
21
- // Keep it outside of strict mode
22
- function UserScript(str) {
23
- // We want a global scoped function that has implicit returns.
24
- return Function('return eval('+JSON.stringify(str+'')+')');
25
- }
26
- // place with a closure that is not exposed thanks to strict mode
27
- return function run(comm, src) {
28
- // stop argument / caller attacks
29
- "use strict";
30
- var send = function send(event) {
31
- "use strict";
32
- //
33
- // All comm must be serialized properly to avoid attacks, JSON or XJSON
34
- //
35
- comm.send(event, JSON.stringify([].slice.call(arguments,1)));
36
- };
37
-
38
- global.print = send.bind(global, 'stdout');
39
- global.console = { log: send.bind(global, 'stdout') };
40
- global.process = {
41
- stdout: { write: send.bind(global, 'stdout') }
42
- };
43
- global.postMessage = send.bind(global, 'message');
44
-
45
- // This is where the user's source code is actually evaluated
46
- var result = UserScript(src)();
47
- send('end', result);
48
- }
49
- };
50
-
51
- // Run code
52
- function run() {
53
-
54
- var context = vm.createContext();
55
- var safeRunner = vm.runInContext('('+getSafeRunner.toString()+')()', context);
56
-
57
- try {
58
- safeRunner({
59
- send: function (event, value) {
60
- "use strict";
61
-
62
- switch (event) {
63
- case 'stdout':
64
- console.push(JSON.parse(value)[0]);
65
- break;
66
- case 'end':
67
- result = JSON.parse(value)[0];
68
- break;
69
- case 'message':
70
- process.send(JSON.parse(value)[0]);
71
- break;
72
- default:
73
- throw new Error('Unknown event type');
74
- }
75
- },
76
- exit: function(){
77
- processExit();
78
- }
79
- }, code);
80
- }
81
- catch (e) {
82
- result = e.name + ': ' + e.message;
83
- // throw e;
84
- }
85
-
86
- process.on('message', processMessageListener.bind(null, context));
87
-
88
- process.send('__sandbox_inner_ready__');
89
-
90
- // This will exit the process if onmessage was not defined
91
- checkIfProcessFinished(context);
92
- };
93
-
94
- function processMessageListener(context, message){
95
- vm.runInContext('if (typeof onmessage === "function") { onmessage('+ JSON.stringify(String(message)) + '); }', context);
96
- checkIfProcessFinished(context);
97
- };
98
-
99
- function checkIfProcessFinished(context) {
100
- if(vm.runInContext('typeof onmessage', context) !== 'function') {
101
- processExit();
102
- }
103
- };
104
-
105
- function processExit() {
106
- process.removeListener('message', processMessageListener);
107
-
108
- process.stdout.on('finish', function() {
109
- process.exit(0);
110
- });
111
-
112
- process.stdout.end(JSON.stringify({ result: util.inspect(result), console: console }));
113
- };
package/test/sandbox.js DELETED
@@ -1,119 +0,0 @@
1
- //-----------------------------------------------------------------------------
2
- // Init
3
- //-----------------------------------------------------------------------------
4
-
5
- var should = require('should');
6
- var sinon = require('sinon');
7
- var Sandbox = require('../lib/sandbox');
8
-
9
- //-----------------------------------------------------------------------------
10
- // Tests
11
- //-----------------------------------------------------------------------------
12
-
13
- describe('Sandbox', function() {
14
- var sb;
15
- beforeEach(function(){
16
- sb = new Sandbox();
17
- });
18
-
19
- it('should execute basic javascript', function(done) {
20
- sb.run('1 + 1', function(output) {
21
- output.result.should.eql('2');
22
- done();
23
- });
24
- });
25
-
26
- it('should gracefully handle syntax errors', function(done) {
27
- sb.run('hi )there', function(output) {
28
- output.result.should.eql("'SyntaxError: Unexpected token )'");
29
- done();
30
- });
31
- });
32
-
33
- it('should effectively prevent code from accessing node', function(done) {
34
- sb.run('process.platform', function(output) {
35
- output.result.should.eql("null");
36
- done();
37
- });
38
- });
39
-
40
- it('should effectively prevent code from circumventing the sandbox', function(done) {
41
- sb.run("var sys=require('sys'); sys.puts('Up in your fridge')", function(output) {
42
- output.result.should.eql("'ReferenceError: require is not defined'");
43
- done();
44
- });
45
- });
46
-
47
- it('should timeout on infinite loops', function(done) {
48
- sb.run('while ( true ) {}', function(output) {
49
- output.result.should.eql('TimeoutError');
50
- done();
51
- });
52
- });
53
-
54
- it('should allow console output via `console.log`', function(done) {
55
- sb.run('console.log(7); 42', function(output) {
56
- output.result.should.eql('42');
57
- output.console[0].should.eql(7);
58
- done();
59
- });
60
- });
61
-
62
- it('should allow console output via `print`', function(done) {
63
- sb.run('print(7); 42', function(output) {
64
- output.result.should.eql('42');
65
- output.console[0].should.eql(7);
66
- done();
67
- });
68
- });
69
-
70
- it('should maintain the order of sync. console output', function(done) {
71
- sb.run('console.log("first"); console.log("second"); 42', function(output) {
72
- output.result.should.eql('42');
73
- output.console[0].should.eql('first');
74
- output.console[1].should.eql('second');
75
- done();
76
- });
77
- });
78
-
79
- it('should expose the postMessage command to the sandboxed code', function(done){
80
- var messageHandler = sinon.spy();
81
- sb.on('message', messageHandler);
82
- sb.run('postMessage("Hello World!");', function(output){
83
- messageHandler.calledOnce.should.eql(true);
84
- messageHandler.calledWith('Hello World!').should.eql(true);
85
- done();
86
- });
87
- });
88
-
89
- it('should allow sandboxed code to receive messages sent by postMessage from the outside by overwriting the onmessage function', function(done){
90
- var messageHandler = sinon.spy();
91
- sb.on('message', messageHandler);
92
- sb.on('ready', function () {
93
- sb.postMessage('Hello World!');
94
- });
95
- sb.run('onmessage = function (msg) { postMessage(msg); };', function(output) {
96
- messageHandler.callCount.should.eql(1);
97
- messageHandler.calledWith('Hello World!').should.eql(true);
98
- done();
99
- });
100
- });
101
-
102
- it('should queue messages posted before the sandbox is ready and process them once it is', function(done){
103
- var messageHandler = sinon.spy();
104
- var num_messages_sent = 0;
105
- var interval = setInterval(function(){
106
- sb.postMessage(++num_messages_sent);
107
- }, 1);
108
- sb.on('message', messageHandler);
109
- sb.run('onmessage = function (msg) { postMessage(msg); };', function(output) {
110
- messageHandler.callCount.should.eql(num_messages_sent);
111
- num_messages_sent.should.be.greaterThan(0);
112
- done();
113
- });
114
- sb.on('ready', function(){
115
- clearInterval(interval);
116
- });
117
- });
118
-
119
- });
package/tmp.js DELETED
@@ -1,8 +0,0 @@
1
- var Sandbox = require("./lib/sandbox")
2
- , s = new Sandbox()
3
-
4
- // Example 10 - Console Log
5
- s.run( "var x = 5; console.log(x * x); console.log('pewpew'); x", function( output ) {
6
- console.log( "Example 10: " + output.console + "\n" )
7
- })
8
-