This question already has answers here:
What are the Alternatives to eval in JavaScript?
(11 answers)
Closed 7 years ago.
Mozilla's Content Security Policy disallows the use of javascript eval function as well as inline scripts. They claim that all instances of eval can be replaced by another (hopefully safer) function. I agree in most scenarios, Javascript eval can be replaced, but I'm not sure whether the replacement is possible for every case.
My question is twofold:
Is there a generic way to replace every javascript eval function? (doesn't have to be safe)
Is there a case where the Javascript eval cannot be replaced?
The most common uses which can be substituted are the following ones. I would certainly use these first.
Accessing dynamic properties
Do use: obj[keyAsVariable]
Don't use eval('obj.' + keyAsVariable)
Parsing JSON
Do use JSON.parse(data)
Don't use eval('(' + data + ')')
Calculating user input
Do use a certain library
Don't use eval(input)
If really necessary, you can also send the script to a server which simply echoes it back, and you can request it as a script tag. It won't use eval but still execute it. It isn't safe as it's sent twice over the Internet.
var s = document.createElement('script')
s.src = 'request_script?data=' + data;
document.getElementsByTagName('head')[0].appendChild(s);
request_script could be a file implemented in PHP, like the following. Again, it's bad practice but is a generic way of circumventing eval.
<?
echo $_GET['data'];
?>
You could say that this also automatically answers your second question with 'no'.
Load as script using a Blob
Instead of using eval you can also use a Blob and load the code as if it was an external js file:
To ensure that functions or variables that are inside the code that you are loading are available you need to use a callback method that will be triggered onload event.
var code = "console.log('hello world');";
// With eval:
eval(code);
// With a blob:
var blob = new Blob([code], {type: 'text/javascript'});
var urlCreator = window.URL || window.webkitURL;
var url = urlCreator.createObjectURL( blob );
function loadScript(url, callback)
{
// Add a the script tag to the head
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
// Bind the callback (depends on browser compatibility).
script.onreadystatechange = callback;
script.onload = callback;
// Load the script
head.appendChild(script);
}
// Any variables or methods inside the code will be on callback.
loadScript(url, callback);
Note Be aware that the danger for code injection is similar to eval.
You could wrap the java script within a function call similar to JSONP and then dynamically create a script tag to load that.
Related
Some days ago I came across with Polymer project and web components. It is said to be the future of the web. I do not know much about this. But if I understood well, the idea is to enhance the web code modularity. I would like to ask a simple question, at this respect.
Is there any disadvantage or objection to use AJAX in order to read a presuntly "plain text" file (but with HTML, CSS and JS) and use the "document.write" JavaScript function for inserting that code into a different file? I wondered this, because this seems to solve web code modularity in a very easy and fast way. Is there any problem in doing that?
Thank you!
The general principle is fine, but certainly using document.write is terrible. document.write cannot be safely used after the document is fully parsed. Therefore, it will be unsafe to use after an asynchronous load of a resource.
A much better approach is to create a DOM node, load the fetched resource into the node, and then load the node into DOM. For example, asynchronously loading a script (but the general principle is generalizable to other resources as well):
var xhr = new XMLHttpRequest();
xhr.open("GET", "somescript.js");
xhr.send();
xhr.addEventListener("load", function(e) {
var fetchedScriptContent = e.target.responseText
var s = document.createElement("script");
s.textContent = fetchedScriptContent;
// s.innerText = fetchedScriptContent; // for older browsers
document.body.appendChild(s);
});
There is no problem doing that, except document.write won't work after the page has loaded but it is common usage to do something like this to load scripts at runtime:
function addScript(anUrl) {
var aScript = document.createElement( 'script' );
aScript.type = 'text/javascript';
aScript.src = anUrl;
$("head").append( aScript );
}
The browser will take care of loading and running the script from the url, so you do not need to insert the source manually, but if you need, you can do this way:
function addScriptSource(sourceText) {
var aScript = document.createElement( 'script' );
aScript.type = 'text/javascript';
aScript.textContent = sourceText;
$("head").append( aScript );
}
Scenario
A page invokes a remote script available at this url: http://url.to.script/myScript?ScriptParamsList. Let's assume that:
Async execution is not required.
Displaying output is not required.
The script is called on a button click event. Let Handler() be the javascript event handler:
function Handler()
{
//invoke the remote script
}
Several methods are available to implement Handler() function:
script vs img tag:
document.write('<script src="http://url.to.script/myScript?ScriptParamsList" type="text/javascript"></script>');
document.write('<img src="http://url.to.script/myScript?ScriptParamsList" />');
jQuery .html() vs .load():
$('#TargetDiv').html('<img src="http://url.to.script/myScript?ScriptParamsList" />');
$('#TargetDiv').load('http://url.to.script/myScript?ScriptParamsList');
Question
Which are the advantages and the disadvantages?
document.write will replace your current document when it's called after the document is loaded. Never use this method.
Using <script> allows you to fetch a request from an external domain, without being hindered by the same origin policy. Additionally, in the server's response, you can add and execute JavaScript, which might be useful.
Using .html('<img ...>') makes no sense, unless your server returns an image with meaningful data. If you intend to only trigger a server request, the following would be better:
new Image().src = 'http://url.to.script/myScript?...';
$('..').load is not going to work if the URL is located at a different domain, because of the same origin policy.
I vote for the new Image().src = '..'; method. If you dislike this syntax, and want more jQuery, use:
$('<img>').attr('src', 'http://...');
Note: The result may be cached. If you don't want this to happen, append a random query string, to break the cache (eg. url = url + '&_t=' + new Date().getTime()).
I thought this question would be trivial but I just can't seem to find an answer.
A website (different origin, no control over it) is making available some JSON files. I want some variables of my script to grab the content of those files. I don't care whether it is done synchrnously or not. How would you go ?
using JSONP consist of using your url, with parameters, and add a script file to your page
www.example.com/process?value=1&callback=Func
add the script to your page.
var url = "www.example.com/process?value=1&callback=Func";
var script = document.createElement('script');
script.type= ' text/javascript';
script.src = url;
document.getElementsByTagName("body")[0].appendChild(script);
now you can use the call back function or access the variables that were added from this script.
UPDATE
At the end of your jsonp script you can call your call back function
Ex: php
<?php
if (isset($_GET['callback'])) {
echo $_GET['callback']."();";
// Func(); // will call your function and use your variables.
}
If the remote host does not supply JSONP or CORS, then you will need to place a server-side component on your own domain which fetches the JSON for you and serves it locally.
The following are the first lines of code in a <script> tag just above the closing body tag in my document (it specifies that a locally-served copy of jQuery is run in the event that Google's CDN fails):
if(!window.jQuery){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/js/jquery.js';
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
jQuery(document).ready(function($){
// page behaviors
});
It does execute successfully, in the sense that if my computer is not connected to the Internet (this is a locally-served page), the local copy of jQuery is inserted. However, the document.ready() section below does not execute. I'm guessing this is because it is invoked before the fallback copy of jQuery takes effect. What's the proper practice for somehow "delaying" its execution so that either copy of jQuery will work properly?
Consider using an existing script loader such as yepnope. There's an example of exactly what you're trying to do on the home page.
You need to be sure that the script you are appending to the dom has finished loading before calling jQuery. You can do this with the technique described here:
if(!window.jQuery){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/js/jquery.js';
script.onreadystatechange= function () {
if (this.readyState == 'complete') jQueryLoaded();
}
script.onload = jQueryLoaded;
var scriptHook = document.getElementsByTagName('script')[0];
scriptHook.parentNode.insertBefore(script, scriptHook);
}
function jQueryLoaded() { };
You can also fetch the jQuery contents as an Ajax request, create a script tag with those as the body of the script and append it. That would also work.
Try that
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.2.min.js"><\/script>')</script>
<script>
jQuery(document).ready(function($){
// page behaviors
});
</script>
This way the script tag will be loaded synchronously.
The question "of how do I cope with my CDN failing and load a file hosted on my server" seems to come up a few times lately.
Question I'd ask is whether adding yet more js is the way to achieve the resilience and what level of resilience do the js approaches really add e.g. if the CDN is down they'll be a quick failure but how well do these approaches if the CDN is slow to respond how well do these solutions cope?
An alternative way to approach this is treat it as an infrastructure problem...
Run a CDN based on a domain/sub-domain you own. Have automated monitoring on it's availability, when it fails switch the DNS over to a backup server (anycast may provide an alternative solution too)
A php solution would be something like this:
$google_jquery = 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js';
$fp = #fsockopen($google_jquery, 'r');
if (!$fp)
{
echo '<script type="text/javascript" src="js/jquery.js"></script>';
}
else
{
echo '<script src="'.$google_jquery.'"></script>' }
}
Out of curiosity, I'm wondering about the best (easiest, fastest, shortest, etc; make your pick) way to perform a GET request in JavaScript without using AJAX or any external libraries.
It must work cross-browser and it's not allowed to distort the hosting web page visually or affect it's functionality in any way.
I don't care about headers in the request, just the url-part. I also don't care about the result of the request. I just want the server to do something as a side effect when it receives this request, so firing it is all that matters. If your solution requires the servers to return something in particular, that's ok as well.
I'll post my own suggestion as a possible answer, but I would love it if someone could find a better way!
Have you tried using an Image object? Something like:
var req = new Image();
req.onload = function() {
// Probably not required if you're only interested in
// making the request and don't need a callback function
}
req.src = 'http://example.com/foo/bar';
function GET(url) {
var head = document.getElementsByTagName('head')[0];
var n = document.createElement('script');
n.src = url;
n.type = 'text/javascript';
n.onload = function() { // this is not really mandatory, but removes the tag when finished.
head.removeChild(n);
};
head.appendChild(n);
}
I would go with Pekka idea and use hidden iframe, the advantage is that no further parsing will be done: for image, the browser will try to parse the result as image, for dynamically creating script tag the browser will try to parse the results as JavaScript code.. iframe is "hit and run", the browser doesn't care what's in there.
Changing your own solution a bit:
function GET(url) {
var oFrame = document.getElementById("MyAjaxFrame");
if (!oFrame) {
oFrame = document.createElement("iframe");
oFrame.style.display = "none";
oFrame.id = "MyAjaxFrame";
document.body.appendChild(oFrame);
}
oFrame.src = url;
}