diff --git a/resources/Resources.php b/resources/Resources.php index 1388128fc0b..a9e29c98277 100644 --- a/resources/Resources.php +++ b/resources/Resources.php @@ -895,6 +895,7 @@ return [ 'dependencies' => [ 'mediawiki.api', 'oojs', + 'mediawiki.Uri', ], 'targets' => [ 'desktop', 'mobile' ], ], diff --git a/resources/src/mediawiki.ForeignApi.core.js b/resources/src/mediawiki.ForeignApi.core.js index 4b6313b93c9..83ea0ce003b 100644 --- a/resources/src/mediawiki.ForeignApi.core.js +++ b/resources/src/mediawiki.ForeignApi.core.js @@ -59,7 +59,6 @@ } }, parameters: { - // Add 'origin' query parameter to all requests. origin: this.getOrigin() } }, @@ -77,17 +76,26 @@ * any). * * @protected - * @return {string} + * @return {string|undefined} */ CoreForeignApi.prototype.getOrigin = function () { - var origin; + var origin, apiUri, apiOrigin; if ( this.anonymous ) { return '*'; } + origin = location.protocol + '//' + location.hostname; if ( location.port ) { origin += ':' + location.port; } + + apiUri = new mw.Uri( this.apiUrl ); + apiOrigin = apiUri.protocol + '://' + apiUri.getAuthority(); + if ( origin === apiOrigin ) { + // requests are not cross-origin, omit parameter + return undefined; + } + return origin; }; @@ -101,10 +109,12 @@ if ( ajaxOptions.type === 'POST' ) { url = ( ajaxOptions && ajaxOptions.url ) || this.defaults.ajax.url; origin = ( parameters && parameters.origin ) || this.defaults.parameters.origin; - url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) + - // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6 - // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235. - 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' ); + if ( origin !== undefined ) { + url += ( url.indexOf( '?' ) !== -1 ? '&' : '?' ) + + // Depending on server configuration, MediaWiki may forbid periods in URLs, due to an IE 6 + // XSS bug. So let's escape them here. See WebRequest::checkUrlExtension() and T30235. + 'origin=' + encodeURIComponent( origin ).replace( /\./g, '%2E' ); + } newAjaxOptions = $.extend( {}, ajaxOptions, { url: url } ); } else { newAjaxOptions = ajaxOptions; diff --git a/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js b/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js index 541c61072d8..22a3a4b4363 100644 --- a/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js +++ b/tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js @@ -29,4 +29,29 @@ return api.post( {} ); } ); + QUnit.test( 'origin is not included in same-origin GET requests', function ( assert ) { + var apiUrl = location.protocol + '//' + location.host + '/w/api.php', + api = new mw.ForeignApi( apiUrl ); + + this.server.respond( function ( request ) { + assert.strictEqual( request.url.match( /origin=.*?(?:&|$)/ ), null, 'origin is not included in GET requests' ); + request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); + } ); + + return api.get( {} ); + } ); + + QUnit.test( 'origin is not included in same-origin POST requests', function ( assert ) { + var apiUrl = location.protocol + '//' + location.host + '/w/api.php', + api = new mw.ForeignApi( apiUrl ); + + this.server.respond( function ( request ) { + assert.strictEqual( request.requestBody.match( /origin=.*?(?:&|$)/ ), null, 'origin is not included in POST request body' ); + assert.strictEqual( request.url.match( /origin=.*?(?:&|$)/ ), null, 'origin is not included in POST request URL, either' ); + request.respond( 200, { 'Content-Type': 'application/json' }, '[]' ); + } ); + + return api.post( {} ); + } ); + }() );