summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/DefaultSettings.php2
-rw-r--r--includes/User.php22
-rw-r--r--includes/Wiki.php13
-rw-r--r--includes/api/ApiFormatBase.php18
-rw-r--r--includes/db/DatabaseOracle.php32
-rw-r--r--includes/db/DatabasePostgres.php16
-rw-r--r--includes/installer/WebInstallerPage.php9
-rw-r--r--includes/specials/SpecialPrefixindex.php1
-rw-r--r--includes/upload/UploadBase.php71
9 files changed, 165 insertions, 19 deletions
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index e9b4f490..820d6093 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -63,7 +63,7 @@ $wgConf = new SiteConfiguration;
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.22.2';
+$wgVersion = '1.22.3';
/**
* Name of the site. It must be changed in LocalSettings.php
diff --git a/includes/User.php b/includes/User.php
index 12912e1c..62324043 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -984,7 +984,8 @@ class User {
# Get the token from DB/cache and clean it up to remove garbage padding.
# This deals with historical problems with bugs and the default column value.
$token = rtrim( $proposedUser->getToken( false ) ); // correct token
- $passwordCorrect = ( strlen( $token ) && $token === $request->getCookie( 'Token' ) );
+ // Make comparison in constant time (bug 61346)
+ $passwordCorrect = strlen( $token ) && $this->compareSecrets( $token, $request->getCookie( 'Token' ) );
$from = 'cookie';
} else {
// No session or persistent login cookie
@@ -1004,6 +1005,25 @@ class User {
}
/**
+ * A comparison of two strings, not vulnerable to timing attacks
+ * @param string $answer the secret string that you are comparing against.
+ * @param string $test compare this string to the $answer.
+ * @return bool True if the strings are the same, false otherwise
+ */
+ protected function compareSecrets( $answer, $test ) {
+ if ( strlen( $answer ) !== strlen( $test ) ) {
+ $passwordCorrect = false;
+ } else {
+ $result = 0;
+ for ( $i = 0; $i < strlen( $answer ); $i++ ) {
+ $result |= ord( $answer{$i} ) ^ ord( $test{$i} );
+ }
+ $passwordCorrect = ( $result == 0 );
+ }
+ return $passwordCorrect;
+ }
+
+ /**
* Load user and user_group data from the database.
* $this->mId must be set, this is how the user is identified.
*
diff --git a/includes/Wiki.php b/includes/Wiki.php
index ae75bf33..074ec1ab 100644
--- a/includes/Wiki.php
+++ b/includes/Wiki.php
@@ -653,12 +653,19 @@ class MediaWiki {
}
if ( !wfShellExecDisabled() && is_executable( $wgPhpCli ) ) {
- // Start a background process to run some of the jobs.
- // This will be asynchronous on *nix though not on Windows.
+ // Start a background process to run some of the jobs
wfProfileIn( __METHOD__ . '-exec' );
$retVal = 1;
$cmd = wfShellWikiCmd( "$IP/maintenance/runJobs.php", array( '--maxjobs', $n ) );
- wfShellExec( "$cmd &", $retVal );
+ $cmd .= " >" . wfGetNull() . " 2>&1"; // don't hang PHP on pipes
+ if ( wfIsWindows() ) {
+ // Using START makes this async and also works around a bug where using
+ // wfShellExec() with a quoted script name causes a filename syntax error.
+ $cmd = "START /B \"bg\" $cmd";
+ } else {
+ $cmd = "$cmd &";
+ }
+ wfShellExec( $cmd, $retVal );
wfProfileOut( __METHOD__ . '-exec' );
} else {
try {
diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php
index b89fb3a7..70495439 100644
--- a/includes/api/ApiFormatBase.php
+++ b/includes/api/ApiFormatBase.php
@@ -272,17 +272,33 @@ See the <a href='https://www.mediawiki.org/wiki/API'>complete documentation</a>,
// encode all comments or tags as safe blue strings
$text = str_replace( '&lt;', '<span style="color:blue;">&lt;', $text );
$text = str_replace( '&gt;', '&gt;</span>', $text );
+
// identify requests to api.php
- $text = preg_replace( "#api\\.php\\?[^ <\n\t]+#", '<a href="\\0">\\0</a>', $text );
+ $text = preg_replace( '#^(\s*)(api\.php\?[^ <\n\t]+)$#m', '\1<a href="\2">\2</a>', $text );
if ( $this->mHelp ) {
// make strings inside * bold
$text = preg_replace( "#\\*[^<>\n]+\\*#", '<b>\\0</b>', $text );
}
+
+ // Armor links (bug 61362)
+ $masked = array();
+ $text = preg_replace_callback( '#<a .*?</a>#', function ( $matches ) use ( &$masked ) {
+ $sha = sha1( $matches[0] );
+ $masked[$sha] = $matches[0];
+ return "<$sha>";
+ }, $text );
+
// identify URLs
$protos = wfUrlProtocolsWithoutProtRel();
// This regex hacks around bug 13218 (&quot; included in the URL)
$text = preg_replace( "#(((?i)$protos).*?)(&quot;)?([ \\'\"<>\n]|&lt;|&gt;|&quot;)#", '<a href="\\1">\\1</a>\\3\\4', $text );
+ // Unarmor links
+ $text = preg_replace_callback( '#<([0-9a-f]{40})>#', function ( $matches ) use ( &$masked ) {
+ $sha = $matches[1];
+ return isset( $masked[$sha] ) ? $masked[$sha] : $matches[0];
+ }, $text );
+
/**
* Temporary fix for bad links in help messages. As a special case,
* XML-escaped metachars are de-escaped one level in the help message
diff --git a/includes/db/DatabaseOracle.php b/includes/db/DatabaseOracle.php
index 32d4d984..fb2d4359 100644
--- a/includes/db/DatabaseOracle.php
+++ b/includes/db/DatabaseOracle.php
@@ -551,8 +551,12 @@ class DatabaseOracle extends DatabaseBase {
} else {
$first = false;
}
-
- $sql .= $this->fieldBindStatement( $table, $col, $val );
+ if ( $this->isQuotedIdentifier( $val ) ) {
+ $sql .= $this->removeIdentifierQuotes( $val );
+ unset( $row[$col] );
+ } else {
+ $sql .= $this->fieldBindStatement( $table, $col, $val );
+ }
}
$sql .= ')';
@@ -679,6 +683,30 @@ class DatabaseOracle extends DatabaseBase {
return $retval;
}
+ public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
+ $fname = __METHOD__
+ ) {
+ if ( !count( $rows ) ) {
+ return true; // nothing to do
+ }
+
+ if ( !is_array( reset( $rows ) ) ) {
+ $rows = array( $rows );
+ }
+
+ $sequenceData = $this->getSequenceData( $table );
+ if ( $sequenceData !== false ) {
+ // add sequence column to each list of columns, when not set
+ foreach ( $rows as &$row ) {
+ if ( !isset( $row[$sequenceData['column']] ) ) {
+ $row[$sequenceData['column']] = $this->addIdentifierQuotes('GET_SEQUENCE_VALUE(\'' . $sequenceData['sequence'] . '\')');
+ }
+ }
+ }
+
+ return parent::upsert( $table, $rows, $uniqueIndexes, $set, $fname );
+ }
+
function tableName( $name, $format = 'quoted' ) {
/*
Replace reserved words with better ones
diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php
index aed35f10..0bd966ba 100644
--- a/includes/db/DatabasePostgres.php
+++ b/includes/db/DatabasePostgres.php
@@ -729,13 +729,15 @@ __INDEXATTR__;
* so causes a DB error. This wrapper checks which tables can be locked and adjusts it accordingly.
*/
function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__, $options = array(), $join_conds = array() ) {
- $forUpdateKey = array_search( 'FOR UPDATE', $options );
- if ( $forUpdateKey !== false && $join_conds ) {
- unset( $options[$forUpdateKey] );
-
- foreach ( $join_conds as $table => $join_cond ) {
- if ( 0 === preg_match( '/^(?:LEFT|RIGHT|FULL)(?: OUTER)? JOIN$/i', $join_cond[0] ) ) {
- $options['FOR UPDATE'][] = $table;
+ if ( is_array( $options ) ) {
+ $forUpdateKey = array_search( 'FOR UPDATE', $options );
+ if ( $forUpdateKey !== false && $join_conds ) {
+ unset( $options[$forUpdateKey] );
+
+ foreach ( $join_conds as $table_cond => $join_cond ) {
+ if ( 0 === preg_match( '/^(?:LEFT|RIGHT|FULL)(?: OUTER)? JOIN$/i', $join_cond[0] ) ) {
+ $options['FOR UPDATE'][] = $table_cond;
+ }
}
}
}
diff --git a/includes/installer/WebInstallerPage.php b/includes/installer/WebInstallerPage.php
index ad399133..d3b550fe 100644
--- a/includes/installer/WebInstallerPage.php
+++ b/includes/installer/WebInstallerPage.php
@@ -955,11 +955,16 @@ class WebInstaller_Options extends WebInstallerPage {
LinkCache::singleton()->useDatabase( false );
foreach ( $extensions as $ext ) {
+ if ( isset( $ext['descriptionmsg'] ) ) {
+ $desc = wfMessage( $ext['descriptionmsg'] )->useDatabase( false )->parse();
+ } else {
+ $desc = '';
+ }
$extHtml .= $this->parent->getCheckBox( array(
'var' => "ext-{$ext['name']}",
'rawtext' => "<b>{$ext['name']}</b>: " .
- wfMessage( $ext['descriptionmsg'] )->useDatabase( false )->parse(),
- ) );
+ $desc,
+ ) );
}
diff --git a/includes/specials/SpecialPrefixindex.php b/includes/specials/SpecialPrefixindex.php
index 28d07ffc..0d065b09 100644
--- a/includes/specials/SpecialPrefixindex.php
+++ b/includes/specials/SpecialPrefixindex.php
@@ -264,6 +264,7 @@ class SpecialPrefixindex extends SpecialAllpages {
'from' => $s->page_title,
'prefix' => $prefix,
'hideredirects' => $this->hideRedirects,
+ 'stripprefix' => $this->stripPrefix,
);
if ( $namespace || $prefix == '' ) {
diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php
index 916ad6c1..c0c37b3f 100644
--- a/includes/upload/UploadBase.php
+++ b/includes/upload/UploadBase.php
@@ -44,7 +44,7 @@ abstract class UploadBase {
protected $mFilteredName, $mFinalExtension;
protected $mLocalFile, $mFileSize, $mFileProps;
protected $mBlackListedExtensions;
- protected $mJavaDetected;
+ protected $mJavaDetected, $mSVGNSError;
protected static $safeXmlEncodings = array( 'UTF-8', 'ISO-8859-1', 'ISO-8859-2', 'UTF-16', 'UTF-32' );
@@ -1162,6 +1162,7 @@ abstract class UploadBase {
* @return bool
*/
protected function detectScriptInSvg( $filename ) {
+ $this->mSVGNSError = false;
$check = new XmlTypeCheck(
$filename,
array( $this, 'checkSvgScriptCallback' ),
@@ -1172,6 +1173,9 @@ abstract class UploadBase {
// Invalid xml (bug 58553)
return array( 'uploadinvalidxml' );
} elseif ( $check->filterMatch ) {
+ if ( $this->mSVGNSError ) {
+ return array( 'uploadscriptednamespace', $this->mSVGNSError );
+ }
return array( 'uploadscripted' );
}
return false;
@@ -1198,7 +1202,51 @@ abstract class UploadBase {
* @return bool
*/
public function checkSvgScriptCallback( $element, $attribs ) {
- $strippedElement = $this->stripXmlNamespace( $element );
+ list( $namespace, $strippedElement ) = $this->splitXmlNamespace( $element );
+
+ static $validNamespaces = array(
+ '',
+ 'adobe:ns:meta/',
+ 'http://creativecommons.org/ns#',
+ 'http://inkscape.sourceforge.net/dtd/sodipodi-0.dtd',
+ 'http://ns.adobe.com/adobeillustrator/10.0/',
+ 'http://ns.adobe.com/adobesvgviewerextensions/3.0/',
+ 'http://ns.adobe.com/extensibility/1.0/',
+ 'http://ns.adobe.com/flows/1.0/',
+ 'http://ns.adobe.com/illustrator/1.0/',
+ 'http://ns.adobe.com/imagereplacement/1.0/',
+ 'http://ns.adobe.com/pdf/1.3/',
+ 'http://ns.adobe.com/photoshop/1.0/',
+ 'http://ns.adobe.com/saveforweb/1.0/',
+ 'http://ns.adobe.com/variables/1.0/',
+ 'http://ns.adobe.com/xap/1.0/',
+ 'http://ns.adobe.com/xap/1.0/g/',
+ 'http://ns.adobe.com/xap/1.0/g/img/',
+ 'http://ns.adobe.com/xap/1.0/mm/',
+ 'http://ns.adobe.com/xap/1.0/rights/',
+ 'http://ns.adobe.com/xap/1.0/stype/dimensions#',
+ 'http://ns.adobe.com/xap/1.0/stype/font#',
+ 'http://ns.adobe.com/xap/1.0/stype/manifestitem#',
+ 'http://ns.adobe.com/xap/1.0/stype/resourceevent#',
+ 'http://ns.adobe.com/xap/1.0/stype/resourceref#',
+ 'http://ns.adobe.com/xap/1.0/t/pg/',
+ 'http://purl.org/dc/elements/1.1/',
+ 'http://purl.org/dc/elements/1.1',
+ 'http://schemas.microsoft.com/visio/2003/svgextensions/',
+ 'http://sodipodi.sourceforge.net/dtd/sodipodi-0.dtd',
+ 'http://web.resource.org/cc/',
+ 'http://www.freesoftware.fsf.org/bkchem/cdml',
+ 'http://www.inkscape.org/namespaces/inkscape',
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
+ 'http://www.w3.org/2000/svg',
+ );
+
+ if ( !in_array( $namespace, $validNamespaces ) ) {
+ wfDebug( __METHOD__ . ": Non-svg namespace '$namespace' in uploaded file.\n" );
+ // @TODO return a status object to a closure in XmlTypeCheck, for MW1.21+
+ $this->mSVGNSError = $namespace;
+ return true;
+ }
/*
* check for elements that can contain javascript
@@ -1220,6 +1268,12 @@ abstract class UploadBase {
return true;
}
+ # Block iframes, in case they pass the namespace check
+ if ( $strippedElement == 'iframe' ) {
+ wfDebug( __METHOD__ . ": iframe in uploaded file.\n" );
+ return true;
+ }
+
foreach ( $attribs as $attrib => $value ) {
$stripped = $this->stripXmlNamespace( $attrib );
$value = strtolower( $value );
@@ -1294,6 +1348,19 @@ abstract class UploadBase {
}
/**
+ * Divide the element name passed by the xml parser to the callback into URI and prifix.
+ * @param $name string
+ * @return array containing the namespace URI and prefix
+ */
+ private static function splitXmlNamespace( $element ) {
+ // 'http://www.w3.org/2000/svg:script' -> array( 'http://www.w3.org/2000/svg', 'script' )
+ $parts = explode( ':', strtolower( $element ) );
+ $name = array_pop( $parts );
+ $ns = implode( ':', $parts );
+ return array( $ns, $name );
+ }
+
+ /**
* @param $name string
* @return string
*/