Content-security-policy and Facebook add-on. Not working - javascript

I am making a Firefox add-on. It needs to
1) Read the webpage
2) Based on that, send information in a POST to my site,
3) Display a text based on what my site returns.
I cannot get this to work on Facebook.com and I believe that it is due to Facebook's restrictive Content-security-policy. I cannot get the add-on content_script to send a POST.
I have tried:
var url = 'https://mysite.com';
var request = new XMLHttpRequest();
request.open("POST", url, true);
request.onload = function () {
alert("returned");
};
request.send();
On non-Facebook sites this works. On Facebook, there is no activity in the Network tab. The console gives me an error:
Content Security Policy: The page's settings blocked the loading of a resource at ...
I have also tried doing something with an iframe:
var onload = "var url = 'https://mysite.com';
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.send();
request.onload = function(){alert();};";
var iframe_wrapper = window.document.createElement("div");
iframe_wrapper.innerHTML='<iframe onLoad="'+onload+'"; src="https://mysite.com"></iframe>';
window.document.body.appendChild(iframe_wrapper);
On non-Facebook sites, two calls are made: the inital iframe src call and then the call in the onLoad function.
On Facebook, only the iframe call is made, which is successful. The console then gives an error (first time I try):
ReferenceError: reference to undefined property la.stack
ReferenceError: reference to undefined property n.name
Is there a way around this? Note that this does work with my Chrome extension (I use the first straight-forward method).

Yes set the csp rules. I got this from another topic here: How to add Content Security Policy to Firefox extension
But this version is slightly different.
But copy paste this:
var httpRequestObserver =
{
observe: function(subject, topic, data)
{
Cu.reportError('observing req')
var httpChannel, requestURL;
httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
requestURL = httpChannel.URI.spec;
if (httpChannel.responseStatus !== 200) {
return;
}
var cspRules;
var mycsp;
// thre is no clean way to check the presence of csp header. an exception
// will be thrown if it is not there.
// https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIHttpChannel
console.info('reading response headers on requestURL = ', requestURL)
try {
console.warn('trying to set init')
cspRules = httpChannel.getResponseHeader("Content-Security-Policy");
mycsp = _getCspAppendingMyHostDirective(cspRules);
httpChannel.setResponseHeader('Content-Security-Policy', mycsp, false);
console.warn('set init done')
} catch (e) {
try {
console.warn('trying to set fallback')
// Fallback mechanism support
cspRules = httpChannel.getResponseHeader("X-Content-Security-Policy");
mycsp = _getCspAppendingMyHostDirective(cspRules);
httpChannel.setResponseHeader('X-Content-Security-Policy', mycsp, false);
console.warn('fallback set done')
} catch (e) {
// no csp headers defined
console.warn('no csp headers defined so SHOULD be able to inject script here url = ' + requestURL);
return;
}
}
}
};
Cu.import('resource://gre/modules/devtools/Console.jsm');
/**
* #var cspRules : content security policy
* For my requirement i have to append rule just to 'script-src' directive. But you can
* modify this function to your need.
*
*/
function _getCspAppendingMyHostDirective(cspRules) {
var rules = cspRules.split(';');
var scriptSrcFound = false;
for (var ii = 0; ii < rules.length; ii++) {
if ( rules[ii].toLowerCase().indexOf('script-src') != -1 ) {
rules[ii] = 'script-src * \'unsafe-inline\' \'unsafe-eval\''; // define your own rule here
scriptSrcFound = true;
}
}
return rules.join(';');
}
Then on startup of addon run this code:
Services.obs.addObserver(httpRequestObserver, 'http-on-examine-response', false);
and on shutdown of addon run this code:
Services.obs.removeObserver(httpRequestObserver, 'http-on-examine-response', false);

Related

Setting a custom content-type xmlhttprequest

I have an API I am trying to interface with that requires a custom content-type header be set, with the value text/xmlmc
I've implemented this like so
Xmlmc.prototype.submitRequest = function (request,callback) {
var self = this;
var port = portMap[request.service] || 5015;
var endpoint = this.endpoint = 'http://' + this.server + ':' + port;
var xml = request.toXml();
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// IE 5 and 6 makes us sad. Please don't use it
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
//handle request
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4) {
var response = self.handleResponse(xhttp);
callback(response);
}
};
xhttp.open('POST',endpoint,true);
xhttp.setRequestHeader('Content-type', 'text/xmlmc');
//xhttp.setRequestHeader('Content-length', xml.length.toString());
if(this.sessionCookie != '') {
xhttp.setRequestHeader('Cookie', this.sessionCookie);
}
xhttp.send(xml);
};
The endpoint is localhost:5015
When I do this, the request fails and never even sends. When I use a standard request header like 'text/plain' the request is sent but returns a status code of 501 not implemented. How can I set a custom HTTP header in an xmlhttprequest?
It turns out this was due to a cross origin issue. Even when the domain is the same, if the ports are different it is a problem. I fixed the issue by adding a reverse proxy to my apache configuration and now I can query the api without cross origin requests. Unfortunately I don't have access to change the API and allow cross origin domains.

Adjacently Dependent AJAX (improved)

This question was posted a couple of days ago, but since I'm a nub it was filled with spaghetti code and that sort of thing (please pardon the form handling as well) That aside, I've added some notes and given some context, but the problem still lies in the second AJAX call.
This is the error that Chrome is throwing "Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https, chrome-extension-resource."
I have hidden the URL because it contains an API key that I would rather not share.
Any and all criticisms are warmly welcomed
/*
This module will take a user's name, return an ID
then search more stats in the api with the ID.
*/
var search = document.getElementById('search');
search.addEventListener('click', function(){
var demo = document.getElementById('demo');
var player_name = document.getElementById('player_name').value;
var player_id;
// Interpolated API URLs
var name_url = 'URL.pre'+player_name+'URL.end';
var stats_url; //nested in the second ajax call to pass updated player_id
// Get player ID
var xhr = new XMLHttpRequest();
var id_return_text;
xhr.onload = function(){
if(xhr.status === 200) {
id_return_text = JSON.parse(xhr.responseText);
player_id = id_return_text[player_name].id;
demo.innerHTML = id_return_text[player_name].name +', your player ID is: '+player_id;
}
};
xhr.open('GET', name_url, true);
xhr.send();
// Search stats with ID
var xhr_2 = new XMLHttpRequest();
var stats_return_text;
xhr.done = function(){
stats_url = "URL.pre"+player_id+"URL.end";
if(xhr_2.status == 200) {
stats_return_text = JSON.parse(xhr_2.responseText);
demo.innerHTML += stats_return_text['playerStatsSummaries'].playerStatType;
}
};
xhr_2.open("GET",stats_url, true);
xhr_2.send();
});
<div id="container">
<img id="duck" src="duck.png" alt="duck">
<div class="form_wrapper">
<h1 id="app_header">*QUACK* What's Your player ID?</h1>
<form>
<input
type="text"
id="player_name"
placeholder="Summoner Name">
<input type="button" id="search" value="Search">
</form>
</div>
<p id="demo"></p>
</div>
<script type="text/javascript" src="script.js"></script>
So your primary error was that if you need to make CORS requests (or any AJAX requests, really), you need to run the code from a server (even localhost).
Google (and most browsers) will freak out at you if your page's protocol is "file:///" and you're trying to load things from the internet (or vice versa). And "file:///" cannot make requests for other files, either.
Future reference: you also can't make "http" requests from an "https" page.
That out of the way, the second issue (the one that was being hidden by CORS security), is that your AJAX requests are being run in parallel right now.
In order to make this work the way you think it should (after the first one returns, run the second one), you would need to:
move all of the code at the bottom, relating to xhr_2 inside of the xhr.onload
move all of the code inside of xhr.done at the bottom inside of the xhr.onload and replace all of the duplicate information (and use the references to the returned results directly)
This results in something like:
var search = document.getElementById('search');
search.addEventListener('click', function(){
var demo = document.getElementById('demo');
var player_name = document.getElementById('player_name').value;
var player_id;
// Interpolated API URLs
var name_url = 'https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/'+player_name+'?api_key=<THIS IS THE API KEY>';
var stats_url; //nested in the second ajax call to pass updated player_id
// Get player ID
var xhr = new XMLHttpRequest();
var id_return_text;
xhr.onload = function(){
if(xhr.status === 200) {
id_return_text = JSON.parse(xhr.responseText);
player_id = id_return_text[player_name].id;
demo.innerHTML = id_return_text[player_name].name +', your player ID is: '+player_id;
// Dropped the XHR_2 stuff here
var xhr_2 = new XMLHttpRequest();
var stats_return_text;
stats_url = "https://na.api.pvp.net/api/lol/na/v1.3/stats/by-summoner/"+player_id+"/summary?season=SEASON2016&api_key=<THIS IS THE API KEY>";
// CHANGED THIS TO BE XHR_2.onload -- IN HERE I KNOW XHR_1 IS ALREADY FINISHED
xhr_2.onload = function(){
if(xhr_2.status == 200) {
stats_return_text = JSON.parse(xhr_2.responseText);
demo.innerHTML += stats_return_text['playerStatsSummaries'].playerStatType;
}
};
xhr_2.open("GET",stats_url, true);
xhr_2.send();
}
};
xhr.open('GET', name_url, true);
xhr.send();
});
That should solve practically all of your woes.
The point of this is that onload is a callback which gets fired long after the program has been run, but xhr_2 was firing immediately after you requested data for xhr_1 (not after it was returning the data).
As such, player_id was undefined.
We want to wait until after we know we have player_id, and we know we have it (or some error) when we're inside the callback to xhr_1.onload.
This gets terribly confusing and very nested, and while I think that Promises and Async Functions / Generators are brilliant solutions for managing that complexity, that's way beyond the scope of this; so instead, I'd suggest looking at some functional composition, to simplify all of this:
function noop () { } // do nothing
function getJSON (url, onload, onerror) {
var xhr = new XMLHttpRequest();
onload = onload || noop; // what I've been given or nothing
onerror = onerror || noop; // " "
xhr.onload = function () {
var data;
var error;
try {
// it's possible for parse to throw on malformed JSON
data = JSON.parse(xhr.responseText);
} catch (e) {
error = e;
}
return error ? onerror(error) : onload(data); // fire one or the other (don't fall into the handler, if onload throws)
};
xhr.onerror = onerror;
xhr.open("GET", url);
xhr.send();
}
// localize URL construction
function buildPlayerIdUrl (name) { return "https://______" + name + "_____"; }
function buildPlayerStatsUrl (id) { return "https://______" + id + "_____"; }
// gets player by name and runs a function after the player has been loaded
function getPlayer (player_name, done, error) {
var id_url = buildPlayerIdUrl(player_name);
function buildPlayer (response) {
var player = response[player_name];
return player;
}
function onload (response) {
done(buildPlayer(response));
}
// Load the JSON, build the player, pass the player to done()
getJSON(url, onload, error);
}
// get stats by player id and runs a function after the stats have been loaded
function getPlayerStats (player_id, done, error) {
var stats_url = buildPlayerStatsUrl(player_id);
function buildStats (response) {
var summary = response.playerStatsSummaries;
return summary;
}
function onload (response) {
done(buildStats(response));
}
// Load the JSON, build the stats, pass the stats to done()
getJSON(stats_url, onload, error);
}
// perform a search by player name
// note: All changes in step-number (1, 2, 3) are asynchronous,
// and thus, must be nested in callbacks of some sort
function search (player_name) {
// Step 1: load the player
getPlayer(playerName, function (player) {
// Step 2a: update the DOM with the player name/id
updatePlayerDom(player);
// Step 2b: load the player stats
getPlayerStats(player.id, function (stats) {
// Step 3: update the DOM with the stats
updateStatsDom(stats);
});
});
}
// player DOM update; keeping it nice and simple
function updatePlayerDom (player) {
document.querySelector(".Player-id").textContent = player.id;
document.querySelector(".Player-name").textContent = player.name;
}
// stats DOM update; same as above
function updateStatsDom (stats) {
document.querySelector(".Player-stats").textContent = stats.playerStatType;
}
// bootstrap yourself to your UI
some_button.onclick = function () {
var player_name = some_input.value;
search(player_name); // kick the whole thing off
};
It's definitely more code, but it's also simpler to make edits to each individual piece, without stepping on the toes of other pieces.
It's (hopefully) also easier to see the _eventual timeline_ of all of the pieces, and how they flow, inside of the search( ) itself.

Getting Facebook to return User Data to Chrome Extension

I'm building a chrome extension that needs to allow user authentication through facebook. I've been following the "manual login flow" since the facebook Javascript SDK doesn't work in extensions. Thus far, I've allowed users to click a link that gets permissions and returns an access token:
index.html
Sign In with Facebook
index.js
function onFacebookLogin(){
if (localStorage.getItem('accessToken')) {
chrome.tabs.query({}, function(tabs) { // get all tabs from every window
for (var i = 0; i < tabs.length; i++) {
if (tabs[i].url.indexOf(successURL) !== -1) {
// below you get string like this: access_token=...&expires_in=...
var params = tabs[i].url.split('#')[1];
// in my extension I have used mootools method: parseQueryString. The following code is just an example ;)
var accessToken = params.split('&')[0];
accessToken = accessToken.split('=')[1];
localStorage.setItem('accessToken', accessToken);
chrome.tabs.remove(tabs[i].id);
console.log(accessToken)
userSignedIn = true
findFacebookName();
}
}
});
}
}
chrome.tabs.onUpdated.addListener(onFacebookLogin);
^ ALL OF THIS WORKS. Everytime a user "logs in" the localstorage.accessToken is updated. Then, I get to the point where I try to retrieve user data by inspecting the token through the api:
var client_token = '{MY_CLIENT_TOKEN(NOT CLIENT_ID}'
var inspectTokenUrl = 'https://graph.facebook.com/debug_token?input_token=' +
localStorage.accessToken +
'&access_token=' +
client_token
function findFacebookName(){
var xhr = new XMLHttpRequest();
xhr.open("GET", inspectTokenUrl, true);
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
}
}
xhr.send();
}
But this returns a 190 oauth error. What am I doing wrong? What is the correct way to request the users email address using the access_token?

Can't get feed in Windows 8 app

I am creating blog reader app for Windows 8 by using RSS feeds. Part of code:
function downloadBlogFeed() {
WinJS.xhr({ url: "http://feeds.feedburner.com/CssTricks" }).then(function (rss) {
var items = rss.responseXML.querySelectorAll("item");
for (var n = 0; n < items.length; n++) {
var article = {};
article.title = items[n].querySelector("title").textContent;
var thumbs = items[n].querySelectorAll("thumbnail");
if (thumbs.length > 1) {
article.thumbnail = thumbs[1].attributes.getNamedItem("url").textContent;
article.content = items[n].textContent;
articlesList.push(article);
}
}
});
}
So, my app can't read feed from FeedBurner. I get this error
Can't load http://feeds.feedburner.com/~d/styles/itemcontent.css. An app can’t load remote web content in the local context.
I've tried http://feeds.feedburner.com/CssTricks?format=xml and http://feeds.feedburner.com/CssTricks?fmt=xml, but the same error.
EDIT: Full code: http://jsfiddle.net/8n67y/
The error you're encountering isn't because you can't read from Feedburner. It's because somewhere in the content that you're attempting to load into the DOM is a reference to a CSS file on the web (itemcontent.css).
When you're operating in the local context, you cannot dynamically load script or CSS from the web, because that poses security risks in the local context.
See here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx
and here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465373.aspx
for more information on the differences between local and web context, and the restrictions that apply to each.
For your particular case, I think what you should try is parsing the content further (you can set a breakpoint in the above code to examine the XML content being returned by the feed) to determine where the CSS file reference is being returned and either remove it programmatically, if it's in a consistent place, or find another means of eliminating the CSS reference, which appears to be what's causing the exception (based on the limited information above).
What you are trying to do can be done with the below code, instead of WinJS.xhr use the xmlHTTPRequest.
The code below is part of the code I use um my RSS reader and it works pretty well in all situations, we can donwload pictures, text, links.. whatever you can find in the feed (http://feeds.feedburner.com/CssTricks) getting the thumbnails, and all worked fine.
Also I tested it with the following modification,
function connectToURL() {
var url = "";
xmlHttp = GetXmlHttpObject();
if (xmlHttp == null) {
return;
}
xmlHttp.onreadystatechange = stateChanged;
xmlHttp.open("GET", url,true);
xmlHttp.send(null);
}
// your job will actually start on this one...
function stateChanged() {
if(xmlHttp != null )
if (xmlHttp[item.key].readyState == 4 ) {
try {
var xmlDoc = xmlHttp.responseXML.documentElement.getElementsByTagName("TAGYOUWANTTOGET");
for (var i = 0; i < xmlDoc.length; i++) {
xmlDoc[i].getElementsByTagName("TAG")[0].childNodes[0].nodeValue
}
} catch (e) {
//work on the exception
}
}
}
}
function GetXmlHttpObject() {
var xmlHttp = null;
try {
xmlHttp = new XMLHttpRequest();
}
catch(e) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch(e) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}

How to send data from outside site to my server via AJAX?

I am written the bookmarklet, which takes pictures and videos from a site and must send it to my server via AJAX. The problem is in crossdomain AJAX request - I have got an error:
XMLHttpRequest cannot load http://mysite.com/community/bookmarklet/. Origin http://www.some-nice-site.com is not allowed by Access-Control-Allow-Origin.
How to solve data sending to my server from third-part sites?
Note: I use only plane javascript, this is the stipulation of development.
my code:
function getXmlHttp(){
var xmlhttp;
if (typeof XMLHttpRequest!='undefined') {
xmlhttp = new XMLHttpRequest();
} else {
try {
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
xmlhttp = false;
}
}
};
return xmlhttp;
};
function vote(data) {
var req = getXmlHttp();
req.onready = function() {
if (req.readyState == 4 & req.status == 200) {
alert('OK');
}
}
req.open('GET', 'http://mydomain.com/community/bookmarklet/');
req.send(JSON.stringify(data()));
};
function dataProcessing(){
//some processing
return data;
};
// I tried it, but not deeply understand.
function JSONPresponse(){
document.getElementById('popup_body').innerHTML = 'done!';
};
(function(){
function pasteIt(){
// this function is builds the form, which get data for dispatch to my server.
};
pasteIt();
document.getElementById('my_button').addEventListener('click', function() {vote(dataProcessing)}, false);
}());
It is quite clear... the site you are attempting is prohibiting connection from outside world. You can try by modifying your http headers in request. See:
Access-Control-Allow-Origin Multiple Origin Domains?
Cannot properly set the Accept HTTP header with jQuery
As #jakeclarkson told - JSON-P is the solution, not the only, but useful for me.
XMLHttpRequest is not needed anymore. Instead it vote(data) function creates the script to build URL with params:
function vote(data) {
var script = document.createElement('script'),
data = JSON.stringify(data());
script.type = 'text/javascript';
script.src = 'http://mysite.com/api/bookmarklet/?vids='+encodeURIComponent(data);
document.getElementsByTagName('head')[0].appendChild(script);
};
The script is fulfilled, so URL is called and params already in the server.

Categories