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

Bug#859754: marked as done (unblock: mediawiki/1:1.27.2-1)



Your message dated Tue, 11 Apr 2017 06:12:00 +0000
with message-id <e7d1654a-d4b4-45d0-3a85-9f900c4e6ff6@thykier.net>
and subject line Re: Bug#859754: unblock: mediawiki/1:1.27.2-1
has caused the Debian Bug report #859754,
regarding unblock: mediawiki/1:1.27.2-1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
859754: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=859754
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

Please unblock package mediawiki

1.27.2 is a security release of MediaWiki fixing the
following CVEs:
CVE-2017-0363, CVE-2017-0364, CVE-2017-0365, CVE-2017-0361,
CVE-2017-0362, CVE-2017-0368, CVE-2017-0366, CVE-2017-0370,
CVE-2017-0369, CVE-2017-0367, CVE-2017-0372

unblock mediawiki/1:1.27.2-1

-- System Information:
Debian Release: 9.0
  APT prefers unstable
  APT policy: (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 4.10.6-200.fc25.x86_64 (SMP w/4 CPU cores)
Locale: LANG=C.UTF-8, LC_CTYPE=C.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
diff -Nru mediawiki-1.27.1/RELEASE-NOTES-1.27 mediawiki-1.27.2/RELEASE-NOTES-1.27
--- mediawiki-1.27.1/RELEASE-NOTES-1.27	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/RELEASE-NOTES-1.27	2017-04-06 11:54:29.000000000 -0700
@@ -1,3 +1,46 @@
+== MediaWiki 1.27.2 ==
+This is a security and maintenance release of the MediaWiki 1.27 branch.
+
+ApiCreateAccount was removed in 1.27.0. It was incorrectly still marked as
+deprecated (rather than already removed) in the RELEASE-NOTES at the point 1.27.0
+was released.
+
+=== Changes since 1.27.1 ===
+
+* (T68404) CSS3 attr() function with url type argument is no longer allowed
+  in inline styles.
+* $wgRunJobsAsync is now false by default (T142751). This change only affects
+  wikis with $wgJobRunRate > 0.
+* (T152717) Better escaping for PHP mail() command
+* Submitting the lgtoken and lgpassword parameters in the query string to
+  action=login is now deprecated and outputs a warning. They should be submitted
+  in the POST body instead.
+* Submitting sensitive authentication request parameters to action=clientlogin,
+  action=createaccount, action=linkaccount, and action=changeauthenticationdata
+  in the query string is now deprecated and outputs a warning. They should be
+  submitted in the POST body instead.
+* (T158766) Avoid SQL error on MSSQL when using selectRowCount()
+* (T145635) Fix too long index error when installing with MSSQL.
+* (T156184) $wgRawHtml will no longer apply to internationalization messages.
+* (T160519) CACHE_ANYTHING will not be CACHE_ACCEL if no accelerator is installed.
+* (T109140) (T122209) SECURITY: Special:UserLogin and Special:Search allow redirect
+  to interwiki links.
+* (T144845) SECURITY: XSS in SearchHighlighter::highlightText() when
+  $wgAdvancedSearchHighlighting is true.
+* (T125177) SECURITY: API parameters may now be marked as "sensitive" to keep
+  their values out of the logs.
+* (T150044) SECURITY: "Mark all pages visited" on the watchlist now requires a CSRF
+  token.
+* (T156184) SECURITY: Escape content model/format url parameter in message.
+* (T151735) SECURITY: SVG filter evasion using default attribute values in DTD
+  declaration.
+* (T161453) SECURITY: LocalisationCache will no longer use the temporary directory
+  in it's fallback chain when trying to work out where to write the cache.
+* (T48143) SECURITY: Spam blacklist ineffective on encoded URLs inside file inclusion
+  syntax's link parameter.
+* (T108138) SECURITY: Sysops can undelete pages, although the page is protected against
+  it.
+
 == MediaWiki 1.27.1 ==
 
 This is a maintenance release of the MediaWiki 1.27 branch.
@@ -311,6 +354,8 @@
 * Added action=changeauthenticationdata.
 * Added action=removeauthenticationdata.
 * Added action=resetpassword.
+* (T125177) SECURITY: API parameters may now be marked as "sensitive" to keep
+  their values out of the logs.
 
 === Action API internal changes in 1.27 ===
 * ApiQueryORM removed.
@@ -343,7 +388,7 @@
 * ApiMain::addFormat() was removed (deprecated in 1.21).
 * ApiMain::getFormats() was removed (deprecated in 1.21).
 * ApiPageSet::finishPageSetGeneration() was removed (deprecated in 1.21).
-* ApiCreateAccount is deprecated, and will be removed soon.
+* ApiCreateAccount was removed.
 
 === Languages updated in 1.27 ===
 
diff -Nru mediawiki-1.27.1/autoload.php mediawiki-1.27.2/autoload.php
--- mediawiki-1.27.1/autoload.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/autoload.php	2017-04-06 11:54:28.000000000 -0700
@@ -5,6 +5,7 @@
 
 $wgAutoloadLocalClasses = [
 	'APCBagOStuff' => __DIR__ . '/includes/libs/objectcache/APCBagOStuff.php',
+	'APCUBagOStuff' => __DIR__ . '/includes/libs/objectcache/APCUBagOStuff.php',
 	'AbstractContent' => __DIR__ . '/includes/content/AbstractContent.php',
 	'Action' => __DIR__ . '/includes/actions/Action.php',
 	'ActiveUsersPager' => __DIR__ . '/includes/specials/pagers/ActiveUsersPager.php',
@@ -1254,6 +1255,7 @@
 	'SpecialExpandTemplates' => __DIR__ . '/includes/specials/SpecialExpandTemplates.php',
 	'SpecialExport' => __DIR__ . '/includes/specials/SpecialExport.php',
 	'SpecialFilepath' => __DIR__ . '/includes/specials/SpecialFilepath.php',
+	'SpecialGoToInterwiki' => __DIR__ . '/includes/specials/SpecialGoToInterwiki.php',
 	'SpecialImport' => __DIR__ . '/includes/specials/SpecialImport.php',
 	'SpecialJavaScriptTest' => __DIR__ . '/includes/specials/SpecialJavaScriptTest.php',
 	'SpecialLinkAccounts' => __DIR__ . '/includes/specials/SpecialLinkAccounts.php',
diff -Nru mediawiki-1.27.1/debian/changelog mediawiki-1.27.2/debian/changelog
--- mediawiki-1.27.1/debian/changelog	2016-09-13 04:17:42.000000000 -0700
+++ mediawiki-1.27.2/debian/changelog	2017-04-06 14:04:24.000000000 -0700
@@ -1,3 +1,13 @@
+mediawiki (1:1.27.2-1) unstable; urgency=medium
+
+  * Improve NEWS file (Closes: #852862, #854352)
+  * Imported Upstream version 1.27.2 (security release), fixing
+    CVE-2017-0363, CVE-2017-0364, CVE-2017-0365, CVE-2017-0361,
+    CVE-2017-0362, CVE-2017-0368, CVE-2017-0366, CVE-2017-0370,
+    CVE-2017-0369, CVE-2017-0367, CVE-2017-0372
+
+ -- Kunal Mehta <legoktm@member.fsf.org>  Thu, 06 Apr 2017 14:04:24 -0700
+
 mediawiki (1:1.27.1-3) unstable; urgency=medium
 
   * Ensure mediawiki depends upon the same version of mediawiki-classes
diff -Nru mediawiki-1.27.1/debian/mediawiki.NEWS mediawiki-1.27.2/debian/mediawiki.NEWS
--- mediawiki-1.27.1/debian/mediawiki.NEWS	2016-09-13 04:17:42.000000000 -0700
+++ mediawiki-1.27.2/debian/mediawiki.NEWS	2017-03-08 22:54:53.000000000 -0800
@@ -1,13 +1,27 @@
 mediawiki (1:1.27.0-1) unstable; urgency=medium
 
     MediaWiki has been updated to the 1.27 upstream release branch, which
-    brings in about 6 years of upstream changes. Release notes may be found
+    brings in about 4 years of upstream changes. Release notes may be found
     in /usr/share/doc/mediawiki/RELEASE-NOTES-1.27 and
     /usr/share/doc/mediawiki/changelog. Please see the "Upgrading" section
     of README.debian and /usr/share/doc/mediawiki/UPGRADE for details on
     what manual steps need to be taken to upgrade.
 
+    Some highlights of new features:
+    * Improvements to the appearance and performance of diffs
+    * Better defaults for email notifications
+    * Performance improvements in client-side JavaScript
+    * Secure storage for passwords
+    * Improved patrolling and anti-spam features out of the box
+    * Additional language and internationalization support
+    * Common extensions are bundled and can be enabled from the installer
+
     Note that the mediawiki-extensions* packages are no longer compatible
     with this version of MediaWiki, and are no longer supported.
 
+    If you are upgrading from wheezy, you may encounter issues with prior
+    apache2 configuration preventing the new one from being installed.
+    Disabling the old one and copying over the new one from
+    /etc/mediawiki/mediawiki.conf is recommended.
+
  -- Kunal Mehta <legoktm@member.fsf.org>  Tue, 27 Sep 2016 12:45:06 -0700
diff -Nru mediawiki-1.27.1/extensions/ConfirmEdit/SimpleCaptcha/Captcha.php mediawiki-1.27.2/extensions/ConfirmEdit/SimpleCaptcha/Captcha.php
--- mediawiki-1.27.1/extensions/ConfirmEdit/SimpleCaptcha/Captcha.php	2016-08-22 13:53:17.000000000 -0700
+++ mediawiki-1.27.2/extensions/ConfirmEdit/SimpleCaptcha/Captcha.php	2017-04-06 11:54:43.000000000 -0700
@@ -599,7 +599,7 @@
 				$newLinks = $this->findLinks( $title, $newtext );
 			}
 
-			$unknownLinks = array_filter( $newLinks, [ &$this, 'filterLink' ] );
+			$unknownLinks = array_filter( $newLinks, [ $this, 'filterLink' ] );
 			$addedLinks = array_diff( $unknownLinks, $oldLinks );
 			$numLinks = count( $addedLinks );
 
diff -Nru mediawiki-1.27.1/extensions/ConfirmEdit/extension.json mediawiki-1.27.2/extensions/ConfirmEdit/extension.json
--- mediawiki-1.27.1/extensions/ConfirmEdit/extension.json	2016-08-22 13:53:17.000000000 -0700
+++ mediawiki-1.27.2/extensions/ConfirmEdit/extension.json	2017-04-06 11:54:43.000000000 -0700
@@ -86,6 +86,9 @@
 		"APIEditBeforeSave": [
 			"ConfirmEditHooks::confirmEditAPI"
 		],
+		"TitleReadWhitelist": [
+			"ConfirmEditHooks::onTitleReadWhitelist"
+		],
 		"UnitTestsList": [
 			"ConfirmEditHooks::onUnitTestsList"
 		]
diff -Nru mediawiki-1.27.1/extensions/ConfirmEdit/includes/ConfirmEditHooks.php mediawiki-1.27.2/extensions/ConfirmEdit/includes/ConfirmEditHooks.php
--- mediawiki-1.27.1/extensions/ConfirmEdit/includes/ConfirmEditHooks.php	2016-08-22 13:53:17.000000000 -0700
+++ mediawiki-1.27.2/extensions/ConfirmEdit/includes/ConfirmEditHooks.php	2017-04-06 11:54:43.000000000 -0700
@@ -172,24 +172,29 @@
 	 * Set up $wgWhitelistRead
 	 */
 	public static function confirmEditSetup() {
-		global $wgGroupPermissions, $wgCaptchaTriggers, $wgWikimediaJenkinsCI;
+		global $wgCaptchaTriggers, $wgWikimediaJenkinsCI;
 
 		// There is no need to run (core) tests with enabled ConfirmEdit - bug T44145
 		if ( isset( $wgWikimediaJenkinsCI ) && $wgWikimediaJenkinsCI === true ) {
 			$wgCaptchaTriggers = array_fill_keys( array_keys( $wgCaptchaTriggers ), false );
 		}
+	}
 
-		if ( !$wgGroupPermissions['*']['read'] && $wgCaptchaTriggers['badlogin'] ) {
-			// We need to ensure that the captcha interface is accessible
-			// so that unauthenticated users can actually get in after a
-			// mistaken password typing.
-			global $wgWhitelistRead;
-			$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
-			$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
-			$wgWhitelistRead[] = $image->getPrefixedText();
-			$wgWhitelistRead[] = $help->getPrefixedText();
+	/**
+	 * TitleReadWhitelist hook handler.
+	 *
+	 * @param Title $title
+	 * @param User $user
+	 * @param $whitelisted
+	 */
+	public static function onTitleReadWhitelist( Title $title, User $user, &$whitelisted ) {
+		$image = SpecialPage::getTitleFor( 'Captcha', 'image' );
+		$help = SpecialPage::getTitleFor( 'Captcha', 'help' );
+		if ( $title->equals( $image ) || $title->equals( $help ) ) {
+			$whitelisted = true;
 		}
 	}
+
 	/**
 	 * Callback for extension.json of FancyCaptcha to set a default captcha directory,
 	 * which depends on wgUploadDirectory
diff -Nru mediawiki-1.27.1/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.class.php mediawiki-1.27.2/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.class.php
--- mediawiki-1.27.1/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.class.php	2016-08-22 13:53:39.000000000 -0700
+++ mediawiki-1.27.2/extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.class.php	2017-04-06 11:55:03.000000000 -0700
@@ -289,7 +289,15 @@
 				->getProcess();
 
 			$process->setInput( $code );
-			$process->run();
+
+			/* Workaround for T151523 (buggy $process->getOutput()).
+				If/when this issue is fixed in HHVM or Symfony,
+				replace this with "$process->run(); $output = $process->getOutput();"
+			*/
+			$output = '';
+			$process->run( function( $type, $capturedOutput ) use ( &$output ) {
+				$output .= $capturedOutput;
+			} );
 
 			if ( !$process->isSuccessful() ) {
 				$status->warning( 'syntaxhighlight-error-pygments-invocation-failure' );
@@ -298,7 +306,6 @@
 				return $status;
 			}
 
-			$output = $process->getOutput();
 			$cache->set( $cacheKey, $output );
 		}
 
diff -Nru mediawiki-1.27.1/extensions/TitleBlacklist/TitleBlacklist.list.php mediawiki-1.27.2/extensions/TitleBlacklist/TitleBlacklist.list.php
--- mediawiki-1.27.1/extensions/TitleBlacklist/TitleBlacklist.list.php	2016-08-22 13:53:41.000000000 -0700
+++ mediawiki-1.27.2/extensions/TitleBlacklist/TitleBlacklist.list.php	2017-04-06 11:55:05.000000000 -0700
@@ -17,6 +17,10 @@
  */
 class TitleBlacklist {
 	private $mBlacklist = null, $mWhitelist = null;
+
+	/** @var TitleBlacklist */
+	protected static $instance = null;
+
 	const VERSION = 3;	// Blacklist format
 
 	/**
@@ -25,12 +29,28 @@
 	 * @return TitleBlacklist
 	 */
 	public static function singleton() {
-		static $instance = null;
+		if ( self::$instance === null ) {
+			self::$instance = new self;
+		}
+		return self::$instance;
+	}
 
-		if ( $instance === null ) {
-			$instance = new self;
+	/**
+	 * Destroy/reset the current singleton instance.
+	 *
+	 * This is solely for testing and will fail unless MW_PHPUNIT_TEST is
+	 * defined.
+	 */
+	public static function destroySingleton() {
+
+		if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+			throw new MWException(
+				'Can not invoke ' . __METHOD__ . '() ' .
+				'out of tests (MW_PHPUNIT_TEST not set).'
+			);
 		}
-		return $instance;
+
+		self::$instance = null;
 	}
 
 	/**
diff -Nru mediawiki-1.27.1/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php mediawiki-1.27.2/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php
--- mediawiki-1.27.1/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php	2016-08-22 13:53:41.000000000 -0700
+++ mediawiki-1.27.2/extensions/TitleBlacklist/tests/ApiQueryTitleBlacklistTest.php	2017-04-06 11:55:05.000000000 -0700
@@ -23,6 +23,7 @@
 		parent::setUp();
 		$this->doLogin();
 
+		TitleBlacklist::destroySingleton();
 		$wgTitleBlacklistSources = array(
 			array(
 				'type' => 'file',
@@ -31,6 +32,11 @@
 		);
 	}
 
+	function tearDown() {
+		TitleBlacklist::destroySingleton();
+		parent::tearDown();
+	}
+
 	/**
 	 * Verify we allow a title which is not blacklisted
 	 */
diff -Nru mediawiki-1.27.1/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js mediawiki-1.27.2/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js
--- mediawiki-1.27.1/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js	2016-08-22 13:53:48.000000000 -0700
+++ mediawiki-1.27.2/extensions/WikiEditor/modules/jquery.wikiEditor.preview.js	2017-04-06 11:55:12.000000000 -0700
@@ -118,11 +118,12 @@
 						rvprop: '',
 						rvsection: section === '' ? undefined : section
 					};
+					var postPromise = api.post( postdata );
 
-					api.post( postdata )
-					.done( function ( data ) {
+					$.when( postPromise, mw.loader.using( 'mediawiki.action.history.diff' ) )
+					.done( function ( postResult ) {
 						try {
-							var diff = data.query.pages[ 0 ]
+							var diff = postResult[ 0 ].query.pages[ 0 ]
 								.revisions[ 0 ].diff.body;
 
 							context.$changesTab.find( 'table.diff tbody' )
diff -Nru mediawiki-1.27.1/includes/DefaultSettings.php mediawiki-1.27.2/includes/DefaultSettings.php
--- mediawiki-1.27.1/includes/DefaultSettings.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/includes/DefaultSettings.php	2017-04-06 11:54:29.000000000 -0700
@@ -75,7 +75,7 @@
  * MediaWiki version number
  * @since 1.2
  */
-$wgVersion = '1.27.1';
+$wgVersion = '1.27.2';
 
 /**
  * Name of the site. It must be changed in LocalSettings.php
@@ -2193,7 +2193,7 @@
  *   - CACHE_NONE:       Do not cache
  *   - CACHE_DB:         Store cache objects in the DB
  *   - CACHE_MEMCACHED:  MemCached, must specify servers in $wgMemCachedServers
- *   - CACHE_ACCEL:      APC, XCache or WinCache
+ *   - CACHE_ACCEL:      APC, APCU, XCache or WinCache
  *   - (other):          A string may be used which identifies a cache
  *                       configuration in $wgObjectCaches.
  *
@@ -2269,6 +2269,7 @@
 	],
 
 	'apc' => [ 'class' => 'APCBagOStuff', 'reportDupes' => false ],
+	'apcu' => [ 'class' => 'APCUBagOStuff', 'reportDupes' => false ],
 	'xcache' => [ 'class' => 'XCacheBagOStuff', 'reportDupes' => false ],
 	'wincache' => [ 'class' => 'WinCacheBagOStuff', 'reportDupes' => false ],
 	'memcached-php' => [ 'class' => 'MemcachedPhpBagOStuff', 'loggroup' => 'memcached' ],
@@ -8028,7 +8029,7 @@
  * execution finishes.
  * @since 1.23
  */
-$wgRunJobsAsync = true;
+$wgRunJobsAsync = false;
 
 /**
  * Number of rows to update per job
@@ -8283,9 +8284,9 @@
 /**
  * Where popular password file is located.
  *
- * Default in core contains 50,000 most popular. This config
+ * Default in core contains 10,000 most popular. This config
  * allows you to change which file, in case you want to generate
- * a password file with > 50000 entries in it.
+ * a password file with > 10000 entries in it.
  *
  * @see maintenance/createCommonPasswordCdb.php
  * @since 1.27
diff -Nru mediawiki-1.27.1/includes/EditPage.php mediawiki-1.27.2/includes/EditPage.php
--- mediawiki-1.27.1/includes/EditPage.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/EditPage.php	2017-04-06 11:54:28.000000000 -0700
@@ -980,7 +980,10 @@
 			throw new ErrorPageError(
 				'editpage-notsupportedcontentformat-title',
 				'editpage-notsupportedcontentformat-text',
-				[ $this->contentFormat, ContentHandler::getLocalizedName( $this->contentModel ) ]
+				[
+					wfEscapeWikiText( $this->contentFormat ),
+					wfEscapeWikiText( ContentHandler::getLocalizedName( $this->contentModel ) )
+				]
 			);
 		}
 
diff -Nru mediawiki-1.27.1/includes/MediaWiki.php mediawiki-1.27.2/includes/MediaWiki.php
--- mediawiki-1.27.1/includes/MediaWiki.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/MediaWiki.php	2017-04-06 11:54:24.000000000 -0700
@@ -312,8 +312,6 @@
 	 * - Normalise empty title:
 	 *   /wiki/ -> /wiki/Main
 	 *   /w/index.php?title= -> /wiki/Main
-	 * - Normalise non-standard title urls:
-	 *   /w/index.php?title=Foo_Bar -> /wiki/Foo_Bar
 	 * - Don't redirect anything with query parameters other than 'title' or 'action=view'.
 	 *
 	 * @param Title $title
@@ -326,6 +324,8 @@
 
 		if ( $request->getVal( 'action', 'view' ) != 'view'
 			|| $request->wasPosted()
+			|| ( $request->getVal( 'title' ) !== null
+				&& $title->getPrefixedDBkey() == $request->getVal( 'title' ) )
 			|| count( $request->getValueNames( [ 'action', 'title' ] ) )
 			|| !Hooks::run( 'TestCanonicalRedirect', [ $request, $title, $output ] )
 		) {
@@ -340,19 +340,7 @@
 		}
 		// Redirect to canonical url, make it a 301 to allow caching
 		$targetUrl = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT );
-
-		if ( $targetUrl != $request->getFullRequestURL() ) {
-			$output->setCdnMaxage( 1200 );
-			$output->redirect( $targetUrl, '301' );
-			return true;
-		}
-
-		// If there is no title, or the title is in a non-standard encoding, we demand
-		// a redirect. If cgi somehow changed the 'title' query to be non-standard while
-		// the url is standard, the server is misconfigured.
-		if ( $request->getVal( 'title' ) === null
-			|| $title->getPrefixedDBkey() != $request->getVal( 'title' )
-		) {
+		if ( $targetUrl == $request->getFullRequestURL() ) {
 			$message = "Redirect loop detected!\n\n" .
 				"This means the wiki got confused about what page was " .
 				"requested; this sometimes happens when moving a wiki " .
@@ -374,7 +362,9 @@
 			}
 			throw new HttpError( 500, $message );
 		}
-		return false;
+		$output->setSquidMaxage( 1200 );
+		$output->redirect( $targetUrl, '301' );
+		return true;
 	}
 
 	/**
@@ -813,16 +803,18 @@
 
 		$runJobsLogger = LoggerFactory::getInstance( 'runJobs' );
 
+		// Fall back to running the job(s) while the user waits if needed
 		if ( !$this->config->get( 'RunJobsAsync' ) ) {
-			// Fall back to running the job here while the user waits
 			$runner = new JobRunner( $runJobsLogger );
-			$runner->run( [ 'maxJobs'  => $n ] );
+			$runner->run( [ 'maxJobs' => $n ] );
 			return;
 		}
 
+		// Do not send request if there are probably no jobs
 		try {
-			if ( !JobQueueGroup::singleton()->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
-				return; // do not send request if there are probably no jobs
+			$group = JobQueueGroup::singleton();
+			if ( !$group->queuesHaveJobs( JobQueueGroup::TYPE_DEFAULT ) ) {
+				return;
 			}
 		} catch ( JobQueueError $e ) {
 			MWExceptionHandler::logException( $e );
@@ -835,9 +827,8 @@
 			$query, $this->config->get( 'SecretKey' ) );
 
 		$errno = $errstr = null;
-		$info = wfParseUrl( $this->config->get( 'Server' ) );
-		MediaWiki\suppressWarnings();
-		$host = $info['host'];
+		$info = wfParseUrl( $this->config->get( 'CanonicalServer' ) );
+		$host = $info ? $info['host'] : null;
 		$port = 80;
 		if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
 			$host = "tls://" . $host;
@@ -846,47 +837,60 @@
 		if ( isset( $info['port'] ) ) {
 			$port = $info['port'];
 		}
-		$sock = fsockopen(
+
+		MediaWiki\suppressWarnings();
+		$sock = $host ? fsockopen(
 			$host,
 			$port,
 			$errno,
 			$errstr,
-			// If it takes more than 100ms to connect to ourselves there
-			// is a problem elsewhere.
-			0.1
-		);
+			// If it takes more than 100ms to connect to ourselves there is a problem...
+			0.100
+		) : false;
 		MediaWiki\restoreWarnings();
-		if ( !$sock ) {
+
+		$invokedWithSuccess = true;
+		if ( $sock ) {
+			$special = SpecialPageFactory::getPage( 'RunJobs' );
+			$url = $special->getPageTitle()->getCanonicalURL( $query );
+			$req = (
+				"POST $url HTTP/1.1\r\n" .
+				"Host: {$info['host']}\r\n" .
+				"Connection: Close\r\n" .
+				"Content-Length: 0\r\n\r\n"
+			);
+
+			$runJobsLogger->info( "Running $n job(s) via '$url'" );
+			// Send a cron API request to be performed in the background.
+			// Give up if this takes too long to send (which should be rare).
+			stream_set_timeout( $sock, 2 );
+			$bytes = fwrite( $sock, $req );
+			if ( $bytes !== strlen( $req ) ) {
+				$invokedWithSuccess = false;
+				$runJobsLogger->error( "Failed to start cron API (socket write error)" );
+			} else {
+				// Do not wait for the response (the script should handle client aborts).
+				// Make sure that we don't close before that script reaches ignore_user_abort().
+				$start = microtime( true );
+				$status = fgets( $sock );
+				$sec = microtime( true ) - $start;
+				if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
+					$invokedWithSuccess = false;
+					$runJobsLogger->error( "Failed to start cron API: received '$status' ($sec)" );
+				}
+			}
+			fclose( $sock );
+		} else {
+			$invokedWithSuccess = false;
 			$runJobsLogger->error( "Failed to start cron API (socket error $errno): $errstr" );
-			// Fall back to running the job here while the user waits
-			$runner = new JobRunner( $runJobsLogger );
-			$runner->run( [ 'maxJobs'  => $n ] );
-			return;
 		}
 
-		$url = wfAppendQuery( wfScript( 'index' ), $query );
-		$req = (
-			"POST $url HTTP/1.1\r\n" .
-			"Host: {$info['host']}\r\n" .
-			"Connection: Close\r\n" .
-			"Content-Length: 0\r\n\r\n"
-		);
+		// Fall back to running the job(s) while the user waits if needed
+		if ( !$invokedWithSuccess ) {
+			$runJobsLogger->warning( "Jobs switched to blocking; Special:RunJobs disabled" );
 
-		$runJobsLogger->info( "Running $n job(s) via '$url'" );
-		// Send a cron API request to be performed in the background.
-		// Give up if this takes too long to send (which should be rare).
-		stream_set_timeout( $sock, 1 );
-		$bytes = fwrite( $sock, $req );
-		if ( $bytes !== strlen( $req ) ) {
-			$runJobsLogger->error( "Failed to start cron API (socket write error)" );
-		} else {
-			// Do not wait for the response (the script should handle client aborts).
-			// Make sure that we don't close before that script reaches ignore_user_abort().
-			$status = fgets( $sock );
-			if ( !preg_match( '#^HTTP/\d\.\d 202 #', $status ) ) {
-				$runJobsLogger->error( "Failed to start cron API: received '$status'" );
-			}
+			$runner = new JobRunner( $runJobsLogger );
+			$runner->run( [ 'maxJobs'  => $n ] );
 		}
-		fclose( $sock );
 	}
 }
diff -Nru mediawiki-1.27.1/includes/OutputPage.php mediawiki-1.27.2/includes/OutputPage.php
--- mediawiki-1.27.1/includes/OutputPage.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/includes/OutputPage.php	2017-04-06 11:54:28.000000000 -0700
@@ -1541,6 +1541,7 @@
 			// been changed somehow, and keep it if so.
 			$anonPO = ParserOptions::newFromAnon();
 			$anonPO->setEditSection( false );
+			$anonPO->setAllowUnsafeRawHtml( false );
 			if ( !$options->matches( $anonPO ) ) {
 				wfLogWarning( __METHOD__ . ': Setting a changed bogus ParserOptions: ' . wfGetAllCallers( 5 ) );
 				$options->isBogus = false;
@@ -1554,6 +1555,7 @@
 				// either.
 				$po = ParserOptions::newFromAnon();
 				$po->setEditSection( false );
+				$po->setAllowUnsafeRawHtml( false );
 				$po->isBogus = true;
 				if ( $options !== null ) {
 					$this->mParserOptions = empty( $options->isBogus ) ? $options : null;
@@ -1563,6 +1565,7 @@
 
 			$this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
 			$this->mParserOptions->setEditSection( false );
+			$this->mParserOptions->setAllowUnsafeRawHtml( false );
 		}
 
 		if ( $options !== null && !empty( $options->isBogus ) ) {
@@ -2636,7 +2639,9 @@
 		} else {
 			$titleObj = Title::newFromText( $returnto );
 		}
-		if ( !is_object( $titleObj ) ) {
+		// We don't want people to return to external interwiki. That
+		// might potentially be used as part of a phishing scheme
+		if ( !is_object( $titleObj ) || $titleObj->isExternal() ) {
 			$titleObj = Title::newMainPage();
 		}
 
diff -Nru mediawiki-1.27.1/includes/Sanitizer.php mediawiki-1.27.2/includes/Sanitizer.php
--- mediawiki-1.27.1/includes/Sanitizer.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/Sanitizer.php	2017-04-06 11:54:25.000000000 -0700
@@ -983,6 +983,7 @@
 				| url\s*\(
 				| image\s*\(
 				| image-set\s*\(
+				| attr\s*\([^)]+[\s,]+url
 			!ix', $value ) ) {
 			return '/* insecure input */';
 		}
diff -Nru mediawiki-1.27.1/includes/Setup.php mediawiki-1.27.2/includes/Setup.php
--- mediawiki-1.27.1/includes/Setup.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/Setup.php	2017-04-06 11:54:25.000000000 -0700
@@ -241,7 +241,7 @@
 		'transformVia404' => true,
 		'fetchDescription' => true,
 		'descriptionCacheExpiry' => 43200,
-		'apiThumbCacheExpiry' => 86400,
+		'apiThumbCacheExpiry' => 0,
 	];
 }
 /*
diff -Nru mediawiki-1.27.1/includes/Title.php mediawiki-1.27.2/includes/Title.php
--- mediawiki-1.27.1/includes/Title.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/includes/Title.php	2017-04-06 11:54:29.000000000 -0700
@@ -1682,6 +1682,33 @@
 	}
 
 	/**
+	 * Get a url appropriate for making redirects based on an untrusted url arg
+	 *
+	 * This is basically the same as getFullUrl(), but in the case of external
+	 * interwikis, we send the user to a landing page, to prevent possible
+	 * phishing attacks and the like.
+	 *
+	 * @note Uses current protocol by default, since technically relative urls
+	 *   aren't allowed in redirects per HTTP spec, so this is not suitable for
+	 *   places where the url gets cached, as might pollute between
+	 *   https and non-https users.
+	 * @see self::getLocalURL for the arguments.
+	 * @param array|string $query
+	 * @param string $proto Protocol type to use in URL
+	 * @return String. A url suitable to use in an HTTP location header.
+	 */
+	public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
+		$target = $this;
+		if ( $this->isExternal() ) {
+			$target = SpecialPage::getTitleFor(
+				'GoToInterwiki',
+				$this->getPrefixedDBKey()
+			);
+		}
+		return $target->getFullUrl( $query, false, $proto );
+	}
+
+	/**
 	 * Get a URL with no fragment or server name (relative URL) from a Title object.
 	 * If this page is generated with action=render, however,
 	 * $wgServer is prepended to make an absolute URL.
@@ -2273,7 +2300,18 @@
 			) {
 				$errors[] = [ 'delete-toobig', $wgLang->formatNum( $wgDeleteRevisionsLimit ) ];
 			}
-		}
+		} elseif ( $action === 'undelete' ) {
+			if ( count( $this->getUserPermissionsErrorsInternal( 'edit', $user, $rigor, true ) ) ) {
+				// Undeleting implies editing
+				$errors[] = [ 'undelete-cantedit' ];
+			}
+			if ( !$this->exists()
+				&& count( $this->getUserPermissionsErrorsInternal( 'create', $user, $rigor, true ) )
+			) {
+				// Undeleting where nothing currently exists implies creating
+				$errors[] = [ 'undelete-cantcreate' ];
+			}
+ 		}
 		return $errors;
 	}
 
@@ -2301,7 +2339,6 @@
 			return $errors;
 		}
 
-
 		if ( $wgEmailConfirmToEdit && !$user->isEmailConfirmed() ) {
 			$errors[] = [ 'confirmedittext' ];
 		}
diff -Nru mediawiki-1.27.1/includes/api/ApiAuthManagerHelper.php mediawiki-1.27.2/includes/api/ApiAuthManagerHelper.php
--- mediawiki-1.27.1/includes/api/ApiAuthManagerHelper.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiAuthManagerHelper.php	2017-04-06 11:54:28.000000000 -0700
@@ -156,8 +156,13 @@
 
 		// Collect the fields for all the requests
 		$fields = [];
+		$sensitive = [];
 		foreach ( $reqs as $req ) {
-			$fields += (array)$req->getFieldInfo();
+			$info = (array)$req->getFieldInfo();
+			$fields += $info;
+			$sensitive += array_filter( $info, function ( $opts ) {
+				return !empty( $opts['sensitive'] );
+			} );
 		}
 
 		// Extract the request data for the fields and mark those request
@@ -165,6 +170,17 @@
 		$data = array_intersect_key( $this->module->getRequest()->getValues(), $fields );
 		$this->module->getMain()->markParamsUsed( array_keys( $data ) );
 
+		if ( $sensitive ) {
+			$this->module->getMain()->markParamsSensitive( array_keys( $sensitive ) );
+			try {
+				$this->module->requirePostedParameters( array_keys( $sensitive ), 'noprefix' );
+			} catch ( UsageException $ex ) {
+				// Make this a warning for now, upgrade to an error in 1.29.
+				$this->module->setWarning( $ex->getMessage() );
+				$this->module->logFeatureUsage( $this->module->getModuleName() . '-params-in-query-string' );
+			}
+		}
+
 		return AuthenticationRequest::loadRequestsFromSubmission( $reqs, $data );
 	}
 
diff -Nru mediawiki-1.27.1/includes/api/ApiBase.php mediawiki-1.27.2/includes/api/ApiBase.php
--- mediawiki-1.27.1/includes/api/ApiBase.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiBase.php	2017-04-06 11:54:28.000000000 -0700
@@ -171,6 +171,13 @@
 	 */
 	const PARAM_SUBMODULE_PARAM_PREFIX = 16;
 
+	/**
+	 * (boolean) Is the parameter sensitive? Note 'password'-type fields are
+	 * always sensitive regardless of the value of this field.
+	 * @since 1.28
+	 */
+	const PARAM_SENSITIVE = 17;
+
 	/**@}*/
 
 	/** Fast query, standard limit. */
@@ -777,6 +784,39 @@
 	}
 
 	/**
+	 * Die if any of the specified parameters were found in the query part of
+	 * the URL rather than the post body.
+	 * @since 1.28
+	 * @param string[] $params Parameters to check
+	 * @param string $prefix Set to 'noprefix' to skip calling $this->encodeParamName()
+	 */
+	public function requirePostedParameters( $params, $prefix = 'prefix' ) {
+		// Skip if $wgDebugAPI is set or we're in internal mode
+		if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) {
+			return;
+		}
+
+		$queryValues = $this->getRequest()->getQueryValues();
+		$badParams = [];
+		foreach ( $params as $param ) {
+			if ( $prefix !== 'noprefix' ) {
+				$param = $this->encodeParamName( $param );
+			}
+			if ( array_key_exists( $param, $queryValues ) ) {
+				$badParams[] = $param;
+			}
+		}
+
+		if ( $badParams ) {
+			$this->dieUsage(
+				'The following parameters were found in the query string, but must be in the POST body: '
+					. join( ', ', $badParams ),
+				'mustpostparams'
+			);
+		}
+	}
+
+	/**
 	 * Callback function used in requireOnlyOneParameter to check whether required parameters are set
 	 *
 	 * @param object $x Parameter to check is not null/false
@@ -915,6 +955,11 @@
 					$type = 'NULL'; // allow everything
 				}
 			}
+
+			if ( $type == 'password' || !empty( $paramSettings[self::PARAM_SENSITIVE] ) ) {
+				$this->getMain()->markParamsSensitive( $encParamName );
+			}
+
 		}
 
 		if ( $type == 'boolean' ) {
@@ -2191,7 +2236,7 @@
 	 * analysis.
 	 * @param string $feature Feature being used.
 	 */
-	protected function logFeatureUsage( $feature ) {
+	public function logFeatureUsage( $feature ) {
 		$request = $this->getRequest();
 		$s = '"' . addslashes( $feature ) . '"' .
 			' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' .
@@ -2267,6 +2312,7 @@
 			$params['token'] = [
 				ApiBase::PARAM_TYPE => 'string',
 				ApiBase::PARAM_REQUIRED => true,
+				ApiBase::PARAM_SENSITIVE => true,
 				ApiBase::PARAM_HELP_MSG => [
 					'api-help-param-token',
 					$this->needsToken(),
diff -Nru mediawiki-1.27.1/includes/api/ApiCheckToken.php mediawiki-1.27.2/includes/api/ApiCheckToken.php
--- mediawiki-1.27.1/includes/api/ApiCheckToken.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiCheckToken.php	2017-04-06 11:54:28.000000000 -0700
@@ -66,6 +66,7 @@
 			'token' => [
 				ApiBase::PARAM_TYPE => 'string',
 				ApiBase::PARAM_REQUIRED => true,
+				ApiBase::PARAM_SENSITIVE => true,
 			],
 			'maxtokenage' => [
 				ApiBase::PARAM_TYPE => 'integer',
diff -Nru mediawiki-1.27.1/includes/api/ApiLogin.php mediawiki-1.27.2/includes/api/ApiLogin.php
--- mediawiki-1.27.1/includes/api/ApiLogin.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiLogin.php	2017-04-06 11:54:28.000000000 -0700
@@ -70,6 +70,14 @@
 			return;
 		}
 
+		try {
+			$this->requirePostedParameters( [ 'password', 'token' ] );
+		} catch ( UsageException $ex ) {
+			// Make this a warning for now, upgrade to an error in 1.29.
+			$this->setWarning( $ex->getMessage() );
+			$this->logFeatureUsage( 'login-params-in-query-string' );
+		}
+
 		$params = $this->extractRequestParams();
 
 		$result = [];
@@ -256,6 +264,7 @@
 			'token' => [
 				ApiBase::PARAM_TYPE => 'string',
 				ApiBase::PARAM_REQUIRED => false, // for BC
+				ApiBase::PARAM_SENSITIVE => true,
 				ApiBase::PARAM_HELP_MSG => [ 'api-help-param-token', 'login' ],
 			],
 		];
diff -Nru mediawiki-1.27.1/includes/api/ApiMain.php mediawiki-1.27.2/includes/api/ApiMain.php
--- mediawiki-1.27.1/includes/api/ApiMain.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiMain.php	2017-04-06 11:54:28.000000000 -0700
@@ -145,6 +145,7 @@
 	private $mCacheMode = 'private';
 	private $mCacheControl = [];
 	private $mParamsUsed = [];
+	private $mParamsSensitive = [];
 
 	/** @var bool|null Cached return value from self::lacksSameOriginSecurity() */
 	private $lacksSameOriginSecurity = null;
@@ -1038,18 +1039,7 @@
 				$this->dieUsageMsg( [ 'missingparam', 'token' ] );
 			}
 
-			if ( !$this->getConfig()->get( 'DebugAPI' ) &&
-				array_key_exists(
-					$module->encodeParamName( 'token' ),
-					$this->getRequest()->getQueryValues()
-				)
-			) {
-				$this->dieUsage(
-					"The '{$module->encodeParamName( 'token' )}' parameter was " .
-						'found in the query string, but must be in the POST body',
-					'mustposttoken'
-				);
-			}
+			$module->requirePostedParameters( [ 'token' ] );
 
 			if ( !$module->validateToken( $moduleParams['token'], $moduleParams ) ) {
 				$this->dieUsageMsg( 'sessionfailure' );
@@ -1429,13 +1419,17 @@
 			" {$logCtx['ip']} " .
 			"T={$logCtx['timeSpentBackend']}ms";
 
+		$sensitive = array_flip( $this->getSensitiveParams() );
 		foreach ( $this->getParamsUsed() as $name ) {
 			$value = $request->getVal( $name );
 			if ( $value === null ) {
 				continue;
 			}
 
-			if ( strlen( $value ) > 256 ) {
+			if ( isset( $sensitive[$name] ) ) {
+				$value = '[redacted]';
+				$encValue = '[redacted]';
+			} elseif ( strlen( $value ) > 256 ) {
 				$value = substr( $value, 0, 256 );
 				$encValue = $this->encodeRequestLogValue( $value ) . '[...]';
 			} else {
@@ -1485,6 +1479,24 @@
 		$this->mParamsUsed += array_fill_keys( (array)$params, true );
 	}
 
+ 	/**
+	 * Get the request parameters that should be considered sensitive
+	 * @since 1.28
+	 * @return array
+	 */
+	protected function getSensitiveParams() {
+		return array_keys( $this->mParamsSensitive );
+	}
+
+	/**
+	 * Mark parameters as sensitive
+	 * @since 1.28
+	 * @param string|string[] $params
+	 */
+	public function markParamsSensitive( $params ) {
+		$this->mParamsSensitive += array_fill_keys( (array)$params, true );
+	}
+
 	/**
 	 * Get a request value, and register the fact that it was used, for logging.
 	 * @param string $name
diff -Nru mediawiki-1.27.1/includes/api/ApiQueryWatchlist.php mediawiki-1.27.2/includes/api/ApiQueryWatchlist.php
--- mediawiki-1.27.1/includes/api/ApiQueryWatchlist.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiQueryWatchlist.php	2017-04-06 11:54:28.000000000 -0700
@@ -495,7 +495,8 @@
 				ApiBase::PARAM_TYPE => 'user'
 			],
 			'token' => [
-				ApiBase::PARAM_TYPE => 'string'
+				ApiBase::PARAM_TYPE => 'string',
+				ApiBase::PARAM_SENSITIVE => true,
 			],
 			'continue' => [
 				ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
diff -Nru mediawiki-1.27.1/includes/api/ApiQueryWatchlistRaw.php mediawiki-1.27.2/includes/api/ApiQueryWatchlistRaw.php
--- mediawiki-1.27.1/includes/api/ApiQueryWatchlistRaw.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiQueryWatchlistRaw.php	2017-04-06 11:54:28.000000000 -0700
@@ -185,7 +185,8 @@
 				ApiBase::PARAM_TYPE => 'user'
 			],
 			'token' => [
-				ApiBase::PARAM_TYPE => 'string'
+				ApiBase::PARAM_TYPE => 'string',
+				ApiBase::PARAM_SENSITIVE => true,
 			],
 			'dir' => [
 				ApiBase::PARAM_DFLT => 'ascending',
diff -Nru mediawiki-1.27.1/includes/api/ApiUndelete.php mediawiki-1.27.2/includes/api/ApiUndelete.php
--- mediawiki-1.27.1/includes/api/ApiUndelete.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/api/ApiUndelete.php	2017-04-06 11:54:29.000000000 -0700
@@ -34,9 +34,6 @@
 
 		$params = $this->extractRequestParams();
 		$user = $this->getUser();
-		if ( !$user->isAllowed( 'undelete' ) ) {
-			$this->dieUsageMsg( 'permdenied-undelete' );
-		}
 
 		if ( $user->isBlocked() ) {
 			$this->dieBlocked( $user->getBlock() );
@@ -47,6 +44,10 @@
 			$this->dieUsageMsg( [ 'invalidtitle', $params['title'] ] );
 		}
 
+		if ( !$titleObj->userCan( 'undelete', $user, 'secure' ) ) {
+			$this->dieUsageMsg( 'permdenied-undelete' );
+		}
+
 		// Check if user can add tags
 		if ( !is_null( $params['tags'] ) ) {
 			$ableToTag = ChangeTags::canAddTagsAccompanyingChange( $params['tags'], $user );
diff -Nru mediawiki-1.27.1/includes/auth/AuthManager.php mediawiki-1.27.2/includes/auth/AuthManager.php
--- mediawiki-1.27.1/includes/auth/AuthManager.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/auth/AuthManager.php	2017-04-06 11:54:25.000000000 -0700
@@ -2000,37 +2000,26 @@
 
 		// Query them and merge results
 		$reqs = [];
-		$allPrimaryRequired = null;
 		foreach ( $providers as $provider ) {
 			$isPrimary = $provider instanceof PrimaryAuthenticationProvider;
-			$thisRequired = [];
 			foreach ( $provider->getAuthenticationRequests( $providerAction, $options ) as $req ) {
 				$id = $req->getUniqueId();
 
-				// If it's from a Primary, mark it as "primary-required" but
-				// track it for later.
+				// If a required request if from a Primary, mark it as "primary-required" instead
 				if ( $isPrimary ) {
 					if ( $req->required ) {
-						$thisRequired[$id] = true;
 						$req->required = AuthenticationRequest::PRIMARY_REQUIRED;
 					}
 				}
 
-				if ( !isset( $reqs[$id] ) || $req->required === AuthenticationRequest::REQUIRED ) {
+				if (
+					!isset( $reqs[$id] )
+					|| $req->required === AuthenticationRequest::REQUIRED
+					|| $reqs[$id] === AuthenticationRequest::OPTIONAL
+				) {
 					$reqs[$id] = $req;
 				}
 			}
-
-			// Track which requests are required by all primaries
-			if ( $isPrimary ) {
-				$allPrimaryRequired = $allPrimaryRequired === null
-					? $thisRequired
-					: array_intersect_key( $allPrimaryRequired, $thisRequired );
-			}
-		}
-		// Any requests that were required by all primaries are required.
-		foreach ( (array)$allPrimaryRequired as $id => $dummy ) {
-			$reqs[$id]->required = AuthenticationRequest::REQUIRED;
 		}
 
 		// AuthManager has its own req for some actions
diff -Nru mediawiki-1.27.1/includes/auth/AuthenticationRequest.php mediawiki-1.27.2/includes/auth/AuthenticationRequest.php
--- mediawiki-1.27.1/includes/auth/AuthenticationRequest.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/auth/AuthenticationRequest.php	2017-04-06 11:54:25.000000000 -0700
@@ -43,7 +43,8 @@
 	const REQUIRED = 1;
 
 	/** Indicates that the request is required by a primary authentication
-	 * provdier, but other primary authentication providers do not require it. */
+	 * provdier. Since the user can choose which primary to authenticate with,
+	 * the request might or might not end up being actually required. */
 	const PRIMARY_REQUIRED = 2;
 
 	/** @var string|null The AuthManager::ACTION_* constant this request was
@@ -101,6 +102,10 @@
 	 *  - label: (Message) Text suitable for a label in an HTML form
 	 *  - help: (Message) Text suitable as a description of what the field is
 	 *  - optional: (bool) If set and truthy, the field may be left empty
+	 *  - skippable: (bool) If set and truthy, the client is free to hide this
+	 *      field from the user to streamline the workflow. If all fields are
+	 *      skippable (except possibly a single button), no user interaction is
+	 *      required at all.
 	 *
 	 * @return array As above
 	 */
diff -Nru mediawiki-1.27.1/includes/auth/PrimaryAuthenticationProvider.php mediawiki-1.27.2/includes/auth/PrimaryAuthenticationProvider.php
--- mediawiki-1.27.1/includes/auth/PrimaryAuthenticationProvider.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/auth/PrimaryAuthenticationProvider.php	2017-04-06 11:54:25.000000000 -0700
@@ -58,6 +58,14 @@
 	const TYPE_NONE = 'none';
 
 	/**
+	 * {@inheritdoc}
+	 *
+	 * Of the requests returned by this method, exactly one should have
+	 * {@link AuthenticationRequest::$required} set to REQUIRED.
+	 */
+	public function getAuthenticationRequests( $action, array $options );
+
+	/**
 	 * Start an authentication flow
 	 *
 	 * @param AuthenticationRequest[] $reqs
diff -Nru mediawiki-1.27.1/includes/auth/RememberMeAuthenticationRequest.php mediawiki-1.27.2/includes/auth/RememberMeAuthenticationRequest.php
--- mediawiki-1.27.1/includes/auth/RememberMeAuthenticationRequest.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/auth/RememberMeAuthenticationRequest.php	2017-04-06 11:54:19.000000000 -0700
@@ -58,6 +58,7 @@
 				'label' => wfMessage( 'userlogin-remembermypassword' )->numParams( $expirationDays ),
 				'help' => wfMessage( 'authmanager-userlogin-remembermypassword-help' ),
 				'optional' => true,
+				'skippable' => true,
 			]
 		];
 	}
diff -Nru mediawiki-1.27.1/includes/cache/MessageCache.php mediawiki-1.27.2/includes/cache/MessageCache.php
--- mediawiki-1.27.1/includes/cache/MessageCache.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/cache/MessageCache.php	2017-04-06 11:54:25.000000000 -0700
@@ -177,11 +177,16 @@
 				// either.
 				$po = ParserOptions::newFromAnon();
 				$po->setEditSection( false );
+				$po->setAllowUnsafeRawHtml( false );
 				return $po;
 			}
 
 			$this->mParserOptions = new ParserOptions;
 			$this->mParserOptions->setEditSection( false );
+			// Messages may take parameters that could come
+			// from malicious sources. As a precaution, disable
+			// the <html> parser tag when parsing messages.
+			$this->mParserOptions->setAllowUnsafeRawHtml( false );
 		}
 
 		return $this->mParserOptions;
diff -Nru mediawiki-1.27.1/includes/cache/localisation/LocalisationCache.php mediawiki-1.27.2/includes/cache/localisation/LocalisationCache.php
--- mediawiki-1.27.1/includes/cache/localisation/LocalisationCache.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/cache/localisation/LocalisationCache.php	2017-04-06 11:54:29.000000000 -0700
@@ -210,19 +210,17 @@
 				case 'detect':
 					if ( !empty( $conf['storeDirectory'] ) ) {
 						$storeClass = 'LCStoreCDB';
+					} elseif ( $wgCacheDirectory ) {
+						$storeConf['directory'] = $wgCacheDirectory;
+						$storeClass = 'LCStoreCDB';
 					} else {
-						$cacheDir = $wgCacheDirectory ?: wfTempDir();
-						if ( $cacheDir ) {
-							$storeConf['directory'] = $cacheDir;
-							$storeClass = 'LCStoreCDB';
-						} else {
-							$storeClass = 'LCStoreDB';
-						}
+						$storeClass = 'LCStoreDB';
 					}
 					break;
 				default:
 					throw new MWException(
-						'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.' );
+						'Please set $wgLocalisationCacheConf[\'store\'] to something sensible.'
+					);
 			}
 		}
 
diff -Nru mediawiki-1.27.1/includes/db/Database.php mediawiki-1.27.2/includes/db/Database.php
--- mediawiki-1.27.1/includes/db/Database.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/db/Database.php	2017-04-06 11:54:25.000000000 -0700
@@ -1324,7 +1324,10 @@
 	) {
 		$rows = 0;
 		$sql = $this->selectSQLText( $tables, '1', $conds, $fname, $options, $join_conds );
-		$res = $this->query( "SELECT COUNT(*) AS rowcount FROM ($sql) tmp_count", $fname );
+		// The identifier quotes is primarily for MSSQL.
+		$rowCountCol = $this->addIdentifierQuotes( "rowcount" );
+		$tableName = $this->addIdentifierQuotes( "tmp_count" );
+		$res = $this->query( "SELECT COUNT(*) AS $rowCountCol FROM ($sql) $tableName", $fname );
 
 		if ( $res ) {
 			$row = $this->fetchRow( $res );
diff -Nru mediawiki-1.27.1/includes/installer/Installer.php mediawiki-1.27.2/includes/installer/Installer.php
--- mediawiki-1.27.1/includes/installer/Installer.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/installer/Installer.php	2017-04-06 11:54:25.000000000 -0700
@@ -242,6 +242,7 @@
 	protected $objectCaches = [
 		'xcache' => 'xcache_get',
 		'apc' => 'apc_fetch',
+		'apcu' => 'apcu_fetch',
 		'wincache' => 'wincache_ucache_get'
 	];
 
diff -Nru mediawiki-1.27.1/includes/installer/MssqlUpdater.php mediawiki-1.27.2/includes/installer/MssqlUpdater.php
--- mediawiki-1.27.1/includes/installer/MssqlUpdater.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/installer/MssqlUpdater.php	2017-04-06 11:54:25.000000000 -0700
@@ -87,7 +87,10 @@
 			[ 'updateSchema', 'recentchanges', 'recentchanges-drop-fks',
 				'patch-recentchanges-drop-fks.sql' ],
 			[ 'updateSchema', 'logging', 'logging-drop-fks', 'patch-logging-drop-fks.sql' ],
-			[ 'updateSchema', 'archive', 'archive-drop-fks', 'patch-archive-drop-fks.sql' ]
+			[ 'updateSchema', 'archive', 'archive-drop-fks', 'patch-archive-drop-fks.sql' ],
+			// 1.29.0, 1.28.1, 1.27.2
+			[ 'dropIndex', 'oldimage', 'oi_name_archive_name',
+				'patch-alter-table-oldimage.sql' ],
 		];
 	}
 
diff -Nru mediawiki-1.27.1/includes/installer/i18n/en.json mediawiki-1.27.2/includes/installer/i18n/en.json
--- mediawiki-1.27.1/includes/installer/i18n/en.json	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/includes/installer/i18n/en.json	2017-04-06 11:54:25.000000000 -0700
@@ -57,6 +57,7 @@
 	"config-memory-bad": "<strong>Warning:</strong> PHP's <code>memory_limit</code> is $1.\nThis is probably too low.\nThe installation may fail!",
 	"config-xcache": "[http://xcache.lighttpd.net/ XCache] is installed",
 	"config-apc": "[http://www.php.net/apc APC] is installed",
+	"config-apcu": "[http://www.php.net/apcu APCu] is installed",
 	"config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] is installed",
 	"config-no-cache-apcu": "<strong>Warning:</strong> Could not find [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] or [http://www.iis.net/download/WinCacheForPhp WinCache].\nObject caching is not enabled.",
 	"config-mod-security": "<strong>Warning:</strong> Your web server has [http://modsecurity.org/ mod_security]/mod_security2 enabled. Many common configurations of this will cause problems for MediaWiki and other software that allows users to post arbitrary content.\nIf possible, this should be disabled. Otherwise, refer to [http://modsecurity.org/documentation/ mod_security documentation] or contact your host's support if you encounter random errors.",
@@ -249,7 +250,7 @@
 	"config-cache-options": "Settings for object caching:",
 	"config-cache-help": "Object caching is used to improve the speed of MediaWiki by caching frequently used data.\nMedium to large sites are highly encouraged to enable this, and small sites will see benefits as well.",
 	"config-cache-none": "No caching (no functionality is removed, but speed may be impacted on larger wiki sites)",
-	"config-cache-accel": "PHP object caching (APC, XCache or WinCache)",
+	"config-cache-accel": "PHP object caching (APC, APCu, XCache or WinCache)",
 	"config-cache-memcached": "Use Memcached (requires additional setup and configuration)",
 	"config-memcached-servers": "Memcached servers:",
 	"config-memcached-help": "List of IP addresses to use for Memcached.\nShould specify one per line and specify the port to be used. For example:\n 127.0.0.1:11211\n 192.168.1.25:1234",
diff -Nru mediawiki-1.27.1/includes/installer/i18n/qqq.json mediawiki-1.27.2/includes/installer/i18n/qqq.json
--- mediawiki-1.27.1/includes/installer/i18n/qqq.json	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/installer/i18n/qqq.json	2017-04-06 11:54:25.000000000 -0700
@@ -75,6 +75,7 @@
 	"config-memory-bad": "Parameters:\n* $1 is the configured <code>memory_limit</code>.",
 	"config-xcache": "Message indicates if this program is available",
 	"config-apc": "Message indicates if this program is available",
+	"config-apcu": "Message indicates if this program is available",
 	"config-wincache": "Message indicates if this program is available",
 	"config-no-cache-apcu": "Status message in the MediaWiki installer environment checks.",
 	"config-mod-security": "Status message in the MediaWiki installer environment checks.",
diff -Nru mediawiki-1.27.1/includes/jobqueue/JobQueueGroup.php mediawiki-1.27.2/includes/jobqueue/JobQueueGroup.php
--- mediawiki-1.27.1/includes/jobqueue/JobQueueGroup.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/jobqueue/JobQueueGroup.php	2017-04-06 11:54:25.000000000 -0700
@@ -142,6 +142,20 @@
 				$this->cache->clear( 'queues-ready' );
 			}
 		}
+
+		$cache = ObjectCache::getLocalClusterInstance();
+		$cache->set(
+			$cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', self::TYPE_ANY ),
+			'true',
+			15
+		);
+		if ( array_intersect( array_keys( $jobsByType ), $this->getDefaultQueueTypes() ) ) {
+			$cache->set(
+				$cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', self::TYPE_DEFAULT ),
+				'true',
+				15
+			);
+		}
 	}
 
 	/**
@@ -298,8 +312,8 @@
 	 * @since 1.23
 	 */
 	public function queuesHaveJobs( $type = self::TYPE_ANY ) {
-		$key = wfMemcKey( 'jobqueue', 'queueshavejobs', $type );
 		$cache = ObjectCache::getLocalClusterInstance();
+		$key = $cache->makeGlobalKey( 'jobqueue', $this->wiki, 'hasjobs', $type );
 
 		$value = $cache->get( $key );
 		if ( $value === false ) {
diff -Nru mediawiki-1.27.1/includes/libs/MemoizedCallable.php mediawiki-1.27.2/includes/libs/MemoizedCallable.php
--- mediawiki-1.27.1/includes/libs/MemoizedCallable.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/MemoizedCallable.php	2017-04-06 11:54:20.000000000 -0700
@@ -1,6 +1,6 @@
 <?php
 /**
- * APC-backed function memoization
+ * APC-backed and APCu-backed function memoization
  *
  * This class provides memoization for pure functions. A function is pure
  * if its result value depends on nothing other than its input parameters
@@ -8,7 +8,7 @@
  *
  * The first invocation of the memoized callable with a particular set of
  * arguments will be delegated to the underlying callable. Repeat invocations
- * with the same input parameters will be served from APC.
+ * with the same input parameters will be served from APC or APCu.
  *
  * @par Example:
  * @code
@@ -70,7 +70,7 @@
 	}
 
 	/**
-	 * Fetch the result of a previous invocation from APC.
+	 * Fetch the result of a previous invocation from APC or APCu.
 	 *
 	 * @param string $key
 	 * @param bool &$success
@@ -79,12 +79,14 @@
 		$success = false;
 		if ( function_exists( 'apc_fetch' ) ) {
 			return apc_fetch( $key, $success );
+		} elseif ( function_exists( 'apcu_fetch' ) ) {
+			return apcu_fetch( $key, $success );
 		}
 		return false;
 	}
 
 	/**
-	 * Store the result of an invocation in APC.
+	 * Store the result of an invocation in APC or APCu.
 	 *
 	 * @param string $key
 	 * @param mixed $result
@@ -92,6 +94,8 @@
 	protected function storeResult( $key, $result ) {
 		if ( function_exists( 'apc_store' ) ) {
 			apc_store( $key, $result, $this->ttl );
+		} elseif ( function_exists( 'apcu_store' ) ) {
+			apcu_store( $key, $result, $this->ttl );
 		}
 	}
 
diff -Nru mediawiki-1.27.1/includes/libs/XmlTypeCheck.php mediawiki-1.27.2/includes/libs/XmlTypeCheck.php
--- mediawiki-1.27.1/includes/libs/XmlTypeCheck.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/XmlTypeCheck.php	2017-04-06 11:54:29.000000000 -0700
@@ -73,19 +73,36 @@
 	 */
 	private $parserOptions = [
 		'processing_instruction_handler' => '',
+		'external_dtd_handler' => '',
+		'dtd_handler' => '',
+		'require_safe_dtd' => true
 	];
 
 	/**
+	 * Allow filtering an XML file.
+	 *
+	 * Filters should return either true or a string to indicate something
+	 * is wrong with the file. $this->filterMatch will store if the
+	 * file failed validation (true = failed validation).
+	 * $this->filterMatchType will contain the validation error.
+	 * $this->wellFormed will contain whether the xml file is well-formed.
+	 *
+	 * @note If multiple filters are hit, only one of them will have the
+	 *  result stored in $this->filterMatchType.
+	 *
 	 * @param string $input a filename or string containing the XML element
 	 * @param callable $filterCallback (optional)
 	 *        Function to call to do additional custom validity checks from the
 	 *        SAX element handler event. This gives you access to the element
 	 *        namespace, name, attributes, and text contents.
-	 *        Filter should return 'true' to toggle on $this->filterMatch
+	 *        Filter should return a truthy value describing the error.
 	 * @param bool $isFile (optional) indicates if the first parameter is a
 	 *        filename (default, true) or if it is a string (false)
 	 * @param array $options list of additional parsing options:
 	 *        processing_instruction_handler: Callback for xml_set_processing_instruction_handler
+	 *        external_dtd_handler: Callback for the url of external dtd subset
+	 *        dtd_handler: Callback given the full text of the <!DOCTYPE declaration.
+	 *        require_safe_dtd: Only allow non-recursive entities in internal dtd (default true)
 	 */
 	function __construct( $input, $filterCallback = null, $isFile = true, $options = [] ) {
 		$this->filterCallback = $filterCallback;
@@ -187,6 +204,9 @@
 			if ( $reader->nodeType === XMLReader::PI ) {
 				$this->processingInstructionHandler( $reader->name, $reader->value );
 			}
+			if ( $reader->nodeType === XMLReader::DOC_TYPE ) {
+				$this->DTDHandler( $reader );
+			}
 		} while ( $reader->nodeType != XMLReader::ELEMENT );
 
 		// Process the rest of the document
@@ -235,8 +255,13 @@
 						$reader->value
 					);
 					break;
+				case XMLReader::DOC_TYPE:
+					// We should never see a doctype after first
+					// element.
+					$this->wellFormed = false;
+					break;
 				default:
-					// One of DOC, DOC_TYPE, ENTITY, END_ENTITY,
+					// One of DOC, ENTITY, END_ENTITY,
 					// NOTATION, or XML_DECLARATION
 					// xml_parse didn't send these to the filter, so we won't.
 			}
@@ -344,4 +369,140 @@
 			$this->filterMatchType = $callbackReturn;
 		}
 	}
+	/**
+	 * Handle coming across a <!DOCTYPE declaration.
+	 *
+	 * @param XMLReader $reader Reader currently pointing at DOCTYPE node.
+	 */
+	private function DTDHandler( XMLReader $reader ) {
+		$externalCallback = $this->parserOptions['external_dtd_handler'];
+		$generalCallback = $this->parserOptions['dtd_handler'];
+		$checkIfSafe = $this->parserOptions['require_safe_dtd'];
+		if ( !$externalCallback && !$generalCallback && !$checkIfSafe ) {
+			return;
+		}
+		$dtd = $reader->readOuterXML();
+		$callbackReturn = false;
+
+		if ( $generalCallback ) {
+			$callbackReturn = call_user_func( $generalCallback, $dtd );
+		}
+		if ( $callbackReturn ) {
+			// Filter hit!
+			$this->filterMatch = true;
+			$this->filterMatchType = $callbackReturn;
+			$callbackReturn = false;
+		}
+
+		$parsedDTD = $this->parseDTD( $dtd );
+		if ( $externalCallback && isset( $parsedDTD['type'] ) ) {
+			$callbackReturn = call_user_func(
+				$externalCallback,
+				$parsedDTD['type'],
+				isset( $parsedDTD['publicid'] ) ? $parsedDTD['publicid'] : null,
+				isset( $parsedDTD['systemid'] ) ? $parsedDTD['systemid'] : null
+			);
+		}
+		if ( $callbackReturn ) {
+			// Filter hit!
+			$this->filterMatch = true;
+			$this->filterMatchType = $callbackReturn;
+			$callbackReturn = false;
+		}
+
+		if ( $checkIfSafe && isset( $parsedDTD['internal'] ) ) {
+			if ( !$this->checkDTDIsSafe( $parsedDTD['internal'] ) ) {
+				$this->wellFormed = false;
+			}
+		}
+	}
+
+	/**
+	 * Check if the internal subset of the DTD is safe.
+	 *
+	 * We whitelist an extremely restricted subset of DTD features.
+	 *
+	 * Safe is defined as:
+	 *  * Only contains entity defintions (e.g. No <!ATLIST )
+	 *  * Entity definitions are not "system" entities
+	 *  * Entity definitions are not "parameter" (i.e. %) entities
+	 *  * Entity definitions do not reference other entites except &amp;
+	 *    and quotes. Entity aliases (where the entity contains only
+	 *    another entity are allowed)
+	 *  * Entity references aren't overly long (>255 bytes).
+	 *  * <!ATTLIST svg xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink";>
+	 *    allowed if matched exactly for compatibility with graphviz
+	 *  * Comments.
+	 *
+	 * @param string $internalSubset The internal subset of the DTD
+	 * @return bool true if safe.
+	 */
+	private function checkDTDIsSafe( $internalSubset ) {
+		$offset = 0;
+		$res = preg_match(
+			'/^(?:\s*<!ENTITY\s+\S+\s+' .
+				'(?:"(?:&[^"%&;]{1,64};|(?:[^"%&]|&amp;|&quot;){0,255})"' .
+				'|\'(?:&[^"%&;]{1,64};|(?:[^\'%&]|&amp;|&apos;){0,255})\')\s*>' .
+				'|\s*<!--(?:[^-]|-[^-])*-->' .
+				'|\s*<!ATTLIST svg xmlns:xlink CDATA #FIXED ' .
+				'"http:\/\/www.w3.org\/1999\/xlink">)*\s*$/',
+			$internalSubset
+		);
+
+		return (bool)$res;
+	}
+
+	/**
+	 * Parse DTD into parts.
+	 *
+	 * If there is an error parsing the dtd, sets wellFormed to false.
+	 *
+	 * @param $dtd string
+	 * @return array Possibly containing keys publicid, systemid, type and internal.
+	 */
+	private function parseDTD( $dtd ) {
+		$m = [];
+		$res = preg_match(
+			'/^<!DOCTYPE\s*\S+\s*' .
+			'(?:(?P<typepublic>PUBLIC)\s*' .
+				'(?:"(?P<pubquote>[^"]*)"|\'(?P<pubapos>[^\']*)\')' . // public identifer
+				'\s*"(?P<pubsysquote>[^"]*)"|\'(?P<pubsysapos>[^\']*)\'' . // system identifier
+			'|(?P<typesystem>SYSTEM)\s*' .
+				'(?:"(?P<sysquote>[^"]*)"|\'(?P<sysapos>[^\']*)\')' .
+			')?\s*' .
+			'(?:\[\s*(?P<internal>.*)\])?\s*>$/s',
+			$dtd,
+			$m
+		);
+		if ( !$res ) {
+			$this->wellFormed = false;
+			return [];
+		}
+		$parsed = [];
+		foreach ( $m as $field => $value ) {
+			if ( $value === '' || is_numeric( $field ) ) {
+				continue;
+			}
+			switch ( $field ) {
+			case 'typepublic':
+			case 'typesystem':
+				$parsed['type'] = $value;
+				break;
+			case 'pubquote':
+			case 'pubapos':
+				$parsed['publicid'] = $value;
+				break;
+			case 'pubsysquote':
+			case 'pubsysapos':
+			case 'sysquote':
+			case 'sysapos':
+				$parsed['systemid'] = $value;
+				break;
+			case 'internal':
+				$parsed['internal'] = $value;
+				break;
+			}
+		}
+		return $parsed;
+	}
 }
diff -Nru mediawiki-1.27.1/includes/libs/composer/ComposerJson.php mediawiki-1.27.2/includes/libs/composer/ComposerJson.php
--- mediawiki-1.27.1/includes/libs/composer/ComposerJson.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/composer/ComposerJson.php	2017-04-06 11:54:20.000000000 -0700
@@ -12,14 +12,9 @@
 	 * @param string $location
 	 */
 	public function __construct( $location ) {
-		$this->hash = md5_file( $location );
 		$this->contents = json_decode( file_get_contents( $location ), true );
 	}
 
-	public function getHash() {
-		return $this->hash;
-	}
-
 	/**
 	 * Dependencies as specified by composer.json
 	 *
diff -Nru mediawiki-1.27.1/includes/libs/composer/ComposerLock.php mediawiki-1.27.2/includes/libs/composer/ComposerLock.php
--- mediawiki-1.27.1/includes/libs/composer/ComposerLock.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/composer/ComposerLock.php	2017-04-06 11:54:20.000000000 -0700
@@ -15,10 +15,6 @@
 		$this->contents = json_decode( file_get_contents( $location ), true );
 	}
 
-	public function getHash() {
-		return $this->contents['hash'];
-	}
-
 	/**
 	 * Dependencies currently installed according to composer.lock
 	 *
diff -Nru mediawiki-1.27.1/includes/libs/objectcache/APCBagOStuff.php mediawiki-1.27.2/includes/libs/objectcache/APCBagOStuff.php
--- mediawiki-1.27.1/includes/libs/objectcache/APCBagOStuff.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/objectcache/APCBagOStuff.php	2017-04-06 11:54:20.000000000 -0700
@@ -75,25 +75,35 @@
 	}
 
 	protected function doGet( $key, $flags = 0 ) {
-		$val = apc_fetch( $key . self::KEY_SUFFIX );
+		return $this->getUnserialize(
+			apc_fetch( $key . self::KEY_SUFFIX )
+		);
+	}
 
-		if ( is_string( $val ) && !$this->nativeSerialize ) {
-			$val = $this->isInteger( $val )
-				? intval( $val )
-				: unserialize( $val );
+	protected function getUnserialize( $value ) {
+		if ( is_string( $value ) && !$this->nativeSerialize ) {
+			$value = $this->isInteger( $value )
+				? intval( $value )
+				: unserialize( $value );
 		}
-
-		return $val;
+		return $value;
 	}
 
 	public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+		apc_store(
+			$key . self::KEY_SUFFIX,
+			$this->setSerialize( $value ),
+			$exptime
+		);
+
+		return true;
+	}
+
+	protected function setSerialize( $value ) {
 		if ( !$this->nativeSerialize && !$this->isInteger( $value ) ) {
 			$value = serialize( $value );
 		}
-
-		apc_store( $key . self::KEY_SUFFIX, $value, $exptime );
-
-		return true;
+		return $value;
 	}
 
 	public function delete( $key ) {
diff -Nru mediawiki-1.27.1/includes/libs/objectcache/APCUBagOStuff.php mediawiki-1.27.2/includes/libs/objectcache/APCUBagOStuff.php
--- mediawiki-1.27.1/includes/libs/objectcache/APCUBagOStuff.php	1969-12-31 16:00:00.000000000 -0800
+++ mediawiki-1.27.2/includes/libs/objectcache/APCUBagOStuff.php	2017-04-06 11:54:25.000000000 -0700
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Object caching using PHP's APCU accelerator.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Cache
+ */
+
+/**
+ * This is a wrapper for APCU's shared memory functions
+ *
+ * @ingroup Cache
+ */
+class APCUBagOStuff extends APCBagOStuff {
+	/**
+	 * Constructor
+	 *
+	 * Available parameters are:
+	 *   - nativeSerialize:     If true, pass objects to apcu_store(), and trust it
+	 *                          to serialize them correctly. If false, serialize
+	 *                          all values in PHP.
+	 *
+	 * @param array $params
+	 */
+	public function __construct( array $params = [] ) {
+		parent::__construct( $params );
+	}
+
+	protected function doGet( $key, $flags = 0 ) {
+		return $this->getUnserialize(
+			apcu_fetch( $key . self::KEY_SUFFIX )
+		);
+	}
+
+	public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+		apcu_store(
+			$key . self::KEY_SUFFIX,
+			$this->setSerialize( $value ),
+			$exptime
+		);
+
+		return true;
+	}
+
+	public function delete( $key ) {
+		apcu_delete( $key . self::KEY_SUFFIX );
+
+		return true;
+	}
+
+	public function incr( $key, $value = 1 ) {
+		/**
+		 * @todo When we only support php 7 or higher remove this hack
+		 *
+		 * https://github.com/krakjoe/apcu/issues/166
+		 */
+		if ( apcu_exists( $key . self::KEY_SUFFIX ) ) {
+			return apcu_inc( $key . self::KEY_SUFFIX, $value );
+		} else {
+			return apcu_set( $key . self::KEY_SUFFIX, $value );
+		}
+	}
+
+	public function decr( $key, $value = 1 ) {
+		/**
+		 * @todo When we only support php 7 or higher remove this hack
+		 *
+		 * https://github.com/krakjoe/apcu/issues/166
+		 */
+		if ( apcu_exists( $key . self::KEY_SUFFIX ) ) {
+			return apcu_dec( $key . self::KEY_SUFFIX, $value );
+		} else {
+			return apcu_set( $key . self::KEY_SUFFIX, -$value );
+		}
+	}
+}
diff -Nru mediawiki-1.27.1/includes/libs/objectcache/WinCacheBagOStuff.php mediawiki-1.27.2/includes/libs/objectcache/WinCacheBagOStuff.php
--- mediawiki-1.27.1/includes/libs/objectcache/WinCacheBagOStuff.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/libs/objectcache/WinCacheBagOStuff.php	2017-04-06 11:54:25.000000000 -0700
@@ -29,16 +29,7 @@
  */
 class WinCacheBagOStuff extends BagOStuff {
 	protected function doGet( $key, $flags = 0 ) {
-		$casToken = null;
-
-		return $this->getWithToken( $key, $casToken, $flags );
-	}
-
-	protected function getWithToken( $key, &$casToken, $flags = 0 ) {
 		$val = wincache_ucache_get( $key );
-
-		$casToken = $val;
-
 		if ( is_string( $val ) ) {
 			$val = unserialize( $val );
 		}
@@ -54,10 +45,6 @@
 		return ( is_array( $result ) && $result === [] ) || $result;
 	}
 
-	protected function cas( $casToken, $key, $value, $exptime = 0 ) {
-		return wincache_ucache_cas( $key, $casToken, serialize( $value ) );
-	}
-
 	public function delete( $key ) {
 		wincache_ucache_delete( $key );
 
diff -Nru mediawiki-1.27.1/includes/mail/UserMailer.php mediawiki-1.27.2/includes/mail/UserMailer.php
--- mediawiki-1.27.1/includes/mail/UserMailer.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/mail/UserMailer.php	2017-04-06 11:54:25.000000000 -0700
@@ -268,7 +268,14 @@
 		// Add the envelope sender address using the -f command line option when PHP mail() is used.
 		// Will default to the $from->address when the UserMailerChangeReturnPath hook fails and the
 		// generated VERP address when the hook runs effectively.
-		$extraParams .= ' -f ' . $returnPath;
+
+		// PHP runs this through escapeshellcmd(). However that's not sufficient
+		// escaping (e.g. due to spaces). MediaWiki's email sanitizer should generally
+		// be good enough, but just in case, put in double quotes, and remove any
+		// double quotes present (" is not allowed in emails, so should have no
+		// effect, although this might cause apostrophees to be double escaped)
+		$returnPathCLI = '"' . str_replace( '"', '', $returnPath ) . '"';
+		$extraParams .= ' -f ' . $returnPathCLI;
 
 		$headers['Return-Path'] = $returnPath;
 
diff -Nru mediawiki-1.27.1/includes/objectcache/ObjectCache.php mediawiki-1.27.2/includes/objectcache/ObjectCache.php
--- mediawiki-1.27.1/includes/objectcache/ObjectCache.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/objectcache/ObjectCache.php	2017-04-06 11:54:25.000000000 -0700
@@ -50,7 +50,7 @@
  *
  * - ObjectCache::getLocalServerInstance( $fallbackType )
  *   Purpose: Memory cache for very hot keys.
- *   Stored only on the individual web server (typically APC for web requests,
+ *   Stored only on the individual web server (typically APC or APCu for web requests,
  *   and EmptyBagOStuff in CLI mode).
  *   Not replicated to the other servers.
  *
@@ -222,8 +222,14 @@
 		global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType;
 		$candidates = [ $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType ];
 		foreach ( $candidates as $candidate ) {
+			$cache = false;
 			if ( $candidate !== CACHE_NONE && $candidate !== CACHE_ANYTHING ) {
-				return self::getInstance( $candidate );
+				$cache = self::getInstance( $candidate );
+				// CACHE_ACCEL might default to nothing if no APCu
+				// See includes/ServiceWiring.php
+				if ( !( $cache instanceof EmptyBagOStuff ) ) {
+					return $cache;
+				}
 			}
 		}
 		return self::getInstance( CACHE_DB );
@@ -232,7 +238,7 @@
 	/**
 	 * Factory function for CACHE_ACCEL (referenced from DefaultSettings.php)
 	 *
-	 * This will look for any APC style server-local cache.
+	 * This will look for any APC or APCu style server-local cache.
 	 * A fallback cache can be specified if none is found.
 	 *
 	 *     // Direct calls
@@ -249,6 +255,8 @@
 	public static function getLocalServerInstance( $fallback = CACHE_NONE ) {
 		if ( function_exists( 'apc_fetch' ) ) {
 			$id = 'apc';
+		} elseif ( function_exists( 'apcu_fetch' ) ) {
+			$id = 'apcu';
 		} elseif ( function_exists( 'xcache_get' ) && wfIniGetBool( 'xcache.var_size' ) ) {
 			$id = 'xcache';
 		} elseif ( function_exists( 'wincache_ucache_get' ) ) {
diff -Nru mediawiki-1.27.1/includes/parser/CoreTagHooks.php mediawiki-1.27.2/includes/parser/CoreTagHooks.php
--- mediawiki-1.27.1/includes/parser/CoreTagHooks.php	2016-08-22 13:52:56.000000000 -0700
+++ mediawiki-1.27.2/includes/parser/CoreTagHooks.php	2017-04-06 11:54:20.000000000 -0700
@@ -79,12 +79,25 @@
 	 * @param array $attributes
 	 * @param Parser $parser
 	 * @throws MWException
-	 * @return array
+	 * @return array|string Output of tag hook
 	 */
 	public static function html( $content, $attributes, $parser ) {
 		global $wgRawHtml;
 		if ( $wgRawHtml ) {
-			return [ $content, 'markerType' => 'nowiki' ];
+			if ( $parser->getOptions()->getAllowUnsafeRawHtml() ) {
+				return [ $content, 'markerType' => 'nowiki' ];
+			} else {
+				// In a system message where raw html is
+				// not allowed (but it is allowed in other
+				// contexts).
+				return Html::rawElement(
+					'span',
+					[ 'class' => 'error' ],
+					// Using ->text() not ->parse() as
+					// a paranoia measure against a loop.
+					wfMessage( 'rawhtml-notallowed' )->escaped()
+				);
+			}
 		} else {
 			throw new MWException( '<html> extension tag encountered unexpectedly' );
 		}
diff -Nru mediawiki-1.27.1/includes/parser/Parser.php mediawiki-1.27.2/includes/parser/Parser.php
--- mediawiki-1.27.1/includes/parser/Parser.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/includes/parser/Parser.php	2017-04-06 11:54:29.000000000 -0700
@@ -1538,9 +1538,7 @@
 				true, 'free',
 				$this->getExternalLinkAttribs( $url ) );
 			# Register it in the output object...
-			# Replace unnecessary URL escape codes with their equivalent characters
-			$pasteurized = self::normalizeLinkUrl( $url );
-			$this->mOutput->addExternalLink( $pasteurized );
+			$this->mOutput->addExternalLink( $url );
 		}
 		return $text . $trail;
 	}
@@ -1836,10 +1834,7 @@
 				$this->getExternalLinkAttribs( $url ) ) . $dtrail . $trail;
 
 			# Register link in the output object.
-			# Replace unnecessary URL escape codes with the referenced character
-			# This prevents spammers from hiding links from the filters
-			$pasteurized = self::normalizeLinkUrl( $url );
-			$this->mOutput->addExternalLink( $pasteurized );
+			$this->mOutput->addExternalLink( $url );
 		}
 
 		return $s;
@@ -5445,9 +5440,11 @@
 							// check to see if link matches an absolute url, if not then it must be a wiki link.
 							if ( preg_match( "/^($prots)$addr$chars*$/u", $linkValue ) ) {
 								$link = $linkValue;
+								$this->mOutput->addExternalLink( $link );
 							} else {
 								$localLinkTitle = Title::newFromText( $linkValue );
 								if ( $localLinkTitle !== null ) {
+									$this->mOutput->addLink( $localLinkTitle );
 									$link = $localLinkTitle->getLinkURL();
 								}
 							}
diff -Nru mediawiki-1.27.1/includes/parser/ParserOptions.php mediawiki-1.27.2/includes/parser/ParserOptions.php
--- mediawiki-1.27.1/includes/parser/ParserOptions.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/parser/ParserOptions.php	2017-04-06 11:54:25.000000000 -0700
@@ -222,6 +222,21 @@
 	 */
 	private $redirectTarget = null;
 
+	/**
+	 * If the wiki is configured to allow raw html ($wgRawHtml = true)
+	 * is it allowed in the specific case of parsing this page.
+	 *
+	 * This is meant to disable unsafe parser tags in cases where
+	 * a malicious user may control the input to the parser.
+	 *
+	 * @note This is expected to be true for normal pages even if the
+	 *  wiki has $wgRawHtml disabled in general. The setting only
+	 *  signifies that raw html would be unsafe in the current context
+	 *  provided that raw html is allowed at all.
+	 * @var boolean
+	 */
+	private $allowUnsafeRawHtml = true;
+
 	public function getInterwikiMagic() {
 		return $this->mInterwikiMagic;
 	}
@@ -409,6 +424,14 @@
 		return $this->getUserLangObj()->getCode();
 	}
 
+	/**
+	 * @since 1.29
+	 * @return bool
+	 */
+	public function getAllowUnsafeRawHtml() {
+		return $this->allowUnsafeRawHtml;
+	}
+
 	public function setInterwikiMagic( $x ) {
 		return wfSetVar( $this->mInterwikiMagic, $x );
 	}
@@ -544,6 +567,15 @@
 	}
 
 	/**
+	 * @param bool|null Value to set or null to get current value
+	 * @return bool Current value for allowUnsafeRawHtml
+	 * @since 1.29
+	 */
+	public function setAllowUnsafeRawHtml( $x ) {
+		return wfSetVar( $this->allowUnsafeRawHtml, $x );
+	}
+
+	/**
 	 * Set the redirect target.
 	 *
 	 * Note that setting or changing this does not *make* the page a redirect
diff -Nru mediawiki-1.27.1/includes/parser/ParserOutput.php mediawiki-1.27.2/includes/parser/ParserOutput.php
--- mediawiki-1.27.1/includes/parser/ParserOutput.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/parser/ParserOutput.php	2017-04-06 11:54:29.000000000 -0700
@@ -509,6 +509,10 @@
 		# We don't register links pointing to our own server, unless... :-)
 		global $wgServer, $wgRegisterInternalExternals;
 
+		# Replace unnecessary URL escape codes with the referenced character
+		# This prevents spammers from hiding links from the filters
+		$url = parser::normalizeLinkUrl( $url );
+
 		$registerExternalLink = true;
 		if ( !$wgRegisterInternalExternals ) {
 			$registerExternalLink = !self::isLinkInternal( $wgServer, $url );
diff -Nru mediawiki-1.27.1/includes/search/SearchHighlighter.php mediawiki-1.27.2/includes/search/SearchHighlighter.php
--- mediawiki-1.27.1/includes/search/SearchHighlighter.php	2016-08-22 13:52:57.000000000 -0700
+++ mediawiki-1.27.2/includes/search/SearchHighlighter.php	2017-04-06 11:54:28.000000000 -0700
@@ -29,6 +29,10 @@
 class SearchHighlighter {
 	protected $mCleanWikitext = true;
 
+	/**
+	 * @warning If you pass false to this constructor, then
+	 *  the caller is responsible for HTML escaping.
+	 */
 	function __construct( $cleanupWikitext = true ) {
 		$this->mCleanWikitext = $cleanupWikitext;
 	}
@@ -456,6 +460,10 @@
 		$text = preg_replace( "/('''|<\/?[iIuUbB]>)/", "", $text );
 		$text = preg_replace( "/''/", "", $text );
 
+		// Note, the previous /<\/?[^>]+>/ is insufficient
+		// for XSS safety as the HTML tag can span multiple
+		// search results (T144845).
+		$text = Sanitizer::escapeHtmlAllowEntities( $text );
 		return $text;
 	}
 
diff -Nru mediawiki-1.27.1/includes/specialpage/AuthManagerSpecialPage.php mediawiki-1.27.2/includes/specialpage/AuthManagerSpecialPage.php
--- mediawiki-1.27.1/includes/specialpage/AuthManagerSpecialPage.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specialpage/AuthManagerSpecialPage.php	2017-04-06 11:54:25.000000000 -0700
@@ -537,7 +537,7 @@
 		$form->setAction( $this->getFullTitle()->getFullURL( $this->getPreservedParams() ) );
 		$form->addHiddenField( $this->getTokenName(), $this->getToken()->toString() );
 		$form->addHiddenField( 'authAction', $this->authAction );
-		$form->suppressDefaultSubmit( !$this->needsSubmitButton( $formDescriptor ) );
+		$form->suppressDefaultSubmit( !$this->needsSubmitButton( $requests ) );
 
 		return $form;
 	}
@@ -555,24 +555,46 @@
 	}
 
 	/**
-	 * Returns true if the form has fields which take values. If all available providers use the
-	 * redirect flow, the form might contain nothing but submit buttons, in which case we should
-	 * not add an extra submit button which does nothing.
+	 * Returns true if the form built from the given AuthenticationRequests needs a submit button.
+	 * Providers using redirect flow (e.g. Google login) need their own submit buttons; if using
+	 * one of those custom buttons is the only way to proceed, there is no point in displaying the
+	 * default button which won't do anything useful.
 	 *
-	 * @param array $formDescriptor A HTMLForm descriptor
+	 * @param AuthenticationRequest[] $requests An array of AuthenticationRequests from which the
+	 *  form will be built
 	 * @return bool
 	 */
-	protected function needsSubmitButton( $formDescriptor ) {
-		return (bool)array_filter( $formDescriptor, function ( $item ) {
-			$class = false;
-			if ( array_key_exists( 'class', $item ) ) {
-				$class = $item['class'];
-			} elseif ( array_key_exists( 'type', $item ) ) {
-				$class = HTMLForm::$typeMappings[$item['type']];
+	protected function needsSubmitButton( array $requests ) {
+		$customSubmitButtonPresent = false;
+
+		// Secondary and preauth providers always need their data; they will not care what button
+		// is used, so they can be ignored. So can OPTIONAL buttons createdby primary providers;
+		// that's the point in being optional. Se we need to check whether all primary providers
+		// have their own buttons and whether there is at least one button present.
+		foreach ( $requests as $req ) {
+			if ( $req->required === AuthenticationRequest::PRIMARY_REQUIRED ) {
+				if ( $this->hasOwnSubmitButton( $req ) ) {
+					$customSubmitButtonPresent = true;
+				} else {
+					return true;
+				}
 			}
-			return !is_a( $class, \HTMLInfoField::class, true ) &&
-				!is_a( $class, \HTMLSubmitField::class, true );
-		} );
+		}
+		return !$customSubmitButtonPresent;
+	}
+
+	/**
+	 * Checks whether the given AuthenticationRequest has its own submit button.
+	 * @param AuthenticationRequest $req
+	 * @return bool
+	 */
+	protected function hasOwnSubmitButton( AuthenticationRequest $req ) {
+		foreach ( $req->getFieldInfo() as $field => $info ) {
+			if ( $info['type'] === 'button' ) {
+				return true;
+			}
+		}
+		return false;
 	}
 
 	/**
diff -Nru mediawiki-1.27.1/includes/specialpage/LoginSignupSpecialPage.php mediawiki-1.27.2/includes/specialpage/LoginSignupSpecialPage.php
--- mediawiki-1.27.1/includes/specialpage/LoginSignupSpecialPage.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specialpage/LoginSignupSpecialPage.php	2017-04-06 11:54:25.000000000 -0700
@@ -290,6 +290,14 @@
 			return;
 		}
 
+		if ( $this->canBypassForm( $button_name ) ) {
+			$this->setRequest( [], true );
+			$this->getRequest()->setVal( $this->getTokenName(), $this->getToken() );
+			if ( $button_name ) {
+				$this->getRequest()->setVal( $button_name, true );
+			}
+		}
+
 		$status = $this->trySubmit();
 
 		if ( !$status || !$status->isGood() ) {
@@ -363,6 +371,46 @@
 	}
 
 	/**
+	 * Determine if the login form can be bypassed. This will be the case when no more than one
+	 * button is present and no other user input fields that are not marked as 'skippable' are
+	 * present. If the login form were not bypassed, the user would be presented with a
+	 * superfluous page on which they must press the single button to proceed with login.
+	 * Not only does this cause an additional mouse click and page load, it confuses users,
+	 * especially since there are a help link and forgotten password link that are
+	 * provided on the login page that do not apply to this situation.
+	 *
+	 * @param string|null &$button_name if the form has a single button, returns
+	 *   the name of the button; otherwise, returns null
+	 * @return bool
+	 */
+	private function canBypassForm( &$button_name ) {
+		$button_name = null;
+		if ( $this->isContinued() ) {
+			return false;
+		}
+		$fields = AuthenticationRequest::mergeFieldInfo( $this->authRequests );
+		foreach ( $fields as $fieldname => $field ) {
+			if ( !isset( $field['type'] ) ) {
+				return false;
+			}
+			if ( !empty( $field['skippable'] ) ) {
+				continue;
+			}
+			if ( $field['type'] === 'button' ) {
+				if ( $button_name !== null ) {
+					$button_name = null;
+					return false;
+				} else {
+					$button_name = $fieldname;
+				}
+			} elseif ( $field['type'] !== 'null' ) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
 	 * Show the success page.
 	 *
 	 * @param string $type Condition of return to; see `executeReturnTo`
@@ -586,7 +634,7 @@
 		$this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook
 		// this will call onAuthChangeFormFields()
 		$formDescriptor = static::fieldInfoToFormDescriptor( $requests, $fieldInfo, $this->authAction );
-		$this->postProcessFormDescriptor( $formDescriptor );
+		$this->postProcessFormDescriptor( $formDescriptor, $requests );
 
 		$context = $this->getContext();
 		if ( $context->getRequest() !== $this->getRequest() ) {
@@ -1249,7 +1297,7 @@
 	/**
 	 * @param array $formDescriptor
 	 */
-	protected function postProcessFormDescriptor( &$formDescriptor ) {
+	protected function postProcessFormDescriptor( &$formDescriptor, $requests ) {
 		// Pre-fill username (if not creating an account, T46775).
 		if (
 			isset( $formDescriptor['username'] ) &&
@@ -1267,7 +1315,7 @@
 
 		// don't show a submit button if there is nothing to submit (i.e. the only form content
 		// is other submit buttons, for redirect flows)
-		if ( !$this->needsSubmitButton( $formDescriptor ) ) {
+		if ( !$this->needsSubmitButton( $requests ) ) {
 			unset( $formDescriptor['createaccount'], $formDescriptor['loginattempt'] );
 		}
 
diff -Nru mediawiki-1.27.1/includes/specialpage/RedirectSpecialPage.php mediawiki-1.27.2/includes/specialpage/RedirectSpecialPage.php
--- mediawiki-1.27.1/includes/specialpage/RedirectSpecialPage.php	2016-08-22 13:52:57.000000000 -0700
+++ mediawiki-1.27.2/includes/specialpage/RedirectSpecialPage.php	2017-04-06 11:54:28.000000000 -0700
@@ -41,7 +41,7 @@
 		$query = $this->getRedirectQuery();
 		// Redirect to a page title with possible query parameters
 		if ( $redirect instanceof Title ) {
-			$url = $redirect->getFullURL( $query );
+			$url = $redirect->getFullUrlForRedirect( $query );
 			$this->getOutput()->redirect( $url );
 
 			return $redirect;
diff -Nru mediawiki-1.27.1/includes/specialpage/SpecialPageFactory.php mediawiki-1.27.2/includes/specialpage/SpecialPageFactory.php
--- mediawiki-1.27.1/includes/specialpage/SpecialPageFactory.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specialpage/SpecialPageFactory.php	2017-04-06 11:54:28.000000000 -0700
@@ -142,6 +142,7 @@
 		'RandomInCategory' => 'SpecialRandomInCategory',
 		'Randomredirect' => 'SpecialRandomredirect',
 		'Randomrootpage' => 'SpecialRandomrootpage',
+		'GoToInterwiki' => 'SpecialGoToInterwiki',
 
 		// High use pages
 		'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
diff -Nru mediawiki-1.27.1/includes/specials/SpecialChangeCredentials.php mediawiki-1.27.2/includes/specials/SpecialChangeCredentials.php
--- mediawiki-1.27.1/includes/specials/SpecialChangeCredentials.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialChangeCredentials.php	2017-04-06 11:54:28.000000000 -0700
@@ -159,7 +159,7 @@
 		return $form;
 	}
 
-	protected function needsSubmitButton( $formDescriptor ) {
+	protected function needsSubmitButton( array $requests ) {
 		// Change/remove forms show are built from a single AuthenticationRequest and do not allow
 		// for redirect flow; they always need a submit button.
 		return true;
@@ -243,7 +243,7 @@
 		}
 
 		$title = Title::newFromText( $returnTo );
-		return $title->getFullURL( $returnToQuery );
+		return $title->getFullUrlForRedirect( $returnToQuery );
 	}
 
 	protected function getRequestBlacklist() {
diff -Nru mediawiki-1.27.1/includes/specials/SpecialChangeEmail.php mediawiki-1.27.2/includes/specials/SpecialChangeEmail.php
--- mediawiki-1.27.1/includes/specials/SpecialChangeEmail.php	2016-08-22 13:52:57.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialChangeEmail.php	2017-04-06 11:54:28.000000000 -0700
@@ -136,7 +136,7 @@
 		$query = $request->getVal( 'returntoquery' );
 
 		if ( $this->status->value === true ) {
-			$this->getOutput()->redirect( $titleObj->getFullURL( $query ) );
+			$this->getOutput()->redirect( $titleObj->getFullUrlForRedirect( $query ) );
 		} elseif ( $this->status->value === 'eauth' ) {
 			# Notify user that a confirmation email has been sent...
 			$this->getOutput()->wrapWikiMsg( "<div class='error' style='clear: both;'>\n$1\n</div>",
diff -Nru mediawiki-1.27.1/includes/specials/SpecialCreateAccount.php mediawiki-1.27.2/includes/specials/SpecialCreateAccount.php
--- mediawiki-1.27.1/includes/specials/SpecialCreateAccount.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialCreateAccount.php	2017-04-06 11:54:25.000000000 -0700
@@ -120,7 +120,12 @@
 				} else {
 					$out->addWikiMsg( 'accountcreatedtext', $user->getName() );
 				}
-				$out->addReturnTo( $this->getPageTitle() );
+
+				$rt = Title::newFromText( $this->mReturnTo );
+				$out->addReturnTo(
+					( $rt && !$rt->isExternal() ) ? $rt : $this->getPageTitle(),
+					wfCgiToArray( $this->mReturnToQuery )
+				);
 				return;
 			}
 		}
diff -Nru mediawiki-1.27.1/includes/specials/SpecialGoToInterwiki.php mediawiki-1.27.2/includes/specials/SpecialGoToInterwiki.php
--- mediawiki-1.27.1/includes/specials/SpecialGoToInterwiki.php	1969-12-31 16:00:00.000000000 -0800
+++ mediawiki-1.27.2/includes/specials/SpecialGoToInterwiki.php	2017-04-06 11:54:28.000000000 -0700
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Implements Special:GoToInterwiki
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup SpecialPage
+ */
+
+/**
+ * Landing page for non-local interwiki links.
+ *
+ * Meant to warn people that the site they're visiting
+ * is not the local wiki (In case of phishing tricks).
+ * Only meant to be used for things that directly
+ * redirect from url (e.g. Special:Search/google:foo )
+ * Not meant for general interwiki linking (e.g.
+ * [[google:foo]] should still directly link)
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialGoToInterwiki extends UnlistedSpecialPage {
+	public function __construct( $name = 'GoToInterwiki' ) {
+		parent::__construct( $name );
+	}
+
+	public function execute( $par ) {
+		$this->setHeaders();
+		$target = Title::newFromText( $par );
+		// Disallow special pages as a precaution against
+		// possible redirect loops.
+		if ( !$target || $target->isSpecialPage() ) {
+			$this->getOutput()->setStatusCode( 404 );
+			$this->getOutput()->addWikiMsg( 'gotointerwiki-invalid' );
+			return;
+		}
+
+		$url = $target->getFullURL();
+		if ( !$target->isExternal() || $target->isLocal() ) {
+			// Either a normal page, or a local interwiki.
+			// just redirect.
+			$this->getOutput()->redirect( $url, '301' );
+		} else {
+			$this->getOutput()->addWikiMsg(
+				'gotointerwiki-external',
+				$url,
+				$target->getFullText()
+			);
+		}
+	}
+
+	/**
+	 * @return bool
+	 */
+	public function requiresWrite() {
+		return false;
+	}
+
+	/**
+	 * @return String
+	 */
+	protected function getGroupName() {
+		return 'redirects';
+	}
+}
diff -Nru mediawiki-1.27.1/includes/specials/SpecialPageLanguage.php mediawiki-1.27.2/includes/specials/SpecialPageLanguage.php
--- mediawiki-1.27.1/includes/specials/SpecialPageLanguage.php	2016-08-22 13:52:57.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialPageLanguage.php	2017-04-06 11:54:28.000000000 -0700
@@ -141,7 +141,7 @@
 		);
 
 		// Url to redirect to after the operation
-		$this->goToUrl = $title->getFullURL();
+		$this->goToUrl = $title->getFullUrlForRedirect();
 
 		// Check if user wants to use default language
 		if ( $data['selectoptions'] == 1 ) {
diff -Nru mediawiki-1.27.1/includes/specials/SpecialPreferences.php mediawiki-1.27.2/includes/specials/SpecialPreferences.php
--- mediawiki-1.27.1/includes/specials/SpecialPreferences.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialPreferences.php	2017-04-06 11:54:28.000000000 -0700
@@ -147,7 +147,7 @@
 		// Set session data for the success message
 		$this->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
 
-		$url = $this->getPageTitle()->getFullURL();
+		$url = $this->getPageTitle()->getFullUrlForRedirect();
 		$this->getOutput()->redirect( $url );
 
 		return true;
diff -Nru mediawiki-1.27.1/includes/specials/SpecialSearch.php mediawiki-1.27.2/includes/specials/SpecialSearch.php
--- mediawiki-1.27.1/includes/specials/SpecialSearch.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialSearch.php	2017-04-06 11:54:28.000000000 -0700
@@ -218,7 +218,7 @@
 			Hooks::run( 'SpecialSearchGoResult', [ $term, $title, &$url ] )
 		) {
 			if ( $url === null ) {
-				$url = $title->getFullURL();
+				$url = $title->getFullUrlForRedirect();
 			}
 			$this->getOutput()->redirect( $url );
 
diff -Nru mediawiki-1.27.1/includes/specials/SpecialUpload.php mediawiki-1.27.2/includes/specials/SpecialUpload.php
--- mediawiki-1.27.1/includes/specials/SpecialUpload.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialUpload.php	2017-04-06 11:54:25.000000000 -0700
@@ -1051,12 +1051,14 @@
 				global $wgContLang;
 
 				$mto = $file->transform( [ 'width' => 120 ] );
-				$this->addHeaderText(
-					'<div class="thumb t' . $wgContLang->alignEnd() . '">' .
-					Html::element( 'img', [
-						'src' => $mto->getUrl(),
-						'class' => 'thumbimage',
-					] ) . '</div>', 'description' );
+				if ( $mto ) {
+					$this->addHeaderText(
+						'<div class="thumb t' . $wgContLang->alignEnd() . '">' .
+						Html::element( 'img', [
+							'src' => $mto->getUrl(),
+							'class' => 'thumbimage',
+						] ) . '</div>', 'description' );
+				}
 			}
 		}
 
diff -Nru mediawiki-1.27.1/includes/specials/SpecialWatchlist.php mediawiki-1.27.2/includes/specials/SpecialWatchlist.php
--- mediawiki-1.27.1/includes/specials/SpecialWatchlist.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/SpecialWatchlist.php	2017-04-06 11:54:28.000000000 -0700
@@ -76,6 +76,7 @@
 		if ( ( $config->get( 'EnotifWatchlist' ) || $config->get( 'ShowUpdatedMarker' ) )
 			&& $request->getVal( 'reset' )
 			&& $request->wasPosted()
+			&& $user->matchEditToken( $request->getVal( 'token' ) )
 		) {
 			$user->clearAllNotifications();
 			$output->redirect( $this->getPageTitle()->getFullURL( $opts->getChangedValues() ) );
@@ -604,6 +605,7 @@
 				'action' => $this->getPageTitle()->getLocalURL(),
 				'id' => 'mw-watchlist-resetbutton' ] ) . "\n" .
 			Xml::submitButton( $this->msg( 'enotif_reset' )->text(), [ 'name' => 'dummy' ] ) . "\n" .
+			Html::hidden( 'token', $user->getEditToken() ) . "\n" .
 			Html::hidden( 'reset', 'all' ) . "\n";
 			foreach ( $nondefaults as $key => $value ) {
 				$form .= Html::hidden( $key, $value ) . "\n";
diff -Nru mediawiki-1.27.1/includes/specials/helpers/LoginHelper.php mediawiki-1.27.2/includes/specials/helpers/LoginHelper.php
--- mediawiki-1.27.1/includes/specials/helpers/LoginHelper.php	2016-08-22 13:52:57.000000000 -0700
+++ mediawiki-1.27.2/includes/specials/helpers/LoginHelper.php	2017-04-06 11:54:28.000000000 -0700
@@ -89,7 +89,7 @@
 		}
 
 		if ( $type === 'successredirect' ) {
-			$redirectUrl = $returnToTitle->getFullURL( $returnToQuery, false, $proto );
+			$redirectUrl = $returnToTitle->getFullUrlForRedirect( $returnToQuery, $proto );
 			$this->getOutput()->redirect( $redirectUrl );
 		} else {
 			$this->getOutput()->addReturnTo( $returnToTitle, $returnToQuery, null, $options );
diff -Nru mediawiki-1.27.1/includes/upload/UploadBase.php mediawiki-1.27.2/includes/upload/UploadBase.php
--- mediawiki-1.27.1/includes/upload/UploadBase.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/includes/upload/UploadBase.php	2017-04-06 11:54:29.000000000 -0700
@@ -1274,7 +1274,10 @@
 			$filename,
 			[ $this, 'checkSvgScriptCallback' ],
 			true,
-			[ 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' ]
+			[
+				'processing_instruction_handler' => 'UploadBase::checkSvgPICallback',
+				'external_dtd_handler' => 'UploadBase::checkSvgExternalDTD',
+			]
 		);
 		if ( $check->wellFormed !== true ) {
 			// Invalid xml (bug 58553)
@@ -1306,6 +1309,34 @@
 		return false;
 	}
 
+	/**
+	 * Verify that DTD urls referenced are only the standard dtds
+	 *
+	 * Browsers seem to ignore external dtds. However just to be on the
+	 * safe side, only allow dtds from the svg standard.
+	 *
+	 * @param string $type PUBLIC or SYSTEM
+	 * @param string $publicId The well-known public identifier for the dtd
+	 * @param string $systemId The url for the external dtd
+	 */
+	public static function checkSvgExternalDTD( $type, $publicId, $systemId ) {
+		// This doesn't include the XHTML+MathML+SVG doctype since we don't
+		// allow XHTML anyways.
+		$allowedDTDs = [
+			'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd',
+			'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd',
+			'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-basic.dtd',
+			'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd'
+		];
+		if ( $type !== 'PUBLIC'
+			|| !in_array( $systemId, $allowedDTDs )
+			|| strpos( $publicId, "-//W3C//" ) !== 0
+		) {
+			return [ 'upload-scripted-dtd' ];
+		}
+		return false;
+	}
+
 	/**
 	 * @todo Replace this with a whitelist filter!
 	 * @param string $element
diff -Nru mediawiki-1.27.1/includes/user/User.php mediawiki-1.27.2/includes/user/User.php
--- mediawiki-1.27.1/includes/user/User.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/includes/user/User.php	2017-04-06 11:54:25.000000000 -0700
@@ -325,7 +325,7 @@
 	 * @return string
 	 */
 	public function __toString() {
-		return $this->getName();
+		return (string)$this->getName();
 	}
 
 	/**
@@ -1355,8 +1355,6 @@
 	 */
 	protected function loadFromUserObject( $user ) {
 		$user->load();
-		$user->loadGroups();
-		$user->loadOptions();
 		foreach ( self::$mCacheVars as $var ) {
 			$this->$var = $user->$var;
 		}
diff -Nru mediawiki-1.27.1/languages/i18n/en.json mediawiki-1.27.2/languages/i18n/en.json
--- mediawiki-1.27.1/languages/i18n/en.json	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/languages/i18n/en.json	2017-04-06 11:54:29.000000000 -0700
@@ -1469,6 +1469,7 @@
 	"php-uploaddisabledtext": "File uploads are disabled in PHP.\nPlease check the file_uploads setting.",
 	"uploadscripted": "This file contains HTML or script code that may be erroneously interpreted by a web browser.",
 	"upload-scripted-pi-callback": "Cannot upload a file that contains XML-stylesheet processing instruction.",
+	"upload-scripted-dtd": "Cannot upload SVG files that contain a non-standard DTD declaration.",
 	"uploaded-script-svg": "Found scriptable element \"$1\" in the uploaded SVG file.",
 	"uploaded-hostile-svg": "Found unsafe CSS in the style element of uploaded SVG file.",
 	"uploaded-event-handler-on-svg": "Setting event-handler attributes <code>$1=\"$2\"</code> is not allowed in SVG files.",
@@ -4169,5 +4170,11 @@
 	"linkaccounts-submit": "Link accounts",
 	"unlinkaccounts": "Unlink accounts",
 	"unlinkaccounts-success": "The account was unlinked.",
-	"authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?"
+	"authenticationdatachange-ignored": "The authentication data change was not handled. Maybe no provider was configured?",
+	"rawhtml-notallowed": "&lt;html&gt; tags cannot be used outside of normal pages.",
+	"gotointerwiki": "Leaving {{SITENAME}}",
+	"gotointerwiki-invalid": "The specified title was invalid.",
+	"gotointerwiki-external": "You are about to leave {{SITENAME}} to visit [[$2]] which is a separate website.\n\n[$1 Click here to continue on to $1].",
+	"undelete-cantedit": "You cannot undelete this page as you are not allowed to edit this page.",
+	"undelete-cantcreate": "You cannot undelete this page as there is no existing page with this name and you are not allowed to create this page."
 }
diff -Nru mediawiki-1.27.1/languages/i18n/qqq.json mediawiki-1.27.2/languages/i18n/qqq.json
--- mediawiki-1.27.1/languages/i18n/qqq.json	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/languages/i18n/qqq.json	2017-04-06 11:54:29.000000000 -0700
@@ -1647,6 +1647,7 @@
 	"php-uploaddisabledtext": "This means that file uploading is disabled in PHP, not upload of PHP-files.",
 	"uploadscripted": "Used as error message when uploading a file.\n\nSee also:\n* {{msg-mw|zip-wrong-format}}\n* {{msg-mw|uploadjava}}\n* {{msg-mw|uploadvirus}}",
 	"upload-scripted-pi-callback": "Used as error message when uploading an SVG file that contains xml-stylesheet processing instruction.",
+	"upload-scripted-dtd": "Used as an error message when uploading an svg file that contains a DTD declaration where the system identifier of the DTD is for something other than the standard SVG DTDS, or it is a SYSTEM DTD, or the public identifier does not start with -//W3C//. Note that errors related to the internal dtd subset do not use this error message.",
 	"uploaded-script-svg": "Used as error message when uploading an SVG file that contains scriptable tags (script, handler, stylesheet, iframe).\n\nParameters:\n* $1 - The scriptable tag that blocked the SVG file from uploading.",
 	"uploaded-hostile-svg": "Used as error message when uploading an SVG file that contains unsafe CSS.",
 	"uploaded-event-handler-on-svg": "Used as error message when uploading an SVG file that contains event-handler attributes.\n\nParameters:\n* $1 - The event-handler attribute that is being modified in the SVG file.\n* $2 - The value that is given to the event-handler attribute.",
@@ -4347,5 +4348,11 @@
 	"linkaccounts-submit": "Text of the main submit button on [[Special:LinkAccounts]] (when there is one)",
 	"unlinkaccounts": "Title of the special page [[Special:UnlinkAccounts]] which allows the user to remove linked remote accounts.",
 	"unlinkaccounts-success": "Account unlinking form success message",
-	"authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems."
+	"authenticationdatachange-ignored": "Shown when authentication data change was unsuccessful due to configuration problems.",
+	"rawhtml-notallowed": "Error message given when $wgRawHtml = true; is set and a user uses an &lt;html&gt; tag in a system message or somewhere other than a normal page.",
+	"gotointerwiki": "{{doc-special|GoToInterwiki}}\n\nSpecial:GoToInterwiki is a warning page displayed before redirecting users to external interwiki links. Its triggered by people going to something like [[Special:Search/google:foo]].",
+	"gotointerwiki-invalid": "Message shown on Special:GoToInterwiki if given an invalid title.",
+	"gotointerwiki-external": "Message shown on Special:GoToInterwiki if given a external interwiki link (e.g. [[Special:GoToInterwiki/Google:Foo]]). $1 is the full url the user is trying to get to. $2 is the text of the interwiki link (e.g. \"Google:foo\").",
+	"undelete-cantedit": "Shown if the user tries to undelete a page that they cannot edit",
+	"undelete-cantcreate": "Shown if the user tries to undelete a page which currently does not exist, and they are not allowed to create it. This could for example happen on a wiki with custom protection levels where the page name has been create-protected and the user has the right to undelete but not the right to edit protected pages."
 }
diff -Nru mediawiki-1.27.1/languages/messages/MessagesEn.php mediawiki-1.27.2/languages/messages/MessagesEn.php
--- mediawiki-1.27.1/languages/messages/MessagesEn.php	2016-08-22 13:52:58.000000000 -0700
+++ mediawiki-1.27.2/languages/messages/MessagesEn.php	2017-04-06 11:54:28.000000000 -0700
@@ -426,6 +426,7 @@
 	'Fewestrevisions'           => [ 'FewestRevisions' ],
 	'FileDuplicateSearch'       => [ 'FileDuplicateSearch' ],
 	'Filepath'                  => [ 'FilePath' ],
+	'GoToInterwiki'             => [ 'GoToInterwiki' ],
 	'Import'                    => [ 'Import' ],
 	'Invalidateemail'           => [ 'InvalidateEmail' ],
 	'JavaScriptTest'            => [ 'JavaScriptTest' ],
diff -Nru mediawiki-1.27.1/maintenance/checkComposerLockUpToDate.php mediawiki-1.27.2/maintenance/checkComposerLockUpToDate.php
--- mediawiki-1.27.1/maintenance/checkComposerLockUpToDate.php	2016-08-22 13:52:58.000000000 -0700
+++ mediawiki-1.27.2/maintenance/checkComposerLockUpToDate.php	2017-04-06 11:54:27.000000000 -0700
@@ -34,11 +34,7 @@
 		$lock = new ComposerLock( $lockLocation );
 		$json = new ComposerJson( $jsonLocation );
 
-		if ( $lock->getHash() === $json->getHash() ) {
-			$this->output( "Your composer.lock file is up to date with current dependencies!\n" );
-			return;
-		}
-		// Out of date, lets figure out which dependencies are old
+		// Check all the dependencies to see if any are old
 		$found = false;
 		$installed = $lock->getInstalledDependencies();
 		foreach ( $json->getRequiredDependencies() as $name => $version ) {
@@ -61,8 +57,6 @@
 				1
 			);
 		} else {
-			// The hash is the entire composer.json file,
-			// so it can be updated without any of the dependencies changing
 			// We couldn't find any out-of-date dependencies, so assume everything is ok!
 			$this->output( "Your composer.lock file is up to date with current dependencies!\n" );
 		}
diff -Nru mediawiki-1.27.1/maintenance/mssql/archives/patch-alter-table-oldimage.sql mediawiki-1.27.2/maintenance/mssql/archives/patch-alter-table-oldimage.sql
--- mediawiki-1.27.1/maintenance/mssql/archives/patch-alter-table-oldimage.sql	1969-12-31 16:00:00.000000000 -0800
+++ mediawiki-1.27.2/maintenance/mssql/archives/patch-alter-table-oldimage.sql	2017-04-06 11:54:22.000000000 -0700
@@ -0,0 +1 @@
+DROP INDEX /*i*/oi_name_archive_name ON /*_*/oldimage;
diff -Nru mediawiki-1.27.1/maintenance/mssql/tables.sql mediawiki-1.27.2/maintenance/mssql/tables.sql
--- mediawiki-1.27.1/maintenance/mssql/tables.sql	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/maintenance/mssql/tables.sql	2017-04-06 11:54:27.000000000 -0700
@@ -652,7 +652,6 @@
 
 CREATE INDEX /*i*/oi_usertext_timestamp ON /*_*/oldimage (oi_user_text,oi_timestamp);
 CREATE INDEX /*i*/oi_name_timestamp ON /*_*/oldimage (oi_name,oi_timestamp);
-CREATE INDEX /*i*/oi_name_archive_name ON /*_*/oldimage (oi_name,oi_archive_name);
 CREATE INDEX /*i*/oi_sha1 ON /*_*/oldimage (oi_sha1);
 
 
diff -Nru mediawiki-1.27.1/mw-config/index.php mediawiki-1.27.2/mw-config/index.php
--- mediawiki-1.27.1/mw-config/index.php	2016-08-22 13:52:58.000000000 -0700
+++ mediawiki-1.27.2/mw-config/index.php	2017-04-06 11:54:22.000000000 -0700
@@ -1,4 +1,5 @@
 <?php
+// @codingStandardsIgnoreFile Generic.Arrays.DisallowLongArraySyntax
 /**
  * New version of MediaWiki web-based config/installation
  *
@@ -60,7 +61,7 @@
 	if ( isset( $_SESSION['installData'][$fingerprint] ) ) {
 		$session = $_SESSION['installData'][$fingerprint];
 	} else {
-		$session = [];
+		$session = array();
 	}
 
 	if ( !is_null( $wgRequest->getVal( 'uselang' ) ) ) {
diff -Nru mediawiki-1.27.1/resources/src/mediawiki/mediawiki.js mediawiki-1.27.2/resources/src/mediawiki/mediawiki.js
--- mediawiki-1.27.1/resources/src/mediawiki/mediawiki.js	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/resources/src/mediawiki/mediawiki.js	2017-04-06 11:54:27.000000000 -0700
@@ -632,21 +632,27 @@
 				obj[ key ] = val;
 			} : function ( obj, key, val, msg ) {
 				msg = 'Use of "' + key + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
-				Object.defineProperty( obj, key, {
-					configurable: true,
-					enumerable: true,
-					get: function () {
-						mw.track( 'mw.deprecate', key );
-						mw.log.warn( msg );
-						return val;
-					},
-					set: function ( newVal ) {
-						mw.track( 'mw.deprecate', key );
-						mw.log.warn( msg );
-						val = newVal;
-					}
-				} );
-
+				// Support: Safari 5.0
+				// Throws "not supported on DOM Objects" for Node or Element objects (incl. document)
+				// Safari 4.0 doesn't have this method, and it was fixed in Safari 5.1.
+				try {
+					Object.defineProperty( obj, key, {
+						configurable: true,
+						enumerable: true,
+						get: function () {
+							mw.track( 'mw.deprecate', key );
+							mw.log.warn( msg );
+							return val;
+						},
+						set: function ( newVal ) {
+							mw.track( 'mw.deprecate', key );
+							mw.log.warn( msg );
+							val = newVal;
+						}
+					} );
+				} catch ( err ) {
+					obj[ key ] = val;
+				}
 			};
 
 			return log;
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/MediaWikiTest.php mediawiki-1.27.2/tests/phpunit/includes/MediaWikiTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/MediaWikiTest.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/MediaWikiTest.php	2017-04-06 11:54:24.000000000 -0700
@@ -34,7 +34,7 @@
 				'url' => 'http://example.org/w/index.php?title=Foo_Bar',
 				'query' => [ 'title' => 'Foo_Bar' ],
 				'title' => 'Foo_Bar',
-				'redirect' => 'http://example.org/wiki/Foo_Bar',
+				'redirect' => false,
 			],
 			[
 				// View: Script path with implicit title from page id
@@ -76,21 +76,21 @@
 				'url' => 'http://example.org/w/?title=Foo_Bar',
 				'query' => [ 'title' => 'Foo_Bar' ],
 				'title' => 'Foo_Bar',
-				'redirect' => 'http://example.org/wiki/Foo_Bar',
+				'redirect' => false,
 			],
 			[
 				// View: Root path with escaped title
 				'url' => 'http://example.org/?title=Foo_Bar',
 				'query' => [ 'title' => 'Foo_Bar' ],
 				'title' => 'Foo_Bar',
-				'redirect' => 'http://example.org/wiki/Foo_Bar',
+				'redirect' => false,
 			],
 			[
 				// View: Canonical with redundant query
 				'url' => 'http://example.org/wiki/Foo_Bar?action=view',
 				'query' => [ 'action' => 'view' ],
 				'title' => 'Foo_Bar',
-				'redirect' => 'http://example.org/wiki/Foo_Bar',
+				'redirect' => false,
 			],
 			[
 				// Edit: Canonical view url with action query
@@ -104,7 +104,7 @@
 				'url' => 'http://example.org/w/index.php?title=Foo_Bar&action=view',
 				'query' => [ 'title' => 'Foo_Bar', 'action' => 'view' ],
 				'title' => 'Foo_Bar',
-				'redirect' => 'http://example.org/wiki/Foo_Bar',
+				'redirect' => false,
 			],
 			[
 				// Edit: Index with action query
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/MessageTest.php mediawiki-1.27.2/tests/phpunit/includes/MessageTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/MessageTest.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/MessageTest.php	2017-04-06 11:54:27.000000000 -0700
@@ -1,4 +1,5 @@
 <?php
+use MediaWiki\MediaWikiServices;
 
 class MessageTest extends MediaWikiLangTestCase {
 
@@ -304,6 +305,22 @@
 		$this->assertEquals( 'example &amp;', $msg->escaped() );
 	}
 
+	public function testRawHtmlInMsg() {
+		global $wgParserConf;
+		$this->setMwGlobals( 'wgRawHtml', true );
+		// We have to reset the core hook registration.
+		// to register the html hook
+		MessageCache::destroyInstance();
+		$this->setMwGlobals( 'wgParser',
+			ObjectFactory::constructClassInstance( $wgParserConf['class'], [ $wgParserConf ] )
+		);
+
+		$msg = new RawMessage( '<html><script>alert("xss")</script></html>' );
+		$txt = '<span class="error">&lt;html&gt; tags cannot be' .
+			' used outside of normal pages.</span>';
+		$this->assertSame( $txt, $msg->parse() );
+	}
+
 	/**
 	 * @covers Message::params
 	 * @covers Message::toString
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/SanitizerTest.php mediawiki-1.27.2/tests/phpunit/includes/SanitizerTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/SanitizerTest.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/SanitizerTest.php	2017-04-06 11:54:27.000000000 -0700
@@ -314,6 +314,8 @@
 				'/* insecure input */',
 				'background-image: -moz-image-set("asdf.png" 1x, "asdf.png" 2x);'
 			],
+			[ '/* insecure input */', 'foo: attr( title, url );' ],
+			[ '/* insecure input */', 'foo: attr( title url );' ],
 		];
 	}
 
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/auth/AuthManagerTest.php mediawiki-1.27.2/tests/phpunit/includes/auth/AuthManagerTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/auth/AuthManagerTest.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/auth/AuthManagerTest.php	2017-04-06 11:54:28.000000000 -0700
@@ -3087,7 +3087,7 @@
 		$actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
 		$expected = [
 			$rememberReq,
-			$makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
+			$makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
 			$makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
 			$makeReq( "required2", AuthenticationRequest::PRIMARY_REQUIRED ),
 			$makeReq( "optional", AuthenticationRequest::OPTIONAL ),
@@ -3107,10 +3107,10 @@
 		$actual = $this->manager->getAuthenticationRequests( AuthManager::ACTION_LOGIN );
 		$expected = [
 			$rememberReq,
-			$makeReq( "primary-shared", AuthenticationRequest::REQUIRED ),
-			$makeReq( "required", AuthenticationRequest::REQUIRED ),
+			$makeReq( "primary-shared", AuthenticationRequest::PRIMARY_REQUIRED ),
+			$makeReq( "required", AuthenticationRequest::PRIMARY_REQUIRED ),
 			$makeReq( "optional", AuthenticationRequest::OPTIONAL ),
-			$makeReq( "foo", AuthenticationRequest::REQUIRED ),
+			$makeReq( "foo", AuthenticationRequest::PRIMARY_REQUIRED ),
 			$makeReq( "bar", AuthenticationRequest::REQUIRED ),
 			$makeReq( "baz", AuthenticationRequest::REQUIRED ),
 		];
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/libs/MemoizedCallableTest.php mediawiki-1.27.2/tests/phpunit/includes/libs/MemoizedCallableTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/libs/MemoizedCallableTest.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/libs/MemoizedCallableTest.php	2017-04-06 11:54:24.000000000 -0700
@@ -1,7 +1,7 @@
 <?php
 /**
  * A MemoizedCallable subclass that stores function return values
- * in an instance property rather than APC.
+ * in an instance property rather than APC or APCu.
  */
 class ArrayBackedMemoizedCallable extends MemoizedCallable {
 	private $cache = [];
@@ -44,7 +44,7 @@
 	 * Consecutive calls to the memoized callable with the same arguments
 	 * should result in just one invocation of the underlying callable.
 	 *
-	 * @requires function apc_store
+	 * @requires function apc_store/apcu_store
 	 */
 	public function testCallableMemoized() {
 		$observer = $this->getMock( 'stdClass', [ 'computeSomething' ] );
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/libs/composer/ComposerJsonTest.php mediawiki-1.27.2/tests/phpunit/includes/libs/composer/ComposerJsonTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/libs/composer/ComposerJsonTest.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/libs/composer/ComposerJsonTest.php	2017-04-06 11:54:28.000000000 -0700
@@ -11,22 +11,6 @@
 		$this->json2 = "$IP/tests/phpunit/data/composer/new-composer.json";
 	}
 
-	public static function provideGetHash() {
-		return [
-			[ 'json', 'cc6e7fc565b246cb30b0cac103a2b31e' ],
-			[ 'json2', '19921dd1fc457f1b00561da932432001' ],
-		];
-	}
-
-	/**
-	 * @dataProvider provideGetHash
-	 * @covers ComposerJson::getHash
-	 */
-	public function testIsHashUpToDate( $file, $expected ) {
-		$json = new ComposerJson( $this->$file );
-		$this->assertEquals( $expected, $json->getHash() );
-	}
-
 	/**
 	 * @covers ComposerJson::getRequiredDependencies
 	 */
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/libs/composer/ComposerLockTest.php mediawiki-1.27.2/tests/phpunit/includes/libs/composer/ComposerLockTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/libs/composer/ComposerLockTest.php	2016-08-22 13:52:59.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/libs/composer/ComposerLockTest.php	2017-04-06 11:54:28.000000000 -0700
@@ -11,14 +11,6 @@
 	}
 
 	/**
-	 * @covers ComposerLock::getHash
-	 */
-	public function testGetHash() {
-		$lock = new ComposerLock( $this->lock );
-		$this->assertEquals( 'a3bb80b0ac4c4a31e52574d48c032923', $lock->getHash() );
-	}
-
-	/**
 	 * @covers ComposerLock::getInstalledDependencies
 	 */
 	public function testGetInstalledDependencies() {
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/upload/UploadBaseTest.php mediawiki-1.27.2/tests/phpunit/includes/upload/UploadBaseTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/upload/UploadBaseTest.php	2016-08-22 13:53:00.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/upload/UploadBaseTest.php	2017-04-06 11:54:29.000000000 -0700
@@ -130,8 +130,8 @@
 	 */
 	public function testCheckSvgScriptCallback( $svg, $wellFormed, $filterMatch, $message ) {
 		list( $formed, $match ) = $this->upload->checkSvgString( $svg );
-		$this->assertSame( $wellFormed, $formed, $message );
-		$this->assertSame( $filterMatch, $match, $message );
+		$this->assertSame( $wellFormed, $formed, $message . " (well-formed)" );
+		$this->assertSame( $filterMatch, $match, $message . " (filter match)" );
 	}
 
 	public static function provideCheckSvgScriptCallback() {
@@ -254,11 +254,17 @@
 			],
 			[
 				'<?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="#stylesheet"?> <!DOCTYPE doc [ <!ATTLIST xsl:stylesheet id ID #REQUIRED>]> <svg xmlns="http://www.w3.org/2000/svg";> <xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";> <xsl:template match="/"> <iframe xmlns="http://www.w3.org/1999/xhtml"; src="javascript:alert(1)"></iframe> </xsl:template> </xsl:stylesheet> <circle fill="red" r="40"></circle> </svg>',
-				true,
+				false,
 				true,
 				'SVG with embedded stylesheet (http://html5sec.org/#125)'
 			],
 			[
+				'<?xml version="1.0"?> <?xml-stylesheet type="text/xml" href="#stylesheet"?> <svg xmlns="http://www.w3.org/2000/svg";> <xsl:stylesheet id="stylesheet" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";> <xsl:template match="/"> <iframe xmlns="http://www.w3.org/1999/xhtml"; src="javascript:alert(1)"></iframe> </xsl:template> </xsl:stylesheet> <circle fill="red" r="40"></circle> </svg>',
+				true,
+				true,
+				'SVG with embedded stylesheet no doctype'
+			],
+			[
 				'<svg xmlns="http://www.w3.org/2000/svg"; id="x"> <listener event="load" handler="#y" xmlns="http://www.w3.org/2001/xml-events"; observer="x"/> <handler id="y">alert(1)</handler> </svg>',
 				true,
 				true,
@@ -364,7 +370,7 @@
 			],
 			[
 				'<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!ENTITY lol "lol"> <!ENTITY lol2 "&#x3C;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3E;&#x61;&#x6C;&#x65;&#x72;&#x74;&#x28;&#x27;&#x58;&#x53;&#x53;&#x45;&#x44;&#x20;&#x3D;&#x3E;&#x20;&#x27;&#x2B;&#x64;&#x6F;&#x63;&#x75;&#x6D;&#x65;&#x6E;&#x74;&#x2E;&#x64;&#x6F;&#x6D;&#x61;&#x69;&#x6E;&#x29;&#x3B;&#x3C;&#x2F;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74;&#x3E;"> ]> <svg xmlns="http://www.w3.org/2000/svg"; width="68" height="68" viewBox="-34 -34 68 68" version="1.1"> <circle cx="0" cy="0" r="24" fill="#c8c8c8"/> <text x="0" y="0" fill="black">&lol2;</text> </svg>',
-				true,
+				false,
 				true,
 				'SVG with encoded script tag in internal entity (reported by Beyond Security)'
 			],
@@ -375,6 +381,16 @@
 				'SVG with external entity'
 			],
 			[
+				// The base64 = <script>alert(1)</script>. If for some reason
+				// entities actually do get loaded, this should trigger
+				// filterMatch to be true. So this test verifies that we
+				// are not loading external entities.
+				'<?xml version="1.0"?> <!DOCTYPE svg [ <!ENTITY foo SYSTEM "data:text/plain;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo="> ]> <svg xmlns="http://www.w3.org/2000/svg"; version="1.1"> <desc>&foo;</desc> <rect width="300" height="100" style="fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,2)" /> </svg>',
+				false,
+				false, /* False verifies entities aren't getting loaded */
+				'SVG with data: uri external entity'
+			],
+			[
 				"<svg xmlns=\"http://www.w3.org/2000/svg\"; xmlns:xlink=\"http://www.w3.org/1999/xlink\";> <g> <a xlink:href=\"javascript:alert('1&#10;https://google.com')\"> <rect width=\"300\" height=\"100\" style=\"fill:rgb(0,0,255);stroke-width:1;stroke:rgb(0,0,2)\" /> </a> </g> </svg>",
 				true,
 				true,
@@ -393,6 +409,104 @@
 				false,
 				'SVG with local urls, including filter: in style'
 			],
+			[
+				'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE x [<!ATTLIST image x:href CDATA "data:image/png,foo" onerror CDATA "alert(\'XSSED = \'+document.domain)" onload CDATA "alert(\'XSSED = \'+document.domain)"> ]> <svg xmlns:h="http://www.w3.org/1999/xhtml"; xmlns:x="http://www.w3.org/1999/xlink"; xmlns="http://www.w3.org/2000/svg";> <image /> </svg>',
+				false,
+				false,
+				'SVG with evil default attribute values'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg SYSTEM "data:application/xml-dtd;base64,PCFET0NUWVBFIHN2ZyBbPCFBVFRMSVNUIGltYWdlIHg6aHJlZiBDREFUQSAiZGF0YTppbWFnZS9wbmcsZm9vIiBvbmVycm9yIENEQVRBICJhbGVydCgnWFNTRUQgPSAnK2RvY3VtZW50LmRvbWFpbikiIG9ubG9hZCBDREFUQSAiYWxlcnQoJ1hTU0VEID0gJytkb2N1bWVudC5kb21haW4pIj4gXT4K"><svg xmlns:x="http://www.w3.org/1999/xlink"; xmlns="http://www.w3.org/2000/svg";> <image /> </svg>',
+				true,
+				true,
+				'SVG with an evil external dtd'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//FOO/bar" "http://example.com";><svg></svg>',
+				true,
+				true,
+				'SVG with random public doctype'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg SYSTEM \'http://example.com/evil.dtd\' ><svg></svg>',
+				true,
+				true,
+				'SVG with random SYSTEM doctype'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY % foo "bar" >] ><svg></svg>',
+				false,
+				false,
+				'SVG with parameter entity'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY foo "bar%a;" ] ><svg></svg>',
+				false,
+				false,
+				'SVG with entity referencing parameter entity'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY foo "bar0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"> ] ><svg></svg>',
+				false,
+				false,
+				'SVG with long entity'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY  foo \'"Hi", said bob\'> ] ><svg><g>&foo;</g></svg>',
+				true,
+				false,
+				'SVG with apostrophe quote entity'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg [<!ENTITY name "Bob"><!ENTITY  foo \'"Hi", said &name;.\'> ] ><svg><g>&foo;</g></svg>',
+				false,
+				false,
+				'SVG with recursive entity',
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"; [ <!ATTLIST svg xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink";> ]> <svg width="417pt" height="366pt"
+ viewBox="0.00 0.00 417.00 366.00" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";></svg>',
+				true, /* well-formed */
+				false, /* filter-hit */
+				'GraphViz-esque svg with #FIXED xlink ns (Should be allowed)'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"; [ <!ATTLIST svg xmlns:xlink CDATA #FIXED "http://www.w3.org/1999/xlink2";> ]> <svg width="417pt" height="366pt"
+ viewBox="0.00 0.00 417.00 366.00" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink";></svg>',
+				false,
+				false,
+				'GraphViz ATLIST exception should match exactly'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!-- Comment-here --> <!ENTITY foo "#ff6666">]><svg xmlns="http://www.w3.org/2000/svg";></svg>',
+				true,
+				false,
+				'DTD with comments (Should be allowed)'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!-- invalid--comment  --> <!ENTITY foo "#ff6666">]><svg xmlns="http://www.w3.org/2000/svg";></svg>',
+				false,
+				false,
+				'DTD with invalid comment'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!-- invalid ---> <!ENTITY foo "#ff6666">]><svg xmlns="http://www.w3.org/2000/svg";></svg>',
+				false,
+				false,
+				'DTD with invalid comment 2'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!ENTITY bar "&foo;"> <!ENTITY foo "#ff6666">]><svg xmlns="http://www.w3.org/2000/svg";></svg>',
+				true,
+				false,
+				'DTD with aliased entities (Should be allowed)'
+			],
+			[
+				'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"; [ <!ENTITY bar \'&foo;\'> <!ENTITY foo \'#ff6666\'>]><svg xmlns="http://www.w3.org/2000/svg";></svg>',
+				true,
+				false,
+				'DTD with aliased entities apos (Should be allowed)'
+			]
 		];
 		// @codingStandardsIgnoreEnd
 	}
@@ -421,7 +535,10 @@
 			$svg,
 			[ $this, 'checkSvgScriptCallback' ],
 			false,
-			[ 'processing_instruction_handler' => 'UploadBase::checkSvgPICallback' ]
+			[
+				'processing_instruction_handler' => 'UploadBase::checkSvgPICallback',
+				'external_dtd_handler' => 'UploadBase::checkSvgExternalDTD'
+			]
 		);
 		return [ $check->wellFormed, $check->filterMatch ];
 	}
diff -Nru mediawiki-1.27.1/tests/phpunit/includes/user/UserTest.php mediawiki-1.27.2/tests/phpunit/includes/user/UserTest.php
--- mediawiki-1.27.1/tests/phpunit/includes/user/UserTest.php	2016-08-22 13:53:01.000000000 -0700
+++ mediawiki-1.27.2/tests/phpunit/includes/user/UserTest.php	2017-04-06 11:54:28.000000000 -0700
@@ -110,7 +110,7 @@
 		// Add a hook manipluating the rights
 		$this->mergeMwGlobalArrayValue( 'wgHooks', [ 'UserGetRights' => [ function ( $user, &$rights ) {
 			$rights[] = 'nukeworld';
-			$rights = array_diff( $rights, array( 'writetest' ) );
+			$rights = array_diff( $rights, [ 'writetest' ] );
 		} ] ] );
 
 		$userWrapper->mRights = null;

--- End Message ---
--- Begin Message ---
Kunal Mehta:
> Package: release.debian.org
> Severity: normal
> User: release.debian.org@packages.debian.org
> Usertags: unblock
> 
> Please unblock package mediawiki
> 
> 1.27.2 is a security release of MediaWiki fixing the
> following CVEs:
> CVE-2017-0363, CVE-2017-0364, CVE-2017-0365, CVE-2017-0361,
> CVE-2017-0362, CVE-2017-0368, CVE-2017-0366, CVE-2017-0370,
> CVE-2017-0369, CVE-2017-0367, CVE-2017-0372
> 
> unblock mediawiki/1:1.27.2-1
> 
> [...]

Unblocked, thanks.

~Niels

--- End Message ---

Reply to: