Specify scope for eval() in JavaScript? - javascript

is there any way I can execute eval() on a specific scope (but NOT global)?
for example, the following code doesn't work (a is undefined on the second statement) because they are on different scope:
eval(var a = 1);
eval(alert(a));
If possible, I would like to create a scope on the fly. for example (the syntax is definitely wrong, but just to illustrate the idea)
var scope1;
var scope2;
with scope1{
eval(var a = 1); eval(alert(a)); // this will alert 1
}
with scope2{
eval(var a = 1); eval(a++); eval(alert(a)); // this will alert 2
}
with scope1{
eval(a += 2); eval(alert(a)); // this will alert 3 because a is already defined in scope1
}
Any idea on how to achieve something like this? Thanks!

you can use the "use strict" to contain the eval'ed code within the eval itself.
Second, eval of strict mode code does not introduce new variables into the surrounding scope. In normal code eval("var x;") introduces a variable x into the surrounding function or the global scope. This means that, in general, in a function containing a call to eval every name not referring to an argument or local variable must be mapped to a particular definition at runtime (because that eval might have introduced a new variable that would hide the outer variable). In strict mode eval creates variables only for the code being evaluated, so eval can't affect whether a name refers to an outer variable or some local variable
var x = 17; //a local variable
var evalX = eval("'use strict'; var x = 42; x"); //eval an x internally
assert(x === 17); //x is still 17 here
assert(evalX === 42); //evalX takes 42 from eval'ed x
If a function is declared with "use strict", everything in it will be executed in strict mode. the following will do the same as above:
function foo(){
"use strict";
var x = 17;
var evalX = eval("var x = 42; x");
assert(x === 17);
assert(evalX === 42);
}

Create the variables you want to exist in your scope as local variables in a function. Then, from that function, return a locally-defined function that has a single argument and calls eval on it. That instance of eval will use the scope of its containing function, which is nested inside the scope of your top level function. Each invocation of the top level function creates a new scope with a new instance of the eval function. To keep everything dynamic, you can even use a call to eval in the top level function to declare the variables that will be local to that scope.
Example code:
function makeEvalContext (declarations)
{
eval(declarations);
return function (str) { eval(str); }
}
eval1 = makeEvalContext ("var x;");
eval2 = makeEvalContext ("var x;");
eval1("x = 'first context';");
eval2("x = 'second context';");
eval1("window.alert(x);");
eval2("window.alert(x);");
https://jsfiddle.net/zgs73ret/

Simple as pie.
// Courtesy of Hypersoft-Systems: U.-S.-A.
function scopeEval(scope, script) {
return Function('"use strict";return (' + script + ')').bind(scope)();
}
scopeEval(document, 'alert(this)');

This worked for me the best:
const scopedEval = (scope, script) => Function(`"use strict"; ${script}`).bind(scope)();
Usage:
scopedEval({a:1,b:2},"return this.a+this.b")

You can look into the vm-browserify project, which would be used in conjunction with browserify.
It works by creating <iframe>s, and evaling the code in that <iframe>. The code is actually pretty simple, so you could adapt the basic idea for your own purposes if you don't want to use the library itself.

Here is a 20-line or so JS class that implements an extensible context using eval in a lexical scope:
// Scope class
// aScope.eval(str) -- eval a string within the scope
// aScope.newNames(name...) - adds vars to the scope
function Scope() {
"use strict";
this.names = [];
this.eval = function(s) {
return eval(s);
};
}
Scope.prototype.newNames = function() {
"use strict";
var names = [].slice.call(arguments);
var newNames = names.filter((x)=> !this.names.includes(x));
if (newNames.length) {
var i, len;
var totalNames = newNames.concat(this.names);
var code = "(function() {\n";
for (i = 0, len = newNames.length; i < len; i++) {
code += 'var ' + newNames[i] + ' = null;\n';
}
code += 'return function(str) {return eval(str)};\n})()';
this.eval = this.eval(code);
this.names = totalNames;
}
}
// LOGGING FOR EXAMPLE RUN
function log(s, eval, expr) {
s = '<span class="remark">' + String(s);
if (expr) {
s += ':\n<b>' + expr + '</b> --> ';
}
s += '</span>';
if (expr) {
try {
s += '<span class="result">' + JSON.stringify(eval(expr)) + '</span>';
} catch (err) {
s += '<span class="error">' + err.message + '</span>';
}
}
document.body.innerHTML += s + '\n\n';
}
document.body.innerHTML = '';
// EXAMPLE RUN
var scope = new Scope();
log("Evaluating a var statement doesn't change the scope but newNames does (should return undefined)", scope.eval, 'var x = 4')
log("X in the scope object should raise 'x not defined' error", scope.eval, 'x');
log("X in the global scope should raise 'x not defined' error", eval, 'x');
log("Adding X and Y to the scope object");
scope.newNames('x', 'y');
log("Assigning x and y", scope.eval, 'x = 3; y = 4');
log("X in the global scope should still raise 'x not defined' error", eval, 'x');
log("X + Y in the scope object should be 7", scope.eval, 'x + y');
log("X + Y in the global scope should raise 'x not defined' error", eval, 'x + y');
.remark {
font-style: italic;
}
.result, .error {
font-weight: bold;
}
.error {
color: red;
}
<body style='white-space: pre'></body>

The approach here was to allow a context object to parameterize the evaluation of the expression.
First a function is created using the Function() constructor that accepts every key of the context as well as the expression to evaluate; the body returns the evaluated expression. Then that function is called with all of the values of the context and the expression to evaluate.
function scopedEval(context, expr) {
const evaluator = Function.apply(null, [...Object.keys(context), 'expr', "return eval('expr = undefined;' + expr)"]);
return evaluator.apply(null, [...Object.values(context), expr]);
}
// Usage
const context = {a: 1, b: 2, c: {d: 3}};
scopedEval(context, "a+b+c.d"); // 6
By using Function.prototype.apply the number of arguments and the names don't need to be known beforehand. Because the arguments are scoped to the evaluator function they are directly accessible from the expression (instead of requiring this.a).

The poor man's method:
If your scope is not too dynamic, just a couple of static and read-only declarations, simply put it in a string and concatenate with the string what you wanna execute like this:
const scopeAll = `
const myFunc = (a, b) => a + b + s;
`
const scope1 = `
${scopeAll}
const s = 'c';
`
const scope2 = `
${scopeAll}
const s = 'd';
`
const myStringToExecute = `
myFunc('a', 'b')
`
console.log(eval(scope1 + myStringToExecute));
console.log(eval(scope2 + myStringToExecute));

I'm not sure how much this adds, but I thought I'd post my version of the Function constructor based solution with modern syntactic sugar and what I think is a good solution to avoid polluting the scope with the internal one containing the evaluated text. (By sacrificing the this context, on which properties can be deleted even inside use strict; code)
class ScopedEval {
/** #param {Record<string,unknown>} scope */
constructor(scope) {
this.scope = scope;
}
eval(__script) {
return new Function(...Object.keys(this.scope),`
return eval(
'"use strict";delete this.__script;'
+ this.__script
);
`.replace(/[\n\t]| +/g,'')
).bind({__script})(...Object.values(this.scope));
}
}
Personally I prefer being able to separate when I add or adjust the scope and when I eval some code, so this could be used like:
const context = {
hi: 12,
x: () => 'this is x',
get xGet() {
return 'this is the xGet getter'
}
};
const x = new ScopedEval(context)
console.log(x.eval('"hi = " + hi'));
console.log(x.eval(`
let k = x();
"x() returns " + k
`));
console.log(x.eval('xGet'));
x.scope.someId = 42;
console.log(x.eval('(() => someId)()'))

be careful; it seems some instances will still get access to outside variables. the code below should fail, but if you run it, you can see it works
let exp = 'a+b';
let a = 1;
let b = 2;
function scopeEval(scope, script) {
return Function('"use strict";return (' + script + ')').bind(scope)();
}
console.log(scopeEval({}, exp));

This is the simplest way I found to do that, but it doesn't use eval.
function execInContext(code, context)
{
return Function(...Object.keys(context), 'return '+ code (...Object.values(context));
}
Here we create a Function object. The Function constructor accepts an array of parameters, the last one is the code to be executed by the function and all others are argument names for the function. What we're doing is creating a function that has arguments with the same names as the fields in the context object and then calling this function with the values of the fields in context. So if you call
execInContext('myVar', {myVar: 'hi!'});
it's the same as
((myVar) => { return myVar; })('hi!');
and the result will be hi!

Function
const evaluate = (context, expr) =>
Function(Object.keys(context).join(','), `return ${expr}`)
(...Object.values(context));
Usage
const result = evaluate({a: 1, b: 2, c: {d: 3}}, "a+b+c.d"); // 6
Example
const evaluate = (context, expr) => Function(Object.keys(context).join(','), `return ${expr}`)(...Object.values(context));
const result = evaluate({a: 1, b: 2, c: {d: 3}}, "a+b+c.d");
console.log('result is: ', result);

script evaluation that provides script-scope, app-level-scope and app-level-scripts-this-object. it is safe and not leaking to app
it also provides a console proxy to redirect console output, it is alive in script-scope, so the script call console.log would go to the proxy.
// app level scope, to share additional function and variable that can be used without `this` keyword.
const scope = {};
// app level this obj for all scripts, use this to reference it;
// so we could share data between scripts.
const thisObj = {};
/**
* execute scripts
* #param thisObj 'this' is an app level obj to share data between scripts
* #param scriptsScope app level scope to share some global predefined functions.
* #param localScope we can setup a 'console' proxy is in this scope.
* #param script code to execute.
*/
const scopedEval = function scopedEval(thisObj, scriptsScope, localScope, script) {
const context = { ...scriptsScope, ...localScope };
// create new Function with keys from context as parameters, 'script' is the last parameter.
const evaluator = Function.apply(null, [...Object.keys(context), 'script',
`"use strict";
try{${script}}
catch (e) {
console.error(e);
}`]);
// call the function with values from context and 'script' as arguments.
evaluator.apply(thisObj, [...Object.values(context), script]);
}
/**
* create a proxy for console. that will also write to the consoleElement
* #param consoleElement the ui element to show the console logs, i.e. a div.
* #returns {Proxy}
*/
const consoleProxy = consoleElement => new Proxy(console, {
get: function (target, propKey) {
const originalMethod = target[propKey];
return function (...args) {
// get time with milliseconds
const now = new Date();
const time = now.getHours().toString().padStart(2,'0') + ':' + now.getMinutes().toString().padStart(2,'0') + ':' + now.getSeconds().toString().padStart(2,'0') + '.' + now.getMilliseconds().toString().padStart(3,'0');
// text to show
const text = document.createTextNode(`${time}: ${args.join(' ')}\n`);
const span = document.createElement('span');
span.appendChild(text);
if (propKey === 'error') {
span.style.color = 'red';
} else if (propKey === 'warn') {
span.style.color = 'orange';
} else if (propKey === 'info') {
span.style.color = 'blue';
} else if (propKey === 'debug') {
span.style.color = 'gray';
}
consoleElement.appendChild(span);
// original console logs, if you need
//originalMethod.apply(target, args);
}
}
});
const codes = document.querySelector('.codes');
const script1 = codes.querySelector('.script1').textContent;
const script2 = codes.querySelector('.script2').textContent;
const scriptConsole1 = codes.querySelector('.script-console1');
const scriptConsole2 = codes.querySelector('.script-console2');
scopedEval(thisObj, scope, { console: consoleProxy(scriptConsole1) }, script1);
scopedEval(thisObj, scope, { console: consoleProxy(scriptConsole2) }, script2);
.code {
background: lightgray;
}
<div class="codes">
<div>
<pre class="code">
<code class="script1">
console.log('hello');
this.a = 1
// error
b++
</code>
</pre>
<pre class="script-console1">
</pre>
<div>
<div>
<pre class="code">
<code class="script2">
this.a++
console.log(this.a);
</code>
</pre>
<pre class="script-console2">
</pre>
</div>
<div>

Related

Sandboxing a function to make it safe? [duplicate]

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.
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.
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
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 );
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);
})();
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.
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.
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.
In my knowledge, in Javascript, any variable declared outside of a function belongs to the global scope, and is therefore accessible from anywhere in your code.
Each function has its own scope, and any variable declared within that function is only accessible from that function and any nested functions. Local scope in JavaScript is only created by functions, which is also called function scope.
Putting a function inside another function could be one possibility where you could achieve reduced scope ( ie nested scope)
if you are talking about a function that is exposed to you by loading a third party script, you are pretty much out of luck. that's because the scope for the function is defined in the source file it's defined in. sure, you can bind it to something else, but in most cases, that's going to make the function useless if it needs to call other functions or touch any data inside it's own source file - changing it's scope is only really feasible if you can predict what it needs to be able to access, and have access to that yourself - in the case of a third party script that touches data defined inside a closure, object or function that's not in your scope, you can't emulate what might need.
if you have access to the source file then it's pretty simple - look at the source file, see if it attempts to access the variable, and edit the code so it can't.
but assuming you have the function loaded, and it doesn't need to interact with anything other than "window", and for academic reasons you want to do this, here is one way - make a sandbox for it to play in. here's a simple shim wrapper that excludes certain items by name
function suspectCode() {
console.log (window.document.querySelector("#myspan").innerText);
console.log('verbotten_data:',typeof verbotten_data==='string'?verbotten_data:'<BANNED>');
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
secret_data = 'i changed the secret data !';
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
}
var verbotten_data = 'a special secret';
window.secret_data = 'special secret.data';
console.log("first call the function directly");
suspectCode() ;
console.log("now run it in a sandbox, which banns 'verbotten_data' and 'secret_data'");
runFunctionInSandbox (suspectCode,[
'verbotten_data','secret_data',
// we can't touch items tied to stack overflows' domain anyway so don't clone it
'sessionStorage','localStorage','caches',
// we don't want the suspect code to beable to run any other suspect code using this method.
'runFunctionInSandbox','runSanitizedFunctionInSandbox','executeSandboxedScript','shim',
]);
function shim(obj,forbidden) {
const valid=Object.keys(obj).filter(function(key){return forbidden.indexOf(key)<0;});
var shimmed = {};
valid.forEach(function(key){
try {
shimmed[key]=obj[key];
} catch(e){
console.log("skipping:",key);
}
});
return shimmed;
}
function fnSrc (fn){
const src = fn.toString();
return src.substring(src.indexOf('{')+1,src.lastIndexOf('}')-1);
}
function fnArgs (fn){
let src = fn.toString();
src = src.substr(src.indexOf('('));
src = src.substr(0,src.indexOf(')')-1);
src = src.substr(1,src.length-2);
return src.split(',');
}
function runSanitizedFunctionInSandbox(fn,forbidden) {
const playground = shim(window,forbidden);
playground.window = playground;
let sandboxed_code = fn.bind(playground,playground.window);
sandboxed_code();
}
function runFunctionInSandbox(fn,forbidden) {
const src = fnSrc(fn);
const args = fnArgs(fn);
executeSandboxedScript(src,args,forbidden);
}
function executeSandboxedScript(sourceCode,arg_names,forbidden) {
var script = document.createElement("script");
script.onload = script.onerror = function(){ this.remove(); };
script.src = "data:text/plain;base64," + btoa(
[
'runSanitizedFunctionInSandbox(function(',
arg_names,
['window'].concat(forbidden),
'){ ',
sourceCode,
'},'+JSON.stringify(forbidden)+')'
].join('\n')
);
document.body.appendChild(script);
}
<span id="myspan">Page Access IS OK<span>
or a slightly more involved version that allows arguments to be passed to the function
function suspectCode(argument1,argument2) {
console.log (window.document.querySelector("#myspan").innerText);
console.log(argument1,argument2);
console.log('verbotten_data:',typeof verbotten_data==='string'?verbotten_data:'<BANNED>');
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
secret_data = 'i changed the secret data !';
console.log('secret_data:',typeof secret_data==='string'?secret_data:'<BANNED>'); // undefined === we can't
console.log('window.secret_data:',typeof window.secret_data==='string'?window.secret_data:'<BANNED>');
}
var verbotten_data = 'a special secret';
window.secret_data = 'special secret.data';
console.log("first call the function directly");
suspectCode('hello','world') ;
console.log("now run it in a sandbox, which banns 'verbotten_data' and 'secret_data'");
runFunctionInSandbox (suspectCode,['hello','sandboxed-world'],
[
'verbotten_data','secret_data',
// we can't touch items tied to stack overflows' domain anyway so don't clone it
'sessionStorage','localStorage','caches',
// we don't want the suspect code to beable to run any other suspect code using this method.
'runFunctionInSandbox','runSanitizedFunctionInSandbox','executeSandboxedScript','shim',
]);
function shim(obj,forbidden) {
const valid=Object.keys(obj).filter(function(key){return forbidden.indexOf(key)<0;});
var shimmed = {};
valid.forEach(function(key){
try {
shimmed[key]=obj[key];
} catch(e){
console.log("skipping:",key);
}
});
return shimmed;
}
function fnSrc (fn){
const src = fn.toString();
return src.substring(src.indexOf('{')+1,src.lastIndexOf('}')-1);
}
function fnArgs (fn){
let src = fn.toString();
src = src.substr(src.indexOf('('));
src = src.substr(0,src.indexOf(')'));
src = src.substr(1,src.length);
return src.split(',');
}
function runSanitizedFunctionInSandbox(fn,args,forbidden) {
const playground = shim(window,forbidden);
playground.window = playground;
let sandboxed_code = fn.bind(playground,playground.window);
sandboxed_code.apply(this,new Array(forbidden.length).concat(args));
}
function runFunctionInSandbox(fn,args,forbidden) {
const src = fnSrc(fn);
const arg_names = fnArgs(fn);
executeSandboxedScript(src,args,arg_names,forbidden);
}
function executeSandboxedScript(sourceCode,args,arg_names,forbidden) {
var script = document.createElement("script");
script.onload = script.onerror = function(){ this.remove(); };
let id = "exec"+Math.floor(Math.random()*Number.MAX_SAFE_INTEGER).toString();
window.execArgs=window.execArgs||{};
window.execArgs[id]=args;
let script_src = [
'runSanitizedFunctionInSandbox(function(',
['window'].concat(forbidden),
(arg_names.length===0?'':','+arg_names.join(","))+'){',
sourceCode,
'},',
'window.execArgs["'+id+'"],',
JSON.stringify(forbidden)+');',
'delete window.execArgs["'+id+'"];'
].join('\n');
let script_b64 = btoa(script_src);
script.src = "data:text/plain;base64," +script_b64;
document.body.appendChild(script);
}
<span id="myspan">hello computer...</span>
I verified #josh3736's answer but he didn't leave an example
Here's one to verify it works
parent.html
<h1>parent</h1>
<script>
abc = 'parent';
function foo() {
console.log('parent foo: abc = ', abc);
}
</script>
<iframe></iframe>
<script>
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', function() {
console.log('-calling from parent-');
iframe.contentWindow.foo();
});
iframe.src = 'child.html';
</script>
child.html
<h1>
child
</h1>
<script>
abc = 'child';
function foo() {
console.log('child foo: abc = ', abc);
}
console.log('-calling from child-');
parent.foo();
</script>
When run it prints
-calling from child-
parent foo: abc = parent
-calling from parent-
child foo: abc = child
Both child and parent have a variable abc and a function foo.
When the child calls into the parent's foo that function in the parent sees the parent's global variables and when the parent calls the child's foo that function sees the child's global variables.
This also works for eval.
parent.html
<h1>parent</h1>
<iframe></iframe>
<script>
const iframe = document.querySelector('iframe');
iframe.addEventListener('load', function() {
console.log('-call from parent-');
const fn = iframe.contentWindow.makeFn(`(
function() {
return abc;
}
)`);
console.log('from fn:', fn());
});
iframe.src = 'child.html';
</script>
child.html
<h1>
child
</h1>
<script>
abc = 'child';
function makeFn(s) {
return eval(s);
}
</script>
When run it prints
-call from parent-
from fn: child
showing that it saw the child's abc variable not the parent's
note: if you create iframes programmatically they seem to have to be added to the DOM or else they won't load. So for example
function loadIFrame(src) {
return new Promise((resolve) => {
const iframe = document.createElement('iframe');
iframe.addEventListener('load', resolve);
iframe.src = src;
iframe.style.display = 'none';
document.body.appendChild(iframe); // iframes don't load if not in the document?!?!
});
}
Of course in the child above we saw that the child can reach into the parent so this code is NOT SANDBOXED. You'd probably have to add some stuff to hide the various ways to access the parent if you want make sure the child can't get back but at least as a start you can apparently use this technique to give code a different global scope.
Also note that of course the iframes must be in the same domain as the parent.
Here's another answer. This one's based on how chained scopes work in javascript. It also uses Function(), which produces faster code than eval.
/** This takes a string 'expr', e.g. 'Math.max(x,1)' and returns a function (x,y)=>Math.max(x,1).
* It protects against malicious input strings by making it so that, for the function,
* (1) no names are in scope other than the parameters 'x' and 'y' and a whitelist
* of other names like 'Math' and 'Array'; (2) also 'this' binds to the empty object {}.
* I don't think there's any way for malicious strings to have any effect.
* Warning: if you add something into the global scope after calling make_fn but
* before executing the function you get back, it won't protect that new thing.
*/
function make_fn(expr) {
const whitelist = ['Math', 'Array']; // Warning: not 'Function'
let scope = {};
for (let obj = this; obj; obj = Object.getPrototypeOf(obj)) {
Object.getOwnPropertyNames(obj).forEach(name => scope[name] = undefined);
}
whitelist.forEach(name => scope[name] = this[name]);
const fn = Function("scope","x","y","with (scope) return "+expr).bind({});
return (x,y) => fn(scope,x,y);
}
This is how it behaves: https://jsfiddle.net/rkq5otme/
make_fn("x+y")(3,5) ==> 8
make_fn("Math.max(x,y)")(3,5) ==> 5
make_fn("this")(3,5) ==> {}
make_fn("alert('oops')") ==> TypeError: alert is not a function
make_fn("trace((function(){return this}()))")(3,5) ==> ReferenceError: trace is not defined
Explanation. Consider a simpler version
Function("x", "return Math.max(x, window, this);")
This creates a function with the specified body which has two chained scopes: (1) the function scope which binds x, (2) the global scope. Let's spell out how the symbols Math, x, window and this are all resolved.
x is bound to the property in the function scope
window is bound to the property in the next chained scope, global, i.e. window['window'].
We don't want this! To prevent it, we create our own scope object scope = {window:undefined,...} and write with (scope) return Math.max(x,window,this). The with statement is only allowed in non-strict code. It adds an additional scope, so the chain is now: (0) the specified scope object we created, (1) the function scope which binds x, (2) the global scope. Because name 'window' is found in our scope object, it can never bind to the one in global scope.
Math will suffer the same fate.
We want to whitelist it! So we make our scope object {Math:global.Math, window:undefined, ...}
this is bound upon invocation of the function to the global object, window.
We don't want this! To prevent it, we call .bind({}) on the function, which wraps the function in a wrapper which sets this={}. Alas bind(null) didn't seem to bind.
Note: there's another possible construction of the scope object, using Proxy. It doesn't seem particularly better.
const scope = new Proxy({}, {
has: (obj, key) => !['x','y'].includes(key),
get: (obj, key) => (whitelist.includes(key)) ? this[key] : undefined,
set: (obj, key, value) => {},
deleteProperty: (obj, key) => {},
enumerate: (obj, key) => [],
ownKeys: (obj, key) => [],
defineProperty: (obj, key, desc) => {},
getOwnPropertyDescriptor: (obj, key) => undefined,
});

confusing use of commas and newlines in variable assignment expression makes var look global

Update: it was really the comma before the that variable assignment which threw me off, not so much about any patterns. (don't use this notation. see https://stackoverflow.com/a/38893132/244811 for more information)
So I was reading this article about Javascript's inheritance mechanisms, one of which is the "functional inheritance" pattern. The code example given (my //wtf's):
var vehicle = function(attrs) {
var _privateObj = {
hasEngine: true
},
that = {}; //wtf??
that.name = attrs.name || null;
that.engineSize = attrs.engineSize || null;
that.hasEngine = function () {
alert('This ' + that.name + ' has an engine: ' + _privateObj.hasEngine);
};
return that;
}
var motorbike = function () {
// private
var _privateObj = {
numWheels: 2
},
// inherit
that = vehicle({
name: 'Motorbike',
engineSize: 'Small'
}); //wtf??
// public
that.totalNumWheels = function () {
alert('This Motobike has ' + _privateObj.numWheels + ' wheels');
};
that.increaseWheels = function () {
_privateObj.numWheels++;
};
return that;
};
var boat = function () {
// inherit
that = vehicle({
name: 'Boat',
engineSize: 'Large'
}); //wtf??
return that;
};
myBoat = boat();
myBoat.hasEngine(); // This Boat has an engine: true
alert(myBoat.engineSize); // Large
myMotorbike = motorbike();
myMotorbike.hasEngine(); // This Motorbike has an engine: true
myMotorbike.increaseWheels();
myMotorbike.totalNumWheels(); // This Motorbike has 3 wheels
alert(myMotorbike.engineSize); // Small
myMotorbike2 = motorbike();
myMotorbike2.totalNumWheels(); // This Motorbike has 2 wheels
myMotorbike._privateObj.numWheels = 0; // undefined
myBoat.totalNumWheels(); // undefined
with the conclusion:
You can see that it is fairly easy to provide encapsulation. The
_privateObj can not be modified from outside of the object, unless exposed by a public method like increaseWheels(). Similarly, private
values can also only be read when exposed by a public method, such as
motorbike’s totalNumWheels() function.
Each and every function here seems to declare a global variable "that", which you should never do - right? There are no closures that I can see (inner function has access to whatever variables were in scope at the time of the inner function's declaration). Is there a closure mechanism I'm missing? Does this code illustrate a valid pattern?
They aren't declaring global variables, they're declaring closure variables. Every function definition you attach to that is a closure (provided it uses a variable from the surrounding scope).
Example:
function createObj() {
var that = {}; // Not global but will be used in a closure
that.name = 'Bob';
that.doSomething = function() {
return that.name; // Used as a closure variable
};
return that; // Return a new object, not a global one
}
They're applying the same principle except they're also creating a separate object, _privateObj which is never directly exposed. This lets you have private data and methods which no one else can access.
You might think they're declaring a global due to the different syntax for declaring multiple variables.
This:
var a = 1,
b = 2;
is equivalent to this:
var a = 1;
var b = 2;
Notice the use of the , in the previous example. That allows you to declare multiple variables in a single var statement.
Your //wtf's code means:
var that = new Object();

Scope of variables in module pattern

I am trying to understand how I can touch/change/increment my privately scoped variable x in the following script. I'm using the Module pattern here, and thought I could reach into and set the private variables from a public return module declared property or method, but nothing I'm trying is working. Related: when do you declare a new instance of func vs. accessing func as a static delcared variable?
var func = (function() {
var x = 1;
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
}
return {
X: x,
Xq: squareX,
AddOne: addOne
};
});
func().X = 9; // expecting privately scoped x = 9
func().AddOne(); // expecting privately scoped x = 10
document.write(func().Xq()); // expecting 100 actual = 1
The point of the module pattern is to create a persistent, private scope which is invisible from the outside. Unfortunately, every time you call func, you're creating a new scope (with new return functions and their closures), so all of your operations are discarded afterwards.
Instead of calling func multiple times, just do it once to setup the "module" (you can even do this immediately, with an IIFE), and then perform operations on the result.
var func = function() {
var x = 1; // this is the private variable
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
};
return {
// Note, you can't just do "X: x,"
// since that will just create a copy;
// you have to use properties
get X() { return x; },
set X(val) { x = val; },
Xq: squareX,
AddOne: addOne
};
};
var funcModule = func();
funcModule.X = 9;
funcModule.AddOne();
document.write(funcModule.Xq());
Note that the reason you need an explicit getter and setter for the X module property is because you need to be able to modify the inner (hidden) variable x. Properties are available in all modern browsers, including IE9+. If you're working in IE8 or below, you'll need to define explicit getX and setX methods, and call them directly (you won't just be able to do funcModule.X = 5).
You need a setter, and you need an IIFE:
var func = (function() {
var x = 1;
var squareX = function() {
return x * x;
};
var addOne = function() {
x++;
}
return {
X: function(value) {
if (value !== undefined) {
x = value;
}
return x; // we can use this as a getter too!
},
Xq: squareX,
AddOne: addOne
};
})(); // <-- this actually runs the function - this makes it an IIFE
document.write("X is " + func.X() + "</br>");
func.X(9); // expecting privately scoped x = 9
document.write("now X is " + func.X() + "</br>");
func.AddOne(); // expecting privately scoped x = 10
document.write("now X is " + func.X() + "</br>");
document.write(func.Xq()); // expecting 100 actual = 100
You're using the Revealing Module Pattern to hide your private instance variable. When using that pattern, you must use a setter of some kind to change your private instance variable. Here is another StackOverflow post where someone else was having the same problem.
I don't recommend that you use the Revealing Module Pattern. In fact, I just gave a talk at NationJS 2014 titled "The Revealing Module is an Anti-Pattern".

Can you dynamically add local variables to a function?

I'm using objects to namespace my JavaScript code. These objects usually contain functions that are called mapping the this-pointer to the object itself using apply. However, I find it inconvenient to use the this-pointer everytime I want to access other functions or properties of the object, especially because in lots of cases I use the new-operator to use function-objects the way you would use classes. I would prefer writing new Line() instead if new this.Line().
It would be great if you could add local variables to a function the way php does it with extract (pseudocode following, it's a little more complicated)
var sample_object = {
"some_function": function() {}
}
test() {
extract(sample_object);
some_function(); // imported from sample_object
}
Is that even possible?
I'm pretty sure eval is your only answer; but you need to be aware that if there's any input outside of your control involved, it isn't safe
function dynamicArgs (varName, varValue) {
eval("var " + varName + "=" + JSON.encode(varValue) );
alert(a);
}
dynamicArgs("a", "value");
You can see the problem with this. How is your function supposed to call the dynamic variable if it doesn't know its name? I hardcoded it to the a variable since I pass it in when calling it, but that's not a good solution. The only solution would be another eval. You really should think about what you need to do and whether this is useful. But it's doable.
Here it is in action: http://jsfiddle.net/mendesjuan/GG3Wu/
function dynamicArgs (varName, varValue) {
eval('var ' + varName + "='" + varValue + "';");
alert(eval(varName));
}
dynamicArgs("f", "Here I am");
Now here's an example like what you're doing, creating a variable from this.MyConstructor
http://jsfiddle.net/mendesjuan/AK3WD/
var ns = {
MyConstructor: function(val) {
this.prop = val;
},
runConstructor: function(val) {
var Ctor = "MyConstructor";
eval('var ' + Ctor + ' = this.' + Ctor);
return new MyConstructor(val);
}
}
alert( ns.runConstructor("Hello").prop );
And here's an example if you wanted to import all the values from an object into the scope;
http://jsfiddle.net/mendesjuan/AK3WD/1/
var ns = {
MyConstructor: function(val) {
this.val= val;
},
anotherProperty: 5,
runConstructor: function(val) {
// Bring all the variables from this into this scope
for (var prop in this) {
eval('var ' + prop + ' = this.' + prop);
}
alert('Testing var anotherProperty: ' + anotherProperty);
var obj = new MyConstructor(val);
alert('Created MyConstructor: its prop is ' + obj.val)
}
}
ns.runConstructor("Hello");
There is controversial with, which has some great applications, but is marginally slow and prone to errors. It throws an error in the strict mode (which you should always opt into) and is going to be deprecated.
var sampleObject = {
someFunction: function() {},
b: 10
}
with (sampleObject) {
typeof someFunction // "function"
var a = 42
var b = 20
}
sampleObject.a // undefined
sampleObject.b // 20
Note, that new variables defined in a with-block won't be added to the object. Nevertheless, if the object already had an eponymous property in it, this property would be modified (thanks, #Rocket).
Just for fun, here's an implementation of extract using eval (which is even more evil than with). You can do unspeakable things with it, for example if your object has properties like sampleObject['x; while (true) { alert("Hi!") }'].
This is how I did it:
function smObject ( object) {
return function () {
function getter(prop) {
return function() {
return this[prop];
}
}
function setter(prop) {
return function(data) {
this[prop]=data;
}
}
for (var o = 0; o < object.length; o++) {
this[object[o]] = {};
this['get' + object[o]] = getter(object[o]);
this['set' + object[o]] = setter(object[o]);
}
}
}
now you can instantiate a function like this:
var fields = ['Name', 'Id', 'Other', '....' ]
var MyFunction = smObject( fields );
var myObject = new MyFunction();
// getter/setters
myObject.setId(5);
myObject.getId(); // will return 5
Regards,
Emanouil

JavaScript: Get Argument Value and NAME of Passed Variable [duplicate]

This question already has answers here:
Determine original name of variable after its passed to a function
(9 answers)
Closed 8 years ago.
What I want to do is get the NAME of a variable passed to a function and the VALUE of that variable, and only have to pass in one variable to the function. So:
var x = "anything";
function showName() {
}
showName(x);
or
showName("x");
Which will return: "x = anything".
Right now, I have to specify the variable twice:
showName("x", x);
In order to get the name and value of the variable I am passing in.
Note that I am not interested in the name of argument in the prototype of showName, but the name of the variable in the calling function. Also, the variable passed may be local, so I can't use the window object to find the variable.
The short answer is that you can't.
The longer, evil answer is that you sort of can with some real nastiness. And it only works when called from another function.
there are two interesting attributes available to you that could help
arguments.callee
caller
for fn to do something like this:
(function(){
var showMe = function(s){
alert(arguments.callee.caller.toString().match(/showMe\((\S)\)/)[1] +
' = '+ s)
}
x = 1
showMe(x)
})()
What arguments.callee.caller.toString().match(..)[1] does is look for the showMe being called in the function calling it and prints it and its value.
But this is still pretty limited because it will only hit the first call of showMe(x). So if there is two calls to it, it won't work.
But, it was fun to play with these arcane things.
Strategy 1:
If you can control the data structure during function invocation then you can pass a dictionary which will encode name as a key, paired with its value, notice the stealth curly braces:
var foo = "bar";
yourfunction({foo});
Which passes a javascript dictionary that looks like this:
{foo : "bar"}
When yourfunction( is executed, unpack name and value thustly:
yourfunction = function(dict) {
var name = Object.keys(dict)[0];
var value = dict[name];
console.log(name); //prints foo
console.log(value); //prints bar
}
Strategy 2:
If you can maintain an as-you-go list of name-value pairs in a global scope, then reflection and introspection is always available for set and get, for example:
var my_global_stack = [];
yourfunction = function() {
//Chomp the stack
var dict = my_global_stack.pop();
//The name is the key at index 0
var name = Object.keys(dict)[0];
//Fetch the value by keyname:
var value = dict[name];
console.log(name); //prints foo
console.log(value); //prints bar
}
foo = "bar";
my_global_stack.push({foo});
yourfunction();
Strategy 3:
If user-hostile input isn't an issue, you can use eval( to rediscover value given variablename, for example:
yourfunction = function(somevariable) {
console.log(somevariable); //prints foo
console.log(eval(somevariable)); //prints bar
}
foo = "bar";
yourfunction("foo");
People say eval( is evil here, because if a hostile user is able to overwrite the value of foo in memory at any point, then they can do OS Command Injection and run any command they want.
http://cwe.mitre.org/top25/#Guidance
var x = "anything";
function showName(s) {
alert(s + " = " + eval(s));
}
showName("x");
Not recommended, but there it is.
You could create a hash and pass that in:
var x = {a: 1,b:2}
function showVars(y) {
for (var z in y) { alert(z + " is " + y[z]); }
}
showVars(x);
This doesn't necessarily show the name of the variable, but it does allow for key-value pairs, which may be more to the point of what you need.
This is what I use for debugging. No global variables, no eval, no arguments.callee or arguments.caller:
var Helpers = (function () {
// ECMAScript 5 strict mode
'use strict';
var Module = {};
Module.debug = function () {
var i;
for (i = 0; i < arguments.length; i++) {
console.log(arguments[i] + ':', this[arguments[i]]);
}
};
Module.SomeObject = function SomeObject() {
this.someMember = 1;
this.anotherMember = 'Whatever';
Module.debug.call(this, 'someMember', 'anotherMember');
var privateMember = {
name: 'Rip Steakface',
battleCry: 'Raaaaaaaaahhhhhhhhhrrrrrrrrrg!'
};
Module.debug.call(privateMember, 'name', 'battleCry');
};
return Module;
}());
For those who are wondering why you would want to do this, it's just a way to efficiently log multiple variables along with their names.
If you want to be able to log nested members, as in Module.debug.call(obj, 'hair.fluffiness'), you can modify the function like so:
Module.debug = function () {
var i, j, props, tmp;
for (i = 0; i < arguments.length; i++) {
tmp = this;
props = arguments[i].split('.');
for (j = 0; j < props.length; j++) {
tmp = tmp[props[j]];
}
console.log(arguments[i] + ':', tmp);
}
};
Unfortunately, I can't find any way to efficiently log multiple private variables that aren't members of an object, e.g. var roll = 3, value = 4; Module.debug.call(???);
Not sure you can directly get what you want from JavaScript, since the variable name is not carried around with the value it references (think of variable names as identifiers only the compiler knows about; but which get thrown away at runtime).
You can, however, do something slightly different which allows for passing around named arguments. Create an anonymous object and pass that to your function:
function showNames(o)
{
for( var ix in o )
{
alert( ix + ":" + o[ix] );
}
}
var z = { x : "Anything" }
showNames( z );
// or
showNames( { a : "ay", b : "bee", c: "see" } )
For iterating object properties, I tend to prefer a functional-style, as in:
Array.iteri = function(o, f)
{
for(var i in o) { f(i, o[i]) }
}
function showNames(o)
{
Array.iteri( o, function(i,v)
{
alert( i + ": " + v )
});
}
showNames( { a : "ay", b : "bee", c: "see" } )
The below code is about the best you can do. Unfortunately local variables in a function are properties of the hidden Call Object so they can't be accessed from Javascript like window[a] where a is a property of the window object.
x = "this is x";
var say = function(a) {
document.write(a + " = " + window[a]);
}
say("x");
var wrapper = function () {
var x = "this is x";
document.write(x + " = " + eval("x"))
}
wrapper()

Categories

Resources