diff -u mediawiki-1.11.2/debian/patches/series mediawiki-1.11.2/debian/patches/series --- mediawiki-1.11.2/debian/patches/series +++ mediawiki-1.11.2/debian/patches/series @@ -1,3 +1,4 @@ +CVE-2008-5249_CVE-2008-5250_CVE-2008-5252.patch CVE-2008-4408.patch texvc_location.patch mimetypes.patch diff -u mediawiki-1.11.2/debian/changelog mediawiki-1.11.2/debian/changelog --- mediawiki-1.11.2/debian/changelog +++ mediawiki-1.11.2/debian/changelog @@ -1,3 +1,38 @@ +mediawiki (1:1.11.2-2ubuntu0.2) hardy-security; urgency=low + + * SECURITY UPDATE: + - CVE-2008-5249 + - CVE-2008-5250 + - CVE-2008-5252 + - other security-related problems (see full patch description). + - patch based on Debian patch + - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508870 + - http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=508869 + - http://lists.wikimedia.org/pipermail/mediawiki-announce/2008-December/000080.html + * debian/patches/CVE-2008-5249_CVE-2008-5250_CVE-2008-5252.patch: + - Fixed output escaping for reporting of non-MediaWiki exceptions. + Potential XSS if an extension throws one of these with user input. + - Avoid fatal error in profileinfo.php when not configured. + - Fixed CSRF vulnerability in Special:Import. Fixed input validation in + transwiki import feature. + - Add a .htaccess to deleted images directory for additional protection + against exposure of deleted files with known SHA-1 hashes on default + installations. + - Fixed XSS vulnerability for Internet Explorer clients, via file uploads + which are interpreted by IE as HTML. + - Fixed XSS vulnerability for clients with SVG scripting, on wikis where SVG + uploads are enabled. Firefox 1.5+ is affected. + - Avoid streaming uploaded files to the user via index.php. This allows + security-conscious users to serve uploaded files via a different domain, + and thus client-side scripts executed from that domain cannot access the + login cookies. Affects Special:Undelete, img_auth.php and thumb.php. + - When streaming files via index.php, use the MIME type detected from the + file extension, not from the data. This reduces the XSS attack surface. + - Blacklist redirects via Special:Filepath. Such redirects exacerbate any + XSS vulnerabilities involving uploads of files containing scripts. + + -- Andreas Wenning Sun, 01 Feb 2009 08:50:19 +0100 + mediawiki (1:1.11.2-2ubuntu0.1) hardy-security; urgency=low * SECURITY UPDATE: only in patch2: unchanged: --- mediawiki-1.11.2.orig/debian/patches/CVE-2008-5249_CVE-2008-5250_CVE-2008-5252.patch +++ mediawiki-1.11.2/debian/patches/CVE-2008-5249_CVE-2008-5250_CVE-2008-5252.patch @@ -0,0 +1,1440 @@ +Backported fix for CVE-2008-5249 CVE-2008-5250 CVE-2008-5252 +Index: mediawiki-1.11.2/img_auth.php +=================================================================== +--- mediawiki-1.11.2.orig/img_auth.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/img_auth.php 2009-02-01 10:35:51.000000000 +0100 +@@ -17,6 +17,12 @@ + wfProfileIn( 'img_auth.php' ); + require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' ); + ++$perms = User::getGroupPermissions( array( '*' ) ); ++if ( in_array( 'read', $perms, true ) ) { ++ wfDebugLog( 'img_auth', 'Public wiki' ); ++ wfPublicError(); ++} ++ + // Extract path and image information + if( !isset( $_SERVER['PATH_INFO'] ) ) { + wfDebugLog( 'img_auth', 'Missing PATH_INFO' ); +@@ -86,4 +92,25 @@ + END; + wfLogProfilingData(); + exit(); +-} +\ No newline at end of file ++} ++ ++/** ++ * Show a 403 error for use when the wiki is public ++ */ ++function wfPublicError() { ++ header( 'HTTP/1.0 403 Forbidden' ); ++ header( 'Content-Type: text/html; charset=utf-8' ); ++ echo << ++ ++

Access Denied

++

The function of img_auth.php is to output files from a private wiki. This wiki ++is configured as a public wiki. For optimal security, img_auth.php is disabled in ++this case. ++

++ ++ ++ENDS; ++ wfLogProfilingData(); ++ exit; ++} +Index: mediawiki-1.11.2/includes/AutoLoader.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/AutoLoader.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/AutoLoader.php 2009-02-01 10:24:44.000000000 +0100 +@@ -95,6 +95,7 @@ + 'HistoryBlobCurStub' => 'includes/HistoryBlob.php', + 'HTMLCacheUpdate' => 'includes/HTMLCacheUpdate.php', + 'Http' => 'includes/HttpFunctions.php', ++ 'IEContentAnalyzer' => 'includes/IEContentAnalyzer.php', + 'IP' => 'includes/IP.php', + 'ImageGallery' => 'includes/ImageGallery.php', + 'ImagePage' => 'includes/ImagePage.php', +Index: mediawiki-1.11.2/includes/DefaultSettings.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/DefaultSettings.php 2009-02-01 10:23:55.000000000 +0100 ++++ mediawiki-1.11.2/includes/DefaultSettings.php 2009-02-01 10:24:44.000000000 +0100 +@@ -1538,6 +1538,8 @@ + 'application/x-php', 'text/x-php', + # Other types that may be interpreted by some servers + 'text/x-python', 'text/x-perl', 'text/x-bash', 'text/x-sh', 'text/x-csh', ++ # Client-side hazards on Internet Explorer ++ 'text/scriptlet', 'application/x-msdownload', + # Windows metafile, client-side vulnerability on some systems + 'application/x-msmetafile' + ); +Index: mediawiki-1.11.2/includes/Exception.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/Exception.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/Exception.php 2009-02-01 10:24:44.000000000 +0100 +@@ -205,7 +205,16 @@ + } + } + } else { +- echo $e->__toString(); ++ $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" . ++ $e->__toString() . "\n"; ++ if ( $GLOBALS['wgShowExceptionDetails'] ) { ++ $message .= "\n" . $e->getTraceAsString() ."\n"; ++ } ++ if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) { ++ wfPrintError( $message ); ++ } else { ++ echo nl2br( htmlspecialchars( $message ) ). "\n"; ++ } + } + } + +Index: mediawiki-1.11.2/includes/IEContentAnalyzer.php +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ mediawiki-1.11.2/includes/IEContentAnalyzer.php 2009-02-01 10:24:44.000000000 +0100 +@@ -0,0 +1,823 @@ ++ array( ++ 'text/plain', ++ 'application/octet-stream', ++ 'application/x-netcdf', // [sic] ++ ), ++ 'text' /*3*/ => array( ++ 'text/richtext', 'image/x-bitmap', 'application/postscript', 'application/base64', ++ 'application/macbinhex40', 'application/x-cdf', 'text/scriptlet' ++ ), ++ 'binary' /*4*/ => array( ++ 'application/pdf', 'audio/x-aiff', 'audio/basic', 'audio/wav', 'image/gif', ++ 'image/pjpeg', 'image/jpeg', 'image/tiff', 'image/x-png', 'image/png', 'image/bmp', ++ 'image/x-jg', 'image/x-art', 'image/x-emf', 'image/x-wmf', 'video/avi', ++ 'video/x-msvideo', 'video/mpeg', 'application/x-compressed', ++ 'application/x-zip-compressed', 'application/x-gzip-compressed', 'application/java', ++ 'application/x-msdownload' ++ ), ++ 'html' /*5*/ => array( 'text/html' ), ++ ); ++ ++ /** ++ * Changes to the type table in later versions of IE ++ */ ++ protected $addedTypes = array( ++ 'ie07' => array( ++ 'text' => array( 'text/xml', 'application/xml' ) ++ ), ++ ); ++ ++ /** ++ * An approximation of the "Content Type" values in HKEY_CLASSES_ROOT in a ++ * typical Windows installation. ++ * ++ * Used for extension to MIME type mapping if detection fails. ++ */ ++ protected $registry = array( ++ '.323' => 'text/h323', ++ '.3g2' => 'video/3gpp2', ++ '.3gp' => 'video/3gpp', ++ '.3gp2' => 'video/3gpp2', ++ '.3gpp' => 'video/3gpp', ++ '.aac' => 'audio/aac', ++ '.ac3' => 'audio/ac3', ++ '.accda' => 'application/msaccess', ++ '.accdb' => 'application/msaccess', ++ '.accdc' => 'application/msaccess', ++ '.accde' => 'application/msaccess', ++ '.accdr' => 'application/msaccess', ++ '.accdt' => 'application/msaccess', ++ '.ade' => 'application/msaccess', ++ '.adp' => 'application/msaccess', ++ '.adts' => 'audio/aac', ++ '.ai' => 'application/postscript', ++ '.aif' => 'audio/aiff', ++ '.aifc' => 'audio/aiff', ++ '.aiff' => 'audio/aiff', ++ '.amc' => 'application/x-mpeg', ++ '.application' => 'application/x-ms-application', ++ '.asf' => 'video/x-ms-asf', ++ '.asx' => 'video/x-ms-asf', ++ '.au' => 'audio/basic', ++ '.avi' => 'video/avi', ++ '.bmp' => 'image/bmp', ++ '.caf' => 'audio/x-caf', ++ '.cat' => 'application/vnd.ms-pki.seccat', ++ '.cbo' => 'application/sha', ++ '.cdda' => 'audio/aiff', ++ '.cer' => 'application/x-x509-ca-cert', ++ '.conf' => 'text/plain', ++ '.crl' => 'application/pkix-crl', ++ '.crt' => 'application/x-x509-ca-cert', ++ '.css' => 'text/css', ++ '.csv' => 'application/vnd.ms-excel', ++ '.der' => 'application/x-x509-ca-cert', ++ '.dib' => 'image/bmp', ++ '.dif' => 'video/x-dv', ++ '.dll' => 'application/x-msdownload', ++ '.doc' => 'application/msword', ++ '.docm' => 'application/vnd.ms-word.document.macroEnabled.12', ++ '.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ++ '.dot' => 'application/msword', ++ '.dotm' => 'application/vnd.ms-word.template.macroEnabled.12', ++ '.dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', ++ '.dv' => 'video/x-dv', ++ '.dwfx' => 'model/vnd.dwfx+xps', ++ '.edn' => 'application/vnd.adobe.edn', ++ '.eml' => 'message/rfc822', ++ '.eps' => 'application/postscript', ++ '.etd' => 'application/x-ebx', ++ '.exe' => 'application/x-msdownload', ++ '.fdf' => 'application/vnd.fdf', ++ '.fif' => 'application/fractals', ++ '.gif' => 'image/gif', ++ '.gsm' => 'audio/x-gsm', ++ '.hqx' => 'application/mac-binhex40', ++ '.hta' => 'application/hta', ++ '.htc' => 'text/x-component', ++ '.htm' => 'text/html', ++ '.html' => 'text/html', ++ '.htt' => 'text/webviewhtml', ++ '.hxa' => 'application/xml', ++ '.hxc' => 'application/xml', ++ '.hxd' => 'application/octet-stream', ++ '.hxe' => 'application/xml', ++ '.hxf' => 'application/xml', ++ '.hxh' => 'application/octet-stream', ++ '.hxi' => 'application/octet-stream', ++ '.hxk' => 'application/xml', ++ '.hxq' => 'application/octet-stream', ++ '.hxr' => 'application/octet-stream', ++ '.hxs' => 'application/octet-stream', ++ '.hxt' => 'application/xml', ++ '.hxv' => 'application/xml', ++ '.hxw' => 'application/octet-stream', ++ '.ico' => 'image/x-icon', ++ '.iii' => 'application/x-iphone', ++ '.ins' => 'application/x-internet-signup', ++ '.iqy' => 'text/x-ms-iqy', ++ '.isp' => 'application/x-internet-signup', ++ '.jfif' => 'image/jpeg', ++ '.jnlp' => 'application/x-java-jnlp-file', ++ '.jpe' => 'image/jpeg', ++ '.jpeg' => 'image/jpeg', ++ '.jpg' => 'image/jpeg', ++ '.jtx' => 'application/x-jtx+xps', ++ '.latex' => 'application/x-latex', ++ '.log' => 'text/plain', ++ '.m1v' => 'video/mpeg', ++ '.m2v' => 'video/mpeg', ++ '.m3u' => 'audio/x-mpegurl', ++ '.mac' => 'image/x-macpaint', ++ '.man' => 'application/x-troff-man', ++ '.mda' => 'application/msaccess', ++ '.mdb' => 'application/msaccess', ++ '.mde' => 'application/msaccess', ++ '.mfp' => 'application/x-shockwave-flash', ++ '.mht' => 'message/rfc822', ++ '.mhtml' => 'message/rfc822', ++ '.mid' => 'audio/mid', ++ '.midi' => 'audio/mid', ++ '.mod' => 'video/mpeg', ++ '.mov' => 'video/quicktime', ++ '.mp2' => 'video/mpeg', ++ '.mp2v' => 'video/mpeg', ++ '.mp3' => 'audio/mpeg', ++ '.mp4' => 'video/mp4', ++ '.mpa' => 'video/mpeg', ++ '.mpe' => 'video/mpeg', ++ '.mpeg' => 'video/mpeg', ++ '.mpf' => 'application/vnd.ms-mediapackage', ++ '.mpg' => 'video/mpeg', ++ '.mpv2' => 'video/mpeg', ++ '.mqv' => 'video/quicktime', ++ '.NMW' => 'application/nmwb', ++ '.nws' => 'message/rfc822', ++ '.odc' => 'text/x-ms-odc', ++ '.ols' => 'application/vnd.ms-publisher', ++ '.p10' => 'application/pkcs10', ++ '.p12' => 'application/x-pkcs12', ++ '.p7b' => 'application/x-pkcs7-certificates', ++ '.p7c' => 'application/pkcs7-mime', ++ '.p7m' => 'application/pkcs7-mime', ++ '.p7r' => 'application/x-pkcs7-certreqresp', ++ '.p7s' => 'application/pkcs7-signature', ++ '.pct' => 'image/pict', ++ '.pdf' => 'application/pdf', ++ '.pdx' => 'application/vnd.adobe.pdx', ++ '.pfx' => 'application/x-pkcs12', ++ '.pic' => 'image/pict', ++ '.pict' => 'image/pict', ++ '.pinstall' => 'application/x-picasa-detect', ++ '.pko' => 'application/vnd.ms-pki.pko', ++ '.png' => 'image/png', ++ '.pnt' => 'image/x-macpaint', ++ '.pntg' => 'image/x-macpaint', ++ '.pot' => 'application/vnd.ms-powerpoint', ++ '.potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', ++ '.potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', ++ '.ppa' => 'application/vnd.ms-powerpoint', ++ '.ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', ++ '.pps' => 'application/vnd.ms-powerpoint', ++ '.ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', ++ '.ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', ++ '.ppt' => 'application/vnd.ms-powerpoint', ++ '.pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', ++ '.pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ++ '.prf' => 'application/pics-rules', ++ '.ps' => 'application/postscript', ++ '.pub' => 'application/vnd.ms-publisher', ++ '.pwz' => 'application/vnd.ms-powerpoint', ++ '.py' => 'text/plain', ++ '.pyw' => 'text/plain', ++ '.qht' => 'text/x-html-insertion', ++ '.qhtm' => 'text/x-html-insertion', ++ '.qt' => 'video/quicktime', ++ '.qti' => 'image/x-quicktime', ++ '.qtif' => 'image/x-quicktime', ++ '.qtl' => 'application/x-quicktimeplayer', ++ '.rat' => 'application/rat-file', ++ '.rmf' => 'application/vnd.adobe.rmf', ++ '.rmi' => 'audio/mid', ++ '.rqy' => 'text/x-ms-rqy', ++ '.rtf' => 'application/msword', ++ '.sct' => 'text/scriptlet', ++ '.sd2' => 'audio/x-sd2', ++ '.sdp' => 'application/sdp', ++ '.shtml' => 'text/html', ++ '.sit' => 'application/x-stuffit', ++ '.sldm' => 'application/vnd.ms-powerpoint.slide.macroEnabled.12', ++ '.sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide', ++ '.slk' => 'application/vnd.ms-excel', ++ '.snd' => 'audio/basic', ++ '.so' => 'application/x-apachemodule', ++ '.sol' => 'text/plain', ++ '.sor' => 'text/plain', ++ '.spc' => 'application/x-pkcs7-certificates', ++ '.spl' => 'application/futuresplash', ++ '.sst' => 'application/vnd.ms-pki.certstore', ++ '.stl' => 'application/vnd.ms-pki.stl', ++ '.swf' => 'application/x-shockwave-flash', ++ '.thmx' => 'application/vnd.ms-officetheme', ++ '.tif' => 'image/tiff', ++ '.tiff' => 'image/tiff', ++ '.txt' => 'text/plain', ++ '.uls' => 'text/iuls', ++ '.vcf' => 'text/x-vcard', ++ '.vdx' => 'application/vnd.ms-visio.viewer', ++ '.vsd' => 'application/vnd.ms-visio.viewer', ++ '.vss' => 'application/vnd.ms-visio.viewer', ++ '.vst' => 'application/vnd.ms-visio.viewer', ++ '.vsx' => 'application/vnd.ms-visio.viewer', ++ '.vtx' => 'application/vnd.ms-visio.viewer', ++ '.wav' => 'audio/wav', ++ '.wax' => 'audio/x-ms-wax', ++ '.wbk' => 'application/msword', ++ '.wdp' => 'image/vnd.ms-photo', ++ '.wiz' => 'application/msword', ++ '.wm' => 'video/x-ms-wm', ++ '.wma' => 'audio/x-ms-wma', ++ '.wmd' => 'application/x-ms-wmd', ++ '.wmv' => 'video/x-ms-wmv', ++ '.wmx' => 'video/x-ms-wmx', ++ '.wmz' => 'application/x-ms-wmz', ++ '.wpl' => 'application/vnd.ms-wpl', ++ '.wsc' => 'text/scriptlet', ++ '.wvx' => 'video/x-ms-wvx', ++ '.xaml' => 'application/xaml+xml', ++ '.xbap' => 'application/x-ms-xbap', ++ '.xdp' => 'application/vnd.adobe.xdp+xml', ++ '.xfdf' => 'application/vnd.adobe.xfdf', ++ '.xht' => 'application/xhtml+xml', ++ '.xhtml' => 'application/xhtml+xml', ++ '.xla' => 'application/vnd.ms-excel', ++ '.xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', ++ '.xlk' => 'application/vnd.ms-excel', ++ '.xll' => 'application/vnd.ms-excel', ++ '.xlm' => 'application/vnd.ms-excel', ++ '.xls' => 'application/vnd.ms-excel', ++ '.xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', ++ '.xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', ++ '.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ++ '.xlt' => 'application/vnd.ms-excel', ++ '.xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', ++ '.xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', ++ '.xlw' => 'application/vnd.ms-excel', ++ '.xml' => 'text/xml', ++ '.xps' => 'application/vnd.ms-xpsdocument', ++ '.xsl' => 'text/xml', ++ ); ++ ++ /** ++ * IE versions which have been analysed to bring you this class, and for ++ * which some substantive difference exists. These will appear as keys ++ * in the return value of getRealMimesFromData(). The names are chosen to sort correctly. ++ */ ++ protected $versions = array( 'ie05', 'ie06', 'ie07', 'ie07.strict', 'ie07.nohtml' ); ++ ++ /** ++ * Type table with versions expanded ++ */ ++ protected $typeTable = array(); ++ ++ /** constructor */ ++ function __construct() { ++ // Construct versioned type arrays from the base type array plus additions ++ $types = $this->baseTypeTable; ++ foreach ( $this->versions as $version ) { ++ if ( isset( $this->addedTypes[$version] ) ) { ++ foreach ( $this->addedTypes[$version] as $format => $addedTypes ) { ++ $types[$format] = array_merge( $types[$format], $addedTypes ); ++ } ++ } ++ $this->typeTable[$version] = $types; ++ } ++ } ++ ++ /** ++ * Get the MIME types from getMimesFromData(), but convert the result from IE's ++ * idiosyncratic private types into something other apps will understand. ++ * ++ * @param string $fileName The file name (unused at present) ++ * @param string $chunk The first 256 bytes of the file ++ * @param string $proposed The MIME type proposed by the server ++ * ++ * @return array Map of IE version to detected mime type ++ */ ++ public function getRealMimesFromData( $fileName, $chunk, $proposed ) { ++ $types = $this->getMimesFromData( $fileName, $chunk, $proposed ); ++ $types = array_map( array( $this, 'translateMimeType' ), $types ); ++ return $types; ++ } ++ ++ /** ++ * Translate a MIME type from IE's idiosyncratic private types into ++ * more commonly understood type strings ++ */ ++ public function translateMimeType( $type ) { ++ static $table = array( ++ 'image/pjpeg' => 'image/jpeg', ++ 'image/x-png' => 'image/png', ++ 'image/x-wmf' => 'application/x-msmetafile', ++ 'image/bmp' => 'image/x-bmp', ++ 'application/x-zip-compressed' => 'application/zip', ++ 'application/x-compressed' => 'application/x-compress', ++ 'application/x-gzip-compressed' => 'application/x-gzip', ++ 'audio/mid' => 'audio/midi', ++ ); ++ if ( isset( $table[$type] ) ) { ++ $type = $table[$type]; ++ } ++ return $type; ++ } ++ ++ /** ++ * Get the untranslated MIME types for all known versions ++ * ++ * @param string $fileName The file name (unused at present) ++ * @param string $chunk The first 256 bytes of the file ++ * @param string $proposed The MIME type proposed by the server ++ * ++ * @return array Map of IE version to detected mime type ++ */ ++ public function getMimesFromData( $fileName, $chunk, $proposed ) { ++ $types = array(); ++ foreach ( $this->versions as $version ) { ++ $types[$version] = $this->getMimeTypeForVersion( $version, $fileName, $chunk, $proposed ); ++ } ++ return $types; ++ } ++ ++ /** ++ * Get the MIME type for a given named version ++ */ ++ protected function getMimeTypeForVersion( $version, $fileName, $chunk, $proposed ) { ++ // Strip text after a semicolon ++ $semiPos = strpos( $proposed, ';' ); ++ if ( $semiPos !== false ) { ++ $proposed = substr( $proposed, 0, $semiPos ); ++ } ++ ++ $proposedFormat = $this->getDataFormat( $version, $proposed ); ++ if ( $proposedFormat == 'unknown' ++ && $proposed != 'multipart/mixed' ++ && $proposed != 'multipart/x-mixed-replace' ) ++ { ++ return $proposed; ++ } ++ if ( strval( $chunk ) === '' ) { ++ return $proposed; ++ } ++ ++ // Truncate chunk at 255 bytes ++ $chunk = substr( $chunk, 0, 255 ); ++ ++ // IE does the Check*Headers() calls last, and instead does the following image ++ // type checks by directly looking for the magic numbers. What I do here should ++ // have the same effect since the magic number checks are identical in both cases. ++ $result = $this->sampleData( $version, $chunk ); ++ $sampleFound = $result['found']; ++ $counters = $result['counters']; ++ $binaryType = $this->checkBinaryHeaders( $version, $chunk ); ++ $textType = $this->checkTextHeaders( $version, $chunk ); ++ ++ if ( $proposed == 'text/html' && isset( $sampleFound['html'] ) ) { ++ return 'text/html'; ++ } ++ if ( $proposed == 'image/gif' && $binaryType == 'image/gif' ) { ++ return 'image/gif'; ++ } ++ if ( ( $proposed == 'image/pjpeg' || $proposed == 'image/jpeg' ) ++ && $binaryType == 'image/pjpeg' ) ++ { ++ return $proposed; ++ } ++ // PNG check added in IE 7 ++ if ( $version >= 'ie07' ++ && ( $proposed == 'image/x-png' || $proposed == 'image/png' ) ++ && $binaryType == 'image/x-png' ) ++ { ++ return $proposed; ++ } ++ ++ // CDF was removed in IE 7 so it won't be in $sampleFound for later versions ++ if ( isset( $sampleFound['cdf'] ) ) { ++ return 'application/x-cdf'; ++ } ++ ++ // RSS and Atom were added in IE 7 so they won't be in $sampleFound for ++ // previous versions ++ if ( isset( $sampleFound['rss'] ) ) { ++ return 'application/rss+xml'; ++ } ++ if ( isset( $sampleFound['rdf-tag'] ) ++ && isset( $sampleFound['rdf-url'] ) ++ && isset( $sampleFound['rdf-purl'] ) ) ++ { ++ return 'application/rss+xml'; ++ } ++ if ( isset( $sampleFound['atom'] ) ) { ++ return 'application/atom+xml'; ++ } ++ ++ if ( isset( $sampleFound['xml'] ) ) { ++ // TODO: I'm not sure under what circumstances this flag is enabled ++ if ( strpos( $version, 'strict' ) !== false ) { ++ if ( $proposed == 'text/html' || $proposed == 'text/xml' ) { ++ return 'text/xml'; ++ } ++ } else { ++ return 'text/xml'; ++ } ++ } ++ if ( isset( $sampleFound['html'] ) ) { ++ // TODO: I'm not sure under what circumstances this flag is enabled ++ if ( strpos( $version, 'nohtml' ) !== false ) { ++ if ( $proposed == 'text/plain' ) { ++ return 'text/html'; ++ } ++ } else { ++ return 'text/html'; ++ } ++ } ++ if ( isset( $sampleFound['xbm'] ) ) { ++ return 'image/x-bitmap'; ++ } ++ if ( isset( $sampleFound['binhex'] ) ) { ++ return 'application/macbinhex40'; ++ } ++ if ( isset( $sampleFound['scriptlet'] ) ) { ++ if ( strpos( $version, 'strict' ) !== false ) { ++ if ( $proposed == 'text/plain' || $proposed == 'text/scriptlet' ) { ++ return 'text/scriptlet'; ++ } ++ } else { ++ return 'text/scriptlet'; ++ } ++ } ++ ++ // Freaky heuristics to determine if the data is text or binary ++ // The heuristic is of course broken for non-ASCII text ++ if ( $counters['ctrl'] != 0 && ( $counters['ff'] + $counters['low'] ) ++ < ( $counters['ctrl'] + $counters['high'] ) * 16 ) ++ { ++ $kindOfBinary = true; ++ $type = $binaryType ? $binaryType : $textType; ++ if ( $type === false ) { ++ $type = 'application/octet-stream'; ++ } ++ } else { ++ $kindOfBinary = false; ++ $type = $textType ? $textType : $binaryType; ++ if ( $type === false ) { ++ $type = 'text/plain'; ++ } ++ } ++ ++ // Check if the output format is ambiguous ++ // This generally means that detection failed, real types aren't ambiguous ++ $detectedFormat = $this->getDataFormat( $version, $type ); ++ if ( $detectedFormat != 'ambiguous' ) { ++ return $type; ++ } ++ ++ if ( $proposedFormat != 'ambiguous' ) { ++ // FormatAgreesWithData() ++ if ( $proposedFormat == 'text' && !$kindOfBinary ) { ++ return $proposed; ++ } ++ if ( $proposedFormat == 'binary' && $kindOfBinary ) { ++ return $proposed; ++ } ++ if ( $proposedFormat == 'html' ) { ++ return $proposed; ++ } ++ } ++ ++ // Find a MIME type by searching the registry for the file extension. ++ $dotPos = strrpos( $fileName, '.' ); ++ if ( $dotPos === false ) { ++ return $type; ++ } ++ $ext = substr( $fileName, $dotPos ); ++ if ( isset( $this->registry[$ext] ) ) { ++ return $this->registry[$ext]; ++ } ++ ++ // TODO: If the extension has an application registered to it, IE will return ++ // application/octet-stream. We'll skip that, so we could erroneously ++ // return text/plain or application/x-netcdf where application/octet-stream ++ // would be correct. ++ ++ return $type; ++ } ++ ++ /** ++ * Check for text headers at the start of the chunk ++ * Confirmed same in 5 and 7. ++ */ ++ private function checkTextHeaders( $version, $chunk ) { ++ $chunk2 = substr( $chunk, 0, 2 ); ++ $chunk4 = substr( $chunk, 0, 4 ); ++ $chunk5 = substr( $chunk, 0, 5 ); ++ if ( $chunk4 == '%PDF' ) { ++ return 'application/pdf'; ++ } ++ if ( $chunk2 == '%!' ) { ++ return 'application/postscript'; ++ } ++ if ( $chunk5 == '{\\rtf' ) { ++ return 'text/richtext'; ++ } ++ if ( $chunk5 == 'begin' ) { ++ return 'application/base64'; ++ } ++ return false; ++ } ++ ++ /** ++ * Check for binary headers at the start of the chunk ++ * Confirmed same in 5 and 7. ++ */ ++ private function checkBinaryHeaders( $version, $chunk ) { ++ $chunk2 = substr( $chunk, 0, 2 ); ++ $chunk3 = substr( $chunk, 0, 3 ); ++ $chunk4 = substr( $chunk, 0, 4 ); ++ $chunk5 = substr( $chunk, 0, 5 ); ++ $chunk8 = substr( $chunk, 0, 8 ); ++ if ( $chunk5 == 'GIF87' || $chunk5 == 'GIF89' ) { ++ return 'image/gif'; ++ } ++ if ( $chunk2 == "\xff\xd8" ) { ++ return 'image/pjpeg'; // actually plain JPEG but this is what IE returns ++ } ++ ++ if ( $chunk2 == 'BM' ++ && substr( $chunk, 6, 2 ) == "\000\000" ++ && substr( $chunk, 8, 2 ) != "\000\000" ) ++ { ++ return 'image/bmp'; // another non-standard MIME ++ } ++ if ( $chunk4 == 'RIFF' ++ && substr( $chunk, 8, 4 ) == 'WAVE' ) ++ { ++ return 'audio/wav'; ++ } ++ // These were integer literals in IE ++ // Perhaps the author was not sure what the target endianness was ++ if ( $chunk4 == ".sd\000" ++ || $chunk4 == ".snd" ++ || $chunk4 == "\000ds." ++ || $chunk4 == "dns." ) ++ { ++ return 'audio/basic'; ++ } ++ if ( $chunk3 == "MM\000" ) { ++ return 'image/tiff'; ++ } ++ if ( $chunk2 == 'MZ' ) { ++ return 'application/x-msdownload'; ++ } ++ if ( $chunk8 == "\x89PNG\x0d\x0a\x1a\x0a" ) { ++ return 'image/x-png'; // [sic] ++ } ++ if ( strlen( $chunk ) >= 5 ) { ++ $byte2 = ord( $chunk[2] ); ++ $byte4 = ord( $chunk[4] ); ++ if ( $byte2 >= 3 && $byte2 <= 31 && $byte4 == 0 && $chunk2 == 'JG' ) { ++ return 'image/x-jg'; ++ } ++ } ++ // More endian confusion? ++ if ( $chunk4 == 'MROF' ) { ++ return 'audio/x-aiff'; ++ } ++ $chunk4_8 = substr( $chunk, 8, 4 ); ++ if ( $chunk4 == 'FORM' && ( $chunk4_8 == 'AIFF' || $chunk4_8 == 'AIFC' ) ) { ++ return 'audio/x-aiff'; ++ } ++ if ( $chunk4 == 'RIFF' && $chunk4_8 == 'AVI ' ) { ++ return 'video/avi'; ++ } ++ if ( $chunk4 == "\x00\x00\x01\xb3" || $chunk4 == "\x00\x00\x01\xba" ) { ++ return 'video/mpeg'; ++ } ++ if ( $chunk4 == "\001\000\000\000" ++ && substr( $chunk, 40, 4 ) == ' EMF' ) ++ { ++ return 'image/x-emf'; ++ } ++ if ( $chunk4 == "\xd7\xcd\xc6\x9a" ) { ++ return 'image/x-wmf'; ++ } ++ if ( $chunk4 == "\xca\xfe\xba\xbe" ) { ++ return 'application/java'; ++ } ++ if ( $chunk2 == 'PK' ) { ++ return 'application/x-zip-compressed'; ++ } ++ if ( $chunk2 == "\x1f\x9d" ) { ++ return 'application/x-compressed'; ++ } ++ if ( $chunk2 == "\x1f\x8b" ) { ++ return 'application/x-gzip-compressed'; ++ } ++ // Skip redundant check for ZIP ++ if ( $chunk5 == "MThd\000" ) { ++ return 'audio/mid'; ++ } ++ if ( $chunk4 == '%PDF' ) { ++ return 'application/pdf'; ++ } ++ return false; ++ } ++ ++ /** ++ * Do heuristic checks on the bulk of the data sample. ++ * Search for HTML tags. ++ */ ++ protected function sampleData( $version, $chunk ) { ++ $found = array(); ++ $counters = array( ++ 'ctrl' => 0, ++ 'high' => 0, ++ 'low' => 0, ++ 'lf' => 0, ++ 'cr' => 0, ++ 'ff' => 0 ++ ); ++ $htmlTags = array( ++ 'html', ++ 'head', ++ 'title', ++ 'body', ++ 'script', ++ 'a href', ++ 'pre', ++ 'img', ++ 'plaintext', ++ 'table' ++ ); ++ $rdfUrl = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; ++ $rdfPurl = 'http://purl.org/rss/1.0/'; ++ $xbmMagic1 = '#define'; ++ $xbmMagic2 = '_width'; ++ $xbmMagic3 = '_bits'; ++ $binhexMagic = 'converted with BinHex'; ++ ++ for ( $offset = 0; $offset < strlen( $chunk ); $offset++ ) { ++ $curChar = $chunk[$offset]; ++ if ( $curChar == "\x0a" ) { ++ $counters['lf']++; ++ continue; ++ } elseif ( $curChar == "\x0d" ) { ++ $counters['cr']++; ++ continue; ++ } elseif ( $curChar == "\x0c" ) { ++ $counters['ff']++; ++ continue; ++ } elseif ( $curChar == "\t" ) { ++ $counters['low']++; ++ continue; ++ } elseif ( ord( $curChar ) < 32 ) { ++ $counters['ctrl']++; ++ continue; ++ } elseif ( ord( $curChar ) >= 128 ) { ++ $counters['high']++; ++ continue; ++ } ++ ++ $counters['low']++; ++ if ( $curChar == '<' ) { ++ // XML ++ $remainder = substr( $chunk, $offset + 1 ); ++ if ( !strncasecmp( $remainder, '?XML', 4 ) ) { ++ $nextChar = substr( $chunk, $offset + 5, 1 ); ++ if ( $nextChar == ':' || $nextChar == ' ' || $nextChar == "\t" ) { ++ $found['xml'] = true; ++ } ++ } ++ // Scriptlet (JSP) ++ if ( !strncasecmp( $remainder, 'SCRIPTLET', 9 ) ) { ++ $found['scriptlet'] = true; ++ break; ++ } ++ // HTML ++ foreach ( $htmlTags as $tag ) { ++ if ( !strncasecmp( $remainder, $tag, strlen( $tag ) ) ) { ++ $found['html'] = true; ++ } ++ } ++ // Skip broken check for additional tags (HR etc.) ++ ++ // CHANNEL replaced by RSS, RDF and FEED in IE 7 ++ if ( $version < 'ie07' ) { ++ if ( !strncasecmp( $remainder, 'CHANNEL', 7 ) ) { ++ $found['cdf'] = true; ++ } ++ } else { ++ // RSS ++ if ( !strncasecmp( $remainder, 'RSS', 3 ) ) { ++ $found['rss'] = true; ++ break; // return from SampleData ++ } ++ if ( !strncasecmp( $remainder, 'rdf:RDF', 7 ) ) { ++ $found['rdf-tag'] = true; ++ // no break ++ } ++ if ( !strncasecmp( $remainder, 'FEED', 4 ) ) { ++ $found['atom'] = true; ++ break; ++ } ++ } ++ continue; ++ } ++ // Skip broken check for --> ++ ++ // RSS URL checks ++ // For some reason both URLs must appear before it is recognised ++ $remainder = substr( $chunk, $offset ); ++ if ( !strncasecmp( $remainder, $rdfUrl, strlen( $rdfUrl ) ) ) { ++ $found['rdf-url'] = true; ++ if ( isset( $found['rdf-tag'] ) ++ && isset( $found['rdf-purl'] ) ) // [sic] ++ { ++ break; ++ } ++ continue; ++ } ++ ++ if ( !strncasecmp( $remainder, $rdfPurl, strlen( $rdfPurl ) ) ) { ++ if ( isset( $found['rdf-tag'] ) ++ && isset( $found['rdf-url'] ) ) // [sic] ++ { ++ break; ++ } ++ continue; ++ } ++ ++ // XBM checks ++ if ( !strncasecmp( $remainder, $xbmMagic1, strlen( $xbmMagic1 ) ) ) { ++ $found['xbm1'] = true; ++ continue; ++ } ++ if ( $curChar == '_' ) { ++ if ( isset( $found['xbm2'] ) ) { ++ if ( !strncasecmp( $remainder, $xbmMagic3, strlen( $xbmMagic3 ) ) ) { ++ $found['xbm'] = true; ++ break; ++ } ++ } elseif ( isset( $found['xbm1'] ) ) { ++ if ( !strncasecmp( $remainder, $xbmMagic2, strlen( $xbmMagic2 ) ) ) { ++ $found['xbm2'] = true; ++ } ++ } ++ } ++ ++ // BinHex ++ if ( !strncasecmp( $remainder, $binhexMagic, strlen( $binhexMagic ) ) ) { ++ $found['binhex'] = true; ++ } ++ } ++ return array( 'found' => $found, 'counters' => $counters ); ++ } ++ ++ protected function getDataFormat( $version, $type ) { ++ $types = $this->typeTable[$version]; ++ if ( $type == '(null)' || strval( $type ) === '' ) { ++ return 'ambiguous'; ++ } ++ foreach ( $types as $format => $list ) { ++ if ( in_array( $type, $list ) ) { ++ return $format; ++ } ++ } ++ return 'unknown'; ++ } ++} ++ +Index: mediawiki-1.11.2/includes/MimeMagic.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/MimeMagic.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/MimeMagic.php 2009-02-01 10:24:44.000000000 +0100 +@@ -98,6 +98,10 @@ + */ + var $mExtToMime= NULL; + ++ /** IEContentAnalyzer instance ++ */ ++ var $mIEAnalyzer; ++ + /** The singleton instance + */ + private static $instance; +@@ -761,6 +765,29 @@ + + return MEDIATYPE_UNKNOWN; + } ++ ++ /** ++ * Get the MIME types that various versions of Internet Explorer would ++ * detect from a chunk of the content. ++ * ++ * @param string $fileName The file name (unused at present) ++ * @param string $chunk The first 256 bytes of the file ++ * @param string $proposed The MIME type proposed by the server ++ */ ++ public function getIEMimeTypes( $fileName, $chunk, $proposed ) { ++ $ca = $this->getIEContentAnalyzer(); ++ return $ca->getRealMimesFromData( $fileName, $chunk, $proposed ); ++ } ++ ++ /** ++ * Get a cached instance of IEContentAnalyzer ++ */ ++ protected function getIEContentAnalyzer() { ++ if ( is_null( $this->mIEAnalyzer ) ) { ++ $this->mIEAnalyzer = new IEContentAnalyzer; ++ } ++ return $this->mIEAnalyzer; ++ } + } + + +Index: mediawiki-1.11.2/includes/SpecialImport.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/SpecialImport.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/SpecialImport.php 2009-02-01 10:34:15.000000000 +0100 +@@ -37,26 +37,30 @@ + if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') { + $isUpload = false; + $namespace = $wgRequest->getIntOrNull( 'namespace' ); ++ $sourceName = $wgRequest->getVal( "source" ); + +- switch( $wgRequest->getVal( "source" ) ) { +- case "upload": ++ if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'editToken' ) ) ) { ++ $source = new WikiErrorMsg( 'import-token-mismatch' ); ++ } elseif ( $sourceName == 'upload' ) { + $isUpload = true; + if( $wgUser->isAllowed( 'importupload' ) ) { + $source = ImportStreamSource::newFromUpload( "xmlimport" ); + } else { + return $wgOut->permissionRequired( 'importupload' ); + } +- break; +- case "interwiki": ++ } elseif ( $sourceName == "interwiki" ) { + $interwiki = $wgRequest->getVal( 'interwiki' ); +- $history = $wgRequest->getCheck( 'interwikiHistory' ); +- $frompage = $wgRequest->getText( "frompage" ); +- $source = ImportStreamSource::newFromInterwiki( +- $interwiki, +- $frompage, +- $history ); +- break; +- default: ++ if ( !in_array( $interwiki, $wgImportSources ) ) { ++ $source = new WikiErrorMsg( "import-invalid-interwiki" ); ++ } else { ++ $history = $wgRequest->getCheck( 'interwikiHistory' ); ++ $frompage = $wgRequest->getText( "frompage" ); ++ $source = ImportStreamSource::newFromInterwiki( ++ $interwiki, ++ $frompage, ++ $history ); ++ } ++ } else { + $source = new WikiErrorMsg( "importunknownsource" ); + } + +@@ -98,6 +102,7 @@ + + + ++ + + + " ); +@@ -115,6 +120,7 @@ + $wgOut->parse( wfMsg( 'import-interwiki-text' ) ) . " + + ++ + + +
+Index: mediawiki-1.11.2/includes/SpecialUndelete.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/SpecialUndelete.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/SpecialUndelete.php 2009-02-01 10:24:44.000000000 +0100 +@@ -463,7 +463,7 @@ + */ + class UndeleteForm { + var $mAction, $mTarget, $mTimestamp, $mRestore, $mTargetObj; +- var $mTargetTimestamp, $mAllowed, $mComment; ++ var $mTargetTimestamp, $mAllowed, $mComment, $mToken; + + function UndeleteForm( $request, $par = "" ) { + global $wgUser; +@@ -479,6 +479,7 @@ + $this->mRestore = $request->getCheck( 'restore' ) && $posted; + $this->mPreview = $request->getCheck( 'preview' ) && $posted; + $this->mComment = $request->getText( 'wpComment' ); ++ $this->mToken = $request->getVal( 'token' ); + + if( $par != "" ) { + $this->mTarget = $par; +@@ -536,7 +537,12 @@ + return $this->showRevision( $this->mTimestamp ); + } + if( $this->mFile !== null ) { +- return $this->showFile( $this->mFile ); ++ if ( !$wgUser->matchEditToken( $this->mToken, $this->mFile ) ) { ++ $this->showFileConfirmationForm( $this->mFile ); ++ return false; ++ } else { ++ return $this->showFile( $this->mFile ); ++ } + } + if( $this->mRestore && $this->mAction == "submit" ) { + return $this->undelete(); +@@ -662,6 +668,29 @@ + } + + /** ++ * Show a form confirming whether a tokenless user really wants to see a file ++ */ ++ private function showFileConfirmationForm( $key ) { ++ global $wgOut, $wgUser, $wgLang; ++ $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile ); ++ $wgOut->addWikiMsg( 'undelete-show-file-confirm', ++ $this->mTargetObj->getText(), ++ $wgLang->timeanddate( $file->getTimestamp() ) ); ++ $wgOut->addHTML( ++ Xml::openElement( 'form', array( ++ 'method' => 'POST', ++ 'action' => SpecialPage::getTitleFor( 'Undelete' )->getLocalUrl( ++ 'target=' . urlencode( $this->mTarget ) . ++ '&file=' . urlencode( $key ) . ++ '&token=' . urlencode( $wgUser->editToken( $key ) ) ) ++ ) ++ ) . ++ Xml::submitButton( wfMsg( 'undelete-show-file-submit' ) ) . ++ '' ++ ); ++ } ++ ++ /** + * Show a deleted file version requested by the visitor. + */ + function showFile( $key ) { +@@ -833,7 +862,9 @@ + $target = urlencode( $this->mTarget ); + $pageLink = $sk->makeKnownLinkObj( $titleObj, + $wgLang->timeanddate( $ts, true ), +- "target=$target&file=$key" ); ++ "target=$target" . ++ "&file=$key" . ++ "&token=" . urlencode( $wgUser->editToken( $key ) ) ); + } else { + $checkBox = ''; + $pageLink = $wgLang->timeanddate( $ts, true ); +Index: mediawiki-1.11.2/includes/SpecialUpload.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/SpecialUpload.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/SpecialUpload.php 2009-02-01 10:24:44.000000000 +0100 +@@ -1016,11 +1016,11 @@ + $magic=& MimeMagic::singleton(); + $mime= $magic->guessMimeType($tmpfile,false); + ++ + #check mime type, if desired + global $wgVerifyMimeType; + if ($wgVerifyMimeType) { +- +- wfDebug ( "\n\nmime: <$mime> extension: <$extension>\n\n"); ++ wfDebug ( "\n\nmime: <$mime> extension: <$extension>\n\n"); + #check mime type against file extension + if( !$this->verifyExtension( $mime, $extension ) ) { + return new WikiErrorMsg( 'uploadcorrupt' ); +@@ -1028,9 +1028,22 @@ + + #check mime type blacklist + global $wgMimeTypeBlacklist; +- if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist) +- && $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) { +- return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) ); ++ if( isset($wgMimeTypeBlacklist) && !is_null($wgMimeTypeBlacklist) ) { ++ if ( $this->checkFileExtension( $mime, $wgMimeTypeBlacklist ) ) { ++ return new WikiErrorMsg( 'filetype-badmime', htmlspecialchars( $mime ) ); ++ } ++ ++ # Check IE type ++ $fp = fopen( $tmpfile, 'rb' ); ++ $chunk = fread( $fp, 256 ); ++ fclose( $fp ); ++ $extMime = $magic->guessTypesForExtension( $extension ); ++ $ieTypes = $magic->getIEMimeTypes( $tmpfile, $chunk, $extMime ); ++ foreach ( $ieTypes as $ieType ) { ++ if ( $this->checkFileExtension( $ieType, $wgMimeTypeBlacklist ) ) { ++ return new WikiErrorMsg( 'filetype-bad-ie-mime', $ieType ); ++ } ++ } + } + } + +@@ -1038,6 +1051,11 @@ + if( $this->detectScript ( $tmpfile, $mime, $extension ) ) { + return new WikiErrorMsg( 'uploadscripted' ); + } ++ if( $extension == 'svg' || $mime == 'image/svg+xml' ) { ++ if( $this->detectScriptInSvg( $tmpfile ) ) { ++ return new WikiErrorMsg( 'uploadscripted' ); ++ } ++ } + + /** + * Scan the uploaded file for viruses +@@ -1051,6 +1069,7 @@ + return true; + } + ++ + /** + * Checks if the mime type of the uploaded file matches the file extension. + * +@@ -1149,6 +1168,7 @@ + */ + + $tags = array( ++ 'filterMatch; ++ } ++ ++ /** ++ * @todo Replace this with a whitelist filter! ++ */ ++ function checkSvgScriptCallback( $element, $attribs ) { ++ $stripped = $this->stripXmlNamespace( $element ); ++ ++ if( $stripped == 'script' ) { ++ wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" ); ++ return true; ++ } ++ ++ foreach( $attribs as $attrib => $value ) { ++ $stripped = $this->stripXmlNamespace( $attrib ); ++ if( substr( $stripped, 0, 2 ) == 'on' ) { ++ wfDebug( __METHOD__ . ": Found script attribute '$attrib'='value' in uploaded file.\n" ); ++ return true; ++ } ++ if( $stripped == 'href' && strpos( strtolower( $value ), 'javascript:' ) !== false ) { ++ wfDebug( __METHOD__ . ": Found script href attribute '$attrib'='$value' in uploaded file.\n" ); ++ return true; ++ } ++ } ++ } ++ ++ private function stripXmlNamespace( $name ) { ++ // 'http://www.w3.org/2000/svg:script' -> 'script' ++ $parts = explode( ':', strtolower( $name ) ); ++ return array_pop( $parts ); ++ } ++ + /** + * Generic wrapper function for a virus scanner program. + * This relies on the $wgAntivirus and $wgAntivirusSetup variables. +Index: mediawiki-1.11.2/includes/StreamFile.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/StreamFile.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/StreamFile.php 2009-02-01 10:24:44.000000000 +0100 +@@ -31,6 +31,12 @@ + header('Content-type: application/x-wiki'); + } + ++ // Don't stream it out as text/html if there was a PHP error ++ if ( headers_sent() ) { ++ echo "Headers already sent, terminating.\n"; ++ return; ++ } ++ + global $wgContLanguageCode; + header( "Content-Disposition: inline;filename*=utf-8'$wgContLanguageCode'" . urlencode( basename( $fname ) ) ); + +@@ -49,27 +55,54 @@ + } + + /** */ +-function wfGetType( $filename ) { ++function wfGetType( $filename, $safe = true ) { + global $wgTrivialMimeDetection; + ++ $ext = strrchr($filename, '.'); ++ $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) ); ++ + # trivial detection by file extension, + # used for thumbnails (thumb.php) + if ($wgTrivialMimeDetection) { +- $ext= strtolower(strrchr($filename, '.')); + + switch ($ext) { +- case '.gif': return 'image/gif'; +- case '.png': return 'image/png'; +- case '.jpg': return 'image/jpeg'; +- case '.jpeg': return 'image/jpeg'; ++ case 'gif': return 'image/gif'; ++ case 'png': return 'image/png'; ++ case 'jpg': return 'image/jpeg'; ++ case 'jpeg': return 'image/jpeg'; + } + + return 'unknown/unknown'; + } +- else { +- $magic=& MimeMagic::singleton(); +- return $magic->guessMimeType($filename); //full fancy mime detection ++ ++ $magic = MimeMagic::singleton(); ++ // Use the extension only, rather than magic numbers, to avoid opening ++ // up vulnerabilities due to uploads of files with allowed extensions ++ // but disallowed types. ++ $type = $magic->guessTypesForExtension( $ext ); ++ ++ /** ++ * Double-check some security settings that were done on upload but might ++ * have changed since. ++ */ ++ if ( $safe ) { ++ global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions, ++ $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist, $wgRequest; ++ $form = new UploadForm( $wgRequest ); ++ list( $partName, $extList ) = $form->splitExtensions( $filename ); ++ if ( $form->checkFileExtensionList( $extList, $wgFileBlacklist ) ) { ++ return 'unknown/unknown'; ++ } ++ if ( $wgCheckFileExtensions && $wgStrictFileExtensions ++ && !$form->checkFileExtensionList( $extList, $wgFileExtensions ) ) ++ { ++ return 'unknown/unknown'; ++ } ++ if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) { ++ return 'unknown/unknown'; ++ } + } ++ return $type; + } + + +Index: mediawiki-1.11.2/includes/Title.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/Title.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/Title.php 2009-02-01 10:24:44.000000000 +0100 +@@ -290,9 +290,13 @@ + $m[1] = urldecode( ltrim( $m[1], ':' ) ); + } + $title = Title::newFromText( $m[1] ); +- // Redirects to Special:Userlogout are not permitted +- if( $title instanceof Title && !$title->isSpecial( 'Userlogout' ) ) ++ // Redirects to some special pages are not permitted ++ if( $title instanceof Title ++ && !$title->isSpecial( 'Userlogout' ) ++ && !$title->isSpecial( 'Filepath' ) ) ++ { + return $title; ++ } + } + } + return null; +Index: mediawiki-1.11.2/includes/filerepo/FSRepo.php +=================================================================== +--- mediawiki-1.11.2.orig/includes/filerepo/FSRepo.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/includes/filerepo/FSRepo.php 2009-02-01 10:24:44.000000000 +0100 +@@ -146,10 +146,8 @@ + if ( !wfMkdirParents( $dstDir ) ) { + return $this->newFatal( 'directorycreateerror', $dstDir ); + } +- // In the deleted zone, seed new directories with a blank +- // index.html, to prevent crawling + if ( $dstZone == 'deleted' ) { +- file_put_contents( "$dstDir/index.html", '' ); ++ $this->initDeletedDir( $dstDir ); + } + } + +@@ -212,6 +210,20 @@ + } + + /** ++ * Take all available measures to prevent web accessibility of new deleted ++ * directories, in case the user has not configured offline storage ++ */ ++ protected function initDeletedDir( $dir ) { ++ // Add a .htaccess file to the root of the deleted zone ++ $root = $this->getZonePath( 'deleted' ); ++ if ( !file_exists( "$root/.htaccess" ) ) { ++ file_put_contents( "$root/.htaccess", "Deny from all\n" ); ++ } ++ // Seed new directories with a blank index.html, to prevent crawling ++ file_put_contents( "$dir/index.html", '' ); ++ } ++ ++ /** + * Pick a random name in the temp zone and store a file to it. + * @param string $originalName The base name of the file as specified + * by the user. The file extension will be maintained. +@@ -387,8 +399,7 @@ + $status->fatal( 'directorycreateerror', $archiveDir ); + continue; + } +- // Seed new directories with a blank index.html, to prevent crawling +- file_put_contents( "$archiveDir/index.html", '' ); ++ $this->initDeletedDir( $archiveDir ); + } + // Check if the archive directory is writable + // This doesn't appear to work on NTFS +Index: mediawiki-1.11.2/languages/messages/MessagesEn.php +=================================================================== +--- mediawiki-1.11.2.orig/languages/messages/MessagesEn.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/languages/messages/MessagesEn.php 2009-02-01 10:26:17.000000000 +0100 +@@ -1390,6 +1390,7 @@ + 'illegalfilename' => 'The filename "$1" contains characters that are not allowed in page titles. Please rename the file and try uploading it again.', + 'badfilename' => 'File name has been changed to "$1".', + 'filetype-badmime' => 'Files of the MIME type "$1" are not allowed to be uploaded.', ++'filetype-bad-ie-mime' => 'Cannot upload this file because Internet Explorer would detect it as "$1", which is a disallowed and potentially dangerous file type.', + 'filetype-badtype' => "'''\".\$1\"''' is an unwanted file type + : List of allowed file types: \$2", + 'filetype-missing' => 'The file has no extension (like ".jpg").', +@@ -1938,6 +1939,8 @@ + 'It may have already been undeleted.', + 'undelete-error-short' => 'Error undeleting file: $1', + 'undelete-error-long' => "Errors were encountered while undeleting the file:\n\n$1\n", ++'undelete-show-file-confirm' => 'Are you sure you want to view a deleted revision of the file "$1" from $2?', ++'undelete-show-file-submit' => 'Yes', + + # Namespace form on various pages + 'namespace' => 'Namespace:', +@@ -2218,6 +2221,8 @@ + 'importnosources' => 'No transwiki import sources have been defined and direct history uploads are disabled.', + 'importnofile' => 'No import file was uploaded.', + 'importuploaderror' => 'Upload of import file failed; perhaps the file is bigger than the allowed upload size.', ++'import-token-mismatch' => 'Loss of session data. Please try again.', ++'import-invalid-interwiki' => 'Cannot import from the specified wiki.', + + # Import log + 'importlogpage' => 'Import log', +Index: mediawiki-1.11.2/maintenance/language/messages.inc +=================================================================== +--- mediawiki-1.11.2.orig/maintenance/language/messages.inc 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/maintenance/language/messages.inc 2009-02-01 10:27:38.000000000 +0100 +@@ -798,6 +798,7 @@ + 'illegalfilename', + 'badfilename', + 'filetype-badmime', ++ 'filetype-bad-ie-mime', + 'filetype-badtype', + 'filetype-missing', + 'large-file', +@@ -1271,6 +1272,8 @@ + 'undelete-missing-filearchive', + 'undelete-error-short', + 'undelete-error-long', ++ 'undelete-show-file-confirm', ++ 'undelete-show-file-submit', + ), + 'nsform' => array( + 'namespace', +@@ -1493,6 +1496,8 @@ + 'importnosources', + 'importnofile', + 'importuploaderror', ++ 'import-token-mismatch', ++ 'import-invalid-interwiki', + ), + 'importlog' => array( + 'importlogpage', +Index: mediawiki-1.11.2/profileinfo.php +=================================================================== +--- mediawiki-1.11.2.orig/profileinfo.php 2009-02-01 08:50:09.000000000 +0100 ++++ mediawiki-1.11.2/profileinfo.php 2009-02-01 10:28:19.000000000 +0100 +@@ -48,7 +48,7 @@ + + define( 'MW_NO_SETUP', 1 ); + require_once( './includes/WebStart.php' ); +-require_once("./AdminSettings.php"); ++@include_once("./AdminSettings.php"); + + if (!$wgEnableProfileInfo) { + echo "disabled\n";