Package: release.debian.org
Severity: normal
User: release.debian....@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 <lego...@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 <lego...@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;

Reply via email to