From 686e17353b936735cb251dd0bedb29dab861520a Mon Sep 17 00:00:00 2001 From: Brian Wolff Date: Tue, 27 Oct 2015 01:39:34 -0600 Subject: [PATCH] SECURITY: mediawiki.jqueryMsg: Sanitize URLs and 'style' attribute Previously you could leverage the style attribute, and external links to execute javascript. Bug: T86738 Change-Id: I6f15ece1db136369e06dfeee34d1a0c5bc03e32b --- .../mediawiki.jqueryMsg.js | 22 +++++++++++-- .../mediawiki/mediawiki.jqueryMsg.test.js | 32 +++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js b/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js index 989efcec67..0d51e83c08 100644 --- a/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js +++ b/resources/src/mediawiki.jqueryMsg/mediawiki.jqueryMsg.js @@ -709,7 +709,7 @@ mw.jqueryMsg.Parser.prototype = { * @return {boolean} true if this is HTML is allowed, false otherwise */ function isAllowedHtml( startTagName, endTagName, attributes ) { - var i, len, attributeName; + var i, len, attributeName, badStyle; startTagName = startTagName.toLowerCase(); endTagName = endTagName.toLowerCase(); @@ -717,12 +717,18 @@ mw.jqueryMsg.Parser.prototype = { return false; } + badStyle = /[\000-\010\013\016-\037\177]|expression|filter\s*:|accelerator\s*:|-o-link\s*:|-o-link-source\s*:|-o-replace\s*:|url\s*\(|image\s*\(|image-set\s*\(/i; + for ( i = 0, len = attributes.length; i < len; i += 2 ) { attributeName = attributes[ i ]; if ( settings.allowedHtmlCommonAttributes.indexOf( attributeName ) === -1 && ( settings.allowedHtmlAttributesByElement[ startTagName ] || [] ).indexOf( attributeName ) === -1 ) { return false; } + if ( attributeName === 'style' && attributes[ i + 1 ].search( badStyle ) !== -1 ) { + mw.log( 'HTML tag not parsed due to dangerous style attribute' ); + return false; + } } return true; @@ -1230,7 +1236,8 @@ mw.jqueryMsg.HtmlEmitter.prototype = { extlink: function ( nodes ) { var $el, arg = nodes[ 0 ], - contents = nodes[ 1 ]; + contents = nodes[ 1 ], + target; if ( arg instanceof $ && !arg.hasClass( 'mediaWiki_htmlEmitter' ) ) { $el = arg; } else { @@ -1248,7 +1255,16 @@ mw.jqueryMsg.HtmlEmitter.prototype = { } } ); } else { - $el.attr( 'href', textify( arg ) ); + target = textify( arg ); + if ( target.search( new RegExp( '^(/|' + mw.config.get( 'wgUrlProtocols' ) + ')' ) ) !== -1 ) { + $el.attr( 'href', target ); + } else { + mw.log( 'External link in message had illegal target ' + target ); + return appendWithoutParsing( + $( '' ), + [ '[' + target + ' ' ].concat( contents ).concat( ']' ) + ).contents(); + } } } return appendWithoutParsing( $el.empty(), contents ); diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js index e6b933d35b..bc49b67dae 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js @@ -1173,6 +1173,38 @@ } } ); + QUnit.test( 'Do not allow javascript: urls', function ( assert ) { + mw.messages.set( 'illegal-url', '[javascript:alert(1) foo]' ); + mw.messages.set( 'illegal-url-param', '[$1 foo]' ); + + this.suppressWarnings(); + + assert.strictEqual( + formatParse( 'illegal-url' ), + '[javascript:alert(1) foo]', + 'illegal-url: \'parse\' format' + ); + + assert.strictEqual( + // eslint-disable-next-line no-script-url + formatParse( 'illegal-url-param', 'javascript:alert(1)' ), + '[javascript:alert(1) foo]', + 'illegal-url-param: \'parse\' format' + ); + } ); + + QUnit.test( 'Do not allow arbitrary style', function ( assert ) { + mw.messages.set( 'illegal-style', 'bar' ); + + this.suppressWarnings(); + + assert.strictEqual( + formatParse( 'illegal-style' ), + '<span style="background-image:url( http://example.com )">bar</span>', + 'illegal-style: \'parse\' format' + ); + } ); + QUnit.test( 'Integration', function ( assert ) { var expected, msg, $bar; -- 2.27.0