Created
March 18, 2011 05:52
-
-
Save yozlet/875668 to your computer and use it in GitHub Desktop.
Creation of a limited sandbox in node.js 0.2.x
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* sandbox.js - create a single sandbox object with exposed functions and services | |
* | |
* by [email protected] | |
* | |
* | |
**/ | |
// Started doing this with proper constructors and stuff. Javascript Patterns | |
// book has a good Sandbox pattern. However, didn't have time to do that | |
// properly here. Next time. | |
var LIBS_ROOT = '/local/node/'; | |
var ES5_PATH = LIBS_ROOT+"es5-shim/es5-shim.js"; | |
var ORBDB_PATH = '/local/node/orbital/orbdb/'; | |
var request_fields = [ 'method', 'params', 'body', 'headers', 'scriptName', 'projectName', 'scriptID']; | |
var | |
http = require('http'), | |
fs = require('fs'), | |
url = require('url'), | |
oauth = require('oauth'), | |
path = require('path'), | |
querystring = require("querystring"), | |
request = require('request'), | |
Script = process.binding('evals').Script, | |
OrbDB = require('./orbdb').OrbDB; | |
; | |
/** | |
* makeBasicSandbox() - creates a basic sandbox for use by require()'d scripts | |
* and is the basis for the main sandbox | |
**/ | |
var makeBasicSandbox = function(req, project_path) { | |
var projectname = req.projectName; | |
var sandbox = { | |
MODULES: { | |
http: { createClient: http.createClient }, | |
url: url, | |
JSON: JSON, | |
oauth: oauth, | |
querystring: querystring, | |
request: request // the library called "request" | |
}, | |
setTimeout: setTimeout, | |
REQUEST: {} | |
}; | |
// populate the REQUEST object | |
for (var i=0; i<request_fields.length; i++) { | |
sandbox.REQUEST[request_fields[i]] = req[request_fields[i]]; | |
} | |
sandbox.REQUEST.path = req.params.pathname + "." + req.params.pathsuffix; | |
// add the custom closures | |
sandbox.readFile = make_readFile(sandbox, project_path, req.projectName); | |
sandbox.require = make_require(sandbox,req,project_path); | |
sandbox.log = make_log(sandbox, req, project_path); | |
sandbox.MODULES.OrbDB = new OrbDB(ORBDB_PATH, req.username, req.projectName); | |
// load up es5-shim | |
var scriptSource = fs.readFileSync(ES5_PATH,'utf8'); | |
var script = new Script(scriptSource,"es5-shim.js"); | |
script.runInNewContext(sandbox); | |
return sandbox; | |
}; | |
exports.makeSandbox = function(req, res, project_path) { | |
// shorthand for responding 200 + text/html | |
function respond(body) { | |
if (!body) { | |
body = "ERROR: Script did not return a response"; | |
} | |
body = body.toString(); | |
res.writeHead(200, { | |
'Content-Type': 'text/html', | |
'Content-Length': body.length | |
}); | |
res.end(body); | |
} | |
var sandbox = makeBasicSandbox(req, project_path); | |
sandbox.respond = respond; | |
sandbox.RESPONSE = res; | |
return sandbox; | |
}; | |
/** | |
* make_readFile(sandbox, project_path, projectname) | |
* Added by Yoz for Orbital | |
* Function factory, returns a sandboxed file-reader limited to the project path | |
* TODO: Handle file read failures | |
* TODO: Abstract file-reading for readFile() and use() | |
**/ | |
function make_readFile(mainSandbox,project_path,projectname) { | |
return function (filename) { | |
// TODO: Finer-grained exception handling | |
try { | |
if (/\.\.\//.test(filename)) { | |
mainSandbox.respond("readFile() can't climb up directories. Nice try!"); | |
} | |
var filePath = path.join(project_path, projectname, filename); | |
var fileContents = fs.readFileSync(filePath,'utf8'); | |
return fileContents; | |
} catch (e) { | |
// TODO: work out best exception behaviour here | |
mainSandbox.respond("readFile('"+filename+"') failed with error: " + e.message); | |
} | |
} | |
} | |
/** | |
* makeRequire(req,project_path) | |
* Added by Yoz for Orbital | |
* Function factory, returns a sandboxed equivalent of require() | |
* for loading other user-created libraries | |
* TODO: Cache compiled scripts | |
* TODO: Block circular use()s | |
* TODO: Handle file read failures | |
* TODO: Abstract sandboxed file-reading for readFile() and use() | |
* Maybe use() should work in existing sandbox, and make a new require() | |
* that uses temp sandbox? | |
**/ | |
function make_require(mainSandbox,req,project_path) { | |
var projectname = req.projectName; | |
return function (scriptname) { | |
if (/^\./.test(scriptname)) { | |
mainSandbox.respond("require() can't load script name with a dot in it. Nice try!"); | |
} | |
// add .js suffix if there isn't one | |
// TODO: Finer-grained exception handling | |
try { | |
var scriptSource = mainSandbox.readFile(scriptname); | |
tempSandbox = makeBasicSandbox(req, project_path); | |
tempSandbox.exports = {}; | |
tempSandbox.REQUEST = { | |
scriptName: scriptname, | |
projectName: projectname, | |
username: req.username | |
}; | |
var script = new Script(scriptSource, req.username + "/" + projectname + "/" + scriptname); | |
script.runInNewContext(tempSandbox); | |
return tempSandbox.exports; | |
} catch (e) { | |
// TODO: work out best exception behaviour here | |
mainSandbox.respond("use('"+scriptname+"') failed with error: " + e.message); | |
} | |
} | |
} | |
/** | |
* make_log(scriptName, project_path, projectname) | |
* creates a log() function for the project, outputting to a log file | |
* in the project dir | |
**/ | |
function make_log(mainSandbox, req, project_path) { | |
return function (msg) { | |
var logFile = path.join(project_path, req.projectName, "log.txt"); | |
try { | |
var date = new Date().toUTCString(); | |
var f = fs.openSync(logFile,"a"); | |
fs.write(f,date+" ["+req.scriptName+"] "+msg+"\n"); | |
fs.close(f); | |
} catch (e) { | |
// TODO: work out best exception behaviour here | |
mainSandbox.respond("log('"+msg+"') failed with error: " + e.message); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment