CSP: Why is document.createElement("script") good and document.write("<script ...") bad? - javascript

I'm just a bit confused. I've been going through a presentation slide deck on Content Security Policies. On slide 10, when discussing the 'strict-dynamic' directive, three examples are given: one good, two bad:
// good
<script nonce="r4nd0m">
var s = document.createElement("script");
s.src = "//example.com/bar.js";
document.body.appendChild(s);
</script>
// bad
<script nonce="r4nd0m">
var s = "<script src=//example.com/bar.js></script>";
document.write(s);
// -OR-
document.body.innerHTML = s;
</script>
Google also advises to make similar changes on their CSP page ("Refactor calls to JS APIs incompatible with CSP").
I understand that the good example loads async while the bad one doesn't (unless async defer is added). But...
If I wanted to load an evil script, wouldn't both ways get me there?
Is the issue that the "bad" way doesn't propagate the nonce (automatically)? Or is it about the "bad" way being a bit like a call to eval() in that you can do pretty much anything with it (although, in HTML 5, a script wouldn't execute unless attached to an event handler, apparently)? Is the assumption that the "bad" way likely uses parser-inserted user input and that's why it's bad (that's what I got from the presentation video I found later)?
Why is one call permissible while the other isn't (when, in the example given, they seem to achieve the same thing - loading an external script)?

Related

Override document.write

I'm trying to override document.write so i will be able to take the raw html parse it make some manipulations on the code and return the call.
My entire process is async so it's needless to say that when document.write is called if the page has finished loading the document.write will erase the entire document, so i can't recall document.write.
I've searched and found many discussions about that but almost every one of them is very very old so i can't find any good answer.
my code for now is very basic:
const prevDocWrite = document.write;
document.write = function(str,patched) {
if(patched){
prevDocWrite.call(this, str);
}else{
Dom_Parser(str, context).then(newStr => {
prevDocWrite.call(this, newStr);
})};
};
I have added the "patched" part because i'm also calling document.write on my code so i call it like that document.write("str",true) and it will call the original document.write immediately.
I would really appreciate any help or ideas how to make this happen (other projects for reference will be great)
BTW i saw a lot of implementation using innerHtml but that's screwing up the <script> tags:(
Thanks a LOT

How Javascript is getting executed in browser by Javascript Engine?

Question not for solution, Question to understand the system better
Experts! I know whenever you feed javascript code into javascript engine, It will execute by javascript engine immediately. Since, I haven't seen Engine's source code, I have few of questions as follows,
Let us assume I am loading couple of files from remote server namely FILE_1.js and FILE_2.js.
And the code in FILE_2.js is requiring some of the code in FILE_1.js. So I have included files as follows,
<script type="text/javascript" src="FILE_1.js" ></script>
<script type="text/javascript" src="FILE_2.js" ></script>
So hopefully, I have done what Javascript Engine requires. Here unfortunately I have written 5000KB of code in FILE_1.js, and however I have 5KB of code in FILE_2.js. Since server is multi-threaded definitely FILE_2.js will be loaded into my browser before FILE_1.js completed.
How javascript engine handle this?
And If moved the code from FILE_2.js to inline-script tag as follows, what are actions taken by javascript engine to manage this dependency?
<script type="text/javascript" src="FILE_1.js" ></script>
<script type="text/javascript" >
// Dependent code goes here
</script>
Note: I am not expecting single word answer Single Threaded. I just want to know deep who is manage issuing request either browser or javascript engine or common guy? if the request/response is handled by common guy, then how javascript engine aware about this?
When I post an answer about the behavior of code, I always like to go to two places:
The specification
The implementation
The specification:
The DOM API explicitly specifies scripts must be executed in order:
If the element has a src attribute, does not have an async attribute, and does not have the "force-async" flag set
The element must be added to the end of the list of scripts that will execute in order as soon as possible associated with the Document of the script element at the time the prepare a script algorithm started.
From 4.1 Scripting. Please check the list of exceptions to this rule before - having the defer or async attribute. This is specified well in 4.12.1.15.
This makes sense, imagine:
//FILE_1.js
var trololo = "Unicorn";
....
// 1 million lines later
trololo = "unicorn";
var message = "Hello World";
//FILE_2.js
alert(message); // if file 1 doesn't execute first, this throws a reference error.
It is generally better to use a module loader (that will defer script insertion and execution, and will manage dependencies correctly for you).
At the moment, the best approach is to use something like Browserify or RequireJS . In the future, we'll be able to use ECMAScript 6 modules.
The implementation:
Well, you mentioned it and I couldn't resist. So, if we check the Chromium blink source (still similar in WebKit):
bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition,
LegacyTypeSupport supportLegacyTypes)
{
.....
} else if (client->hasSourceAttribute() && // has src attribute
!client->asyncAttributeValue() &&// and no `async` or `defer`
!m_forceAsync // and it was not otherwise forced
) { // - woah, this is just like the spec
m_willExecuteInOrder = true; // tell it to execute in order
contextDocument->scriptRunner()->queueScriptForExecution(this,
m_resource,
ScriptRunner::IN_ORDER_EXECUTION);
Great, so we can see in the source code that it adds them in order parsed - just like the specification says.
Let's see how a script runner does:
void ScriptRunner::queueScriptForExecution(ScriptLoader* scriptLoader,
ResourcePtr<ScriptResource> resource,
ExecutionType executionType){
.....
// Adds it in the order of execution, as we can see, this just
// adds it to a queue
case IN_ORDER_EXECUTION:
m_scriptsToExecuteInOrder.append(PendingScript(element, resource.get()));
break;
}
And, using a timer, it fires them one by one when ready (or immediately, if nothing is pending):
void ScriptRunner::timerFired(Timer<ScriptRunner>* timer)
{
...
scripts.swap(m_scriptsToExecuteSoon);
for (size_t i = 0; i < size; ++i) {
....
//fire!
toScriptLoaderIfPossible(element.get())->execute(resource);
m_document->decrementLoadEventDelayCount();
}

Dynamically Included Javascript and Dependencies

So, as a sort of exercise for myself, I'm writing a little async script loader utility (think require.js, head.js, yepnope.js), and have run across a little bit of a conundrum. First, the basic syntax is like this:
using("Models/SomeModel", function() {
//callback when all dependencies loaded
});
Now, I want to know, when this call is made, what file I'm in. I could do it with an ajax call, so that I can mark a flag after the content loads, but before I eval it to mark that all using calls are going to be for a specific file, then unset the flag immediately after the eval (I know eval is evil, but in this case it's javascript in the first place, not json, so it's not AS evil). I'm pretty sure this would get what I need, however I would prefer to do this with a script tag for a few reasons:
It's semantically more correct
Easier to find scripts for debugging (unique file names are much easier to look through than anonymous script blocks and debugger statements)
Cross-domain requests. I know I could try to use XDomainRequest, but most servers aren't going to be set up for that, and I want the ability to reference external scripts on CDN's.
I tried something that almost got me what I needed. I keep a list of every time using is called. When one of the scripts loads, I take any of those using references and incorporate them into the correct object for the file that just loaded, and clear the global list. This actually seems to work alright in Firefox and Chrome, but fails in IE because the load events seem to go off at weird times (a jQuery reference swallowed a reference to another type and ended up showing it as a dependency). I thought I could latch on to the "interactive" readystate, but it doesn't appear to ever happen.
So now I come asking if anybody here has any thoughts on this. If y'all want, I can post the code, but it's still very messy and probably hard to read.
Edit: Additional usages
//aliasing and multiple dependencies
using.alias("ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js", "jQuery");
using(["jQuery", "Models/SomeModel"], function() {
//should run after both jQuery and SomeModel have been loaded and run
});
//css and conditionals (using some non-existant variables here)
using.css({ src: "IEFix", conditionally: browser === "MSIE" && version < 9 });
//should include the IEFix.css file if the browser is IE8 or below
and to expound more on my response below, consider this to be file A (and consider the jquery alias from before to be there still):
using(["jQuery", "B"], function() {
console.log("This should be last (after both jQuery and B have loaded)");
console.log(typeof($));
});
Then this would be B:
using("C", function() {
console.log("This should be second");
});
And finally, C:
console.log("This should be first");
The output should be:
This should be first
This should be second
This should be last (after both jQuery and B have loaded)
[Object Object]
Commendable that you are taking on such an educational project.
However, you won't be able to pull it off quite the way you want to do it.
The good news is:
No need to know what file you are in
No need to mess with eval.
You actually have everything you need right there: A function reference. A callback, if you will.
A rough P-code for your using function would be:
function using(modules, callback) {
var loadedModules = []
// This will be an ajax call to load things, several different ways to do it..
loadedModules[0] = loadModule(modules[0]);
loadedModules[1] = loadModule(modules[1]);
// Great, now we have all the modules
// null = value for `this`
callback.apply(null, loadedModules);
}

dynamic script tag loading is not working as expected

We have an application that uses both the google closure and dojo libraries. We have the following in our index page which works as expected:
<script type="text/javascript" src="runtime/src/lib/google-closure-rev26/closure/goog/base.js"></script>
<script type="text/javascript" src="runtime/src/lib/dojo_release_132_src/dojo/dojo.js"></script>
<script type="text/javascript" src="runtime/src/core/loader.js"></script>
We would like to use only one script tag in the actual html source. So we tried to do the following:
<head>
<script type="text/javascript" src="runtime/src-bootstrap.js"></script>
</head>
and then in src-bootstrap.js:
var head = document.getElementsByTagName("head")[0];
var s1 = document.createElement("script");
s1.type = "text/javascript";
s1.src = "runtime/src/lib/google-closure-rev26/closure/goog/base.js";
var s2 = document.createElement("script");
s2.type = "text/javascript";
s2.src = "runtime/src/lib/dojo_release_132_src/dojo/dojo.js";
var s3 = document.createElement("script");
s3.type = "text/javascript";
s3.src = "runtime/src/core/loader.js";
head.appendChild(s1);
head.appendChild(s2);
head.appendChild(s3);
However, this doesn't work in FF. core/loader.js runs before dojo is loaded completely. Any ideas why this doesn't work?
For this type of mechanism, you'd be better off using document.write() to include your scripts. The technique you're currently using is suited to lazy-loading scripts, and it downloads and executes the scripts asynchronously: http://www.nczonline.net/blog/2009/06/23/loading-javascript-without-blocking/
...or you could have a build process that actually concatenates these files, and just request the one script, which would save on the number of requests too, as what you've actually done is increased the number of requests.
My guess would be that because you are creating the elements through the DOM, instead of having them as markup, the browser doesn't wait for one script to be finished before executing the next (as would be the case in a straight <script></script><script></script>setup).
How about appending the scripts in a cascaded form (Google closure appends s2 at its end, Dojo s3) or, as Lee Kowalkowski suggests, writing <script> commands using document.write()?
Generally Speaking - add a namespace under window, and edit your external resources-
leave one action.js or main.js file locally, that will be added a method, preferably under global scope (meaning under window..).
edit your external resource, adding 1 extra line at the end, calling for a method on action.js or main.js, when the loading will be done, the "callback like" will execute that method you've been adding to the DOM previously. it works very much like JSONProtocol.
it works wonders even with with the most complex combination of dynamically loaded resources.
see the example for this very similar solution provided for dynamically loading the Google-Closure-Library on another thread (https://stackoverflow.com/a/17226714/257319)

alternative to cross-domain javascripting?

currently i am relying on a proxy script to handle this problem of Single Origin Policy. it is slow, and creates overhead. Not to mention, javascript is not rendered.
is there a working alternative out there?
If you can provide a callback name as a parameter to the service providing the JavaScript code in question, then you can append a script tag to your document, with a src attribute pointing to the service call. Otherwise, you're out of luck.
Use an iframe and try window.postMessage(message, origin) (it would be parent.postMessage from the iframe and iframeElement.contentWindow.postMessage from the top page) for all of the latest major browsers (Firefox, IE, Safari, Chrome, etc.) and changing/polling window.name for old browsers.
Oh dear, I think the solution you're looking for is with IFRAMEs. However the iframe approach is both a mental and technical undertaking. I suggest you start with this guide:
Cross-Domain Communication with IFrames
The alternative approach is getting data from another server asynchronously using script tags and json:
<script src="http://remotesite.com/path/to/script/blah.js"></script>
You can create a new SCRIPT tag element to pass and load data and append to DOM or insert the markup into an elements innerHTML.
I'm sure you can find some detailed examples and ways to implement but one thing you should keep a track of with the new SCRIPT method is adding so many tot he DOM. This might help and provide a starting point for you:
function require (url, callback) {
if (!isScriptLoaded(url)) {
document.write('<script src="' + url + '" type="text/javascript" charset="utf-8"><\/script>');
if (callback) {
callback();
}
}
}
function isScriptLoaded(src) {
var scriptsLoaded = {};
var scriptTags = document.getElementsByTagName("script");
for (var i = 0, script; script = scriptTags[i]; i++) {
if (script.src) {
scriptsLoaded[script.src] = 1;
}
};
if (scriptsLoaded[src]) {
return true;
}
return false;
}
(untested, but should work!)
Either way - best of luck.
JSON-P is pretty much ideal for this kind of thing. If you're using jQuery, or similar JavaScript libraries, your job is made even easier:
http://docs.jquery.com/Ajax/jQuery.getJSON#urldatacallback
Of course, it will depend on exactly what you are trying to do that will determine whether to use JSON-P, hidden iframes, postMessage, Flash proxies, or any other exotic solution.
If you control both domains and only care about Firefox 3.5+, you can use the XMLHttpRequest Object and set up permissions with Access Control.

Categories