r43669 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r43668‎ | r43669 | r43670 >
Date:11:36, 18 November 2008
Author:tstarling
Status:deferred
Tags:
Comment:
Backported r43621, r43622, r43623, r43624, r43625, r43627, r43660, r43661. Needs testing.
Modified paths:
  • /branches/REL1_13/phase3/RELEASE-NOTES (modified) (history)
  • /branches/REL1_13/phase3/img_auth.php (modified) (history)
  • /branches/REL1_13/phase3/includes/DefaultSettings.php (modified) (history)
  • /branches/REL1_13/phase3/includes/Exception.php (modified) (history)
  • /branches/REL1_13/phase3/includes/StreamFile.php (modified) (history)
  • /branches/REL1_13/phase3/includes/Title.php (modified) (history)
  • /branches/REL1_13/phase3/includes/XmlTypeCheck.php (modified) (history)
  • /branches/REL1_13/phase3/includes/specials/SpecialImport.php (modified) (history)
  • /branches/REL1_13/phase3/includes/specials/SpecialUndelete.php (modified) (history)
  • /branches/REL1_13/phase3/includes/specials/SpecialUpload.php (modified) (history)
  • /branches/REL1_13/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /branches/REL1_13/phase3/profileinfo.php (modified) (history)

Diff [purge]

Index: branches/REL1_13/phase3/includes/Title.php
@@ -320,9 +320,13 @@
321321 $m[1] = urldecode( ltrim( $m[1], ':' ) );
322322 }
323323 $title = Title::newFromText( $m[1] );
324 - // Redirects to Special:Userlogout are not permitted
325 - if( $title instanceof Title && !$title->isSpecial( 'Userlogout' ) )
 324+ // Redirects to some special pages are not permitted
 325+ if( $title instanceof Title
 326+ && !$title->isSpecial( 'Userlogout' )
 327+ && !$title->isSpecial( 'Filepath' ) )
 328+ {
326329 return $title;
 330+ }
327331 }
328332 }
329333 return null;
Index: branches/REL1_13/phase3/includes/StreamFile.php
@@ -31,6 +31,12 @@
3232 header('Content-type: application/x-wiki');
3333 }
3434
 35+ // Don't stream it out as text/html if there was a PHP error
 36+ if ( headers_sent() ) {
 37+ echo "Headers already sent, terminating.\n";
 38+ return;
 39+ }
 40+
3541 global $wgContLanguageCode;
3642 header( "Content-Disposition: inline;filename*=utf-8'$wgContLanguageCode'" . urlencode( basename( $fname ) ) );
3743
@@ -53,25 +59,51 @@
5460 }
5561
5662 /** */
57 -function wfGetType( $filename ) {
 63+function wfGetType( $filename, $safe = true ) {
5864 global $wgTrivialMimeDetection;
5965
 66+ $ext = strrchr($filename, '.');
 67+ $ext = $ext === false ? '' : strtolower( substr( $ext, 1 ) );
 68+
6069 # trivial detection by file extension,
6170 # used for thumbnails (thumb.php)
6271 if ($wgTrivialMimeDetection) {
63 - $ext= strtolower(strrchr($filename, '.'));
64 -
6572 switch ($ext) {
66 - case '.gif': return 'image/gif';
67 - case '.png': return 'image/png';
68 - case '.jpg': return 'image/jpeg';
69 - case '.jpeg': return 'image/jpeg';
 73+ case 'gif': return 'image/gif';
 74+ case 'png': return 'image/png';
 75+ case 'jpg': return 'image/jpeg';
 76+ case 'jpeg': return 'image/jpeg';
7077 }
7178
7279 return 'unknown/unknown';
7380 }
74 - else {
75 - $magic = MimeMagic::singleton();
76 - return $magic->guessMimeType($filename); //full fancy mime detection
 81+
 82+ $magic = MimeMagic::singleton();
 83+ // Use the extension only, rather than magic numbers, to avoid opening
 84+ // up vulnerabilities due to uploads of files with allowed extensions
 85+ // but disallowed types.
 86+ $type = $magic->guessTypesForExtension( $ext );
 87+
 88+ /**
 89+ * Double-check some security settings that were done on upload but might
 90+ * have changed since.
 91+ */
 92+ if ( $safe ) {
 93+ global $wgFileBlacklist, $wgCheckFileExtensions, $wgStrictFileExtensions,
 94+ $wgFileExtensions, $wgVerifyMimeType, $wgMimeTypeBlacklist, $wgRequest;
 95+ $form = new UploadForm( $wgRequest );
 96+ list( $partName, $extList ) = $form->splitExtensions( $filename );
 97+ if ( $form->checkFileExtensionList( $extList, $wgFileBlacklist ) ) {
 98+ return 'unknown/unknown';
 99+ }
 100+ if ( $wgCheckFileExtensions && $wgStrictFileExtensions
 101+ && !$form->checkFileExtensionList( $extList, $wgFileExtensions ) )
 102+ {
 103+ return 'unknown/unknown';
 104+ }
 105+ if ( $wgVerifyMimeType && in_array( strtolower( $type ), $wgMimeTypeBlacklist ) ) {
 106+ return 'unknown/unknown';
 107+ }
77108 }
 109+ return $type;
78110 }
Index: branches/REL1_13/phase3/includes/DefaultSettings.php
@@ -31,7 +31,7 @@
3232 $wgConf = new SiteConfiguration;
3333
3434 /** MediaWiki version number */
35 -$wgVersion = '1.13.2';
 35+$wgVersion = '1.13.3';
3636
3737 /** Name of the site. It must be changed in LocalSettings.php */
3838 $wgSitename = 'MediaWiki';
Index: branches/REL1_13/phase3/includes/specials/SpecialUndelete.php
@@ -571,7 +571,7 @@
572572 */
573573 class UndeleteForm {
574574 var $mAction, $mTarget, $mTimestamp, $mRestore, $mTargetObj;
575 - var $mTargetTimestamp, $mAllowed, $mComment;
 575+ var $mTargetTimestamp, $mAllowed, $mComment, $mToken;
576576
577577 function UndeleteForm( $request, $par = "" ) {
578578 global $wgUser;
@@ -589,6 +589,7 @@
590590 $this->mDiff = $request->getCheck( 'diff' );
591591 $this->mComment = $request->getText( 'wpComment' );
592592 $this->mUnsuppress = $request->getVal( 'wpUnsuppress' ) && $wgUser->isAllowed( 'suppressrevision' );
 593+ $this->mToken = $request->getVal( 'token' );
593594
594595 if( $par != "" ) {
595596 $this->mTarget = $par;
@@ -655,6 +656,9 @@
656657 if( !$file->userCan( File::DELETED_FILE ) ) {
657658 $wgOut->permissionRequired( 'suppressrevision' );
658659 return false;
 660+ } elseif ( !$wgUser->matchEditToken( $this->mToken, $this->mFile ) ) {
 661+ $this->showFileConfirmationForm( $this->mFile );
 662+ return false;
659663 } else {
660664 return $this->showFile( $this->mFile );
661665 }
@@ -880,6 +884,29 @@
881885 }
882886
883887 /**
 888+ * Show a form confirming whether a tokenless user really wants to see a file
 889+ */
 890+ private function showFileConfirmationForm( $key ) {
 891+ global $wgOut, $wgUser, $wgLang;
 892+ $file = new ArchivedFile( $this->mTargetObj, '', $this->mFile );
 893+ $wgOut->addWikiMsg( 'undelete-show-file-confirm',
 894+ $this->mTargetObj->getText(),
 895+ $wgLang->timeanddate( $file->getTimestamp() ) );
 896+ $wgOut->addHTML(
 897+ Xml::openElement( 'form', array(
 898+ 'method' => 'POST',
 899+ 'action' => SpecialPage::getTitleFor( 'Undelete' )->getLocalUrl(
 900+ 'target=' . urlencode( $this->mTarget ) .
 901+ '&file=' . urlencode( $key ) .
 902+ '&token=' . urlencode( $wgUser->editToken( $key ) ) )
 903+ )
 904+ ) .
 905+ Xml::submitButton( wfMsg( 'undelete-show-file-submit' ) ) .
 906+ '</form>'
 907+ );
 908+ }
 909+
 910+ /**
884911 * Show a deleted file version requested by the visitor.
885912 */
886913 private function showFile( $key ) {
@@ -1191,13 +1218,15 @@
11921219 * @return string
11931220 */
11941221 function getFileLink( $file, $titleObj, $ts, $key, $sk ) {
1195 - global $wgLang;
 1222+ global $wgLang, $wgUser;
11961223
11971224 if( !$file->userCan(File::DELETED_FILE) ) {
11981225 return '<span class="history-deleted">' . $wgLang->timeanddate( $ts, true ) . '</span>';
11991226 } else {
12001227 $link = $sk->makeKnownLinkObj( $titleObj, $wgLang->timeanddate( $ts, true ),
1201 - "target=".$this->mTargetObj->getPrefixedUrl()."&file=$key" );
 1228+ "target=".$this->mTargetObj->getPrefixedUrl().
 1229+ "&file=$key" .
 1230+ "&token=" . urlencode( $wgUser->editToken( $key ) ) );
12021231 if( $file->isDeleted(File::DELETED_FILE) )
12031232 $link = '<span class="history-deleted">' . $link . '</span>';
12041233 return $link;
Index: branches/REL1_13/phase3/includes/specials/SpecialImport.php
@@ -43,26 +43,30 @@
4444 if( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit') {
4545 $isUpload = false;
4646 $namespace = $wgRequest->getIntOrNull( 'namespace' );
 47+ $sourceName = $wgRequest->getVal( "source" );
4748
48 - switch( $wgRequest->getVal( "source" ) ) {
49 - case "upload":
 49+ if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'editToken' ) ) ) {
 50+ $source = new WikiErrorMsg( 'import-token-mismatch' );
 51+ } elseif ( $sourceName == 'upload' ) {
5052 $isUpload = true;
5153 if( $wgUser->isAllowed( 'importupload' ) ) {
5254 $source = ImportStreamSource::newFromUpload( "xmlimport" );
5355 } else {
5456 return $wgOut->permissionRequired( 'importupload' );
5557 }
56 - break;
57 - case "interwiki":
 58+ } elseif ( $sourceName == "interwiki" ) {
5859 $interwiki = $wgRequest->getVal( 'interwiki' );
59 - $history = $wgRequest->getCheck( 'interwikiHistory' );
60 - $frompage = $wgRequest->getText( "frompage" );
61 - $source = ImportStreamSource::newFromInterwiki(
62 - $interwiki,
63 - $frompage,
64 - $history );
65 - break;
66 - default:
 60+ if ( !in_array( $interwiki, $wgImportSources ) ) {
 61+ $source = new WikiErrorMsg( "import-invalid-interwiki" );
 62+ } else {
 63+ $history = $wgRequest->getCheck( 'interwikiHistory' );
 64+ $frompage = $wgRequest->getText( "frompage" );
 65+ $source = ImportStreamSource::newFromInterwiki(
 66+ $interwiki,
 67+ $frompage,
 68+ $history );
 69+ }
 70+ } else {
6771 $source = new WikiErrorMsg( "importunknownsource" );
6872 }
6973
@@ -106,6 +110,7 @@
107111 Xml::hidden( 'action', 'submit' ) .
108112 Xml::hidden( 'source', 'upload' ) .
109113 Xml::input( 'xmlimport', 50, '', array( 'type' => 'file' ) ) . ' ' .
 114+ Xml::hidden( 'editToken', $wgUser->editToken() ) .
110115 Xml::submitButton( wfMsg( 'uploadbtn' ) ) .
111116 Xml::closeElement( 'form' ) .
112117 Xml::closeElement( 'fieldset' )
@@ -124,6 +129,7 @@
125130 wfMsgExt( 'import-interwiki-text', array( 'parse' ) ) .
126131 Xml::hidden( 'action', 'submit' ) .
127132 Xml::hidden( 'source', 'interwiki' ) .
 133+ Xml::hidden( 'editToken', $wgUser->editToken() ) .
128134 Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
129135 "<tr>
130136 <td>" .
Index: branches/REL1_13/phase3/includes/specials/SpecialUpload.php
@@ -1348,6 +1348,11 @@
13491349 if( $this->detectScript ( $tmpfile, $mime, $extension ) ) {
13501350 return new WikiErrorMsg( 'uploadscripted' );
13511351 }
 1352+ if( $extension == 'svg' || $mime == 'image/svg+xml' ) {
 1353+ if( $this->detectScriptInSvg( $tmpfile ) ) {
 1354+ return new WikiErrorMsg( 'uploadscripted' );
 1355+ }
 1356+ }
13521357
13531358 /**
13541359 * Scan the uploaded file for viruses
@@ -1459,6 +1464,7 @@
14601465 */
14611466
14621467 $tags = array(
 1468+ '<a href',
14631469 '<body',
14641470 '<head',
14651471 '<html', #also in safari
@@ -1497,7 +1503,42 @@
14981504 return false;
14991505 }
15001506
 1507+ function detectScriptInSvg( $filename ) {
 1508+ $check = new XmlTypeCheck( $filename, array( $this, 'checkSvgScriptCallback' ) );
 1509+ return $check->filterMatch;
 1510+ }
 1511+
15011512 /**
 1513+ * @todo Replace this with a whitelist filter!
 1514+ */
 1515+ function checkSvgScriptCallback( $element, $attribs ) {
 1516+ $stripped = $this->stripXmlNamespace( $element );
 1517+
 1518+ if( $stripped == 'script' ) {
 1519+ wfDebug( __METHOD__ . ": Found script element '$element' in uploaded file.\n" );
 1520+ return true;
 1521+ }
 1522+
 1523+ foreach( $attribs as $attrib => $value ) {
 1524+ $stripped = $this->stripXmlNamespace( $attrib );
 1525+ if( substr( $stripped, 0, 2 ) == 'on' ) {
 1526+ wfDebug( __METHOD__ . ": Found script attribute '$attrib'='value' in uploaded file.\n" );
 1527+ return true;
 1528+ }
 1529+ if( $stripped == 'href' && strpos( strtolower( $value ), 'javascript:' ) !== false ) {
 1530+ wfDebug( __METHOD__ . ": Found script href attribute '$attrib'='$value' in uploaded file.\n" );
 1531+ return true;
 1532+ }
 1533+ }
 1534+ }
 1535+
 1536+ private function stripXmlNamespace( $name ) {
 1537+ // 'http://www.w3.org/2000/svg:script' -> 'script'
 1538+ $parts = explode( ':', strtolower( $name ) );
 1539+ return array_pop( $parts );
 1540+ }
 1541+
 1542+ /**
15021543 * Generic wrapper function for a virus scanner program.
15031544 * This relies on the $wgAntivirus and $wgAntivirusSetup variables.
15041545 * $wgAntivirusRequired may be used to deny upload if the scan fails.
Index: branches/REL1_13/phase3/includes/XmlTypeCheck.php
@@ -6,6 +6,12 @@
77 * well-formed XML. Note that this doesn't check schema validity.
88 */
99 public $wellFormed = false;
 10+
 11+ /**
 12+ * Will be set to true if the optional element filter returned
 13+ * a match at some point.
 14+ */
 15+ public $filterMatch = false;
1016
1117 /**
1218 * Name of the document's root element, including any namespace
@@ -13,33 +19,26 @@
1420 */
1521 public $rootElement = '';
1622
17 - private $softNamespaces;
18 - private $namespaces = array();
19 -
2023 /**
2124 * @param $file string filename
22 - * @param $softNamespaces bool
23 - * If set to true, use of undeclared XML namespaces will be ignored.
24 - * This matches the behavior of rsvg, but more compliant consumers
25 - * such as Firefox will reject such files.
26 - * Leave off for the default, stricter checks.
 25+ * @param $filterCallback callable (optional)
 26+ * Function to call to do additional custom validity checks from the
 27+ * SAX element handler event. This gives you access to the element
 28+ * namespace, name, and attributes, but not to text contents.
 29+ * Filter should return 'true' to toggle on $this->filterMatch
2730 */
28 - function __construct( $file, $softNamespaces=false ) {
29 - $this->softNamespaces = $softNamespaces;
 31+ function __construct( $file, $filterCallback=null ) {
 32+ $this->filterCallback = $filterCallback;
3033 $this->run( $file );
3134 }
3235
3336 private function run( $fname ) {
34 - if( $this->softNamespaces ) {
35 - $parser = xml_parser_create( 'UTF-8' );
36 - } else {
37 - $parser = xml_parser_create_ns( 'UTF-8' );
38 - }
 37+ $parser = xml_parser_create_ns( 'UTF-8' );
3938
4039 // case folding violates XML standard, turn it off
4140 xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
4241
43 - xml_set_element_handler( $parser, array( $this, 'elementOpen' ), false );
 42+ xml_set_element_handler( $parser, array( $this, 'rootElementOpen' ), false );
4443
4544 $file = fopen( $fname, "rb" );
4645 do {
@@ -59,35 +58,22 @@
6059 xml_parser_free( $parser );
6160 }
6261
 62+ private function rootElementOpen( $parser, $name, $attribs ) {
 63+ $this->rootElement = $name;
 64+
 65+ if( is_callable( $this->filterCallback ) ) {
 66+ xml_set_element_handler( $parser, array( $this, 'elementOpen' ), false );
 67+ $this->elementOpen( $parser, $name, $attribs );
 68+ } else {
 69+ // We only need the first open element
 70+ xml_set_element_handler( $parser, false, false );
 71+ }
 72+ }
 73+
6374 private function elementOpen( $parser, $name, $attribs ) {
64 - if( $this->softNamespaces ) {
65 - // Check namespaces manually, so expat doesn't throw
66 - // errors on use of undeclared namespaces.
67 - foreach( $attribs as $attrib => $val ) {
68 - if( $attrib == 'xmlns' ) {
69 - $this->namespaces[''] = $val;
70 - } elseif( substr( $attrib, 0, strlen( 'xmlns:' ) ) == 'xmlns:' ) {
71 - $this->namespaces[substr( $attrib, strlen( 'xmlns:' ) )] = $val;
72 - }
73 - }
74 -
75 - if( strpos( $name, ':' ) === false ) {
76 - $ns = '';
77 - $subname = $name;
78 - } else {
79 - list( $ns, $subname ) = explode( ':', $name, 2 );
80 - }
81 -
82 - if( isset( $this->namespaces[$ns] ) ) {
83 - $name = $this->namespaces[$ns] . ':' . $subname;
84 - } else {
85 - // Technically this is invalid for XML with Namespaces.
86 - // But..... we'll just let it slide in soft mode.
87 - }
 75+ if( call_user_func( $this->filterCallback, $name, $attribs ) ) {
 76+ // Filter hit!
 77+ $this->filterMatch = true;
8878 }
89 -
90 - // We only need the first open element
91 - $this->rootElement = $name;
92 - xml_set_element_handler( $parser, false, false );
9379 }
9480 }
Index: branches/REL1_13/phase3/includes/Exception.php
@@ -274,7 +274,16 @@
275275 }
276276 }
277277 } else {
278 - echo $e->__toString();
 278+ $message = "Unexpected non-MediaWiki exception encountered, of type \"" . get_class( $e ) . "\"\n" .
 279+ $e->__toString() . "\n";
 280+ if ( $GLOBALS['wgShowExceptionDetails'] ) {
 281+ $message .= "\n" . $e->getTraceAsString() ."\n";
 282+ }
 283+ if ( !empty( $GLOBALS['wgCommandLineMode'] ) ) {
 284+ wfPrintError( $message );
 285+ } else {
 286+ echo nl2br( htmlspecialchars( $message ) ). "\n";
 287+ }
279288 }
280289 }
281290
Index: branches/REL1_13/phase3/img_auth.php
@@ -17,6 +17,12 @@
1818 wfProfileIn( 'img_auth.php' );
1919 require_once( dirname( __FILE__ ) . '/includes/StreamFile.php' );
2020
 21+$perms = User::getGroupPermissions( array( '*' ) );
 22+if ( in_array( 'read', $perms, true ) ) {
 23+ wfDebugLog( 'img_auth', 'Public wiki' );
 24+ wfPublicError();
 25+}
 26+
2127 // Extract path and image information
2228 if( !isset( $_SERVER['PATH_INFO'] ) ) {
2329 wfDebugLog( 'img_auth', 'Missing PATH_INFO' );
@@ -88,3 +94,25 @@
8995 wfLogProfilingData();
9096 exit();
9197 }
 98+
 99+/**
 100+ * Show a 403 error for use when the wiki is public
 101+ */
 102+function wfPublicError() {
 103+ header( 'HTTP/1.0 403 Forbidden' );
 104+ header( 'Content-Type: text/html; charset=utf-8' );
 105+ echo <<<ENDS
 106+<html>
 107+<body>
 108+<h1>Access Denied</h1>
 109+<p>The function of img_auth.php is to output files from a private wiki. This wiki
 110+is configured as a public wiki. For optimal security, img_auth.php is disabled in
 111+this case.
 112+</p>
 113+</body>
 114+</html>
 115+ENDS;
 116+ wfLogProfilingData();
 117+ exit;
 118+}
 119+
Index: branches/REL1_13/phase3/profileinfo.php
@@ -60,7 +60,7 @@
6161
6262 define( 'MW_NO_SETUP', 1 );
6363 require_once( './includes/WebStart.php' );
64 -require_once("./AdminSettings.php");
 64+@include_once("./AdminSettings.php");
6565 require_once( './includes/GlobalFunctions.php' );
6666
6767 if (!$wgEnableProfileInfo) {
Index: branches/REL1_13/phase3/languages/messages/MessagesEn.php
@@ -2288,6 +2288,8 @@
22892289 'undelete-error-long' => 'Errors were encountered while undeleting the file:
22902290
22912291 $1',
 2292+'undelete-show-file-confirm' => 'Are you sure you want to view a deleted revision of the file "<nowiki>$1</nowiki>" from $2?',
 2293+'undelete-show-file-submit' => 'Yes',
22922294
22932295 # Namespace form on various pages
22942296 'namespace' => 'Namespace:',
@@ -2583,6 +2585,8 @@
25842586 'import-nonewrevisions' => 'All revisions were previously imported.',
25852587 'xml-error-string' => '$1 at line $2, col $3 (byte $4): $5',
25862588 'import-upload' => 'Upload XML data',
 2589+'import-token-mismatch' => 'Loss of session data. Please try again.',
 2590+'import-invalid-interwiki' => 'Cannot import from the specified wiki.',
25872591
25882592 # Import log
25892593 'importlogpage' => 'Import log',
Index: branches/REL1_13/phase3/RELEASE-NOTES
@@ -3,9 +3,9 @@
44 Security reminder: MediaWiki does not require PHP's register_globals
55 setting since version 1.2.0. If you have it on, turn it *off* if you can.
66
7 -== MediaWiki 1.13.2 ==
 7+== MediaWiki 1.13.3 ==
88
9 -October 2, 2008
 9+November 18, 2008
1010
1111 This is a security and bugfix release of the Summer 2008 snapshot release of
1212 MediaWiki.
@@ -21,6 +21,18 @@
2222 Those wishing to use the latest code instead of a branch release can obtain
2323 it from source control: http://www.mediawiki.org/wiki/Download_from_SVN
2424
 25+== Changes since 1.13.2 ==
 26+
 27+* Safer handling of non-MediaWiki exceptions -- now obeys our settings for formatting and path exposure. (Rem1)
 28+* Less verbose errors from profileinfo.php when not configured (Rem8)
 29+* Blacklist redirects via Special:Filepath, hard to use. (Rem7)
 30+* Improved input validation on Special:Import form (Rem10, Rem11)
 31+* Add a .htaccess to deleted images directory for additional protection against exposure of deleted files with known SHA-1 hashes on default installations. (Rem13)
 32+* Improved scripting safety heuristics for IE 5/6 content-type detection. (Rem14)
 33+* Improved scripting safety heuristics on SVG uploads. (Rem2, Rem3, Rem5, Rem6)
 34+* Improved the security of file streaming (Special:Undelete, img_auth.php and thumb.php): use the extension to determine the type, check it against the blacklist. (Rem12.2)
 35+* Restrict img_auth.php to private wikis only. Require a session token before streaming out Special:Undelete. If uploads are hosted on a different domain, then these changes reduce the chance that an upload containing a script might steal cookies from the wiki. (Rem12.1)
 36+
2537 == Changes since 1.13.1 ==
2638
2739 * Security: Work around misconfiguration by requiring strict comparisons for

Follow-up revisions

RevisionCommit summaryAuthorDate
r44570Backported r43625 again, apparently it got left out of r43669 somehow.tstarling08:38, 14 December 2008

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r43621* Safer handling of non-MediaWiki exceptions -- now obeys our settings for fo...brion18:51, 17 November 2008
r43622* Less verbose errors from profileinfo.php when not configuredbrion18:53, 17 November 2008
r43623* Blacklist redirects via Special:Filepath, hard to use....brion18:54, 17 November 2008
r43624* Improved input validation on Special:Import form...brion18:58, 17 November 2008
r43625* Add a .htaccess to deleted images directory for additional protection...brion19:01, 17 November 2008
r43627* Improved scripting safety heuristics for IE 5/6 content-type detection....brion19:03, 17 November 2008
r43660Improved the security of wfStreamFile():...tstarling05:07, 18 November 2008
r43661Protect users from attacks against their browsers via malicious script-contai...tstarling05:57, 18 November 2008

Status & tagging log