--- Begin Message ---
Package: release.debian.org
Severity: normal
User: release.debian....@packages.debian.org
Usertags: unblock
Please unblock package mediawiki.
The only change is an addition of an upstream patch for this release branch
which fixes a number of security issues.
unblock mediawiki/1:1.19.20+dfsg-2.3
Thanks,
Thijs
diff -Nru mediawiki-1.19.20+dfsg/debian/changelog mediawiki-1.19.20+dfsg/debian/changelog
--- mediawiki-1.19.20+dfsg/debian/changelog 2014-12-21 12:11:10.000000000 +0000
+++ mediawiki-1.19.20+dfsg/debian/changelog 2015-04-06 16:55:57.000000000 +0000
@@ -1,3 +1,21 @@
+mediawiki (1:1.19.20+dfsg-2.3) unstable; urgency=high
+
+ * Non-maintainer upload.
+ * Add patch fixing several security issues:
+ - (bug T85848, bug T71210) SECURITY: Don't parse XMP blocks that
+ contain XML entities, to prevent various DoS attacks.
+ - (bug T88310) SECURITY: Always expand xml entities when checking
+ SVG's.
+ - (bug T73394) SECURITY: Escape > in Html::expandAttributes to
+ prevent XSS.
+ - (bug T85855) SECURITY: Don't execute another user's CSS or JS
+ on preview.
+ - (bug T85349, bug T85850, bug T86711) SECURITY: Multiple issues
+ fixed in SVG filtering to prevent XSS and protect viewer's
+ privacy.
+
+ -- Thijs Kinkhorst <th...@debian.org> Mon, 06 Apr 2015 16:53:54 +0000
+
mediawiki (1:1.19.20+dfsg-2.2) unstable; urgency=medium
* Non-maintainer upload.
diff -Nru mediawiki-1.19.20+dfsg/debian/patches/security_1.19.24.patch mediawiki-1.19.20+dfsg/debian/patches/security_1.19.24.patch
--- mediawiki-1.19.20+dfsg/debian/patches/security_1.19.24.patch 1970-01-01 00:00:00.000000000 +0000
+++ mediawiki-1.19.20+dfsg/debian/patches/security_1.19.24.patch 2015-04-06 17:03:41.000000000 +0000
@@ -0,0 +1,636 @@
+From: Mediawiki
+Subject: Fix security issues as fixed in upstream security fix release 1.19.24:
+(bug T85848, bug T71210) SECURITY: Don't parse XMP blocks that contain XML entities, to prevent various DoS attacks.
+(bug T88310) SECURITY: Always expand xml entities when checking SVG's.
+(bug T73394) SECURITY: Escape > in Html::expandAttributes to prevent XSS.
+(bug T85855) SECURITY: Don't execute another user's CSS or JS on preview.
+(bug T85349, bug T85850, bug T86711) SECURITY: Multiple issues fixed in SVG filtering to prevent XSS and protect viewer's privacy.
+Origin: upstream, https://lists.wikimedia.org/pipermail/mediawiki-announce/2015-March/000175.html
+
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/EditPage.php mediawiki-1.19.24/includes/EditPage.php
+--- mediawiki-1.19.23/includes/EditPage.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/EditPage.php 2015-03-31 13:23:38.000000000 +0000
+@@ -1988,14 +1988,19 @@
+ if ( $this->isWrongCaseCssJsPage ) {
+ $wgOut->wrapWikiMsg( "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>", array( 'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ) );
+ }
++ if ( $this->getTitle()->isSubpageOf( $wgUser->getUserPage() ) ) {
+ if ( $this->formtype !== 'preview' ) {
+- if ( $this->isCssSubpage )
++ if ( $this->isCssSubpage ) {
+ $wgOut->wrapWikiMsg( "<div id='mw-usercssyoucanpreview'>\n$1\n</div>", array( 'usercssyoucanpreview' ) );
+- if ( $this->isJsSubpage )
++ }
++
++ if ( $this->isJsSubpage ) {
+ $wgOut->wrapWikiMsg( "<div id='mw-userjsyoucanpreview'>\n$1\n</div>", array( 'userjsyoucanpreview' ) );
+ }
+ }
+ }
++ }
++ }
+
+ if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
+ # Is the title semi-protected?
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/Html.php mediawiki-1.19.24/includes/Html.php
+--- mediawiki-1.19.23/includes/Html.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/Html.php 2015-03-31 13:23:38.000000000 +0000
+@@ -525,17 +525,20 @@
+ } else {
+ # Apparently we need to entity-encode \n, \r, \t, although the
+ # spec doesn't mention that. Since we're doing strtr() anyway,
+- # and we don't need <> escaped here, we may as well not call
+- # htmlspecialchars().
++ # we may as well not call htmlspecialchars().
+ # @todo FIXME: Verify that we actually need to
+ # escape \n\r\t here, and explain why, exactly.
+ #
+ # We could call Sanitizer::encodeAttribute() for this, but we
+ # don't because we're stubborn and like our marginal savings on
+ # byte size from not having to encode unnecessary quotes.
++ # The only difference between this transform and the one by
++ # Sanitizer::encodeAttribute() is '<' is only encoded here if
++ # $wgWellFormedXml is set, and ' is not encoded.
+ $map = array(
+ '&' => '&',
+ '"' => '"',
++ '>' => '>',
+ "\n" => ' ',
+ "\r" => ' ',
+ "\t" => '	'
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/media/BitmapMetadataHandler.php mediawiki-1.19.24/includes/media/BitmapMetadataHandler.php
+--- mediawiki-1.19.23/includes/media/BitmapMetadataHandler.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/media/BitmapMetadataHandler.php 2015-03-31 13:23:38.000000000 +0000
+@@ -126,7 +126,7 @@
+ * @throws MWException on invalid file.
+ */
+ static function Jpeg ( $filename ) {
+- $showXMP = function_exists( 'xml_parser_create_ns' );
++ $showXMP = XMPReader::isSupported();
+ $meta = new self();
+
+ $seg = JpegMetadataExtractor::segmentSplitter( $filename );
+@@ -168,7 +168,7 @@
+ * @return Array Array for storage in img_metadata.
+ */
+ static public function PNG ( $filename ) {
+- $showXMP = function_exists( 'xml_parser_create_ns' );
++ $showXMP = XMPReader::isSupported();
+
+ $meta = new self();
+ $array = PNGMetadataExtractor::getMetadata( $filename );
+@@ -205,7 +205,7 @@
+ $meta->addMetadata( array( 'GIFFileComment' => $baseArray['comment'] ), 'native' );
+ }
+
+- if ( $baseArray['xmp'] !== '' && function_exists( 'xml_parser_create_ns' ) ) {
++ if ( $baseArray['xmp'] !== '' && XMPReader::isSupported() ) {
+ $xmp = new XMPReader();
+ $xmp->parse( $baseArray['xmp'] );
+ $xmpRes = $xmp->getResults();
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/media/JpegMetadataExtractor.php mediawiki-1.19.24/includes/media/JpegMetadataExtractor.php
+--- mediawiki-1.19.23/includes/media/JpegMetadataExtractor.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/media/JpegMetadataExtractor.php 2015-03-31 13:23:38.000000000 +0000
+@@ -24,7 +24,7 @@
+ * @throws MWException if given invalid file.
+ */
+ static function segmentSplitter ( $filename ) {
+- $showXMP = function_exists( 'xml_parser_create_ns' );
++ $showXMP = XMPReader::isSupported();
+
+ $segmentCount = 0;
+
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/media/XMP.php mediawiki-1.19.24/includes/media/XMP.php
+--- mediawiki-1.19.23/includes/media/XMP.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/media/XMP.php 2015-03-31 13:23:38.000000000 +0000
+@@ -40,6 +40,12 @@
+
+ protected $items;
+
++ /** @var int Flag determining if the XMP is safe to parse **/
++ private $parsable = 0;
++
++ /** @var string Buffer of XML to parse **/
++ private $xmlParsableBuffer = '';
++
+ /**
+ * These are various mode constants.
+ * they are used to figure out what to do
+@@ -69,6 +75,12 @@
+ const NS_XML = 'http://www.w3.org/XML/1998/namespace';
+
+
++ // States used while determining if XML is safe to parse
++ const PARSABLE_UNKNOWN = 0;
++ const PARSABLE_OK = 1;
++ const PARSABLE_BUFFERING = 2;
++ const PARSABLE_NO = 3;
++
+ /**
+ * Constructor.
+ *
+@@ -106,6 +118,9 @@
+ array( $this, 'endElement' ) );
+
+ xml_set_character_data_handler( $this->xmlParser, array( $this, 'char' ) );
++
++ $this->parsable = self::PARSABLE_UNKNOWN;
++ $this->xmlParsableBuffer = '';
+ }
+
+ /** Destroy the xml parser
+@@ -117,6 +132,13 @@
+ xml_parser_free( $this->xmlParser );
+ }
+
++ /**
++ * Check if this instance supports using this class
++ */
++ public static function isSupported() {
++ return function_exists( 'xml_parser_create_ns' ) && class_exists( 'XMLReader' );
++ }
++
+ /** Get the result array. Do some post-processing before returning
+ * the array, and transform any metadata that is special-cased.
+ *
+@@ -263,6 +285,27 @@
+ wfRestoreWarnings();
+ }
+
++ // Ensure the XMP block does not have an xml doctype declaration, which
++ // could declare entities unsafe to parse with xml_parse (T85848/T71210).
++ if ( $this->parsable !== self::PARSABLE_OK ) {
++ if ( $this->parsable === self::PARSABLE_NO ) {
++ throw new MWException( 'Unsafe doctype declaration in XML.' );
++ }
++
++ $content = $this->xmlParsableBuffer . $content;
++ if ( !$this->checkParseSafety( $content ) ) {
++ if ( !$allOfIt && $this->parsable !== self::PARSABLE_NO ) {
++ // parse wasn't Unsuccessful yet, so return true
++ // in this case.
++ return true;
++ }
++ $msg = ( $this->parsable === self::PARSABLE_NO ) ?
++ 'Unsafe doctype declaration in XML.' :
++ 'No root element found in XML.';
++ throw new MWException( $msg );
++ }
++ }
++
+ $ok = xml_parse( $this->xmlParser, $content, $allOfIt );
+ if ( !$ok ) {
+ $error = xml_error_string( xml_get_error_code( $this->xmlParser ) );
+@@ -385,6 +428,59 @@
+
+ }
+
++ /**
++ * Check if a block of XML is safe to pass to xml_parse, i.e. doesn't
++ * contain a doctype declaration which could contain a dos attack if we
++ * parse it and expand internal entities (T85848).
++ *
++ * @param string $content xml string to check for parse safety
++ * @return bool true if the xml is safe to parse, false otherwise
++ */
++ private function checkParseSafety( $content ) {
++ $reader = new XMLReader();
++ $result = null;
++
++ // For XMLReader to parse incomplete/invalid XML, it has to be open()'ed
++ // instead of using XML().
++ $reader->open(
++ 'data://text/plain,' . urlencode( $content ),
++ null,
++ LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET
++ );
++
++ $oldDisable = libxml_disable_entity_loader( true );
++ $reader->setParserProperty( XMLReader::SUBST_ENTITIES, false );
++
++ // Even with LIBXML_NOWARNING set, XMLReader::read gives a warning
++ // when parsing truncated XML, which causes unit tests to fail.
++ wfSuppressWarnings();
++ while ( $reader->read() ) {
++ if ( $reader->nodeType === XMLReader::ELEMENT ) {
++ // Reached the first element without hitting a doctype declaration
++ $this->parsable = self::PARSABLE_OK;
++ $result = true;
++ break;
++ }
++ if ( $reader->nodeType === XMLReader::DOC_TYPE ) {
++ $this->parsable = self::PARSABLE_NO;
++ $result = false;
++ break;
++ }
++ }
++ wfRestoreWarnings();
++ libxml_disable_entity_loader( $oldDisable );
++
++ if ( !is_null( $result ) ) {
++ return $result;
++ }
++
++ // Reached the end of the parsable xml without finding an element
++ // or doctype. Buffer and try again.
++ $this->parsable = self::PARSABLE_BUFFERING;
++ $this->xmlParsableBuffer = $content;
++ return false;
++ }
++
+ /** When we hit a closing element in MODE_IGNORE
+ * Check to see if this is the element we started to ignore,
+ * in which case we get out of MODE_IGNORE
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/OutputPage.php mediawiki-1.19.24/includes/OutputPage.php
+--- mediawiki-1.19.23/includes/OutputPage.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/OutputPage.php 2015-03-31 13:23:38.000000000 +0000
+@@ -2975,6 +2975,10 @@
+ if ( !$this->getTitle()->isJsSubpage() && !$this->getTitle()->isCssSubpage() ) {
+ return false;
+ }
++ if ( !$this->getTitle()->isSubpageOf( $this->getUser()->getUserPage() ) ) {
++ // Don't execute another user's CSS or JS on preview (T85855)
++ return false;
++ }
+
+ return !count( $this->getTitle()->getUserPermissionsErrors( 'edit', $this->getUser() ) );
+ }
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/upload/UploadBase.php mediawiki-1.19.24/includes/upload/UploadBase.php
+--- mediawiki-1.19.23/includes/upload/UploadBase.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/upload/UploadBase.php 2015-03-31 13:23:38.000000000 +0000
+@@ -1210,23 +1210,22 @@
+ }
+ }
+
+- # href with embeded svg as target
+- if( $stripped == 'href' && preg_match( '!data:[^,]*image/svg[^,]*,!sim', $value ) ) {
+- wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
++ # only allow data: targets that should be safe. This prevents vectors like,
++ # image/svg, text/xml, application/xml, and text/html, which can contain scripts
++ if ( $stripped == 'href' && strncasecmp( 'data:', $value, 5 ) === 0 ) {
++ // rfc2397 parameters. This is only slightly slower than (;[\w;]+)*.
++ $parameters = '(?>;[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+=(?>[a-zA-Z0-9\!#$&\'*+.^_`{|}~-]+|"(?>[\0-\x0c\x0e-\x21\x23-\x5b\x5d-\x7f]+|\\\\[\0-\x7f])*"))*(?:;base64)?';
++ if ( !preg_match( "!^data:\s*image/(gif|jpeg|jpg|png)$parameters,!i", $value ) ) {
++ wfDebug( __METHOD__ . ": Found href to unwhitelisted data: uri "
++ . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+ return true;
+ }
+-
+- # href with embeded (text/xml) svg as target
+- if( $stripped == 'href' && preg_match( '!data:[^,]*text/xml[^,]*,!sim', $value ) ) {
+- wfDebug( __METHOD__ . ": Found href to embedded svg \"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+- return true;
+ }
+
+- # Change href with animate from (http://html5sec.org/#137). This doesn't seem
+- # possible without embedding the svg, but filter here in case.
+- if ( $stripped == 'from'
++ # Change href with animate from (http://html5sec.org/#137).
++ if ( $stripped === 'attributename'
+ && $strippedElement === 'animate'
+- && !preg_match( '!^https?://!im', $value )
++ && $this->stripXmlNamespace( $value ) == 'href'
+ ) {
+ wfDebug( __METHOD__ . ": Found animate that might be changing href using from "
+ . "\"<$strippedElement '$attrib'='$value'...\" in uploaded file.\n" );
+@@ -1300,7 +1299,7 @@
+ private static function checkCssFragment( $value ) {
+
+ # Forbid external stylesheets, for both reliability and to protect viewer's privacy
+- if ( strpos( $value, '@import' ) !== false ) {
++ if ( stripos( $value, '@import' ) !== false ) {
+ return true;
+ }
+
+diff -Nruw -x '*~' -x '.js*' -x '.git*' -x '*.xcf' -x '#*#' -x '.#*' -x '.rubocop*' -x .travis.yml -x package.json -x messages -x Gemfile -x '*.png' -x '*.jpg' -x '*.xcf' -x '*.gif' -x '*.svg' -x '*.tiff' -x '*.zip' -x '*.xmp' mediawiki-1.19.23/includes/XmlTypeCheck.php mediawiki-1.19.24/includes/XmlTypeCheck.php
+--- mediawiki-1.19.23/includes/XmlTypeCheck.php 2015-03-31 13:24:03.000000000 +0000
++++ mediawiki-1.19.24/includes/XmlTypeCheck.php 2015-03-31 13:23:38.000000000 +0000
+@@ -1,11 +1,36 @@
+ <?php
++/**
++ * XML syntax and type checker.
++ *
++ * Since 1.24.2, it uses XMLReader instead of xml_parse, which gives us
++ * more control over the expansion of XML entities. When passed to the
++ * callback, entities will be fully expanded, but may report the XML is
++ * invalid if expanding the entities are likely to cause a DoS.
++ *
++ * 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
++ */
+
+ class XmlTypeCheck {
+ /**
+ * Will be set to true or false to indicate whether the file is
+ * well-formed XML. Note that this doesn't check schema validity.
+ */
+- public $wellFormed = false;
++ public $wellFormed = null;
+
+ /**
+ * Will be set to true if the optional element filter returned
+@@ -44,8 +69,8 @@
+ );
+
+ /**
+- * @param $file string filename
+- * @param $filterCallback callable (optional)
++ * @param string $input a filename
++ * @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.
+@@ -53,10 +78,10 @@
+ * @param array $options list of additional parsing options:
+ * processing_instruction_handler: Callback for xml_set_processing_instruction_handler
+ */
+- function __construct( $file, $filterCallback=null, $options=array() ) {
++ function __construct( $input, $filterCallback = null, $options = array() ) {
+ $this->filterCallback = $filterCallback;
+ $this->parserOptions = array_merge( $this->parserOptions, $options );
+- $this->run( $file );
++ $this->validateFromInput( $input, true );
+ }
+
+ /**
+@@ -68,119 +93,211 @@
+ return $this->rootElement;
+ }
+
++
+ /**
+- * @param $fname
++ * @param string $fname the filename
+ */
+- private function run( $fname ) {
+- $parser = xml_parser_create_ns( 'UTF-8' );
+-
+- // case folding violates XML standard, turn it off
+- xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
++ private function validateFromInput( $xml, $isFile ) {
++ $reader = new XMLReader();
++ if ( $isFile ) {
++ $s = $reader->open( $xml, null, LIBXML_NOERROR | LIBXML_NOWARNING );
++ } else {
++ $s = $reader->XML( $xml, null, LIBXML_NOERROR | LIBXML_NOWARNING );
++ }
++ if ( $s !== true ) {
++ // Couldn't open the XML
++ $this->wellFormed = false;
++ } else {
++ $oldDisable = libxml_disable_entity_loader( true );
++ $reader->setParserProperty( XMLReader::SUBST_ENTITIES, true );
++ try {
++ $this->validate( $reader );
++ } catch ( Exception $e ) {
++ // Calling this malformed, because we didn't parse the whole
++ // thing. Maybe just an external entity refernce.
++ $this->wellFormed = false;
++ $reader->close();
++ libxml_disable_entity_loader( $oldDisable );
++ throw $e;
++ }
++ $reader->close();
++ libxml_disable_entity_loader( $oldDisable );
++ }
++ }
+
+- xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
++ private function readNext( XMLReader $reader ) {
++ set_error_handler( array( $this, 'XmlErrorHandler' ) );
++ $ret = $reader->read();
++ restore_error_handler();
++ return $ret;
++ }
+
+- if ( $this->parserOptions['processing_instruction_handler'] ) {
+- xml_set_processing_instruction_handler(
+- $parser,
+- array( $this, 'processingInstructionHandler' )
+- );
++ public function XmlErrorHandler( $errno, $errstr ) {
++ $this->wellFormed = false;
+ }
+
+- if ( file_exists( $fname ) ) {
+- $file = fopen( $fname, "rb" );
+- if ( $file ) {
++ private function validate( $reader ) {
++
++ // First, move through anything that isn't an element, and
++ // handle any processing instructions with the callback
+ do {
+- $chunk = fread( $file, 32768 );
+- $ret = xml_parse( $parser, $chunk, feof( $file ) );
+- if( $ret == 0 ) {
+- // XML isn't well-formed!
+- fclose( $file );
+- xml_parser_free( $parser );
++ if( !$this->readNext( $reader ) ) {
++ // Hit the end of the document before any elements
++ $this->wellFormed = false;
+ return;
+ }
+- } while( !feof( $file ) );
++ if ( $reader->nodeType === XMLReader::PI ) {
++ $this->processingInstructionHandler( $reader->name, $reader->value );
++ }
++ } while ( $reader->nodeType != XMLReader::ELEMENT );
+
+- fclose( $file );
++ // Process the rest of the document
++ do {
++ switch ( $reader->nodeType ) {
++ case XMLReader::ELEMENT:
++ $name = $this->expandNS(
++ $reader->name,
++ $reader->namespaceURI
++ );
++ if ( $this->rootElement === '' ) {
++ $this->rootElement = $name;
+ }
++ $empty = $reader->isEmptyElement;
++ $attrs = $this->getAttributesArray( $reader );
++ $this->elementOpen( $name, $attrs );
++ if ( $empty ) {
++ $this->elementClose();
++ }
++ break;
++
++ case XMLReader::END_ELEMENT:
++ $this->elementClose();
++ break;
++
++ case XMLReader::WHITESPACE:
++ case XMLReader::SIGNIFICANT_WHITESPACE:
++ case XMLReader::CDATA:
++ case XMLReader::TEXT:
++ $this->elementData( $reader->value );
++ break;
++
++ case XMLReader::ENTITY_REF:
++ // Unexpanded entity (maybe external?),
++ // don't send to the filter (xml_parse didn't)
++ break;
++
++ case XMLReader::COMMENT:
++ // Don't send to the filter (xml_parse didn't)
++ break;
++
++ case XMLReader::PI:
++ // Processing instructions can happen after the header too
++ $this->processingInstructionHandler(
++ $reader->name,
++ $reader->value
++ );
++ break;
++ default:
++ // One of DOC, DOC_TYPE, ENTITY, END_ENTITY,
++ // NOTATION, or XML_DECLARATION
++ // xml_parse didn't send these to the filter, so we won't.
+ }
+
++ } while ( $this->readNext( $reader ) );
++
++ if ( $this->stackDepth !== 0 ) {
++ $this->wellFormed = false;
++ } elseif ( $this->wellFormed === null ) {
+ $this->wellFormed = true;
++ }
+
+- xml_parser_free( $parser );
+ }
+
+ /**
+- * @param $parser
+- * @param $name
+- * @param $attribs
++ * Get all of the attributes for an XMLReader's current node
++ * @param $r XMLReader
++ * @return array of attributes
+ */
+- private function rootElementOpen( $parser, $name, $attribs ) {
+- $this->rootElement = $name;
++ private function getAttributesArray( XMLReader $r ) {
++ $attrs = array();
++ while ( $r->moveToNextAttribute() ) {
++ if ( $r->namespaceURI === 'http://www.w3.org/2000/xmlns/' ) {
++ // XMLReader treats xmlns attributes as normal
++ // attributes, while xml_parse doesn't
++ continue;
++ }
++ $name = $this->expandNS( $r->name, $r->namespaceURI );
++ $attrs[$name] = $r->value;
++ }
++ return $attrs;
++ }
+
+- if( is_callable( $this->filterCallback ) ) {
+- xml_set_element_handler(
+- $parser,
+- array( $this, 'elementOpen' ),
+- array( $this, 'elementClose' )
+- );
+- xml_set_character_data_handler( $parser, array( $this, 'elementData' ) );
+- $this->elementOpen( $parser, $name, $attribs );
+- } else {
+- // We only need the first open element
+- xml_set_element_handler( $parser, false, false );
++ /**
++ * @param $name element or attribute name, maybe with a full or short prefix
++ * @param $namespaceURI the namespaceURI
++ * @return string the name prefixed with namespaceURI
++ */
++ private function expandNS( $name, $namespaceURI ) {
++ if ( $namespaceURI ) {
++ $parts = explode( ':', $name );
++ $localname = array_pop( $parts );
++ return "$namespaceURI:$localname";
+ }
++ return $name;
+ }
+
+ /**
+- * @param $parser
+ * @param $name
+ * @param $attribs
+ */
+- private function elementOpen( $parser, $name, $attribs ) {
++ private function elementOpen( $name, $attribs ) {
+ $this->elementDataContext[] = array( $name, $attribs );
+ $this->elementData[] = '';
+ $this->stackDepth++;
+ }
+
+ /**
+- * @param $parser
+- * @param $name
+ */
+- private function elementClose( $parser, $name ) {
++ private function elementClose() {
+ list( $name, $attribs ) = array_pop( $this->elementDataContext );
+ $data = array_pop( $this->elementData );
+ $this->stackDepth--;
+
+- if ( call_user_func(
++ if ( is_callable( $this->filterCallback )
++ && call_user_func(
+ $this->filterCallback,
+ $name,
+ $attribs,
+ $data
+- ) ) {
+- // Filter hit!
++ )
++ ) {
++ // Filter hit
+ $this->filterMatch = true;
+ }
+ }
+
+ /**
+- * @param $parser
+ * @param $data
+ */
+- private function elementData( $parser, $data ) {
+- // xml_set_character_data_handler breaks the data on & characters, so
+- // we collect any data here, and we'll run the callback in elementClose
++ private function elementData( $data ) {
++ // Collect any data here, and we'll run the callback in elementClose
+ $this->elementData[ $this->stackDepth - 1 ] .= trim( $data );
+ }
+
+ /**
+- * @param $parser
+ * @param $target
+ * @param $data
+ */
+- private function processingInstructionHandler( $parser, $target, $data ) {
+- if ( call_user_func( $this->parserOptions['processing_instruction_handler'], $target, $data ) ) {
++ private function processingInstructionHandler( $target, $data ) {
++ if ( $this->parserOptions['processing_instruction_handler'] ) {
++ if ( call_user_func(
++ $this->parserOptions['processing_instruction_handler'],
++ $target,
++ $data
++ ) ) {
+ // Filter hit!
+ $this->filterMatch = true;
+ }
+ }
+ }
++}
diff -Nru mediawiki-1.19.20+dfsg/debian/patches/series mediawiki-1.19.20+dfsg/debian/patches/series
--- mediawiki-1.19.20+dfsg/debian/patches/series 2014-12-21 12:10:23.000000000 +0000
+++ mediawiki-1.19.20+dfsg/debian/patches/series 2015-04-06 17:02:16.000000000 +0000
@@ -12,3 +12,4 @@
CVE-2014-9277_1.patch
CVE-2014-9277_2.patch
T76686.patch
+security_1.19.24.patch
--- End Message ---