How to check if site has allowed me to inject Javascript with my bookmarklet? - javascript

I have a bookmarklet that injects Javascript into a page and opens up an iframe with an HTML page I have created that allows a user to subscribe to a page directly from my bookmarklet.
Issue is, certain domains (Twitter and Facebook being two) do not allow me to inject Javascript, so I have to pop up a window instead.
Javascript console when on Facebook:
Refused to load the script script name because it violates the following Content Security Policy directive: "script-src https://.facebook.com http://.facebook.com https://.fbcdn.net http://.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net .google.com 127.0.0.1: *.spotilocal.com:* chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl 'unsafe-inline' 'unsafe-eval' https://.akamaihd.net http://.akamaihd.net *.atlassolutions.com".
Right now in my bookmarklet I am just checking to see if the URL matches those domains before I try to inject JS, and if it does, I pop open a new window. For obvious reasons, this is not a good practice.
What is a good method of checking if a Javascript function was allowed to run on the current page or not, and if not, to open a new window?

There is no way to know with absolute certainty that an external script failed to load. Even when there is no security policy, an external script could fail to load because of other problems. The only One thing you can really do is set a timeout and if the script hasn't completed some action before the timeout expires, assume it has failed to load.
EDIT: I stand corrected by Sean below. His suggestion also worked in Chrome and Firefox on Windows. The solution is something like this:
newScript.addEventListener('error', function(){ console.log('script failed to load') });
For the specific question of how to check if the page header returns a Content Security Policy which will block your external script, the only solution I know of is to check the HTTP header using AJAX.
Here is some example code. I've tested this on Facebook.
req = new XMLHttpRequest;
req.onreadystatechange = function(){if (req.readyState==4) console.log(req.getResponseHeader('content-security-policy'))};
req.open("HEAD", document.location.href);
req.send();

Bookmarklets should be allowed to run whatever the security policy is. If not, it is a browser bug - at least if I understand this correctly. But the bookmarklet may be forbidden to do some things. If you use try-catch you can find out if an action was allowed or not.

Related

How to prevent an external JS request

I'm using a service that automatically constructs and hosts launch pages. In the body of their code, they have a call to jquery:
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js?cache=2015-09-22-09">
Unfortunately, since I'm in China, googleapis.com is blocked and the page has to wait for this code to timeout before it will render. This portion is autogenerated as part of the template and I can't change it. However I can insert custom javascript in the head and the body.
Are there any ways I can prevent this code from making the request to googleapis.com or to force it to abort after it has already made the request?
-EDIT-
Here's a screen cap of the network tab when I try to load the page. As you can see, the call to googleapis.com hangs for 1.4 mins until it times out, at which point DomContentLoaded triggers and the entire page loads.
Right, if you are able to put html in the head of the document, not just execute javascript you could use a meta tag to block external script loading:
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
From the Mozilla Content-Security-Policy Meta Tag Docs:
Authors are strongly encouraged to place meta elements as early in the document as possible, because policies in meta elements are not applied to content which preceds them. In particular, note that resources fetched or prefetched using the Link HTTP response header field, and resources fetched or prefetched using link and script elements which precede a meta-delivered policy will not be blocked.
So the meta tag will only work in the head, certainly before the script which loads jQuery. You can whitelist URL's in the tag by adding them into the content parameter in the meta tag too.
If you can only execute javascript, you can add the meta tag dynamically. Unfortunately it is likely the browser has probably decided on it's policies by the time it is added. Nevertheless, it can be added with
var meta = document.createElement('meta');
meta.httpEquiv = "Content-Security-Policy";
meta.content = "script-src 'self'";
document.getElementsByTagName('head')[0].appendChild(meta);
More Interesting reading material homework for solving the 'Prevent an external js request' mystery:
Use JavaScript to prevent a later `<script>` tag from being evaluated?
Good Luck!
Consider using a Content Security Policy. It would be an unusual use case, but with the CSP you can tell the browser that it is not allowed to access googleapis.com before your government even gets a say in the matter. In this way, the browser won't even try to load it, and the page will not hang.
Yeah. #Niet's suggestion seems nice. To add to his answer, here's how you can block rendering of googleapi domain using CSP:
Content-Security-Policy: script-src 'self';
This code would instruct the browser to only execute YOUR own domain's scripts.
If you have access to the web server and its windows you could add an entry in the Hosts file to redirect the google address to the local server web application to download the javascript from there? (if you match the folder structure of the javascript link)
c:\windows\system32\drivers\etc\hosts
ajax.googleapis.com 127.0.0.1

Stopping script from changing document.location.href?

A site that I just browsed (cheezburger.com) has apparently vulnerabilities as someone had injected lines like <script>document.location.href="http://net-cheezburger.cu.cc/"</script> into messages. Firefox redirected itself to there and X-Frame-Options stopped the framing resulting an empty screen.
Is there other ways to prevent the script from working in Firefox than adding a CAPS policy to document.location.href on cheezburger sites? That blocks legitimate changes too.
Now I just alert with Greasemonkey that a script is in a wrong place so I know immediately what's going on if they try other malicious scripts.
I'd just like a temporary fix until the site itself is fixed.
I'm wondering is there way to programmatically intercept that script or redirection. If I've understood correctly you can't change inline scripts with Greasemonkey but are there other options?
Since you're on firefox (a modern browser), you could use Object.freeze to turn the location object into read-only:
Object.freeze(document.location);
document.location.href = "http://google.com";
// No navigation happens
console.log(document.location.href);
// => "http://stackoverflow.com/questions/22290948/stopping-script-from-changeing-document-location-href"

Are Bookmarklets Possible in Chrome?

I was trying to create a bookmarklet in chrome (I was using the console). I got the following error:
Refused to load the script 'https://code.jquery.com/jquery-1.6.1.min.js' because
it violates the following Content Security Policy directive: "script-src
https://*.facebook.com http://*.facebook.com https://*.fbcdn.net
http://*.fbcdn.net *.facebook.net *.google-analytics.com *.virtualearth.net
*.google.com 127.0.0.1:* *.spotilocal.com:*
chrome-extension://lifbcibllhkdhoafpjfnlhfpfgnpldfl 'unsafe-inline'
'unsafe-eval' https://*.akamaihd.net http://*.akamaihd.net".
My code was
var jQueryLib = document.createElement("script");
jQueryLib.src = "https://code.jquery.com/jquery-1.6.1.min.js";
document.body.appendChild(jQueryLib);
Does this mean creating bookmarklets is no longer possible? What I wanted to do was like a bunch of new comments on my Facebook group and thought a bookmarklet would be a good idea.
Any advice?
From that error message it is not having an issue with the Bookmarklet itself. The problem is that is that Facebook has declared a list of domains in which scripts may be run from. When you try to inject the jQuery script it sees that the domain (jquery.com in this case) is not on the allowed list and refuses to run it.
As an alternative you could copy and paste the entire jQuery file into console. This would bypass the content restriction. You could also turn the entire jQuery library into a bookmarklet to make it easier to add to a page.
Yes Javascript Bookmarklets are allowed in Chrome. For example, copy and paste the code below into a bookmark. Change the title to whatever you want, but paste the code below into the URL section. Then press it.
javascript:alert("See, it works");

accessing func in iframe. local to online

HI, i got a simple html page, localy with an iframe. the iframe includes a generated page which got a javascript function. i know want to call that function. of course, im getting "permission denied". so since im new to js and all that stuff i dont know if it's actually possible to do that. give me some hints for searching or a nice solution.
i do cal lthe func like: parent.myiframe.myfunc();
I guess the page in the iframe resides on another server / domain. Modern browser do not allow "cross site scripting", see: http://de.wikipedia.org/wiki/Cross-Site_Scripting
If possible, move the site in the iframe to the same server. An alternative (workaround) would be to proxy the page on the local server, so that that for the client it seems to be loaded from the same domain.
Edit: This is also called a "Same Origin Policy". You can only call java script functions in a document that is:
from the same domain (www.mydomain.com)
from the same subdomain (mail.mydomain.com <- no go!)
both use the same port (p.Ex.
accessing a http://... document from
a http*s*:// document won't work).
There might be another workaround if you have access to the iframe's source:
Change the iframe domain to the same as the outer frame's, by applying:
document.domain = "domain.com";
in the iframe source (see http://ajaxian.com/archives/how-to-make-xmlhttprequest-calls-to-another-server-in-your-domain for more information).
Also there is a Draft for "Cross-Origin Resource Sharing" (http://www.w3.org/TR/cors/) that is already partially implemented in several browser, see: http://www.webdavsystem.com/ajax/programming/cross_origin_requests

depress same origin policy

I have a 'toolbar' that displays some code on the top of the window, and then I load an iframe with an external site. I realize that I can't get the active link the user is on because it would be a violation of same origin policy.
Is there any way (using greasemonkey maybe?) that I could get the active url of the external iframe?
I need to do this for demo purposes, not for anything practical. (I realize the real solution would be to process the entire page through my own server)
Thanks!
I'll post a workaround that I wrote:
If you install greasemonkey, write a script does (roughly) something like this:
current_link = document.location.href;
if(current_link !== 'http://my_local_site')
{
GM_setVal("link", current_link); }
Have greasemonkey run this script on your iframe's URL and on your local site and on your iframe's site.
GM will save to its internal memory the link. If you don't trigger the IF statement, you're probably reading the script from your local site, ergo you need to:
unsafeWindow.urlVal = GM_getVal("link");
All you need to do now is to get both the local frame and the iframe to run the script every time a page is navigated on the iframe.
You can get this done on your local frame by a) timing it b) using some type of event trigger.
Best of luck!

Categories