This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
This repository was archived by the owner on Feb 25, 2021. It is now read-only.

Latest commit

History

136 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

SandCastle

Build Status

A simple and powerful sandbox for running untrusted JavaScript.

The Impetus

For a project I'm working on, I needed the ability to run untrusted JavaScript code.

I had a couple specific requirements:

  • I wanted the ability to whitelist an API for inclusion within the sandbox.
  • I wanted to be able to run multiple untrusted scripts in the same sandboxed subprocess.
  • I wanted good error reporting and stack-traces, when a sandboxed script failed.

I could not find a library that met all these requirements, enter SandCastle.

What Makes SandCastle Different?

  • It allows you to queue up multiple scripts for execution within a single sandbox.
    • This better suits Node's evented architecture.
  • It provides reasonable stack traces when the execution of a sandboxed script fails.
  • It allows an API to be provided to the sandboxed script being executed.
  • It provides all this in a simple, well-tested, API.

Installation

npm install sandcastle

Creating and Executing a Script

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports.main = function() {\ exit('Hey ' + name + ' Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);// Hello World!});script.run({name: 'Ben'});// we can pass variables into run.

Outputs

Hey Ben Hello World!
  • exit(output): from within untrusted code, causes a sandboxed script to return.
    • Any JSON serializable data passed into exit() will be passed to the output parameter of an exit event.
  • on('exit'): this event is called when an untrusted script finishes execution.
  • run() starts the execution of an untrusted script.

SandCastle Options

The following options may be passed to the SandCastle constructor:

  • timeout — number of milliseconds to allow script to run (defaults to 5000 ms)
  • memoryLimitMB — maximum amount of memory that a script may consume (defaults to 0)
  • useStrictMode — boolean; when true script runs in strict mode (defaults to false)
  • api — path to file that defines the API accessible to script
  • cwd — path to the current working directory that the script will be run in (defaults to process.cwd())
  • spawnExecPath — path to a external node binary to run the sandbox with. (defaults to process.execPath) This is a temporary workaround which allows you to run the sandbox within node-webkit
  • refreshTimeoutOnTask — boolean; refreshes the timeout whenever an answer to a task will be sent to the script

Executing Scripts on Pool of SandCastles

A pool consists of several SandCastle child-processes, which will handle the script execution. Pool-object is a drop-in replacement of single Sandcastle instance. Only difference is, when creating the Pool-instance.

You can specify the amount of child-processes with parameter named numberOfInstances (default = 1).

varPool=require('sandcastle').Pool;varpoolOfSandcastles=newPool({numberOfInstances: 3},{timeout: 6000});varscript=poolOfSandcastles.createScript("\ exports.main = function() {\ exit('Hello World!');\ }\");script.on('exit',function(err,output){console.log(output);});script.run();

Handling Timeouts

If a script takes too long to execute, a timeout event will be fired:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({timeout: 6000});varscript=sandcastle.createScript("\ exports.main = function() {\ while(true) {};\ }\");script.on('exit',function(err,output){console.log('this will never happen.');});script.on('timeout',function(){console.log('I timed out, oh what a silly script I am!');});script.run();

Outputs

I timed out, oh what a silly script I am!

Handling Errors

If an exception occurs while executing a script, it will be returned as the first parameter in an on(exit) event.

var SandCastle = require('sandcastle').SandCastle;
var sandcastle = new SandCastle();
var script = sandcastle.createScript("\ exports.main = function() {\n\ require('fs');\n\ }\");
script.on('exit', function(err, output) {
console.log(err.message);
console.log(err.stack);
});script.run();

Outputs

require is not defined
ReferenceError: require is not defined
at Object.main ([object Context]:2:5)
at [object Context]:4:9
at Sandbox.executeScript (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:58:8)
at Socket.<anonymous> (/Users/bcoe/hacking/open-source/sandcastle/lib/sandbox.js:16:13)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)

Providing an API

When creating an instance of SandCastle, you can provide an API. Functions within this API will be available inside of the untrustred scripts being executed.

An Example of an API:

varfs=require('fs');exports.api={getFact: function(callback){fs.readFile('./examples/example.txt',function(err,data){if(err)throwerr;callback(data.toString());});},setTimeout: function(callback,timeout){setTimeout(callback,timeout);}}

A Script Using the API:

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle({api: './examples/api.js'});varscript=sandcastle.createScript("\ exports.main = function() {\ getFact(function(fact) {\ exit(fact);\ });\ }\");script.on('exit',function(err,result){equal(result,'The rain in spain falls mostly on the plain.',prefix);sandcastle.kill();finished();});script.run();

Exporting Multiple Functions

Rather than main, you create a script file that exports multiple methods. Notice that one extra parameter methodName is available within the callback functions.

varSandCastle=require('sandcastle').SandCastle;varsandcastle=newSandCastle();varscript=sandcastle.createScript("\ exports = {\ foo: function() {\ exit('Hello Foo!');\ },\ bar: function() {\ exit('Hello Bar!');\ },\ hello: function() {\ exit('Hey ' + name + ' Hello World!');\ }\ }\");script.on('timeout',function(methodName){console.log(methodName);});script.on('exit',function(err,output,methodName){console.log(methodName);// foo, bar, hello});// take note that a single script should only be// executing a single method at a time.varcb=null;async.eachLimit(['foo','bar','hello'],1,function(item,_cb){cb=_cb;script.run(item,{name: 'Ben'});});

Providing Tasks

In contrast to the API which runs trusted code inside the sandbox, the script can request that a task (a snippet of code) is executed in another process.

To run a task call runTask(taskName, options = {}) and provide a onTask(taskName, data) method within the script file. Alternatively you can create a task specific function on{TaskName}Task, to receive data for an individual task.

varscript=sandcastle.createScript("\ exports = {\ onGetContentTask: function (data) {\ // received content. do something here...},\ main: function() {\ runTask('getContent', {url: 'http://foo.bar'});\ }\ }\");script.on('task',function(err,taskName,options,methodName,callback){if(whitelistedUrls.indexOf(options.url)!==-1){http.get(options.url,function(res){callback(res);}).on('error',function(e){callback(null);});}});

refreshTimeoutOnTask can be used to control the timeout behavior of the script executing the task. If set to true, the script will have its timeout reset when the task is completed.

Debugging

Make debugging a little easier by ensuring the DEBUG environment variable includes sandcastle.

Contributing

SandCastle will be an ongoing project, please be liberal with your feedback, criticism, and contributions.

  • send pull requests, for creative exploits that you find find for the SandBox. Sandboxing JavaScript is hard, it's unlikely that this library will ever be 100% bullet-proof.
  • write unit tests for your contributions!

Copyright

Copyright (c) 2012 Benjamin Coe. See LICENSE.txt for further details.

About

A simple and powerful sandbox for running untrusted JavaScript.

Resources

Stars

221 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages