mw.ForeignApi: don’t set origin for same-origin requests
authorLucas Werkmeister <lucas.werkmeister@wikimedia.de>
Mon, 9 Sep 2019 14:55:35 +0000 (16:55 +0200)
committerLucas Werkmeister <lucas.werkmeister@wikimedia.de>
Wed, 11 Sep 2019 14:02:44 +0000 (16:02 +0200)
If the foreign API has the same host as the current page (e. g. if it’s
actually the same wiki, or multiple wikis are installed under different
paths on the same host), then the browser will not send an Origin
header, and if we still set an origin parameter, then the API will
complain that the two don’t match. Detect this and unset the origin
parameter in that case.

Bug: T208601
Change-Id: Ia006f3dc3283ce3f81d4d72cbe9676a00797c4d0

resources/Resources.php
resources/src/mediawiki.ForeignApi.core.js
tests/qunit/suites/resources/mediawiki.api/mediawiki.ForeignApi.test.js

index 1388128..a9e29c9 100644 (file)
@@ -895,6 +895,7 @@ return [
                'dependencies' => [
                        'mediawiki.api',
                        'oojs',
+                       'mediawiki.Uri',
                ],
                'targets' => [ 'desktop', 'mobile' ],
        ],
index 4b6313b..83ea0ce 100644 (file)
@@ -59,7 +59,6 @@
                                        }
                                },
                                parameters: {
-                                       // Add 'origin' query parameter to all requests.
                                        origin: this.getOrigin()
                                }
                        },
         * 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;
        };
 
                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;
index 541c610..22a3a4b 100644 (file)
                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( {} );
+       } );
+
 }() );