[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: Bug#686330: mediawiki: Multiple security issues



On Thu, 13 Sep 2012, Moritz Muehlenhoff wrote:

> On Fri, Aug 31, 2012 at 06:34:38PM +0200, Julien Cristau wrote:

> > Can't answer without a diff.
> 
> Mediawiki maintainers, what's the status?

Oh, sorry. Other stuff made me forget this for too long.

The diff between the two tarballs is over 10 MiB, although
with .gitignore files removed, using -w and removing all
Messages* files (in the hope these are really only trans‐
lation changes/fixes), it gets down to ~21K (attached).

On a quick skim, I couldn’t find anything wrong with it,
but I’m not qualified to say whether these are bugfixes
only without detailed analysis (I’m not normally doing
development on MW core code itself, more integration
work).

bye,
//mirabilos
-- 
tarent solutions GmbH
Rochusstraße 2-4, D-53123 Bonn • http://www.tarent.de/
Tel: +49 228 54881-393 • Fax: +49 228 54881-314
HRB 5168 (AG Bonn) • USt-ID (VAT): DE122264941
Geschäftsführer: Boris Esser, Sebastian Mancke
diff -wpruN mediawiki-1.19.1/RELEASE-NOTES-1.19 mediawiki-1.19.2/RELEASE-NOTES-1.19
--- mediawiki-1.19.1/RELEASE-NOTES-1.19	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/RELEASE-NOTES-1.19	2012-08-31 00:25:34.000000000 +0200
@@ -3,6 +3,19 @@
 Security reminder: MediaWiki does not require PHP's register_globals
 setting since version 1.2.0. If you have it on, turn it '''off''' if you can.
 
+== MediaWiki 1.19.2 ==
+2012-08-30
+
+This is a security release of the MediaWiki 1.19 branch
+
+=== Changes since 1.19.1 ===
+* (bug 39700) File: link to non-existing file can inject html
+* (bug 35839) Hidden block text leaking to admins
+* (bug 39184) LDAP password leakage
+* (bug 39180) Disallow framing of api results
+* (bug 37587) Enforce language codes to be html safe
+* (bug 38333) Check global blocks on account creation
+
 == MediaWiki 1.19 ==
 
 MediaWiki 1.19 is a large release that contains many new features and bug
@@ -13,6 +26,9 @@ this version.
 Our thanks go to everyone who helped to improve MediaWiki by testing the beta
 release and submitting bug reports.
 
+=== Changes since 1.19.1 ===
+* (bug 38406) Properly quote table names in DatabaseBase::tableName()
+
 === Changes since 1.19.0 ===
 * (bug 36568) Fixed "Illegal string offset 'LIMIT'" warnings in updater
 * (bug 36938) Correctly escape uselang attribute to prevent xss
diff -wpruN mediawiki-1.19.1/includes/AuthPlugin.php mediawiki-1.19.2/includes/AuthPlugin.php
--- mediawiki-1.19.1/includes/AuthPlugin.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/AuthPlugin.php	2012-08-31 00:25:34.000000000 +0200
@@ -157,6 +157,15 @@ class AuthPlugin {
 	}
 
 	/**
+	 * Should MediaWiki store passwords in its local database?
+	 *
+	 * @return bool
+	 */
+	public function allowSetLocalPassword() {
+		return true;
+	}
+
+	/**
 	 * Set the given password in the authentication database.
 	 * As a special case, the password may be set to null to request
 	 * locking the password to an unusable value, with the expectation
diff -wpruN mediawiki-1.19.1/includes/DefaultSettings.php mediawiki-1.19.2/includes/DefaultSettings.php
--- mediawiki-1.19.1/includes/DefaultSettings.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/DefaultSettings.php	2012-08-31 00:25:34.000000000 +0200
@@ -33,7 +33,7 @@ $wgConf = new SiteConfiguration;
 /** @endcond */
 
 /** MediaWiki version number */
-$wgVersion = '1.19.1';
+$wgVersion = '1.19.2';
 
 /** Name of the site. It must be changed in LocalSettings.php */
 $wgSitename = 'MediaWiki';
@@ -2420,6 +2420,18 @@ $wgBreakFrames = false;
 $wgEditPageFrameOptions = 'DENY';
 
 /**
+ * Disallow framing of API pages directly, by setting the X-Frame-Options
+ * header. Since the API returns CSRF tokens, allowing the results to be
+ * framed can compromise your user's account security.
+ * Options are:
+ *   - 'DENY': Do not allow framing. This is recommended for most wikis.
+ *   - 'SAMEORIGIN': Allow framing by pages on the same domain.
+ *   - false: Allow all framing.
+ */
+
+$wgApiFrameOptions = 'DENY';
+
+/**
  * Disable output compression (enabled by default if zlib is available)
  */
 $wgDisableOutputCompression = false;
diff -wpruN mediawiki-1.19.1/includes/Linker.php mediawiki-1.19.2/includes/Linker.php
--- mediawiki-1.19.1/includes/Linker.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/Linker.php	2012-08-31 00:25:34.000000000 +0200
@@ -768,31 +768,31 @@ class Linker {
 	 * Make a "broken" link to an image
 	 *
 	 * @param $title Title object
-	 * @param $html String: link label in htmlescaped text form
+	 * @param $label String: link label (plain text)
 	 * @param $query String: query string
-	 * @param $trail String: link trail (HTML fragment)
-	 * @param $prefix String: link prefix (HTML fragment)
+	 * @param $unused1 Unused parameter kept for b/c
+	 * @param $unused2 Unused parameter kept for b/c
 	 * @param $time Boolean: a file of a certain timestamp was requested
 	 * @return String
 	 */
-	public static function makeBrokenImageLinkObj( $title, $html = '', $query = '', $trail = '', $prefix = '', $time = false ) {
+	public static function makeBrokenImageLinkObj( $title, $label = '', $query = '', $unused1 = '', $unused2 = '', $time = false ) {
 		global $wgEnableUploads, $wgUploadMissingFileUrl, $wgUploadNavigationUrl;
 		if ( ! $title instanceof Title ) {
-			return "<!-- ERROR -->{$prefix}{$html}{$trail}";
+			return "<!-- ERROR -->" . htmlspecialchars( $label );
 		}
 		wfProfileIn( __METHOD__ );
+		if ( $label == '' ) {
+			$label = $title->getPrefixedText();
+		}
+		$encLabel = htmlspecialchars( $label );
 		$currentExists = $time ? ( wfFindFile( $title ) != false ) : false;
 
-		list( $inside, $trail ) = self::splitTrail( $trail );
-		if ( $html == '' )
-			$html = htmlspecialchars( $title->getPrefixedText() );
-
 		if ( ( $wgUploadMissingFileUrl || $wgUploadNavigationUrl || $wgEnableUploads ) && !$currentExists ) {
 			$redir = RepoGroup::singleton()->getLocalRepo()->checkRedirect( $title );
 
 			if ( $redir ) {
 				wfProfileOut( __METHOD__ );
-				return self::linkKnown( $title, "$prefix$html$inside", array(), $query ) . $trail;
+				return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
 			}
 
 			$href = self::getUploadUrl( $title, $query );
@@ -800,10 +800,10 @@ class Linker {
 			wfProfileOut( __METHOD__ );
 			return '<a href="' . htmlspecialchars( $href ) . '" class="new" title="' .
 				htmlspecialchars( $title->getPrefixedText(), ENT_QUOTES ) . '">' .
-				"$prefix$html$inside</a>$trail";
+				$encLabel . '</a>';
 		} else {
 			wfProfileOut( __METHOD__ );
-			return self::linkKnown( $title, "$prefix$html$inside", array(), $query ) . $trail;
+			return self::linkKnown( $title, $encLabel, array(), wfCgiToArray( $query ) );
 		}
 	}
 
diff -wpruN mediawiki-1.19.1/includes/User.php mediawiki-1.19.2/includes/User.php
--- mediawiki-1.19.1/includes/User.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/User.php	2012-08-31 00:25:34.000000000 +0200
@@ -2812,11 +2812,16 @@ class User {
 	 * @todo Only rarely do all these fields need to be set!
 	 */
 	public function saveSettings() {
+		global $wgAuth;
+
 		$this->load();
 		if ( wfReadOnly() ) { return; }
 		if ( 0 == $this->mId ) { return; }
 
 		$this->mTouched = self::newTouchedTimestamp();
+		if ( !$wgAuth->allowSetLocalPassword() ) {
+			$this->mPassword = '';
+		}
 
 		$dbw = wfGetDB( DB_MASTER );
 		$dbw->update( 'user',
diff -wpruN mediawiki-1.19.1/includes/api/ApiFormatBase.php mediawiki-1.19.2/includes/api/ApiFormatBase.php
--- mediawiki-1.19.1/includes/api/ApiFormatBase.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/api/ApiFormatBase.php	2012-08-31 00:25:34.000000000 +0200
@@ -143,6 +143,12 @@ abstract class ApiFormatBase extends Api
 
 		$this->getMain()->getRequest()->response()->header( "Content-Type: $mime; charset=utf-8" );
 
+		//Set X-Frame-Options API results (bug 39180)
+		global $wgApiFrameOptions;
+		if ( $wgApiFrameOptions ) {
+			$this->getMain()->getRequest()->response()->header( "X-Frame-Options: $wgApiFrameOptions" );
+		}
+
 		if ( $isHtml ) {
 ?>
 <!DOCTYPE HTML>
diff -wpruN mediawiki-1.19.1/includes/db/Database.php mediawiki-1.19.2/includes/db/Database.php
--- mediawiki-1.19.1/includes/db/Database.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/includes/db/Database.php	2012-08-31 00:25:34.000000000 +0200
@@ -1973,7 +1973,9 @@ abstract class DatabaseBase implements D
 
 		# Quote the $database and $table and apply the prefix if not quoted.
 		if ( isset( $database ) ) {
-			$database = ( $format == 'quoted' || $this->isQuotedIdentifier( $database ) ? $database : $this->addIdentifierQuotes( $database ) );
+			if ( $format == 'quoted' && !$this->isQuotedIdentifier( $database ) ) {
+				$database = $this->addIdentifierQuotes( $database );
+			}
 		}
 
 		$table = "{$prefix}{$table}";
diff -wpruN mediawiki-1.19.1/includes/installer/OracleUpdater.php mediawiki-1.19.2/includes/installer/OracleUpdater.php
--- mediawiki-1.19.1/includes/installer/OracleUpdater.php	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/includes/installer/OracleUpdater.php	2012-08-31 00:25:34.000000000 +0200
@@ -40,17 +40,16 @@ class OracleUpdater extends DatabaseUpda
 
 			//1.19
 			array( 'addIndex', 'logging',       'i05',      'patch-logging_type_action_index.sql'),
-			array( 'addTable', 'globaltemplatelinks', 'patch-globaltemplatelinks.sql' ),
-			array( 'addTable', 'globalnamespaces', 'patch-globalnamespaces.sql' ),
-			array( 'addTable', 'globalinterwiki', 'patch-globalinterwiki.sql' ),
 			array( 'addField', 'revision', 'rev_sha1', 'patch-rev_sha1_field.sql' ),
 			array( 'addField', 'archive', 'ar_sha1', 'patch-ar_sha1_field.sql' ),
 			array( 'doRemoveNotNullEmptyDefaults2' ),
 			array( 'addIndex', 'page', 'i03', 'patch-page_redirect_namespace_len.sql' ),
-			array( 'modifyField', 'user', 'ug_group', 'patch-ug_group-length-increase.sql' ),
+			array( 'modifyField', 'user_groups', 'ug_group', 'patch-ug_group-length-increase.sql' ),
 			array( 'addField', 'uploadstash', 'us_chunk_inx', 'patch-us_chunk_inx_field.sql' ),
 			array( 'addField', 'job', 'job_timestamp', 'patch-job_timestamp_field.sql' ),
 			array( 'addIndex', 'job', 'i02', 'patch-job_timestamp_index.sql' ),
+			array( 'doPageRestrictionsPKUKFix' ),
+			array( 'modifyField', 'user_former_groups', 'ufg_group', 'patch-ufg_group-length-increase.sql' ),
 
 			// KEEP THIS AT THE BOTTOM!!
 			array( 'doRebuildDuplicateFunction' ),
@@ -180,6 +179,23 @@ class OracleUpdater extends DatabaseUpda
 	}
 
 	/**
+	 * Fixed wrong PK, UK definition
+	 */
+	protected function doPageRestrictionsPKUKFix() {
+		$this->output( "Altering PAGE_RESTRICTIONS keys ... " );
+
+		$meta = $this->db->query( 'SELECT column_name FROM all_cons_columns WHERE owner = \''.strtoupper($this->db->getDBname()).'\' AND constraint_name = \'MW_PAGE_RESTRICTIONS_PK\' AND rownum = 1' );
+		$row = $meta->fetchRow();
+		if ( $row['column_name'] == 'PR_ID' ) {
+			$this->output( "seems to be up to date.\n" );
+			return;
+		}
+
+		$this->applyPatch( 'patch-page_restrictions_pkuk_fix.sql', false );
+		$this->output( "ok\n" );
+	}
+
+	/**
 	 * rebuilding of the function that duplicates tables for tests
 	 */
 	protected function doRebuildDuplicateFunction() {
diff -wpruN mediawiki-1.19.1/includes/specials/SpecialBlock.php mediawiki-1.19.2/includes/specials/SpecialBlock.php
--- mediawiki-1.19.1/includes/specials/SpecialBlock.php	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/includes/specials/SpecialBlock.php	2012-08-31 00:25:34.000000000 +0200
@@ -257,7 +257,13 @@ class SpecialBlock extends FormSpecialPa
 				$fields['DisableUTEdit']['default'] = $block->prevents( 'editownusertalk' );
 			}
 
+			// If the username was hidden (ipb_deleted == 1), don't show the reason
+			// unless this user also has rights to hideuser: Bug 35839
+			if ( !$block->mHideName || $this->getUser()->isAllowed( 'hideuser' ) ) {
 			$fields['Reason']['default'] = $block->mReason;
+			} else {
+				$fields['Reason']['default'] = '';
+			}
 
 			if( $this->getRequest()->wasPosted() ){
 				# Ok, so we got a POST submission asking us to reblock a user.  So show the
diff -wpruN mediawiki-1.19.1/includes/specials/SpecialUserlogin.php mediawiki-1.19.2/includes/specials/SpecialUserlogin.php
--- mediawiki-1.19.1/includes/specials/SpecialUserlogin.php	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/includes/specials/SpecialUserlogin.php	2012-08-31 00:25:34.000000000 +0200
@@ -326,6 +326,12 @@ class LoginForm extends SpecialPage {
 			return false;
 		}
 
+		# Include checks that will include GlobalBlocking (Bug 38333)
+		$permErrors = $this->getTitle()->getUserPermissionsErrors( 'createaccount', $currentUser, true );
+		if ( count( $permErrors ) ) {
+				throw new PermissionsError( 'createaccount', $permErrors );
+		}
+
 		$ip = $this->getRequest()->getIP();
 		if ( $currentUser->isDnsBlacklisted( $ip, true /* check $wgProxyWhitelist */ ) ) {
 			$this->mainLoginForm( $this->msg( 'sorbs_create_account_reason' )->text() . ' (' . htmlspecialchars( $ip ) . ')' );
diff -wpruN mediawiki-1.19.1/languages/Language.php mediawiki-1.19.2/languages/Language.php
--- mediawiki-1.19.1/languages/Language.php	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/languages/Language.php	2012-08-31 00:25:34.000000000 +0200
@@ -210,7 +210,11 @@ class Language {
 	 */
 	public static function isValidCode( $code ) {
 		return
-			strcspn( $code, ":/\\\000" ) === strlen( $code )
+			// People think language codes are html safe, so enforce it.
+			// Ideally we should only allow a-zA-Z0-9-
+			// but, .+ and other chars are often used for {{int:}} hacks
+			// see bugs 37564, 37587, 36938
+			strcspn( $code, ":/\\\000&<>'\"" ) === strlen( $code )
 			&& !preg_match( Title::getTitleInvalidRegex(), $code );
 	}
 
diff -wpruN mediawiki-1.19.1/maintenance/language/messages.inc mediawiki-1.19.2/maintenance/language/messages.inc
--- mediawiki-1.19.1/maintenance/language/messages.inc	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/maintenance/language/messages.inc	2012-08-31 00:25:35.000000000 +0200
@@ -1230,6 +1230,7 @@ $wgMessageStructure = array(
 		'newsectionsummary',
 		'rc-enhanced-expand',
 		'rc-enhanced-hide',
+		'rc-old-title',
 	),
 	'recentchangeslinked' => array(
 		'recentchangeslinked',
diff -wpruN mediawiki-1.19.1/maintenance/oracle/archives/patch-page_restrictions_pkuk_fix.sql mediawiki-1.19.2/maintenance/oracle/archives/patch-page_restrictions_pkuk_fix.sql
--- mediawiki-1.19.1/maintenance/oracle/archives/patch-page_restrictions_pkuk_fix.sql	1970-01-01 01:00:00.000000000 +0100
+++ mediawiki-1.19.2/maintenance/oracle/archives/patch-page_restrictions_pkuk_fix.sql	2012-08-31 00:25:34.000000000 +0200
@@ -0,0 +1,7 @@
+define mw_prefix='{$wgDBprefix}';
+
+ALTER TABLE &mw_prefix.page_restrictions DROP CONSTRAINT &mw_prefix.page_restrictions_pk;
+
+ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_id);
+
+CREATE UNIQUE INDEX &mw_prefix.page_restrictions_u01 ON &mw_prefix.page_restrictions (pr_page,pr_type);
diff -wpruN mediawiki-1.19.1/maintenance/oracle/archives/patch-ufg_group-length-increase.sql mediawiki-1.19.2/maintenance/oracle/archives/patch-ufg_group-length-increase.sql
--- mediawiki-1.19.1/maintenance/oracle/archives/patch-ufg_group-length-increase.sql	1970-01-01 01:00:00.000000000 +0100
+++ mediawiki-1.19.2/maintenance/oracle/archives/patch-ufg_group-length-increase.sql	2012-08-31 00:25:34.000000000 +0200
@@ -0,0 +1,9 @@
+define mw_prefix='{$wgDBprefix}';
+
+/*$mw$*/
+BEGIN
+	EXECUTE IMMEDIATE 'ALTER TABLE &mw_prefix.user_former_groups MODIFY ufg_group VARCHAR2(32) NOT NULL';
+EXCEPTION WHEN OTHERS THEN
+	IF (SQLCODE = -01442) THEN NULL; ELSE RAISE; END IF;
+END;
+/*$mw$*/
diff -wpruN mediawiki-1.19.1/maintenance/oracle/archives/patch-ug_group-length-increase.sql mediawiki-1.19.2/maintenance/oracle/archives/patch-ug_group-length-increase.sql
--- mediawiki-1.19.1/maintenance/oracle/archives/patch-ug_group-length-increase.sql	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/maintenance/oracle/archives/patch-ug_group-length-increase.sql	2012-08-31 00:25:34.000000000 +0200
@@ -1,3 +1,9 @@
 define mw_prefix='{$wgDBprefix}';
 
-ALTER TABLE &mw_prefix.user_groups MODIFY ug_group VARCHAR2(32) NOT NULL;
+/*$mw$*/
+BEGIN
+	EXECUTE IMMEDIATE 'ALTER TABLE &mw_prefix.user_groups MODIFY ug_group VARCHAR2(32) NOT NULL';
+EXCEPTION WHEN OTHERS THEN
+	IF (SQLCODE = -01442) THEN NULL; ELSE RAISE; END IF;
+END;
+/*$mw$*/
diff -wpruN mediawiki-1.19.1/maintenance/oracle/tables.sql mediawiki-1.19.2/maintenance/oracle/tables.sql
--- mediawiki-1.19.1/maintenance/oracle/tables.sql	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/maintenance/oracle/tables.sql	2012-08-31 00:25:35.000000000 +0200
@@ -39,7 +39,7 @@ CREATE INDEX &mw_prefix.user_groups_i01 
 
 CREATE TABLE &mw_prefix.user_former_groups (
   ufg_user   NUMBER      DEFAULT 0 NOT NULL,
-  ufg_group  VARCHAR2(16)     NOT NULL
+  ufg_group  VARCHAR2(32)     NOT NULL
 );
 ALTER TABLE &mw_prefix.user_former_groups ADD CONSTRAINT &mw_prefix.user_former_groups_fk1 FOREIGN KEY (ufg_user) REFERENCES &mw_prefix.mwuser(user_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
 CREATE UNIQUE INDEX &mw_prefix.user_former_groups_u01 ON &mw_prefix.user_former_groups (ufg_user,ufg_group);
@@ -564,8 +564,9 @@ CREATE TABLE &mw_prefix.page_restriction
   pr_user    NUMBER          NULL,
   pr_expiry  TIMESTAMP(6) WITH TIME ZONE      NULL
 );
-ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_page,pr_type);
+ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_pk PRIMARY KEY (pr_id);
 ALTER TABLE &mw_prefix.page_restrictions ADD CONSTRAINT &mw_prefix.page_restrictions_fk1 FOREIGN KEY (pr_page) REFERENCES &mw_prefix.page(page_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
+CREATE UNIQUE INDEX &mw_prefix.page_restrictions_u01 ON &mw_prefix.page_restrictions (pr_page,pr_type);
 CREATE INDEX &mw_prefix.page_restrictions_i01 ON &mw_prefix.page_restrictions (pr_type,pr_level);
 CREATE INDEX &mw_prefix.page_restrictions_i02 ON &mw_prefix.page_restrictions (pr_level);
 CREATE INDEX &mw_prefix.page_restrictions_i03 ON &mw_prefix.page_restrictions (pr_cascade);
diff -wpruN mediawiki-1.19.1/tests/parser/parserTests.txt mediawiki-1.19.2/tests/parser/parserTests.txt
--- mediawiki-1.19.1/tests/parser/parserTests.txt	2012-06-13 20:22:39.000000000 +0200
+++ mediawiki-1.19.2/tests/parser/parserTests.txt	2012-08-31 00:25:35.000000000 +0200
@@ -1733,6 +1733,21 @@ Link with double quotes in title part (l
 !! end
 
 !! test
+Broken image links with HTML captions (bug 39700)
+!! input
+[[File:Nonexistent|<script></script>]]
+[[File:Nonexistent|100px|<script></script>]]
+[[File:Nonexistent|&lt;]]
+[[File:Nonexistent|a<i>b</i>c]]
+!! result
+<p><a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;script&gt;&lt;/script&gt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">&lt;</a>
+<a href="/index.php?title=Special:Upload&amp;wpDestFile=Nonexistent" class="new" title="File:Nonexistent">abc</a>
+</p>
+!! end
+
+!! test
 Plain link to URL
 !! input
 [[http://www.example.com]]
diff -wpruN mediawiki-1.19.1/tests/phpunit/includes/db/DatabaseTest.php mediawiki-1.19.2/tests/phpunit/includes/db/DatabaseTest.php
--- mediawiki-1.19.1/tests/phpunit/includes/db/DatabaseTest.php	2012-06-13 20:22:38.000000000 +0200
+++ mediawiki-1.19.2/tests/phpunit/includes/db/DatabaseTest.php	2012-08-31 00:25:34.000000000 +0200
@@ -57,6 +57,98 @@ class DatabaseTest extends MediaWikiTest
 			$this->db->addQuotes( "string's cause trouble" ) );
 	}
 
+	private function getSharedTableName( $table, $database, $prefix, $format = 'quoted' ) {
+		global $wgSharedDB, $wgSharedTables, $wgSharedPrefix;
+
+		$oldName = $wgSharedDB;
+		$oldTables = $wgSharedTables;
+		$oldPrefix = $wgSharedPrefix;
+
+		$wgSharedDB = $database;
+		$wgSharedTables = array( $table );
+		$wgSharedPrefix = $prefix;
+
+		$ret = $this->db->tableName( $table, $format );
+
+		$wgSharedDB = $oldName;
+		$wgSharedTables = $oldTables;
+		$wgSharedPrefix = $oldPrefix;
+
+		return $ret;
+	}
+
+	private function prefixAndQuote( $table, $database = null, $prefix = null, $format = 'quoted' ) {
+		if ( $this->db->getType() === 'sqlite' || $format !== 'quoted' ) {
+			$quote = '';
+		} elseif ( $this->db->getType() === 'mysql' ) {
+			$quote = '`';
+		} else {
+			$quote = '"';
+		}
+
+		if ( $database !== null ) {
+			$database = $quote . $database . $quote . '.';
+		}
+
+		if ( $prefix === null ) {
+			$prefix = $this->dbPrefix();
+		}
+
+		return $database . $quote . $prefix . $table . $quote;
+	}
+
+	function testTableNameLocal() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename' ),
+			$this->db->tableName( 'tablename' )
+		);
+	}
+
+	function testTableNameRawLocal() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', null, null, 'raw' ),
+			$this->db->tableName( 'tablename', 'raw' )
+		);
+	}
+
+	function testTableNameShared() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_' ),
+			$this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_' )
+		);
+
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'sharedatabase', null ),
+			$this->getSharedTableName( 'tablename', 'sharedatabase', null )
+		);
+	}
+
+	function testTableNameRawShared() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'sharedatabase', 'sh_', 'raw' ),
+			$this->getSharedTableName( 'tablename', 'sharedatabase', 'sh_', 'raw' )
+		);
+
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'sharedatabase', null, 'raw' ),
+			$this->getSharedTableName( 'tablename', 'sharedatabase', null, 'raw' )
+		);
+	}
+
+	function testTableNameForeign() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'databasename', '' ),
+			$this->db->tableName( 'databasename.tablename' )
+		);
+	}
+
+	function testTableNameRawForeign() {
+		$this->assertEquals(
+			$this->prefixAndQuote( 'tablename', 'databasename', '', 'raw' ),
+			$this->db->tableName( 'databasename.tablename', 'raw' )
+		);
+	}
+
 	function testFillPreparedEmpty() {
 		$sql = $this->db->fillPrepared(
 			'SELECT * FROM interwiki', array() );

Reply to: