Skip to content

Instantly share code, notes, and snippets.

@vthibault
Last active August 7, 2017 10:28
Show Gist options
  • Save vthibault/50eea0f9c66d8b113c79 to your computer and use it in GitHub Desktop.
Save vthibault/50eea0f9c66d8b113c79 to your computer and use it in GitHub Desktop.
Javascript sandbox
/// Hack it ?
function sandbox(code) {
// Disable private functions not listed in window object
var blackList = [
'Function', // avoid using (new Function("code"))(); to get window access
'eval', // ...
'self', // return window object
'window', // ...
];
// Allowed methods/objects
var whiteList = ['console'];
var global = window;
var _global = {};
var k;
// Check syntax error on code to avoid hack by closing blocks using "}" in code
new Function('"use strict"; ' + code);
// Set environnement
for (k in global) {
_global[k] = undefined;
}
for (k in blackList) {
_global[blackList[k]] = undefined;
}
// Disable shortcut access to dom id
if (typeof document !== 'undefined') {
var elements = document.querySelectorAll('*[name], *[id]');
var i, count = elements.length;
for (i = 0; i < count; ++i) {
if (elements[i].id && elements[i].id in global) {
_global[elements[i].id] = undefined;
}
if (elements[i].name && elements[i].name in global) {
_global[elements[i].name] = undefined;
}
}
}
for (k in whiteList) {
_global[whiteList[k]] = global[whiteList[k]];
}
// Annoying bug on (Image()).ownerDocument ...
function ElementHook(type) {
return function() {
var element = new window[type];
delete element.ownerDocument;
return element;
};
}
var Image = ElementHook('Image');
var Audio = ElementHook('Audio');
// Avoid the hack :
// - (function(){}).constructor(code)();
// - File.constructor(code)();
// - etc.
Function.prototype.constructor = undefined;
// Remove the list so it can't be used in evaled code
whiteList = blackList = k = elements = i = count = global = undefined;
// Eval code
eval('\
// Use our sandboxed global object\n\
with (_global) {\n\
// Strict mode prevent self called function to get window on this.\n\
(function() {\n\
"use strict";\n\
' + code + '\n\
})();\n\
}\n\
');
// Restore
Function.prototype.constructor = Function;
}
<!DOCTYPE html>
<html>
<head>
<title>Sanbox Tests</title>
<script type="text/javascript" src="sandbox.js"></script>
<script type="text/javascript" src="tests.js"></script>
</head>
<body>
<div id="result"></div>
<script type="text/javascript">
runTests();
</script>
</body>
</html>
function prepareTests(codes) {
var _success = 0;
var _result = document.getElementById('result');
var _total = codes.length;
return function runTests() {
codes = codes || codes;
if (!codes.length) {
_result.innerHTML += _success + ' on ' + _total + ' tests succeed';
return;
}
window.hack = false;
var code = codes.shift();
try {
sandbox(code);
}
catch(e) {
console.log(code, e);
}
// Wait to get result
setTimeout(function(){
_success += (window.hack ? 0 : 1);
_result.innerHTML += '<span style="color:' + (!window.hack ? 'green">[SUCCESS]' : 'red">[FAILED]') + '</span> ' + code + '<br/>';
runTests(codes);
}, 20);
};
};
var runTests = prepareTests([
'hack = true;',
'window.hack = true;',
'this.hack = true;',
'self.hack = true;',
'(function(){ hack = true })();',
'(function(){ this.hack = true })();',
'(function(){ self.hack = true })();',
'(function(){ window.hack = true })();',
'new Function("hack = true").call();',
'new Function("this.hack = true").call();',
'new Function("self.hack = true").call();',
'new Function("window.hack = true").call();',
'new Function("hack = true").call(window);',
'new Function("this.hack = true").call(window);',
'new Function("self.hack = true").call(window);',
'new Function("window.hack = true").call(window);',
'setTimeout("hack = true", 10);',
'setTimeout("this.hack = true", 10);',
'setTimeout("self.hack = true", 10);',
'setTimeout("window.hack = true", 10);',
'setTimeout(function(){ hack = true }, 10);',
'setTimeout(function(){ this.hack = true }, 10);',
'setTimeout(function(){ self.hack = true }, 10);',
'setTimeout(function(){ window.hack = true }, 10);',
'var c = setInterval("clearInterval(c); hack = true", 10);',
'var c = setInterval("clearInterval(c); this.hack = true", 10);',
'var c = setInterval("clearInterval(c); self.hack = true", 10);',
'var c = setInterval("clearInterval(c); window.hack = true", 10);',
'var c = setInterval(function(){ clearInterval(c); hack = true }, 10);',
'var c = setInterval(function(){ clearInterval(c); this.hack = true }, 10);',
'var c = setInterval(function(){ clearInterval(c); self.hack = true }, 10);',
'var c = setInterval(function(){ clearInterval(c); window.hack = true }, 10);',
'requestAnimationFrame(function(){ hack = true });',
'requestAnimationFrame(function(){ this.hack = true });',
'requestAnimationFrame(function(){ self.hack = true });',
'requestAnimationFrame(function(){ window.hack = true });',
'var f = function(){}; f.constructor("hack = true")();',
'File.constructor("hack = true")();',
'var d = (new Image()).ownerDocument;\
var s = d.createElement("script");\
s.innerHTML = "hack = true";\
d.body.appendChild(s);',
'var d = (new Audio()).ownerDocument;\
var s = d.createElement("script");\
s.innerHTML = "hack = true";\
d.body.appendChild(s);',
'(new Image()).ownerDocument.defaultView.hack = true;',
'(new Audio()).ownerDocument.defaultView.hack = true;',
'result.ownerDocument.defaultView.hack = true;'
]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment