diff --git a/src/rails.js b/src/rails.js index a4fb0bed..78ff557c 100644 --- a/src/rails.js +++ b/src/rails.js @@ -54,6 +54,9 @@ // Button onClick disable selector with possible reenable after remote submission buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]', + // Whitelisted domains when using data-method='post' usages + csrfWhitelistedDomains: null, + // Make sure that every Ajax request sends the CSRF token CSRFProtection: function(xhr) { var token = $('meta[name="csrf-token"]').attr('content'); @@ -173,18 +176,22 @@ var href = rails.href(link), method = link.data('method'), target = link.attr('target'), + domain = link[0].hostname, csrfToken = $('meta[name=csrf-token]').attr('content'), csrfParam = $('meta[name=csrf-param]').attr('content'), - form = $('
'), - metadataInput = ''; - - if (csrfParam !== undefined && csrfToken !== undefined) { - metadataInput += ''; + form = $('').attr('action', href), + methodInput = $('').attr('value', method); + + if (rails.isCsrfDomainWhitelisted(domain) && csrfParam !== undefined && csrfToken !== undefined) { + csrfInput = $('') + .attr('name', csrfParam) + .attr('value', csrfToken); + form.append(csrfInput); } if (target) { form.attr('target', target); } - form.hide().append(metadataInput).appendTo('body'); + form.hide().append(methodInput).appendTo('body'); form.submit(); }, @@ -258,6 +265,11 @@ return answer && callback; }, + // Verify, when configured, that the href passes the whitelist and should send the CSRF token. + isCsrfDomainWhitelisted: function(domain){ + return rails.csrfWhitelistedDomains ? rails.csrfWhitelistedDomains.test(domain) : true; + }, + // Helper function which checks for blank inputs in a form that match the specified CSS selector blankInputs: function(form, specifiedSelector, nonBlank) { var inputs = $(), input, valueToCheck, diff --git a/test/public/test/data-method.js b/test/public/test/data-method.js index c4426624..57196816 100644 --- a/test/public/test/data-method.js +++ b/test/public/test/data-method.js @@ -39,6 +39,31 @@ asyncTest('link with "data-method" and CSRF', 1, function() { }); }); +asyncTest('whitelisted links with "data-method" get CSRF', 1, function() { + $.rails.csrfWhitelistedDomains = /^localhost$/ + + $('#qunit-fixture') + .append('') + .append(''); + + submit(function(data) { + strictEqual(data.params.authenticity_token, 'cf50faa3fe97702ca1ae'); + }); +}); + +asyncTest('non whitelisted links with "data-method" get no CSRF', 2, function() { + $.rails.csrfWhitelistedDomains = /^rubyonrails.org$/ + + $('#qunit-fixture') + .append('') + .append(''); + + submit(function(data) { + strictEqual(data.params.authenticity_token, undefined); + strictEqual(data.HTTP_X_CSRF_TOKEN, undefined); + }); +}); + asyncTest('link "target" should be carried over to generated form', 1, function() { $('a[data-method]').attr('target', 'super-special-frame'); submit(function(data) {