Suppose I have a variables in the global scope.
Suppose I wish to define a function which I can guarantee will not have access to this variable, is there a way to wrap the function, or call the function, that will ensure this?
In fact, I need any prescribed function to have well defined access to variables, and that access to be defined prior to, and separate from that function definition.
Motivation: I'm considering the possibility of user submitted functions. I should be able to trust that the function is some variety of "safe" and therefore be happy publishing them on my own site.
8 Answers
Answers 1
Run the code in an iframe
hosted on a different Origin. This is the only way to guarantee that untrusted code is sandboxed and prevented from accessing globals or your page's DOM.
Answers 2
Using embedded Web Workers could allow to run safe functions. Something like this allows a user to enter javascript, run it and get the result without having access to your global context.
globalVariable = "I'm global"; document.getElementById('submit').onclick = function() { createWorker(); } function createWorker() { // The text in the textarea is the function you want to run var fnText = document.getElementById('fnText').value; // You wrap the function to add a postMessage // with the function result var workerTemplate = "\ function userDefined(){" + fnText + "}\ postMessage(userDefined());\ onmessage = function(e){console.log(e);\ }" // web workers are normally js files, but using blobs // you can create them with strings. var blob = new Blob([workerTemplate], { type: "text/javascript" }); var wk = new Worker(window.URL.createObjectURL(blob)); wk.onmessage = function(e) { // you listen for the return. console.log('Function result:', e.data); } }
<div>Enter a javascript function and click submit</div> <textarea id="fnText"></textarea> <button id="submit"> Run the function </button>
You can try these for example by pasting it in the textarea:
return "I'm a safe function";
You can see that it's safe:
return globalVariable;
You can even have more complex scripts, something like this:
var a = 4, b = 5; function insideFn(){ // here c is global, but only in the worker context c = a + b; } insideFn(); return c;
See info about webworkers here, especially embedded web workers: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers#Embedded_workers
Answers 3
A little late, but maybe it will help you a bit
function RestrictFunction(params) { params = ( params == undefined ? {} : params ); var scope = ( params.scope == undefined ? window : params.scope ); var data = ( params.data == undefined ? {} : params.data ); var script = ( params.script == undefined ? '' : params.script ); if (typeof params.script == 'function') { script = params.script.toString(); script = script.substring(script.indexOf("{") + 1, script.lastIndexOf("}")); } // example: override native functions that on the white list var setTimeout = function(_function,_interval) { // this is important to prevent the user using `this` in the function and access the DOM var interval = scope.setTimeout( function() { RestrictFunction({ scope:scope, data:data, script:_function }); } , _interval ); // Auto clear long user intervals scope.setTimeout( function() { scope.clearTimeout(interval); } , 60*1000 ); return interval; } // example: create custom functions var trace = function(str) { scope.console.log(str); } return (function() { // remove functions, objects and variables from scope var queue = []; var WhiteList = [ "Blob","Boolean","Date","String","Number","Object","Array","Text","Function", "unescape","escape","encodeURI","encodeURIComponent","parseFloat","parseInt", "isNaN","isFinite","undefined","NaN", "JSON","Math","RegExp", "clearTimeout","setTimeout" ]; var properties = Object.getOwnPropertyNames(scope); for (var k = 0; k<properties.length; k++ ) { if (WhiteList.indexOf(properties[k])!=-1) continue; queue.push("var "+properties[k]+" = undefined;"); } for (var k in scope) { if (WhiteList.indexOf(k)!=-1) continue; queue.push("var "+k+" = undefined;"); } queue.push("var WhiteList = undefined;"); queue.push("var params = undefined;") ; queue.push("var scope = undefined;") ; queue.push("var data = undefined;") ; queue.push("var k = undefined;"); queue.push("var properties = undefined;"); queue.push("var queue = undefined;"); queue.push("var script = undefined;"); queue.push(script); try { return eval( '(function(){'+ queue.join("\n") +'}).apply(data);' ); } catch(err) { } }).apply(data); }
Example of use
// dummy to test if we can access the DOM var dummy = function() { this.notify = function(msg) { console.log( msg ); }; } var result = RestrictFunction({ // Custom data to pass to the user script , Accessible via `this` data:{ prop1: 'hello world', prop2: ["hello","world"], prop3: new dummy() }, // User custom script as string or function script:function() { trace( this ); this.msg = "hello world"; this.prop3.notify(this.msg); setTimeout( function() { trace(this); } , 10 ); trace( data ); trace( params ); trace( scope ); trace( window ); trace( XMLHttpRequest ); trace( eval ); return "done!"; // not required to return value... }, }); console.log( "result:" , result );
Answers 4
I'm going give a technical answer to your question with at least one possibility. Use the name of the global as an argument to that function:
someGlobal = 5; function cantSeeThatGlobal(someGlobal) { console.log(someGlobal); } cantSeeThatGlobal(); // prints undefined cantSeeThatGlobal(10); // prints 10
It would be better of course just to not use global variables ever.
Answers 5
You can't restrict the scope of a Function using the "call" or "apply" methods, but you can use a simple trick using "eval" and scoping to essentially hide any specific global variables from the function to be called.
The reason for this is because the function has access to the "global" variables that are declared at the scope that the function itself what declared. So, by copying the code for the method and injecting it in eval, you can essentially change the global scope of the function you are looking to call. The end result is essentially being able to somewhat sandbox a piece of javascript code.
Here's a full code example:
<html> <head> <title>This is the page title.</title> <script> function displayTitle() { alert(document.title); } function callMethod(method) { var code = "" + // replace global "window" in the scope of the eval "var window = {};" + // replace global "document" in the scope of the eval "var document = {}; " + "(" + // inject the Function you want to call into the eval method.toString() + // call the injected method ")();" + ""; eval(code); } callMethod(displayTitle); </script> </head> <body></body> </html>
The code that gets eval'd looks like this:
var window = {}; var document = {}; (function displayTitle() { alert(document.title); })();
Answers 6
You can use WebWorkers to isolate your code:
Create a completely separate and parallel execution environment (i.e. a separate thread or process or equivalent construct), and run the rest of these steps asynchronously in that context.
Here is a simple example:
someGlobal = 5; //As a worker normally take another JavaScript file to execute we convert the function in an URL: http://stackoverflow.com/a/16799132/2576706 function getScriptPath(foo) { return window.URL.createObjectURL(new Blob([foo], { type: 'text/javascript' })); } function protectCode(code) { var worker = new Worker(getScriptPath(code)); } protectCode('console.log(someGlobal)'); // prints 10 protectCode('console.log(this.someGlobal)'); protectCode('console.log(eval("someGlobal"))'); protectCode('console.log(window.someGlobal)');
This code will return:
Uncaught ReferenceError: someGlobal is not defined
undefined
Uncaught ReferenceError: someGlobal is not defined
and
Uncaught ReferenceError: window is not defined
so you code is now safe.
Answers 7
Create a local variable with the same name. If you have a global variable like this:
var globalvar;
In your function:
function noGlobal(); { var globalvar; }
If the function refers to globalvar, it will refers to the local one.
Answers 8
EDIT: This answer does not hide the window.something variables. But it has a clean way to run user-defined code. I am trying to find a way to mask the window variables
You can use the javascript function Function.prototype.bind() to bind the user submitted function to a custom scope variable of your choosing, in this custom scope you can choose which variables to share with the user defined function, and which to hide. For the user defined functions, the code will be able to access the variables you shared using this.variableName. Here is an example to elaborate on the idea:
// A couple of global variable that we will use to test the idea var sharedGlobal = "I am shared"; var notSharedGlobal = "But I will not be shared"; function submit() { // Another two function scoped variables that we will also use to test var sharedFuncScope = "I am in function scope and shared"; var notSharedFuncScope = "I am in function scope but I am not shared"; // The custom scope object, in here you can choose which variables to share with the custom function var funcScope = { sharedGlobal: sharedGlobal, sharedFuncScope: sharedFuncScope }; // Read the custom function body var customFnText = document.getElementById("customfn").value; // create a new function object using the Function constructor, and bind it to our custom-made scope object var func = new Function(customFnText).bind(funcScope); // execute the function, and print the output to the page. document.getElementById("output").innerHTML = JSON.stringify(func()); } // sample test function body, this will test which of the shared variables does the custom function has access to. /* return { sharedGlobal : this.sharedGlobal || null, sharedFuncScope : this.sharedFuncScope || null, notSharedGlobal : this.notSharedGlobal || null, notSharedFuncScope : this.notSharedFuncScope || null }; */
<script type="text/javascript" src="app.js"></script> <h1>Add your custom body here</h1> <textarea id="customfn"></textarea> <br> <button onclick="submit()">Submit</button> <br> <div id="output"></div>
The example does the following:
- Accept a function body from the user
- When the user clicks submit, the example creates a new function object from the custom body using the Function constructor. In the example we create a custom function with no parameters, but params can be added easily as the first input of the Function constructor
- The function is executed, and its output is printed on the screen.
- A sample function body is included in comments, that tests which of the variables does the custom function has access to.
0 comments:
Post a Comment