I'm trying to load some data from Reddit via a Safari extension. I'm using a JSONP pattern to create the callback function and attach the new src to the script. However, it looks like there are two window namespaces, and the function that I dynamically create is not available to the context of the dynamically added script.
This link seems to detail the problem for chrome, which I'm guessing is similar to mine in Safari.
JSONP request in chrome extension, callback function doesn't exist?
Here's the code (works outside of extension):
function jsonp(url, callback) {
var callbackName = 'jsonp_callback_' + Math.round(100000 * Math.random());
window[callbackName] = function(data) {
delete window[callbackName];
callback(data);
};
console.log('about to create script');
var script = document.createElement('script');
script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'jsonp=' + callbackName;
document.body.appendChild(script);
console.log('script should be appended');
}
function getImages(){
jsonp('http://www.reddit.com/r/cats/.json', function(data) {
data.data.children.forEach(function(el){
console.log(el.data.url);
});
});
}
Any ideas, work arounds?
Thanks!
You're right about there being two namespaces in the same window. Injected scripts cannot access a web page's globals, and vice versa. When you initiate JSONP in an injected script, the script of the inserted <script> tag runs in the web page's namespace.
I know of two ways to work around this limitation in a Safari extension.
The probably better way is to use your extension's global page to communicate with the external API through standard XHR or jQuery Ajax methods (which are supported in global page scripts) and use messages to pass the fetched data to an injected script.
The other way is to go ahead and use JSONP from the injected script, but have the JSONP callback function add the fetched data to the page's DOM, which is accessible by the injected script. For example, you could put the data in a hidden <pre> element and then use the element's innerHTML property to retrieve it. Of course, this technique does make the content visible to the page's own scripts, so exercise caution.
Related
We are attempting to add CSRF protection to our existing java web application by using CSRFGuard. We've followed the OWASP's guide to token injection, and that has gotten us most of the way there. We're using the dynamic DOM manipulation method, and find that most URLS/forms/AJAX calls are properly formatted with the inserted CSRF token. Our issue is this:
Parts of some pages are generated dynamically by AJAX calls that return jspfs. The jspfs that are returned have links which were never subject to the CSRFGuard DOM Manipulation, and as such, don't have the CSRF token. Clicking on those links causes a CSRF violation because no token is present.
Furthermore, according to the OWASP guide for AJAX support, the dynamic script needs to be reference prior to the AJAX call so that the AJAX call can be intercepted and have the CSRF token inserted into the header. This is the same script that dynamically updates the DOM. So - to solve the issue posed in this question I would need to run the script after the AJAX call, but I also need to run it before the AJAX call to make it in the first place. Trying to run it twice causes issues.
What is the proper solution here? Does that CSRFGuard javascript file need to be modified so that dynamic token injection can be run against targeted elements? Has this issue been solved already?
I had the same problem. I modified csrfguard.js this way:
I moved out all functions from (function() {}) block and put them before the block.
I defined 2 new functions
function getTokenNameValuePair() {
var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest : new window.ActiveXObject("Microsoft.XMLHTTP");
var csrfToken = {};
xhr.open("POST", "%SERVLET_PATH%", false);
xhr.setRequestHeader("FETCH-CSRF-TOKEN", "1");
xhr.send(null);
return xhr.responseText;
}
function injectTokensForNewTags() {
var token_pair = getTokenNameValuePair();
token_pair = token_pair.split(":");
var token_name = token_pair[0];
var token_value = token_pair[1];
injectTokens(token_name, token_value);
}
And your AJAX that is returning a HTML chunk with links should look like this:
$.post(loadurl, function(data) {
$(target).html(data);
injectTokensForNewTags();
});
Note: this answer requires modifying CSRFGuard.
I am using SkateJS (https://github.com/skatejs/skatejs) to watch for dynamic updates of <a>, <img>, and <form> tags and calling a routine I added to csrfguard.js to add the token to them.
This catches tags that are inserted after the initial DOM load by javascript toolkits.
I had to also modify the script to keep it from always scanning the entire DOM tree at load time to insert the tokens. This was very inefficient and was unnecessary with the above method.
Here's an example of the SkateJS config:
window.addEventListener('load',
function() {
var config = {
ready : function(element) {
if (CsrfGuard && CsrfGuard.isEnabled) {
CsrfGuard.injectTokens([element]);
} else {
skate.destroy();
}
},
type : skate.types.TAG
};
skate('a', config);
skate('img', config);
skate('form', config);
}
);
Note: this will not work on IE8. For that, I am using Microsoft DHTML behaviors.
<![if IE 8]>
<style>
a,img,form {
behavior: url("csrf_ie8.htc");
}
</style>
<![endif]>
csrf_ie8.htc:
<public:attach event="ondocumentready" onevent="CsrfGuard.injectTokens([element]);" />
I want to develop a Chrome extension, just imagine when Facebook loads you are allowed to add extra JS on it.
But my problem is I can't modify the DOM of the later content, which means the newly loaded content that appear when the user scrolled down.
So I want to detect XHR using JavaScript.
I tried
send = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
/* Wrap onreadystaechange callback */
var callback = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState == 4) {
/* We are in response; do something, like logging or anything you want */
alert('test');
}
callback.apply(this, arguments);
}
_send.apply(this, arguments);
}
But this is not working.. any ideas?
Besides Arun's correct remark that you should use _send for both, your approach doesn't work because of how Content Scripts work.
The code running in the content script works in an isolated environment, to prevent it from conflicting with page's own code. So it's not like you described - you're not simply adding JS to the page, you have it run isolated. As a result, your XHR replacement only affects XHR calls from your extension's content scripts and not the page.
It's possible to inject the code into the page itself. This will affect XHR's from the page, but might not work on all pages, if the Content Security Policy of the page in question disallows inline code. It seems like Facebook's CSP would allow this. Page's CSP should not be a problem according to the docs. So, this approach should work, see the question I linked.
That said, you're not specifically looking for AJAX calls, you're looking for new elements being inserted in the DOM. You can detect that without modifying the page's code, using DOM MutationObservers.
See this answer for more information.
to detect AJAX calls on a webpage you have to inject the code directly in that page and then call the .ajaxStart or .ajaxSuccess
Example:
// To Successfully Intercept AJAX calls, we had to embed the script directly in the Notifications page
var injectedCode = '(' + function() {
$('body').ajaxSuccess(function(evt, request, settings) {
if (evt.delegateTarget.baseURI == 'URL to check against if you want') {
// do your stuff
}
});
} + ')();';
// Inserting the script into the page
var script = document.createElement('script');
script.textContent = injectedCode;
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
tl;dr Can I execute un-trusted scripts on an iframe safely?
Back story:
I'm trying to make secure JSONP requests. A lot of older browsers do not support Web Workers which means that the current solution I came up with is not optimal.
I figured I could create an <iframe> and load a script inside it. That script would perform a JSONP request (creating a script tag), which would post a message to the main page. The main page would get the message, execute the callback and destroy the iframe. I've managed to do this sort of thing.
function jsonp(url, data, callback) {
var iframe = document.createElement("iframe");
iframe.style.display = "none";
document.body.appendChild(iframe);
var iframedoc = iframe.contentDocument || iframe.contentWindow.document;
sc = document.createElement("script");
sc.textContent = "(function(p){ cb = function(result){p.postMessage(result,'http://fiddle.jshell.net');};})(parent);";
//sc.textContent += "alert(cb)";
iframedoc.body.appendChild(sc);
var jr = document.createElement("script");
var getParams = ""; // serialize the GET parameters
for (var i in data) {
getParams += "&" + i + "=" + data[i];
}
jr.src = url + "?callback=cb" + getParams;
iframedoc.body.appendChild(jr);
window.onmessage = function (e) {
callback(e.data);
document.body.removeChild(iframe);
}
}
jsonp("http://jsfiddle.net/echo/jsonp/", {
foo: "bar"
}, function (result) {
alert("Result: " + JSON.stringify(result));
});
The problem is that since the iframes are on the same domain, the injected script still has access to the external scope through .top or .parent and such.
Is there any way to create an iframe that can not access data on the parent scope?
I want to create an iframe where scripts added through script tags will not be able to access variables on the parent window (and the DOM). I tried stuff like top=parent=null but I'm really not sure that's enough, there might be other workarounds. I tried running a for... in loop, but my function stopped working and I was unable to find out why.
NOTE:
I know optimally WebWorkers are a better isolated environment. I know JSONP is a "bad" technique (I even had some random guy tell me he'd never use it today). I'm trying to create a secure environment for scenarios where you have to perform JSONP queries.
You can't really delete the references, setting null will just silently fail and there is always a way to get the reference to the parent dom.
References like frameElement and frameElement.defaultView etc. cannot be deleted. Attempting to do so will either silently fail or throw exception depending on browser.
You could look into Caja/Cajita though.
tl;dr no
Any untrusted script can steal cookies (like a session id!) or read information from the DOM like the value of a credit card input field.
JavaScript relies on the security model that all code is trusted code. Any attempts at access from another domain requires explicit whitelisting.
If you want to sandbox your iframe you can serve the page from another domain. This does mean that you can't share a session or do any kind of communication because it can be abused. It's just like including an unrelated website. Even then there are possibilities for abuse if you allow untrusted JavaScript. You can for instance do: window.top.location.href = 'http://my.phishing.domain/';, the user might not notice the redirect.
Greetings,
my xul app needs to load scripts dynamically, for this I derived a function that works in regular html/js apps :
function loadScript(url)
{
var e = document.createElement("script");
e.src = url;
e.type="text/javascript";
document.getElementsByTagName("head")[0].appendChild(e);
}
to something that ought work in XUL :
function loadScript( url)
{
var e = document.createElement("script");
//I can tell from statically loaded scripts that these 2 are set thru attributes
e.setAttribute( 'type' , "application/javascript" ); //type is as per MDC docs
e.setAttribute( 'src' , url );
//XUL apps attach scripts to the window I can tell from firebug, there is no head
document.getElementsByTagName("window")[0].appendChild(e);
}
The script tags get properly added, the attributes look fine,but it does not work at all, no code inside these loaded scripts is executed or even parsed.
Can any one give a hint as to what might be going on ?
T.
Okay,
as usual whenever I post on stack overflow, the answer will come pretty soon thru one last desperate Google search.
This works :
//Check this for how the url should look like :
//https://developer.mozilla.org/en/mozIJSSubScriptLoader
function loadScript( url)
{
var loader = Components.classes["#mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
//The magic happens here
loader.loadSubScript( url );
}
This will only load local files, which is what I need for my app.
I am fairly disappointed by Mozilla, why not do this the same way like html, in a standard way ?
I've tried this, and I think you're right - I can't seem to get XUL to run dynamically appended script tags - perhaps it's a bug.
I'm curious as to why you would want to though - I can't think of any situation where one would need to do this - perhaps whatever you're trying could be achieved another way. Why is it they need to be dynamically loaded?
Off-topic: on the changes you made to the script.
e.setAttribute('src',url); is valid in normal webpages as well, and is actually technically more "correct" than e.src=url; anyway (although longer and not well supported in old browsers).
Types application/javascript or application/ecmascript are supposed to work in normal webpages and are more "correct" than text/javascript, but IE doesn't support them so they're not normally used.
Inside xul environment you are only allowed to use XHR+eval like the following:
function loadScript (url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, false); // sync
xhr.send(null);
if (xhr.status && xhr.status != 200)
throw xhr.statusText;
try {
eval(xhr.responseText, window);
} catch (x) {
throw new Error("ERROR in loadScript: Can't load script '" + url+ "'\nError message is:" + x.message);
}
};
I am calling a ajax method to update a div. It contains links and functions which require java script files. But these methods and functions are not getting called properly as java script files are not getting included through ajax call. For example, i am trying to call a light box function, but it gets redirected to different page and not in light box.
Thanks in advance, Anubhaw Prakash
The Ajax framework in prototype will properly execute the text content of <script> tags, but will not import new script files via <script src="somefile.js"/>. The only solution I came up with is to import all javascript files I need in the head of the page. That way the functions in the imported file are available to the inline javascript code executed in the Ajax response.
I had a similar problem, where I did want to postload some javascript. What I did is separating loading the html-fragment and loading the script into two calls. For loading the script I call the following function (I have JQuery handling the ajax part):
function loadModule(name, callback) {
$.ajax({type: "POST"
, url: "/js/" + name
, dataType: "script"
, success: callback
});
}
I see you're using Ruby on Rails — does that mean you're using Prototype on the client? If so, Prototype's Ajax.Updater will ignore script tags that reference external files (it will evaluate script tags that have their contents inline). So to add those external files to your page, you'll have to hook into the process via the onSuccess callback, look in the responseText for script tags with src attributes, and handle those yourself. Once you've identified the relevant script tags and extracted their src attributes, you can include them by dynamically adding the scripts as described in this article from the unofficial Prototype & script.aculo.us wiki.
<script> tags written to innerHTML are not executed at write-time. You can do element.getElementsByTagName('script') to try to get hold of them and execute their scripts manually, but it's very ugly and not reliable.
There are tedious browser differences to do with what happens to a <script> element written to innerHTML which is then (directly or via an ancestor) re-inserted into the document. You want to avoid this sort of thing: just don't write <script>s to innerHTML at all.
Then you also don't have to worry about executing scripts twice, which is something you never want to do with library scripts. You don't want to end up with two copies of a function/class that look the same but don't compare equal, and which hold hooks onto the page that don't play well with each other. Dynamically-inserted library scripts are a recipe for confusing failure.
Much better to include your scripts statically, and bind them to page elements manually after writing new elements to the page. If you really need to you can have your AJAX calls grab a JSON object containing both the new HTML to add and a stringful of script to execute.
May want to try running some prepatory javascript in the :before option to setup a variable with the correct files?
hey i found a way to add it....:)
NOTE- this is a synchronous process so you dont have to worry about that the script is loaded or not.... the script will always load the instance u call the function and you can start using the loaded script instantaneously..
lets use these 2 functions
1) first one is the ajax function to retrieve the values
where async should be true to send the request synchronously
// AJAX FUNCTION
function loadXMLDoc(reqt,url,reqp,cfunc,async)
{
var xmlhttp;
try// code for IE7+, Firefox, Chrome, Opera, Safari
{
xmlhttp=new XMLHttpRequest();
}
catch(err)// code for IE6, IE5
{
try{
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e){
try{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
catch(E){}
}
}
if(!xmlhttp)
{
alert("error");
}
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState==4 && xmlhttp.status==200)
{
cfunc(xmlhttp.responseText);
}
}
if(reqt=='GET')
{
url+=(reqp!=""?"?":"")+reqp;
xmlhttp.open("GET",url,(async?false:true));
xmlhttp.send();
}
else if(reqt=='POST')
{
xmlhttp.open("POST",url,(async?false:true));
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send(reqp);
}
else
{
return false;
}
}
/*use this function
loadXMLDoc(reqt,url,reqp,function(response){
});
*/
2)then we use ajax to load the js file as string and then append it to the new script tag's innerHTML and then append it to the head section and one more thing to ensure file is already loaded i used the id of script tag as the path to the file which makes it really easy task to check for the duplicate...:)
//add new script dynamically
function add_script(src)
{
if(!document.getElementById(src))
{
loadXMLDoc("GET",src,"",function(jsresp){
var head = document.getElementsByTagName("head")[0];
var script=document.createElement("script");
script.type='text/javascript';
script.id=src;
script.text=jsresp;
head.appendChild(script);
},true);
}
}
thanks for all help i used to get and will get from this site and its users for the development purposes...
regards VIPIN JAIN
include static scripts on pages that need to use them (IE contain a lightbox, then include the lightbox script)
Problem solved. Do not load scripts using AJAX
Make necessary function calls to the static scripts using AJAX callbacks