From ef1f4af079c507361169f59f194ff298f1e21ae4 Mon Sep 17 00:00:00 2001 From: daniel Date: Mon, 7 Sep 2015 19:02:24 +0200 Subject: [PATCH] Allow Linker::formatComment to link to another wiki per default. This also adds tests against injection to LinkerTest. Bug: T111676 Change-Id: I757f2b91f3b50d789d04e530049c34b4628253e3 --- RELEASE-NOTES-1.26 | 1 + docs/hooks.txt | 3 ++ includes/Linker.php | 71 +++++++++++++++++---------- tests/phpunit/includes/LinkerTest.php | 66 +++++++++++++++++++++++-- 4 files changed, 111 insertions(+), 30 deletions(-) diff --git a/RELEASE-NOTES-1.26 b/RELEASE-NOTES-1.26 index 5b85bbcc7e..3009158d74 100644 --- a/RELEASE-NOTES-1.26 +++ b/RELEASE-NOTES-1.26 @@ -118,6 +118,7 @@ changes to languages because of Phabricator reports. * ChangeTags::tagDescription() will return false if the interface message for the tag is disabled. * Added PageHistoryPager::doBatchLookups hook. +* Added $wikiId parameter to FormatAutocomments hook. * Added ParserCacheSaveComplete to ParserCache * supportsDirectEditing and supportsDirectApiEditing methods added to ContentHandler, to provide a way for ApiEditPage and EditPage to check diff --git a/docs/hooks.txt b/docs/hooks.txt index 5e2269abb7..54ab46c171 100644 --- a/docs/hooks.txt +++ b/docs/hooks.txt @@ -1376,6 +1376,9 @@ $auto: The extracted part of the parsed comment before the call to the hook. $post: Boolean, true if there is text after this autocomment $title: An optional title object used to links to sections. Can be null. $local: Boolean indicating whether section links should refer to local page. +$wikiId: String containing the ID (as used by WikiMap) of the wiki from which the + autocomment originated; null for the local wiki. Added in 1.26, should default + to null in handler functions, for backwards compatibility. 'GalleryGetModes': Get list of classes that can render different modes of a gallery. diff --git a/includes/Linker.php b/includes/Linker.php index db6f379d52..9b5ff27b3d 100644 --- a/includes/Linker.php +++ b/includes/Linker.php @@ -1276,9 +1276,11 @@ class Linker { * @param string $comment * @param Title|null $title Title object (to generate link to the section in autocomment) or null * @param bool $local Whether section links should refer to local page + * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes. + * * @return mixed|string */ - public static function formatComment( $comment, $title = null, $local = false ) { + public static function formatComment( $comment, $title = null, $local = false, $wikiId = null ) { # Sanitize text a bit: $comment = str_replace( "\n", " ", $comment ); @@ -1286,8 +1288,8 @@ class Linker { $comment = Sanitizer::escapeHtmlAllowEntities( $comment ); # Render autocomments and make links: - $comment = self::formatAutocomments( $comment, $title, $local ); - $comment = self::formatLinksInComment( $comment, $title, $local ); + $comment = self::formatAutocomments( $comment, $title, $local, $wikiId ); + $comment = self::formatLinksInComment( $comment, $title, $local, $wikiId ); return $comment; } @@ -1304,9 +1306,11 @@ class Linker { * @param string $comment Comment text * @param Title|null $title An optional title object used to links to sections * @param bool $local Whether section links should refer to local page - * @return string Formatted comment + * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap. + * + * @return string Formatted comment (wikitext) */ - private static function formatAutocomments( $comment, $title = null, $local = false ) { + private static function formatAutocomments( $comment, $title = null, $local = false, $wikiId = null ) { // @todo $append here is something of a hack to preserve the status // quo. Someone who knows more about bidi and such should decide // (1) what sane rendering even *is* for an LTR edit summary on an RTL @@ -1320,7 +1324,7 @@ class Linker { // zero-width assertions optional, so wrap them in a non-capturing // group. '!(?:(?<=(.)))?/\*\s*(.*?)\s*\*/(?:(?=(.)))?!', - function ( $match ) use ( $title, $local, &$append ) { + function ( $match ) use ( $title, $local, $wikiId, &$append ) { global $wgLang; // Ensure all match positions are defined @@ -1330,7 +1334,7 @@ class Linker { $auto = $match[2]; $post = $match[3] !== ''; $comment = null; - Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local ) ); + Hooks::run( 'FormatAutocomments', array( &$comment, $pre, $auto, $post, $title, $local, $wikiId ) ); if ( $comment === null ) { $link = ''; if ( $title ) { @@ -1349,9 +1353,7 @@ class Linker { $title->getDBkey(), $section ); } if ( $sectionTitle ) { - $link = Linker::link( $sectionTitle, - $wgLang->getArrow(), array(), array(), - 'noclasses' ); + $link = Linker::makeCommentLink( $sectionTitle, $wgLang->getArrow(), $wikiId, 'noclasses' ); } else { $link = ''; } @@ -1384,7 +1386,7 @@ class Linker { * @param string $comment Text to format links in * @param Title|null $title An optional title object used to links to sections * @param bool $local Whether section links should refer to local page - * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap + * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap. * * @return string */ @@ -1459,22 +1461,9 @@ class Linker { $newTarget = clone ( $title ); $newTarget->setFragment( '#' . $target->getFragment() ); $target = $newTarget; - - } - - if ( $wikiId !== null ) { - $thelink = Linker::makeExternalLink( - WikiMap::getForeignURL( $wikiId, $target->getPrefixedText(), $target->getFragment() ), - $linkText . $inside, - /* escape = */ false // Already escaped - ) . $trail; - } else { - $thelink = Linker::link( - $target, - $linkText . $inside - ) . $trail; } + $thelink = Linker::makeCommentLink( $target, $linkText . $inside, $wikiId ) . $trail; } } if ( $thelink ) { @@ -1493,6 +1482,32 @@ class Linker { ); } + /** + * Generates a link to the given Title + * + * @note This is only public for technical reasons. It's not intended for use outside Linker. + * + * @param Title $title + * @param string $text + * @param string|null $wikiId Id of the wiki to link to (if not the local wiki), as used by WikiMap. + * @param string|string[] $options See the $options parameter in Linker::link. + * + * @return string HTML link + */ + public static function makeCommentLink( Title $title, $text, $wikiId = null, $options = array() ) { + if ( $wikiId !== null && !$title->isExternal() ) { + $link = Linker::makeExternalLink( + WikiMap::getForeignURL( $wikiId, $title->getPrefixedText(), $title->getFragment() ), + $text, + /* escape = */ false // Already escaped + ); + } else { + $link = Linker::link( $title, $text, array(), array(), $options ); + } + + return $link; + } + /** * @param Title $contextTitle * @param string $target @@ -1579,17 +1594,18 @@ class Linker { * @param string $comment * @param Title|null $title Title object (to generate link to section in autocomment) or null * @param bool $local Whether section links should refer to local page + * @param string|null $wikiId Id (as used by WikiMap) of the wiki to generate links to. For use with external changes. * * @return string */ - public static function commentBlock( $comment, $title = null, $local = false ) { + public static function commentBlock( $comment, $title = null, $local = false, $wikiId = null ) { // '*' used to be the comment inserted by the software way back // in antiquity in case none was provided, here for backwards // compatibility, acc. to brion -ævar if ( $comment == '' || $comment == '*' ) { return ''; } else { - $formatted = self::formatComment( $comment, $title, $local ); + $formatted = self::formatComment( $comment, $title, $local, $wikiId ); $formatted = wfMessage( 'parentheses' )->rawParams( $formatted )->escaped(); return " $formatted"; } @@ -2382,6 +2398,7 @@ class Linker { 'title' => $tooltip ) ); } + } /** diff --git a/tests/phpunit/includes/LinkerTest.php b/tests/phpunit/includes/LinkerTest.php index 823c9330a6..a3efbb8da0 100644 --- a/tests/phpunit/includes/LinkerTest.php +++ b/tests/phpunit/includes/LinkerTest.php @@ -93,12 +93,26 @@ class LinkerTest extends MediaWikiLangTestCase { * @covers Linker::formatAutocomments * @covers Linker::formatLinksInComment */ - public function testFormatComment( $expected, $comment, $title = false, $local = false ) { + public function testFormatComment( $expected, $comment, $title = false, $local = false, $wikiId = null ) { + $conf = new SiteConfiguration(); + $conf->settings = array( + 'wgServer' => array( + 'enwiki' => '//en.example.org', + 'dewiki' => '//de.example.org', + ), + 'wgArticlePath' => array( + 'enwiki' => '/w/$1', + 'dewiki' => '/w/$1', + ), + ); + $conf->suffixes = array( 'wiki' ); + $this->setMwGlobals( array( 'wgScript' => '/wiki/index.php', 'wgArticlePath' => '/wiki/$1', 'wgWellFormedXml' => true, 'wgCapitalLinks' => true, + 'wgConf' => $conf, ) ); if ( $title === false ) { @@ -108,11 +122,13 @@ class LinkerTest extends MediaWikiLangTestCase { $this->assertEquals( $expected, - Linker::formatComment( $comment, $title, $local ) + Linker::formatComment( $comment, $title, $local, $wikiId ) ); } - public static function provideCasesForFormatComment() { + public function provideCasesForFormatComment() { + $wikiId = 'enwiki'; // $wgConf has a fake entry for this + return array( // Linker::formatComment array( @@ -127,6 +143,10 @@ class LinkerTest extends MediaWikiLangTestCase { "'''not bolded'''", "'''not bolded'''", ), + array( + "try <script>evil</scipt> things", + "try */" + ), array( '→‎autocomment', "/* autocomment */", @@ -166,6 +194,16 @@ class LinkerTest extends MediaWikiLangTestCase { "/* autocomment */", null ), + array( + '→‎autocomment', + "/* autocomment */", + false, false + ), + array( + '→‎autocomment', + "/* autocomment */", + false, false, $wikiId + ), // Linker::formatLinksInComment array( 'abc link def', @@ -191,6 +229,28 @@ class LinkerTest extends MediaWikiLangTestCase { 'abc /subpage def', "abc [[/subpage]] def", ), + array( + 'abc "evil!" def', + "abc [[\"evil!\"]] def", + ), + array( + 'abc [[<script>very evil</script>]] def', + "abc [[]] def", + ), + array( + 'abc [[|]] def', + "abc [[|]] def", + ), + array( + 'abc link def', + "abc [[link]] def", + false, false + ), + array( + 'abc link def', + "abc [[link]] def", + false, false, $wikiId + ) ); } -- 2.20.1