├── LICENSE.md ├── README.md ├── changelog.md ├── jsandbox.async.js ├── min ├── jsandbox-worker.min.js └── jsandbox.min.js ├── src ├── jsandbox-worker.js └── jsandbox.js └── test-suite.html /LICENSE.md: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT license. 2 | 3 | MIT license 4 | ----------- 5 | 6 | Copyright © 2010 [Elijah Grey][1], who also goes by [Eli Grey][1]. 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy of this software and associated documentation 10 | files (the "Software"), to deal in the Software without 11 | restriction, including without limitation the rights to use, 12 | copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the 14 | Software is furnished to do so, subject to the following 15 | conditions: 16 | 17 | The above copyright notice and this permission notice shall be 18 | included in all copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 22 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 23 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 24 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 25 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | 30 | [1]: http://eligrey.com 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **DISCONTINUED** - [Not safe](https://github.com/eligrey/jsandbox/issues/10); _Do not use_ 2 | ========================================================================================== 3 | 4 | JSandbox 5 | -------- 6 | 7 | *Version 0.2.3* 8 | 9 | JSandbox is an open source JavaScript 10 | sandboxing library that makes use of HTML5 web workers. JSandbox makes it possible to run 11 | untrusted JavaScript without having to worry about any potential dangers. 12 | 13 | Getting Started 14 | --------------- 15 | 16 | 1. [Download JSandbox][download]. 17 | 2. Include `` anywhere in your 18 | document. I recommend putting it in the document's ``. 19 | 3. Place `` 20 | anywhere after the `` tag. 21 | 4. Read the API documentation below. 22 | 23 | 24 | [download]: http://github.com/eligrey/jsandbox/zipball/master 25 | 26 | 27 | Example Code 28 | ------------ 29 | 30 | This [example code][example] demonstrates the JSandbox API. 31 | 32 | [example]: http://gist.github.com/175160 33 | 34 | 35 | Tested Working Browsers 36 | ----------------------- 37 | 38 | * Firefox 3.5+ 39 | * Google Chrome 4+ 40 | 41 | 42 | API 43 | --- 44 | 45 | ### Worker script location 46 | 47 | Instead of using a `` tag, you may define `JSandbox.url` to specify the location 48 | of the JSandbox worker script. 49 | 50 | 51 | ### Methods 52 | 53 | All of these methods can be accessed on the `JSandbox` constructor (in one-use sandboxes) 54 | and `JSandbox` instances: 55 | 56 |
57 |
eval(options)
58 |
59 | eval()s options.data. If options.callback is a 60 | function, it is passed the results as long as no errors occur. If 61 | options.onerror is a function and an error occurs, it is passed the error 62 | object. The code is eval()ed in a top-level pseudo-function-scope. If you 63 | define a variable using a var statement, the variable is private to the 64 | eval. this is still the global object. If this method is called on 65 | JSandbox, the JSandbox object is returned. Otherwise, the ID 66 | of the request is returned. 67 |
68 | 69 |
exec(options)
70 |
71 | Executes code in a faster method than eval, but does not pass a 72 | return value to the callback function (though the function is still called if 73 | defined). Unlike eval, the code is run in the global scope 74 | (var statements affect this). 75 |
76 | 77 |
load(options)
78 |
79 | If options.data is a string, options.data will attempt to be 80 | loaded in the sandbox. If options.data is an array, every string it 81 | contains will attempt be loaded. If options.onerror is a function and an 82 | error is thrown while parsing a script or a script could not be resolved, 83 | options.onerror is passed the error object. Otherwise, 84 | options.callback is called when the scripts are finished loading. 85 |
86 |
87 | 88 | #### Instance-only methods 89 | 90 | These methods can only be on JSandbox instances: 91 | 92 |
93 |
abort(requestID)
94 |
Aborts a pending request with the ID, requestID.
95 | 96 |
terminate()
97 |
98 | Terminates the worker thread and any pending requests are aborted. You cannot use the 99 | JSandbox instance on which you called this method after it is called. 100 |
101 |
102 | 103 | ### `options` object 104 | 105 | The following are all of the properties that may be included in an `options` object. 106 | 107 |
108 |
data [Required]
109 |
110 | In the case of eval and exec, it is the code to execute. In 111 | the case of load, it is an array of the script(s) to load. If you only 112 | need to load one script, just pass a string instead. 113 |
114 | 115 |
input
116 |
117 | The input data available to the code via the input variable. The input should be 118 | JSON-convertible. 119 |
120 | 121 |
callback
122 |
123 | The callback to pass the return value of the executed code if no exceptions were 124 | thrown. 125 |
126 | 127 |
onerror
128 |
The callback to pass an exception if one is thrown upon executing the code.
129 |
130 | 131 | 132 | ### Alternative syntax 133 | 134 | Any method that takes an options object can also be called using the following 135 | positional-arguments syntax: 136 | 137 | someMethod(data [, callback] [, input] [, onerror]); 138 | 139 | The global `JSandbox` object can also be referenced as `Sandbox`. 140 | 141 | 142 | ![Tracking image](https://in.getclicky.com/212712ns.gif) 143 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | JSandbox Changelog 2 | ================== 3 | 4 | (Worker) 0.2.0.4 5 | ---------------- 6 | 7 | * json2.js is now included directly in the script. 8 | 9 | 10 | 0.2.3 11 | ----- 12 | 13 | * Removed `JSandbox.ready()` as json2.js is now included directly in the script. 14 | * The script now works as a worker. 15 | 16 | 17 | (Worker) 0.2.0.3 18 | ---------------- 19 | 20 | * Now also dereferencing `ActiveXObject` for when or if IE ever supports web workers. 21 | * Same thing as jsandbox.js v0.2.2 in regards to the json2.js API. 22 | * Added a media type to the data: URI used in exec. 23 | 24 | 25 | 0.2.2 26 | ----- 27 | 28 | * Added a `JSandbox.ready(aFunction)` method that calls `aFunction` when JSandbox is 29 | ready (json2.js API is functional). 30 | * Added a global `JSandbox` constructor which is just another reference to `Sandbox`. 31 | * json2.js API methods are now called using JSON as it's `this` object, just in case 32 | it causes trouble for a JSON implementation. 33 | 34 | 35 | (Worker) 0.2.0.2 36 | ---------------- 37 | 38 | * Blocked access to the `Worker` constructor. 39 | * Now using `importScripts.apply` as 40 | [Chromium issue 20192](http://code.google.com/p/chromium/issues/detail?id=20192) 41 | has been fixed. 42 | 43 | 44 | 0.2.1 45 | ----- 46 | 47 | * Removed `document.querySelector` dependancy. 48 | * Preparing test suite for the new, standalone, QUnit library. The library still has 49 | asynchronous bugs so it can't be used yet. 50 | * `self.onmessage` is deleted after every time code is run in case the code defines it. 51 | * Instance catch-alls are now passed the request object as their second argument. 52 | 53 | 54 | 0.2 55 | ----- 56 | 57 | * Renamed the library from "jsandbox" to "JSandbox". 58 | * Renamed the `jsandbox` constructor to `Sandbox`. 59 | * Created unit tests. 60 | * All instances of `uri` in the API have been replaced with `url`. 61 | * Added jsandbox instances (long-lived sandboxes). 62 | * Added `Sandbox.load` to load scripts. `data` must either be a string (one script) or an 63 | array of strings (multiple scripts). 64 | * Added `Sandbox.exec`. Same as eval but no return value and faster. 65 | * Added fixed-position arguments-style API: 66 | `Sandbox[method](data [,callback] [,input] [,onerror])` 67 | * `instance.onresponse` is called on all successfull responses from an instance. 68 | * `instance.onerror` is called all errors from an instance. 69 | * The worker script has been split from jsandbox.js and is now jsandbox-worker.js. 70 | * To specify the location of the worker script, add a 71 | `` tag to your document before 72 | including jsandbox.js. 73 | -------------------------------------------------------------------------------- /jsandbox.async.js: -------------------------------------------------------------------------------- 1 | // JSandbox async.js Module 2 | // Usage: var result = yield [[JSandbox]].async().A_SANDBOX_METHOD(data [, input]); 3 | 4 | JSandbox.prototype.async = function () { 5 | var sandbox = this; 6 | return { 7 | __noSuchMethod__: function (method, args) { 8 | return [function ([code, input], callback) { 9 | sandbox[method](code, callback, input); 10 | }, args]; 11 | } 12 | }; 13 | }; 14 | -------------------------------------------------------------------------------- /min/jsandbox-worker.min.js: -------------------------------------------------------------------------------- 1 | /*! @source http://purl.eligrey.com/github/jsandbox/blob/master/src/jsandbox-worker.js*/ 2 | (function(b,x){"use strict";var y=b.postMessage,u=b.importScripts,l=b.JSON||{};(function(){function o(f){return f<10?"0"+f:f}if(typeof Date.prototype.toJSON!=="function"){Date.prototype.toJSON=function(f){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+o(this.getUTCMonth()+1)+"-"+o(this.getUTCDate())+"T"+o(this.getUTCHours())+":"+o(this.getUTCMinutes())+":"+o(this.getUTCSeconds())+"Z":null};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(f){return this.valueOf()}}var r=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,s=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j,p,z={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},m;function t(a){s.lastIndex=0;return s.test(a)?'"'+a.replace(s,function(f){var d=z[f];return typeof d==="string"?d:"\\u"+("0000"+f.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function q(f,d){var a,g,h,k,n=j,i,c=d[f];if(c&&typeof c==="object"&&typeof c.toJSON==="function"){c=c.toJSON(f)}if(typeof m==="function"){c=m.call(d,f,c)}switch(typeof c){case"string":return t(c);case"number":return isFinite(c)?String(c):"null";case"boolean":case"null":return String(c);case"object":if(!c){return"null"}j+=p;i=[];if(Object.prototype.toString.apply(c)==="[object Array]"){k=c.length;for(a=0;a1){a={data:a,input:b,callback:d,onerror:c}}if(k===L&&typeof a[p]===G){a[p]=[a[p]]}var g=a[p],h=this.createRequestID();b=a[M];delete a[p];delete a[M];this[o][h]=a;this[t].postMessage({id:h,method:k,data:g,input:b,toString:R});return h};l[k]=function(){var a=new l();a[F]=a[s]=function(){a[E]();a=null};l[w][k].apply(a,Array[w].slice[x](arguments));return l}},V=[S,L,T],q=3;while(q--){U(V[q])}z[E]=function(){this[o]={};this[t].onmessage=null;this[t][E]()};z.abort=function(a){delete this[o][a]};z.createRequestID=function(){var a=Math.random().toString();if(a in this[o]){return this.createRequestID()}return a};if(typeof J!==I){var H=J.getElementsByTagName("link");q=H.length;while(q--){if(H[q].getAttribute("rel")==="jsandbox"){l.url=H[q].getAttribute("href");break}}}return l}(self)),Sandbox=JSandbox; 3 | -------------------------------------------------------------------------------- /src/jsandbox-worker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JSandbox worker v0.2.0.4 3 | * 2010-01-25 4 | * By Elijah Grey, http://eligrey.com 5 | * Licensed under the X11/MIT License 6 | * See LICENSE.md 7 | */ 8 | 9 | // This file is requested every time a new sandbox is created. 10 | // Make sure to include a Cache-Control header when serving this over HTTP. 11 | 12 | /*global self */ 13 | 14 | /*jslint evil: true, undef: true, eqeqeq: true, immed: true */ 15 | 16 | /*! @source http://purl.eligrey.com/github/jsandbox/blob/master/src/jsandbox-worker.js*/ 17 | 18 | (function (self, globalEval) { 19 | "use strict"; 20 | 21 | var 22 | postMessage = self.postMessage, 23 | importScripts = self.importScripts, 24 | messageEventType = "message", 25 | 26 | messageHandler = function (event) { 27 | var request = event.data, 28 | response = { 29 | }; 30 | 31 | response.id = request.id; 32 | 33 | var data = request.data; 34 | self.input = request.input; 35 | 36 | try { 37 | switch (request.method) { 38 | 39 | case "eval": // JSLint has something against indenting cases 40 | response.results = globalEval(data); 41 | break; 42 | case "exec": 43 | importScripts("data:application/javascript," + 44 | encodeURIComponent(data)); 45 | break; 46 | case "load": 47 | importScripts.apply(self, data); 48 | break; 49 | 50 | } 51 | } catch (e) { 52 | response.error = { 53 | name: e.name, 54 | message: e.message, 55 | stack: e.stack 56 | }; 57 | } 58 | 59 | delete self.input; 60 | if (self.onmessage) { 61 | delete self.onmessage; // in case the code defined it 62 | } 63 | 64 | postMessage(response); 65 | }; 66 | 67 | if (self.addEventListener) { 68 | self.addEventListener(messageEventType, messageHandler, false); 69 | } else if (self.attachEvent) { // for future compatibility with IE 70 | self.attachEvent("on" + messageEventType, messageHandler); 71 | } 72 | 73 | self.window = self; // provide a window object for scripts 74 | 75 | // dereference unsafe functions 76 | // some might not be dereferenced: https://bugzilla.mozilla.org/show_bug.cgi?id=512464 77 | self.Worker = 78 | self.addEventListener = 79 | self.removeEventListener = 80 | self.importScripts = 81 | self.XMLHttpRequest = 82 | self.postMessage = 83 | //self.dispatchEvent = 84 | // in case IE implements web workers 85 | self.attachEvent = 86 | self.detachEvent = 87 | self.ActiveXObject = 88 | 89 | undefined; 90 | 91 | }(self, eval)); 92 | -------------------------------------------------------------------------------- /src/jsandbox.js: -------------------------------------------------------------------------------- 1 | /* 2 | * JSandbox JavaScript Library v0.2.3 3 | * 2009-01-25 4 | * By Elijah Grey, http://eligrey.com 5 | * Licensed under the MIT License 6 | * See LICENSE.md 7 | */ 8 | 9 | /*global self */ 10 | 11 | /*jslint undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, 12 | newcap: true, immed: true, maxerr: 1000, strict: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/jsandbox/blob/master/src/jsandbox.js*/ 15 | 16 | "use strict"; 17 | 18 | var JSandbox = (function (self) { 19 | var undef_type = "undefined", 20 | doc = self.document, 21 | Worker = self.Worker; 22 | 23 | if (typeof Worker === undef_type) { 24 | return; 25 | } 26 | 27 | var 28 | // repeatedly used properties/strings (for minification) 29 | $eval = "eval", 30 | $exec = "exec", 31 | $load = "load", 32 | $requests = "requests", 33 | $input = "input", 34 | $terminate = "terminate", 35 | $data = "data", 36 | $callback = "callback", 37 | $onerror = "onerror", 38 | $worker = "worker", 39 | $onresponse = "onresponse", 40 | $prototype = "prototype", 41 | $call = "call", 42 | 43 | str_type = "string", 44 | fun_type = "function", 45 | 46 | 47 | Sandbox = function () { 48 | var sandbox = this; 49 | 50 | if (!(sandbox instanceof Sandbox)) { 51 | return new Sandbox(); 52 | } 53 | 54 | sandbox[$worker] = new Worker(Sandbox.url); 55 | sandbox[$requests] = {}; 56 | 57 | sandbox[$worker].onmessage = function (event) { 58 | var data = event[$data], request; 59 | if (typeof data !== "object") { 60 | return; 61 | } 62 | request = sandbox[$requests][data.id]; 63 | if (request) { 64 | if (data.error) { 65 | if (typeof sandbox[$onerror] === fun_type) { 66 | sandbox[$onerror](data, request); 67 | } 68 | if (typeof request[$onerror] === fun_type) { 69 | request[$onerror][$call](sandbox, data.error); 70 | } 71 | } else { 72 | if (typeof sandbox[$onresponse] === fun_type) { 73 | sandbox[$onresponse](data, request); 74 | } 75 | 76 | if (typeof request[$callback] === fun_type) { 77 | request[$callback][$call](sandbox, data.results); 78 | } 79 | } 80 | delete sandbox[$requests][data.id]; 81 | } 82 | }; 83 | }, 84 | proto = Sandbox[$prototype], 85 | 86 | createRequestMethod = function (method) { 87 | proto[method] = function (options, callback, input, onerror) { 88 | if (typeof options === str_type || 89 | Object[$prototype].toString[$call](options) === "[object Array]" || 90 | arguments.length > 1) 91 | { // called in (data, callback, input, onerror) style 92 | options = { 93 | data : options, 94 | input : input, 95 | callback : callback, 96 | onerror : onerror 97 | }; 98 | } 99 | 100 | if (method === $load && typeof options[$data] === str_type) { 101 | options[$data] = [options[$data]]; 102 | } 103 | 104 | var data = options[$data], 105 | id = this.createRequestID(); 106 | 107 | input = options[$input]; 108 | 109 | delete options[$data]; 110 | delete options[$input]; 111 | 112 | this[$requests][id] = options; 113 | 114 | this[$worker].postMessage({ 115 | id : id, 116 | method : method, 117 | data : data, 118 | input : input 119 | }); 120 | 121 | return id; 122 | }; 123 | Sandbox[method] = function () { 124 | var sandbox = new Sandbox(); 125 | 126 | sandbox[$onresponse] = sandbox[$onerror] = function () { 127 | sandbox[$terminate](); 128 | sandbox = null; 129 | }; 130 | 131 | Sandbox[$prototype][method].apply( 132 | sandbox, 133 | Array[$prototype].slice[$call](arguments) 134 | ); 135 | return Sandbox; 136 | }; 137 | }, 138 | methods = [$eval, $load, $exec], 139 | i = 3; // methods.length 140 | 141 | while (i--) { 142 | createRequestMethod(methods[i]); 143 | } 144 | 145 | proto[$terminate] = function () { 146 | this[$requests] = {}; 147 | this[$worker].onmessage = null; 148 | this[$worker][$terminate](); 149 | }; 150 | 151 | proto.abort = function (id) { 152 | delete this[$requests][id]; 153 | }; 154 | 155 | proto.createRequestID = function () { 156 | var id = Math.random().toString(); 157 | if (id in this[$requests]) { 158 | return this.createRequestID(); 159 | } 160 | return id; 161 | }; 162 | 163 | if (typeof doc !== undef_type) { 164 | var linkElems = doc.getElementsByTagName("link"); 165 | i = linkElems.length; 166 | while (i--) { 167 | if (linkElems[i].getAttribute("rel") === "jsandbox") 168 | { 169 | Sandbox.url = linkElems[i].getAttribute("href"); 170 | break; 171 | } 172 | } 173 | } 174 | 175 | return Sandbox; 176 | }(self)), 177 | Sandbox = JSandbox; 178 | -------------------------------------------------------------------------------- /test-suite.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | JSandbox test suite 6 | 7 | 8 | 9 | 10 | 11 | 12 |

JSandbox test suite

13 |

14 |
15 |

16 |
    17 |
    18 | 19 | 122 | 123 | 124 | --------------------------------------------------------------------------------