Chad has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/346853 )
Change subject: SECURITY: Whitelist DTD declaration in SVG ...................................................................... SECURITY: Whitelist DTD declaration in SVG Only allow ENTITY declarations inside the doctype internal subset. Do not allow parameter entities, recursive entity references are entity values longer than 255 bytes, or external entity references. Filter external doctype subset to only allow the standard svg doctypes. This prevents someone bypassing filter by using default attribute values in internal dtd subset. No browser loads the external dtd subset that I could find, but whitelist just to be safe anyways. Issue reported by Cassiogomes11. Bug: T151735 Change-Id: I7cb4690f759ad97e70e06e560978b6207d84c446 --- M RELEASE-NOTES-1.27 M includes/libs/XmlTypeCheck.php M includes/upload/UploadBase.php M languages/i18n/en.json M languages/i18n/qqq.json M tests/phpunit/includes/upload/UploadBaseTest.php 6 files changed, 321 insertions(+), 8 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core refs/changes/53/346853/1 diff --git a/RELEASE-NOTES-1.27 b/RELEASE-NOTES-1.27 index 6ca333a..94efaa1 100644 --- a/RELEASE-NOTES-1.27 +++ b/RELEASE-NOTES-1.27 @@ -32,6 +32,8 @@ * (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. == MediaWiki 1.27.1 == diff --git a/includes/libs/XmlTypeCheck.php b/includes/libs/XmlTypeCheck.php index f057140..7659dfd 100644 --- a/includes/libs/XmlTypeCheck.php +++ b/includes/libs/XmlTypeCheck.php @@ -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 & + * 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};|(?:[^"%&]|&|"){0,255})"' . + '|\'(?:&[^"%&;]{1,64};|(?:[^\'%&]|&|'){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 --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php index ba5171f..db9e699 100644 --- a/includes/upload/UploadBase.php +++ b/includes/upload/UploadBase.php @@ -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) @@ -1307,6 +1310,34 @@ } /** + * 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 * @param array $attribs diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 91e0cda..10e504a 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -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.", diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index 70497ed..a43c8ff 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -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.", diff --git a/tests/phpunit/includes/upload/UploadBaseTest.php b/tests/phpunit/includes/upload/UploadBaseTest.php index 287af29..2c1ef14 100644 --- a/tests/phpunit/includes/upload/UploadBaseTest.php +++ b/tests/phpunit/includes/upload/UploadBaseTest.php @@ -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,9 +254,15 @@ ], [ '<?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>', @@ -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 "<script>alert('XSSED => '+document.domain);</script>"> ]> <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)' ], @@ -373,6 +379,16 @@ false, false, '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 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>", @@ -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 ]; } -- To view, visit https://gerrit.wikimedia.org/r/346853 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I7cb4690f759ad97e70e06e560978b6207d84c446 Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/core Gerrit-Branch: REL1_27 Gerrit-Owner: Chad <ch...@wikimedia.org> Gerrit-Reviewer: Brian Wolff <bawolff...@gmail.com> Gerrit-Reviewer: Chad <ch...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits