How do I avoid using eval on scripts from JSON? - javascript

My web app (running jQuery) gets the following JSON response from the server:
{
"script_0": "$('#objview_0_306').on('click', function(e){ getObjectResponse(96089, '306', 'display'); e.preventDefault();});",
"verb_0": "display",
"obid_0": "0_306",
"body_0": "<div id=\"objview_0_306\"></div>",
"icon_0": "icons/caseman16.png",
"close_0": "previousTab();",
"title_0": "Cases",
"cmdCount": "1"
}
The script_0 element contains scripts that must be run, in this case adding an onClick event to a DOM element.
At the moment, I use eval() to run the scripts, since they are returned as strings. Is there a way to avoid using eval()?

Noop. The eval (and alias Function constructor etc.) is designed to do exactly this, parse strings into runnable javascript.
If you don't want to use eval, instead of return the complete javascript from the server, you can return values in json response that instruct your client app to do predefined tasks.
I'm going to plagiarize the comment made by #Esailija:
Why on earth would you have code in json? it's a data-interchange format...

I do not think that you can achieve what you want without eval or one of its derivates.
You could give a look to jQuery.globalEval() method, as described here: http://api.jquery.com/jQuery.globalEval/
And you could also give a look to the jQuery.getScript() method: http://api.jquery.com/jQuery.getScript/

I'll be the first to spout the usual eval == evil mantra, but im also a realist, and there are times when rules can be broken.
I'd say you might have one of them here.

You can try to append that string with code to DOM and then trigger the click event on needed element.

Related

How to execute string containing function name AND parameters?

In javascript, I have a string like this:
"doSomething('param1','param2')"
And I want to execute it. I am aware that I could normally use
window['doSomething']('param1', 'param2');
But that only works if my function name is separate from the arguments. In my case they are already combined. I think I can use eval() but the consensus seems to be that it should be avoided. Is there another way?
EDIT:
To answer the request for more info: I am using this string like this:
<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
Where closeModalView_Yes will close the modal yes/no window and then execute the given function, although at times I may pass it doSomethingElse(param1) which only takes one parameter.
Use eval, just like:
eval( "console.log( 'hey', 'here I am' )" );
However eval is pretty dangerous and it's not recommended.
If you can (still we don't have much info about your case), render your JavaScript between <script> tags in your template, making it a "regular code", much easier to debug.
Also a good practice is to pass data (i.e. with JSON) rather than code. Try rethinking your logic or provide additional information.
<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
You really shouldn't pass that as a string, but as a function:
closeModalView_Yes(function(){ doSomething('param1','param2'); });
together with
function closeModalView_Yes(callback) {
// do whatever needs to be done to close the window
// and after that
callback();
}
Btw, with your current approach the HTML is not even valid, it would need to be
<a id="YesButton" onclick="closeModalView_Yes("doSomething('param1','param2')")">
<!-- ^^^^^^ ^^^^^^ -->
You could've avoided that by registering the event via javascript instead of inline attributes.

JSON that contains functions

I have a website that returns a JSON-like data structure like this:
{
"name":"tom jones",
"no": 123,
"storedproc": function(){
callbuyer(0123);
}
}
I'm getting this data using $.ajax() with dataType "JSON". Unfortunately, my $.ajax() calls the error callback because my data contains a function().
How can I parse this correctly? I really need to store the function in a variable and call it later.
That is simply not legal JSON (as you know given the title of the question) See the offical JSON syntax. The nice thing about real JSON is that one can use JSON.parse which safely wraps an eval call.
While eval could be used, I would suggest revisiting the architecture of your application and find some other way to do what you are trying to do.
In particular, I would have the server return the 0123 only, and let your client keep the logic that lets it know, in certain cases, which functions apply (in the scenario here, the function would be callbuyer).
This should work because you say you want to call the function which is the value of the storedproc later. Since the body of this function contains a call to callbuyer it follows that your client side script knows what callbuyer is. The trick is for your server not to send back arbitrary, unconstrained functions, but rather data that your client can exploit somehow using the knowledge it has about the overall application.
Could you arrange to have the server return JSON like this:
{"name":"tom jones",
"no": 123,
"storeprocFn": callbuyer,
"arg": "0123"};
Then your callback function can call the callbuyer function and pass arg
Use eval to interpret the string as a javascript object. You won't be able to use the JSON data type though. I believe what you need to do is use 'text' as the dataType for the $.ajax call. Then do something like:
var data = eval('(' + text + ')');
Should work. Of course, eval is evil. But it would solve your problem. As long as you can guarantee there isn't anything malicious in the text (no unsanitized, user entered data) then you should be ok.
AFAIK, functions are left out when using JSON.stringify, it's just not meant to be used to clone full objects (props and methods). However, you might be able to pass the function body as a string.Say you decide on a string format like func=>var foo = 'bar'; return foo;. This should be passed as a regular JSON string, after parsing the object you could then iterate all properties, and convert those strings to functions like so:
for (var prop in parsedObj)
{
if (parsedObj.hasOwnProperty(prop) && parsedObj[prop].match(/^func\=\>/))
{
parsedObj[prop] = new Function(parsedObj[prop].replace('func=>',''));
}
}
Though, seriously, I'd say you might want to rethink your approach, this is not what JSON is for. It's unsafe, all JSON strings are eval'ed, after having made sure they contain no harmful code. This approach is creating a loophole/vulnerability that the JSON people worked hard for to seal off.
For your example will this work:
'user.storeproc = function() { callbuyer( user.no);};'
The Var 'user' is the object of the parsed json.
Ps: maybe you have to format user.no, from 123 to 0123
Following JSON extension, "JFON", does transport of functions and array-properties.
JFON uses eval and is intended for case if:
1) your data is from trusted source ( like not-derived from user input or is a code from your own server), and
2) you know there are no undesired side effects with context of "eval"
(it is a context of eval in function "fromJFON", line 127 )
3) it is costly to refactor your app to use "functionless" JSON;
4) JFON is one-day work, so may be needs more testing;
The idea: use selected property name to escape functions and arrays like
in strings when selected character "\" is used to pass \n and \ for itself.
In JFON, name "wrap" is selected to pass functions and itself: "wrap" : { "fun" : ... and "wrap" : { "esc" : ...
demo: http://landkey.org/Sandbox/z/spaceen86/js/btb/tests/jfon.htm
code ( use commit 0.0.86 ):
https://github.com/lancelab/spaceen/blob/master/js/btb/JFON.js
test: github.com/lancelab/spaceen/blob/master/js/btb/tests/jfon.htm
Here is another, "JWON" extension: JSON-comments, here-documents, monkey-patching of JSONs:
github.com/lancelab/Boardspirator/blob/master/diary/play/tp/jwon.js

How to detect variable dependencies for a javascript function

Say I have this function:
function test(){
return a + b + 1;
}
How can I dynamically figure out that it will require globals a and b to be able to run? E.g. something like get_dependencies(test) returns ['a', 'b']
There's no built-in way to do that in standard JavaScript, if you're trying to do it with JavaScript itself.
On nearly all (but not all) JavaScript engines, you can get a form of the source of a function from the function object's toString function, e.g.:
var testSource = test.toString();
...and then of course you could parse that. This is non-standard behavior (the result of calling toString on a function is not defined in the specification), but it's widely-supported. You'd still have to do the parsing to find the symbols.
For the parsing, you have a couple of options. You could try to separate the parser portion of JSLint out of the rest of it, or alternately the terribly-named UglifyJS compressor has a full JavaScript parser which is already separate from the compressor part (see parse-js.js; apparently there's a tiny bit of NodeJS-specific stuff you might want to remove).
You can use a Javascript 'lint' tool that will test your code for common mistakes or oddities.
Some can be found online:
http://www.jslint.com/
http://www.javascriptlint.com/online_lint.php (can also be downloaded)
In your case, you might want to isolate individual functions via a regular expression for example, and submit them to such a tool.

Javascript security question / Using eval()

I'm seeing code in the following form - is such use of eval() safe?
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset=eval("document."+frm_name);
var x=eval("document."+frm_name+".edit_key");
var y=eval("document."+frm_name+".cAction")
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
Its used as:
<a href="javaScript:;" onClick="genericTakeAction('frmSearch',
'<?php echo $rec_id;?>','<?php echo $pagenum?>','makeOpen')"
class='link6'>Make Open</a>
Whether it's right or wrong, it's needlessly complicated.
function genericTakeAction(frm_name,id,pagenum,action)
{
var rset = document[frm_name];
var x = rset.edit_key;
var y = rset.cAction;
if(x)
x.value=id;
if(y)
y.value=action;
page_list(pagenum);
}
This works because in JavaScript, you can access an object's properties in one of two ways: Either using dotted syntax and a literal identifier, e.g. x = obj.foo;, or using bracket syntax and a string identifier, e.g. x = obj["foo"];. (Note how foo was not in quotes in the first one, but was in quotes for the second; but both do exactly the same thing. Also note that since the property name is a string in the second case, you can use any expression that results in a string, so y = "f"; x = obj[y + "oo"]; also works.)
P.S. It's wrong
eval() is generally frowned upon because, as you are already aware, it is considered unsafe.
In the browser environment, however, it is less of an issue, because in fact, any user could eval() any code they wanted to, using tools like Firebug, etc.
There is still an issue, in that the eval() embedded in the code can be run without the user knowing that he was triggering an eval(), but it's still much less of an issue than in a server-side environment like PHP.
eval() is actually typically used as you've shown to run JSON code being returned from a server-side request. Newer browsers can import JSON more safely using a dedicated JSON parse() function, but older browsers do not have this function and are forced to use eval() for this. Most JSON libraries have eval() in their code somewhere for this reason, but will generally do some sanitisation of the input before running it through eval().
Even if it might look a little bit convoluted, as others have already mentioned, from a pure security perspective, you have to make sure that the 'frm_name' parameter of the genericTakeAction() function can never contain user-supplied data.
In your example, the 'frm_name' parameter contains the hard-coded literal 'frmSearch'. So it is ok as long as this genericTakeAction() function does not get called somewhere else with user-supplied data for the 'frm_name' parameter.
See http://en.wikipedia.org/wiki/Cross-site_scripting#Traditional_versus_DOM-based_vulnerabilities

Parsing XML with default namespace using xPath in javascript

I need to create an XML xPath parser. All parsing has to happen on client side (using javascript). I created an javascript that does this, and everything looks OK until default namespaces come into play. I simply can't query XML that has default namespace.
I created an example code on fiddle. In xmlString is XML string received from server. In xPathString is query done on received XML.
Here are some scenarios:
http://jsfiddle.net/BF34q/1/ - no namespaces - everything works OK
http://jsfiddle.net/BF34q/2/ - ns namespace added. element has ns: prefix. xPath uses this prefix - OK
http://jsfiddle.net/BF34q/3/ - default namespace used - not sure how to configure xPathString.
Note that others will use this parser, so I would really like to avoid solutions like
var xPathString = "//*[local-name()='book']";
and enable them to parse it using simple xPath expressions. I wonder if it is possible to assign default namespace prefix in javascript?
Note: The example provided on fiddle will not work in IE.
I think there are three ways to do this:
Use //*[local-name()='book'] syntax for accessing nodes
Convert XML to string, remove default namespace using RegExp, convert it back to XML
For XML files where you know namespaces in advance, you can create your own namespace resolver, which will allow you to use your own prefix for default namespace.
This can be achieved like this:
function nsResolver(prefix) {
switch (prefix) {
case 'xhtml':
return 'http://www.w3.org/1999/xhtml';
case 'mathml':
return 'http://www.w3.org/1998/Math/MathML';
default:
return 'http://example.com/domain';
}
}
xml.evaluate('//myPrefix:book', xml, nsResolver, XPathResult.ANY_TYPE, null);
I've got the impression that your understanding of the XPath processing doesn't match the implementation - unless, that is, the implementation you're dealing with is very different from the ones I'm familiar with.
Usually, the XPath processor has to have namespaces registered and prefixes mapped to them in order for the expression to be successfully evaluated. So the prefixes could be anything - the only thing that matters is what they're mapped to. See this answer by a known expert to get more information.

Categories