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.

Revisions

  1. vthibault revised this gist Feb 24, 2015. 2 changed files with 16 additions and 4 deletions.
    14 changes: 14 additions & 0 deletions tests.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,14 @@
    <!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>
    6 changes: 2 additions & 4 deletions tests.js
    Original file line number Diff line number Diff line change
    @@ -93,7 +93,5 @@ var runTests = prepareTests([
    '(new Image()).ownerDocument.defaultView.hack = true;',
    '(new Audio()).ownerDocument.defaultView.hack = true;',

    'result.ownerDocument.defaultView.hack = true;',
    ]);

    runTests();
    'result.ownerDocument.defaultView.hack = true;'
    ]);
  2. vthibault created this gist Feb 24, 2015.
    84 changes: 84 additions & 0 deletions sandbox.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,84 @@
    /// 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;
    }
    99 changes: 99 additions & 0 deletions tests.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,99 @@
    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;',
    ]);

    runTests();