From 7227362633d0013152297e232253edb167989ce8 Mon Sep 17 00:00:00 2001 From: Brian Wolff Date: Tue, 27 Oct 2015 01:39:34 -0600 Subject: [PATCH] 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 --- resources/src/mediawiki/mediawiki.jqueryMsg.js | 21 ++++++++++++-- .../mediawiki/mediawiki.jqueryMsg.test.js | 33 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/resources/src/mediawiki/mediawiki.jqueryMsg.js b/resources/src/mediawiki/mediawiki.jqueryMsg.js index bf0884b..d412a7b 100644 --- a/resources/src/mediawiki/mediawiki.jqueryMsg.js +++ b/resources/src/mediawiki/mediawiki.jqueryMsg.js @@ -663,7 +663,7 @@ * @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(); @@ -671,12 +671,17 @@ 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 ( $.inArray( attributeName, settings.allowedHtmlCommonAttributes ) === -1 && $.inArray( attributeName, settings.allowedHtmlAttributesByElement[ startTagName ] || [] ) === -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; @@ -1079,7 +1084,8 @@ extlink: function ( nodes ) { var $el, arg = nodes[ 0 ], - contents = nodes[ 1 ]; + contents = nodes[ 1 ], + target; if ( arg instanceof jQuery && !arg.hasClass( 'mediaWiki_htmlEmitter' ) ) { $el = arg; } else { @@ -1091,7 +1097,16 @@ } ) .click( arg ); } 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 f65ff16..213b113 100644 --- a/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js +++ b/tests/qunit/suites/resources/mediawiki/mediawiki.jqueryMsg.test.js @@ -1078,6 +1078,39 @@ assert.equal( logSpy.callCount, 2, 'mw.log.warn calls' ); } ); + QUnit.test( 'Do not allow javascript: urls', 2, function ( assert ) { + mw.messages.set( 'illegal-url', '[javascript:alert(1) foo]' ); + mw.messages.set( 'illegal-url-param', '[$1 foo]' ); + + this.suppressWarnings(); + + assert.equal( + formatParse( 'illegal-url' ), + '[javascript:alert(1) foo]', + 'illegal-url: \'parse\' format' + ); + + assert.equal( + /* jshint scripturl: true */ + formatParse( 'illegal-url-param', 'javascript:alert(1)' ), + /* jshint scripturl: false */ + '[javascript:alert(1) foo]', + 'illegal-url-param: \'parse\' format' + ); + } ); + + QUnit.test( 'Do not allow arbitrary style', 1, function ( assert ) { + mw.messages.set( 'illegal-style', 'bar' ); + + this.suppressWarnings(); + + assert.equal( + formatParse( 'illegal-style' ), + '<span style="background-image:url( http://example.com )">bar</span>', + 'illegal-style: \'parse\' format' + ); + } ); + QUnit.test( 'Integration', 5, function ( assert ) { var expected, logSpy, msg; -- 1.9.5.msysgit.0