Skip to content

Commit

Permalink
Properly check if the request is cross domain
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelfranca committed Mar 26, 2015
1 parent 519aad9 commit 96561a2
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 53 deletions.
31 changes: 25 additions & 6 deletions src/rails.js
Expand Up @@ -86,16 +86,14 @@

// Default way to get an element's href. May be overridden at $.rails.href.
href: function(element) {
return element.attr('href');
return element[0].href;
},

// Submits "remote" forms and links with ajax
handleRemote: function(element) {
var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
var method, url, data, withCredentials, dataType, options;

if (rails.fire(element, 'ajax:before')) {
elCrossDomain = element.data('cross-domain');
crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
withCredentials = element.data('with-credentials') || null;
dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);

Expand Down Expand Up @@ -147,7 +145,7 @@
error: function(xhr, status, error) {
element.trigger('ajax:error', [xhr, status, error]);
},
crossDomain: crossDomain
crossDomain: rails.isCrossDomain(url)
};

// There is no withCredentials for IE6-8 when
Expand All @@ -167,6 +165,27 @@
}
},

// Determines if the request is a cross domain request.
isCrossDomain: function(url) {
var originAnchor = document.createElement("a");
originAnchor.href = location.href;
var urlAnchor = document.createElement("a");

try {
urlAnchor.href = url;
// This is a workaround to a IE bug.
urlAnchor.href = urlAnchor.href;

// Make sure that the browser parses the URL and that the protocols and hosts match.
return !urlAnchor.protocol || !urlAnchor.host ||
(originAnchor.protocol + "//" + originAnchor.host !==
urlAnchor.protocol + "//" + urlAnchor.host);
} catch (e) {
// If there is an error parsing the URL, assume it is crossDomain.
return true;
}
},

// Handles "data-method" on links such as:
// <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
handleMethod: function(link) {
Expand All @@ -178,7 +197,7 @@
form = $('<form method="post" action="' + href + '"></form>'),
metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';

if (csrfParam !== undefined && csrfToken !== undefined) {
if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) {
metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
}

Expand Down
14 changes: 0 additions & 14 deletions test/public/test/call-remote-callbacks.js
Expand Up @@ -64,20 +64,6 @@ asyncTest('modifying data("type") with "ajax:before" requests new dataType in re
});
});

asyncTest('setting data("cross-domain",true) with "ajax:before" uses new setting in request', 2, function(){
$('form[data-remote]').data('cross-domain',false)
.bind('ajax:before', function() {
var form = $(this);
form.data('cross-domain',true);
});

submit(function(form) {
form.bind('ajax:beforeSend', function(e, xhr, settings) {
equal(settings.crossDomain, true, 'setting modified in ajax:before should have forced cross-domain request');
});
});
});

asyncTest('setting data("with-credentials",true) with "ajax:before" uses new setting in request', 2, function(){
$('form[data-remote]').data('with-credentials',false)
.bind('ajax:before', function() {
Expand Down
32 changes: 0 additions & 32 deletions test/public/test/call-remote.js
Expand Up @@ -122,22 +122,6 @@ asyncTest('sends CSRF token in custom header', 1, function() {
});
});

asyncTest('does not send CSRF token in custom header if crossDomain', 1, function() {
buildForm({ 'data-cross-domain': 'true' });
$('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />');

// Manually set request header to be XHR, since setting crossDomain: true in .ajax()
// causes jQuery to skip setting the request header, to prevent our test/server.rb from
// raising an an error (when request.xhr? is false).
$('#qunit-fixture').find('form').bind('ajax:beforeSend', function(e, xhr) {
xhr.setRequestHeader('X-Requested-With', "XMLHttpRequest");
});

submit(function(e, data, status, xhr) {
equal(data.HTTP_X_CSRF_TOKEN, undefined, 'X-CSRF-Token header should NOT be sent');
});
});

asyncTest('intelligently guesses crossDomain behavior when target URL is a different domain', 1, function(e, xhr) {

// Don't set data-cross-domain here, just set action to be a different domain than localhost
Expand All @@ -156,20 +140,4 @@ asyncTest('intelligently guesses crossDomain behavior when target URL is a diffe

setTimeout(function() { start(); }, 13);
});

asyncTest('does not set crossDomain if explicitly set to false on element', 1, function() {
buildForm({ action: 'http://www.alfajango.com', 'data-cross-domain': false });
$('#qunit-fixture').append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae" />');

$('#qunit-fixture').find('form')
.bind('ajax:beforeSend', function(e, xhr, settings) {
equal(settings.crossDomain, false, 'crossDomain should be set to false');
// prevent request from actually getting sent off-domain
return false;
})
.trigger('submit');

setTimeout(function() { start(); }, 13);
});

})();
26 changes: 26 additions & 0 deletions test/public/test/data-method.js
Expand Up @@ -46,4 +46,30 @@ asyncTest('link "target" should be carried over to generated form', 1, function(
});
});

asyncTest('link with "data-method" and cross origin', 1, function() {
var data = {};

$('#qunit-fixture')
.append('<meta name="csrf-param" content="authenticity_token"/>')
.append('<meta name="csrf-token" content="cf50faa3fe97702ca1ae"/>');

$(document).on('submit', 'form', function(e) {
$(e.currentTarget).serializeArray().map(function(item) {
data[item.name] = item.value;
});

return false;
});

var link = $('#qunit-fixture').find('a');

link.attr('href', 'http://www.alfajango.com');

link.trigger('click');

start();

notEqual(data.authenticity_token, 'cf50faa3fe97702ca1ae');
});

})();
2 changes: 1 addition & 1 deletion test/public/test/override.js
Expand Up @@ -32,7 +32,7 @@ asyncTest("the getter for an element's href is overridable", 1, function() {

asyncTest("the getter for an element's href works normally if not overridden", 1, function() {
$.rails.ajax = function(options) {
equal('/real/href', options.url);
equal(location.protocol + '//' + location.host + '/real/href', options.url);
}
$.rails.handleRemote($('#qunit-fixture').find('a'));
start();
Expand Down

0 comments on commit 96561a2

Please sign in to comment.