Javascript change string to type function? - javascript

I have a list of functions stored as strings such as this one:
var act= "function () { alert() }";
I need to change the type of act from 'string' to 'function' so I can .call() it. I should end up with this:
act = function () { alert() };
How can this be done?

This is one of the few cases where using eval is not only valid but correct:
var act = eval(function_string);
However, I should note that having a bunch of functions in strings is a sign of bad design. Still, if you must then eval is the way to do it.

Try this:
var act = 'alert(5);';
act = new Function( act );

I guess you have to do it with eval. But many people consider it as evil (Be sure that you get the string from a safe source.)
var act= "function () { alert() }";
eval ('act = '+ act)

You can use eval, although it is considered to be particularly unsafe if the string source is unknown. You can't really assign what you have there to any variable. eval argument has to be an expression or a statement. You can do something like this.
eval("var act = function () { alert('hey') }");
act();​

Related

Proper way to evaluate code

So I'm building a small app where you can evaluate some pieces of JavaScript code, but I'm having a huge "moral" problem:
Initially I wanted to use eval, but I found out about its dangers, so I quickly looked for an alternative.
The closest thing I could find was the function constructor, but for one thing it doesn't evaluate simple pieces of code, such as 2 + 3, since it needs a return statement, whereas eval doesn't, and it's also not that much better security-wise than eval (at least from what I've gathered).
Are there any other ways to evaluate a string as if it were code?
If you want to evaluate JavaScript code, use eval. Is it dangerous? Yes. But that's only because evaluating JavaScript is dangerous. There's no safe way to evaluate JavaScript. If you want to evaluate JavaScript, use eval.
Take every security precaution possible. It's impossible to know what security precautions you should take without knowing more details on what you want to support and how you plan to implement it.
This may be useful:
Is It Possible to Sandbox JavaScript Running In the Browser?
https://github.com/google/caja
You can easily make your own interpreter of JS in JS. I made such thing for www.Photopea.com (File - Scripts, I want to let users execute scripts over PSD documents).
Acorn is an advanced JS parser, which takes a string (JS code) and returns a syntax tree. Then, start at the root of the syntax tree and execute commands one by one.
"Jump" across the tree recursively. Use the JS call stack of the environment as a call stack of the interpreted code. Use JS objects {var1: ..., var2: ...} to store values of variables in each execution space (global, local in a function ...).
You can allow that code to access data from the outer environment through some interface, or make it completely sandboxed. I thought that making my own interpreter would take me a week, but I made it like in 6 hours :)
Please never ever use eval no matter what, there is a much better alternative. Instead of eval, use new function. eval is evil, there's no question about that, but most people skip over the most evil aspect of eval: it gives you access to variables in your local scope. Back in the 90's, back before the concept of JIST compilation, eval's sounded like a good idea (and they were): just insert some additional lines dynamically into the code you're already executing line-by-line. This also meant that evals didn't really slow things down all that much. However, now-a-days with JIST compilation eval statements are very taxing on JIST compilers which internally remove the concept of variable names entirely. For JIST compilers, in order to evaluate an eval statement, it has to figure out where all of its variables are stored, and match them with unknown globals found in the evaled statement. The problem extends even deeper if you get really technical.
But, with new function, the JIST compiler doesn't have to do any expensive variable name lookups: the entire code block is self-contained and in the global scope. For example, take the following terribly inefficient eval snippet. Please note that this is only for the purpose of being an example. In production code, you shouldn't even be using eval or new Function to generate a function from a string whose content is already known.
var a = {
prop: -1
};
var k = eval('(function(b){return a.prop + b;})');
alert( k(3) ); // will alert 2
Now, let's take a look at the much better new Function alternative.
var a = {
prop: -1
};
var k = (new Function('a', 'b', 'return a.prop + b')).bind(undefined, a);
alert( k(3) ); // will alert 2
Notice the difference? There is a major one: the eval is executed inside the local scope while the new Function is executed inside the global one.
Now, onto the next problem: security. There is a lot of talk about how security is difficult, and yes, with eval it is pretty much impossible (e.x. if you wrap the whole code in a sandboxing function, then all you have to do is prematurely end the function and start a new one to execute code freely in the current scope). But, with new Function, you can easily (but not the most efficiently) sandbox anything. Look at the following code.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
Then, fiddle around with the whitelist, get it just the way you want it, and then you can use sandboxed_function just like new Function. For example:
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
"use-strict";
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
'"use-strict";' + arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function('return "window = " + window + "\\ndocument = " + document + "\\nBoolean = " + Boolean');
output.textContent = myfunc();
<pre id="output"></pre>
As for writing code to be runned under this strict sandbox, you may be asking, if window is undefined, how do I test for the existence of methods. There are two solutions to this. #1 is just simply to use typeof like so.
output.textContent = 'typeof foobar = ' + typeof foobar;
<div id="output"></div>
As you can see in the above code, using typeof will not throw an error, rather it will only just return undefined. The 2nd primary method to check for a global is to use the try/catch method.
try {
if (foobar)
output.textContent = 'foobar.constructor = ' + foobar.constructor;
else
output.textContent = 'foobar.constructor = undefined';
} catch(e) {
output.textContent = 'foobar = undefined';
}
<div id="output"></div>
So, in conclusion, I hope my code snippets gave you some insight into a much better, nicer, cleaner alternative to eval. And I hope I have aspired you to a greater purpose: snubbing on eval. As for the browser compatibility, while the sandboxed_function will run in IE9, in order for it to actually sandbox anything, IE10+ is required. This is because the "use-strict" statement is very essential to eliminating much of the sneaky sand-box breaking ways like the one below.
var whitelist = ['Math', 'Number', 'Object', 'Boolean', 'Array'];
var blacklist = Object.getOwnPropertyNames(window).filter(function(x){
return whitelist.indexOf(x) === -1 && !/^[^a-zA-Z]|\W/.test(x)
});
var listlen = blacklist.length;
var blanklist = (new Array(listlen+1)).fill(undefined);
function sandboxed_function(){
blacklist.push.apply(blacklist, arguments);
blacklist[blacklist.length-1] =
/*'"use-strict";' +*/ arguments[arguments.length-1];
var newFunc = Function.apply(
Function,
blacklist
);
blacklist.length = listlen;
return newFunc.bind.apply(newFunc, blanklist);
}
var myfunc = sandboxed_function(`return (function(){
var snatched_window = this; // won't work in strict mode where the this
// variable doesn't need to be an object
return snatched_window;
}).call(undefined)`);
output.textContent = "Successful broke out: " + (myfunc() === window);
<pre id="output"></pre>
One last final comment is that if you are going to allow event API's into your sandboxed environment, then you must be careful: the view property can be a window object, making it so you have to erase that too. There are several other things, but I would recommend researching thoroughly and exploring the objects in Chrome's console.

How to parse functions bodies from a string using javascript?

I'm looking for a way to get a function declaration body by name from a string of js code. I'm in Nodejs environment.
Let's say I have some spaghetti.js file. I can read it into a string
const allJs = fs.readFileSync('spaghetti.js');
Now I need a function that receives that string and function name and returns a string with everything between { }.
Something like this
allJs = 'let a=1; const b=[2, 3]; function cook(items){return items}; cook(b)';
parseFunction(allJs, 'cook');//'return items'
The complexity of input js is not limited.
I tried to find an npm module for that, but no luck.
You should have a look at an AST parser for Javascript:
http://esprima.org/
https://github.com/ternjs/acorn
That should be more safe than using RegExp or something.
A String can be evaluated locally with the native eval() method. But remember, eval is a form of evil!
If the parseFunction() above is relying on something like this then the global Function constructor is being used and the 'new' function is bound to the return value of that operation (and thus that return value itself needs to be called).
A simple way to achieve this might be to do something like this...
var funcString = 'var a = 1, b = 3;';
funcString += 'function summit(){return a + b;}';
funcString += 'return summit();';
function makeNewFunc(str) {
return new Function(str);
}
var newFunc = makeNewFunc( funcString );
console.log('newFunc:',newFunc);
//-> newFunc: function anonymous()
console.log('newFunc():',newFunc());
//-> newFunc(): 4
This demonstrates how functions can be created and invoked from String snippets. (EDIT: Turning something like that into a Node module is a simple matter);
Hope that helped. :)

Converting JSON string to Angular function?

I have an angular service fetching some JSON data, and in this data I have a function as a string (building it server side and passing it to angular), something like:
{
"fn": "function( data ) { data.rendered = data.value; }"
}
Is it possible to parse this and use it as an expression with angular? I.e.
settings.myCustomFunction = serviceData.fn;
I've attempted to do this with different combinations of eval, $eval, $parse, but I haven't had any luck with any of them. They all either throw errors about "Unexpected *" or don't throw errors, but also don't seem to do anything.
You could wrap the string in parentheses to avoid the syntax error.
eval("(" + fnString + ")")
This makes it a syntactically valid JavaScript statement which is composed of a single function expression that you can pass around freely.
An anonymous function by itself, without any parentheses or LHS operators (like the commonly used assignment operator =) acting upon it, is not a syntactically valid JavaScript statement. That's why you'll see IIFEs (immediately invoked function expressions) wrapped up in parentheses in JS:
(function() {
// ...
})()
If you really want to get eval to work, then you can do this:
var settings = {};
eval("settings.myCustomFunction = " + serviceData.fn);
Using eval in this way of course opens your app up to possible malicious script injections.
You could do user new Function():
settings.myCustomFunction = new Function('argument',functionbody);
To execute:
settings.myCustomFunction();//
You might want to check out eval.call. This allows you to not care what the arguments are and use any arguments from strings. Of course to call it you must use the same arguments, BUT, you could string-drive that too, by concatting the args together when you eval.call it.
{
"fn": "function( data ) { return data + 1; }"
}
Example:
var serviceData = {
fn: '(function (data) {return data + 1;})'
};
var data = 44;
var result = window.eval.call(window, serviceData.fn)(data);
console.log(result); // 45

Calling a function bypassing eval()

I am working with a Javascript code that uses eval function.
eval(myString)
The value of myString = myFunc(arg), I want to call myFunc directly without using eval.
I dont have any control over the function to call as I am getting that function as a String (here myString).
The arguments to that function is also part of the same string.
So, is there any way through which I can call the intended function without using eval?
I'm a bit skeptical of allowing users to provide function names at all, but... Assume you have the function name in a variable and the value of arg in a variable. Boom:
var myString = window[fn](arg);
arg is already presumably in an argument, so that's simple enough. The next part is exatracting the function name. Just a bit of regex:
var fn = /^([a-z0-9_]+)\(arg\)$/i.exec(str)[1];
if (fn && typeof window[fn] === 'function') {
window[fn](arg);
}
This does of course assume that the function is always in the global scope, but if it's not, you should be able to adjust accordingly for that. Also, my regex is just the first thing I came up with. It probably doesn't cover all possible function names.
If you wanted to limit the string to just a certain set of functions (and you almost certainly should), that becomes quite easy as well once you have the function name:
var allowedFunctions = {fn1: fn1, fn2: fn2, someOtherFunction: function() {} },
fn = /^([a-z0-9_]+)\(arg\)$/i.exec(str)[1];
if (fn && allowedFunctions[fn]) {
allowedFunctions[fn](arg);
} else {
// Hah, nice try.
}
(If arg isn't actually a variable name but is some kind of literal or perhaps an arbitrary expression, this gets a little more complicated and a lot less safe.)
JavaScript does not provide any way of calling a function represented as a string, other than using eval. There's nothing wrong about using it, though. Given that you have no other option.
Possibly you may try using Function:
var sure = function(s) {
return confirm(s);
};
var str = 'sure("Are you sure?")';
var rtn = new Function('return ' + str)();
alert(rtn);

JavaScript: alert object name as a string

I'm trying to alert any JavaScript object as a string, in a function. This means if the parameter given to the function is window.document, the actual object, it should alert "window.document" (without quotes) as a literal string.
The following calls...
example(window);
example(window.document);
example(document.getElementById('something'));
...calling this function...
function example(o) {/* A little help here please? */}
...should output the following strings...
window
window.document
document.getElementById('something')
I've attempted to do this with combinations of toString() and eval() among some more miscellaneous shots in the dark without success.
No need insane backwards compatibility, newer ECMAScript / JavaScript features/functions are fine. Feel free to inquire for clarifications though the goal should be pretty straight forward.
This is not possible to do in a self contained script.
If using a preprocessor would be an option, then you could write one which converts example(whatever) into example('whatever'). Other than that I'm afraid you're out of luck.
The first problem is that objects don't have names.
The second problem is that from your examples, you're not really wanting to print the (nonexistent) name of an object, you want to print the expression that evaluated into a reference to an object. That's what you're trying to do in this example:
example(document.getElementById('something'));
For that to print document.getElementById('something'), JavaScript would have had to keep the actual text of that expression somewhere that it would make available to you. But it doesn't do that. It merely evaluates the parsed and compiled expression without reference to the original text of the expression.
If you were willing to quote the argument to example(), then of course it would be trivial:
example( "document.getElementById('something')" );
Obviously in this case you could either print the string directly, or eval() it to get the result of the expression.
OTOH, if you want to try a real hack, here's a trick you could use in some very limited circumstances:
function example( value ) {
var code = arguments.callee.caller.toString();
var match = code.match( /example\s*\(\s*(.*)\s*\)/ );
console.log( match && match[1] );
}
function test() {
var a = (1);
example( document.getElementById('body') );
var b = (2);
}
test();
This will print what you wanted:
document.getElementById('body')
(The assignments to a and b in the test() function are just there to verify that the regular expression isn't picking up too much code.)
But this will fail if there's more than one call to example() in the calling function, or if that call is split across more than one line. Also, arguments.callee.caller has been deprecated for some time but is still supported by most browsers as long as you're not in strict mode. I suppose this hack could be useful for some kind of debugging purposes though.
Don't know why you need this, but you can try walking the object tree recursively and compare its nodes with your argument:
function objectName(x) {
function search(x, context, path) {
if(x === context)
return path;
if(typeof context != "object" || seen.indexOf(context) >= 0)
return;
seen.push(context);
for(var p in context) {
var q = search(x, context[p], path + "." + p);
if(q)
return q;
}
}
var seen = [];
return search(x, window, "window");
}
Example:
console.log(objectName(document.body))
prints for me
window.document.activeElement

Categories