Seen while upgrading taint-check in https://gerrit.wikimedia.org/r/c/mediawiki/extensions/GlobalUsage/+/642262:
// @var GlobalUsageQuery $query foreach ( $query->getSingleImageResult() as $wiki => $result ) { // ... foreach ( $result as $item ) { $out->addHtml( "\t<li>" . self::formatItem( $item ) . "</li>\n" ); }
$query->getSingleImageResult() returns an array of database fields, including page titles. Then we have
public static function formatItem( $item ) { if ( !$item['namespace'] ) { $page = $item['title']; } else { $page = "{$item['namespace']}:{$item['title']}"; } $link = WikiMap::makeForeignLink( $item['wiki'], $page, str_replace( '_', ' ', $page ) ); // Return only the title if no link can be constructed return $link === false ? $page : $link; }
So if the page cannot be found (which I guess is a possibility?), it will echo the DB fields verbatim. In theory, this should be safe thanks to $wgLegalTitleChars, but it's still outputting user-controlled data.
The fix is as simple as escaping $page, so formatItem() will always return an escaped value. I'm creating this task as security-protected just in case, but I guess it can also be made public; leaving this up to the Security Team.