summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
authorPierre Schmitz <pierre@archlinux.de>2016-05-21 08:33:14 +0200
committerPierre Schmitz <pierre@archlinux.de>2016-05-21 08:33:14 +0200
commit7bf2eb8ba09b54cec804446ea39a3e658773fac9 (patch)
tree12fa50d1d49fe0c7f9b5cff08aa88d93f5d4146f /includes
parentc96958a50a97382ef4ada897d1e7120d7a222a28 (diff)
Update to MediaWiki 1.26.3
Diffstat (limited to 'includes')
-rw-r--r--includes/DefaultSettings.php16
-rw-r--r--includes/Defines.php6
-rw-r--r--includes/GlobalFunctions.php8
-rw-r--r--includes/Import.php18
-rw-r--r--includes/Linker.php11
-rw-r--r--includes/MediaWiki.php13
-rw-r--r--includes/OutputHandler.php4
-rw-r--r--includes/OutputPage.php28
-rw-r--r--includes/User.php3
-rw-r--r--includes/WebStart.php3
-rw-r--r--includes/actions/RawAction.php6
-rw-r--r--includes/api/ApiBase.php8
-rw-r--r--includes/api/ApiFormatJson.php4
-rw-r--r--includes/api/ApiFormatPhp.php2
-rw-r--r--includes/api/ApiMain.php35
-rw-r--r--includes/api/ApiMove.php5
-rw-r--r--includes/db/DatabasePostgres.php3
-rw-r--r--includes/diff/DairikiDiff.php6
-rw-r--r--includes/diff/DifferenceEngine.php31
-rw-r--r--includes/page/Article.php2
-rw-r--r--includes/parser/CoreTagHooks.php24
-rw-r--r--includes/parser/Parser.php28
-rw-r--r--includes/password/MWOldPassword.php4
-rw-r--r--includes/password/MWSaltedPassword.php4
-rw-r--r--includes/password/Pbkdf2Password.php9
-rw-r--r--includes/resourceloader/ResourceLoaderFileModule.php1
-rw-r--r--includes/resourceloader/ResourceLoaderImageModule.php6
-rw-r--r--includes/resourceloader/ResourceLoaderModule.php17
-rw-r--r--includes/resourceloader/ResourceLoaderWikiModule.php2
-rw-r--r--includes/specials/SpecialUserlogin.php15
-rw-r--r--includes/upload/UploadBase.php2
31 files changed, 233 insertions, 91 deletions
diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 61fec6e1..7498a021 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -75,7 +75,7 @@ $wgConfigRegistry = array(
* MediaWiki version number
* @since 1.2
*/
-$wgVersion = '1.26.2';
+$wgVersion = '1.26.3';
/**
* Name of the site. It must be changed in LocalSettings.php
@@ -4188,7 +4188,13 @@ $wgDebugTidy = false;
$wgRawHtml = false;
/**
- * Set a default target for external links, e.g. _blank to pop up a new window
+ * Set a default target for external links, e.g. _blank to pop up a new window.
+ *
+ * This will also set the "noreferrer" and "noopener" link rel to prevent the
+ * attack described at https://mathiasbynens.github.io/rel-noopener/ .
+ * Some older browsers may not support these link attributes, hence
+ * setting $wgExternalLinkTarget to _blank may represent a security risk
+ * to some of your users.
*/
$wgExternalLinkTarget = false;
@@ -4438,9 +4444,9 @@ $wgPasswordConfig = array(
),
'pbkdf2' => array(
'class' => 'Pbkdf2Password',
- 'algo' => 'sha256',
- 'cost' => '10000',
- 'length' => '128',
+ 'algo' => 'sha512',
+ 'cost' => '30000',
+ 'length' => '64',
),
);
diff --git a/includes/Defines.php b/includes/Defines.php
index d55bbcf8..2f3d64fe 100644
--- a/includes/Defines.php
+++ b/includes/Defines.php
@@ -304,3 +304,9 @@ define( 'CONTENT_FORMAT_JSON', 'application/json' );
// for future use with the api, and for use by extensions
define( 'CONTENT_FORMAT_XML', 'application/xml' );
/**@}*/
+
+/**@{
+ * Max string length for shell invocations; based on binfmts.h
+ */
+define( 'SHELL_MAX_ARG_STRLEN', '100000');
+/**@}*/
diff --git a/includes/GlobalFunctions.php b/includes/GlobalFunctions.php
index 64aa87ec..c4d5b5bc 100644
--- a/includes/GlobalFunctions.php
+++ b/includes/GlobalFunctions.php
@@ -2812,6 +2812,14 @@ function wfShellExec( $cmd, &$retval = null, $environ = array(),
}
wfDebug( "wfShellExec: $cmd\n" );
+ // Don't try to execute commands that exceed Linux's MAX_ARG_STRLEN.
+ // Other platforms may be more accomodating, but we don't want to be
+ // accomodating, because very long commands probably include user
+ // input. See T129506.
+ if ( strlen( $cmd ) > SHELL_MAX_ARG_STRLEN ) {
+ throw new Exception( __METHOD__ . '(): total length of $cmd must not exceed SHELL_MAX_ARG_STRLEN' );
+ }
+
$desc = array(
0 => array( 'file', 'php://stdin', 'r' ),
1 => array( 'pipe', 'w' ),
diff --git a/includes/Import.php b/includes/Import.php
index 6a0bfd09..db4a6b2d 100644
--- a/includes/Import.php
+++ b/includes/Import.php
@@ -728,13 +728,14 @@ class WikiImporter {
$title = $this->processTitle( $pageInfo['title'],
isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
- if ( !$title ) {
+ // $title is either an array of two titles or false.
+ if ( is_array( $title ) ) {
+ $this->pageCallback( $title );
+ list( $pageInfo['_title'], $foreignTitle ) = $title;
+ } else {
$badTitle = true;
$skip = true;
}
-
- $this->pageCallback( $title );
- list( $pageInfo['_title'], $foreignTitle ) = $title;
}
if ( $title ) {
@@ -750,10 +751,17 @@ class WikiImporter {
}
}
- $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
+ // @note $pageInfo is only set if a valid $title is processed above with
+ // no error. If we have a valid $title, then pageCallback is called
+ // above, $pageInfo['title'] is set and we do pageOutCallback here.
+ // If $pageInfo['_title'] is not set, then $foreignTitle is also not
+ // set since they both come from $title above.
+ if ( array_key_exists( '_title', $pageInfo ) ) {
+ $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
$pageInfo['revisionCount'],
$pageInfo['successfulRevisionCount'],
$pageInfo );
+ }
}
/**
diff --git a/includes/Linker.php b/includes/Linker.php
index 9b5ff27b..f0fa4a5a 100644
--- a/includes/Linker.php
+++ b/includes/Linker.php
@@ -1073,7 +1073,16 @@ class Linker {
if ( !$title ) {
$title = $wgTitle;
}
- $attribs['rel'] = Parser::getExternalLinkRel( $url, $title );
+ $newRel = Parser::getExternalLinkRel( $url, $title );
+ if ( !isset( $attribs['rel'] ) || $attribs['rel'] === '' ) {
+ $attribs['rel'] = $newRel;
+ } elseif( $newRel !== '' ) {
+ // Merge the rel attributes.
+ $newRels = explode( ' ', $newRel );
+ $oldRels = explode( ' ', $attribs['rel'] );
+ $combined = array_unique( array_merge( $newRels, $oldRels ) );
+ $attribs['rel'] = implode( ' ', $combined );
+ }
$link = '';
$success = Hooks::run( 'LinkerMakeExternalLink',
array( &$url, &$text, &$link, &$attribs, $linktype ) );
diff --git a/includes/MediaWiki.php b/includes/MediaWiki.php
index 2da2f6ce..a902f367 100644
--- a/includes/MediaWiki.php
+++ b/includes/MediaWiki.php
@@ -806,9 +806,18 @@ class MediaWiki {
$errno = $errstr = null;
$info = wfParseUrl( $this->config->get( 'Server' ) );
MediaWiki\suppressWarnings();
+ $host = $info['host'];
+ $port = 80;
+ if ( isset( $info['scheme'] ) && $info['scheme'] == 'https' ) {
+ $host = "tls://" . $host;
+ $port = 443;
+ }
+ if ( isset( $info['port'] ) ) {
+ $port = $info['port'];
+ }
$sock = fsockopen(
- $info['host'],
- isset( $info['port'] ) ? $info['port'] : 80,
+ $host,
+ $port,
$errno,
$errstr,
// If it takes more than 100ms to connect to ourselves there
diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php
index c6209eeb..dd99361e 100644
--- a/includes/OutputHandler.php
+++ b/includes/OutputHandler.php
@@ -154,8 +154,8 @@ function wfGzipHandler( $s ) {
*/
function wfMangleFlashPolicy( $s ) {
# Avoid weird excessive memory usage in PCRE on big articles
- if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $s ) ) {
- return preg_replace( '/\<\s*cross-domain-policy\s*\>/i', '<NOT-cross-domain-policy>', $s );
+ if ( preg_match( '/\<\s*cross-domain-policy(?=\s|\>)/i', $s ) ) {
+ return preg_replace( '/\<(\s*)(cross-domain-policy(?=\s|\>))/i', '<$1NOT-$2', $s );
} else {
return $s;
}
diff --git a/includes/OutputPage.php b/includes/OutputPage.php
index 69ed8def..324cab34 100644
--- a/includes/OutputPage.php
+++ b/includes/OutputPage.php
@@ -610,20 +610,6 @@ class OutputPage extends ContextSource {
* @return array Array of module names
*/
public function getModuleStyles( $filter = false, $position = null ) {
- // T97420
- $resourceLoader = $this->getResourceLoader();
-
- foreach ( $this->mModuleStyles as $val ) {
- $module = $resourceLoader->getModule( $val );
-
- if ( $module instanceof ResourceLoaderModule && $module->isPositionDefault() ) {
- $warning = __METHOD__ . ': style module should define its position explicitly: ' .
- $val . ' ' . get_class( $module );
- wfDebugLog( 'resourceloader', $warning );
- wfLogWarning( $warning );
- }
- }
-
return $this->getModules( $filter, $position, 'mModuleStyles' );
}
@@ -2044,6 +2030,11 @@ class OutputPage extends ContextSource {
* @return string
*/
public function getVaryHeader() {
+ // If we vary on cookies, let's make sure it's always included here too.
+ if ( $this->getCacheVaryCookies() ) {
+ $this->addVaryHeader( 'Cookie' );
+ }
+
return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) );
}
@@ -3074,10 +3065,6 @@ class OutputPage extends ContextSource {
ResourceLoaderModule::TYPE_SCRIPTS
);
- $links[] = $this->makeResourceLoaderLink( $this->getModuleStyles( true, 'bottom' ),
- ResourceLoaderModule::TYPE_STYLES
- );
-
// Modules requests - let the client calculate dependencies and batch requests as it likes
// Only load modules that have marked themselves for loading at the bottom
$modules = $this->getModules( true, 'bottom' );
@@ -3140,9 +3127,6 @@ class OutputPage extends ContextSource {
* @return string
*/
function getBottomScripts() {
- // In case the skin wants to add bottom CSS
- $this->getSkin()->setupSkinUserCss( $this );
-
return $this->getScriptsForBottomQueue();
}
@@ -3665,7 +3649,7 @@ class OutputPage extends ContextSource {
$otherTags = array(); // Tags to append after the normal <link> tags
$resourceLoader = $this->getResourceLoader();
- $moduleStyles = $this->getModuleStyles( true, 'top' );
+ $moduleStyles = $this->getModuleStyles();
// Per-site custom styles
$moduleStyles[] = 'site';
diff --git a/includes/User.php b/includes/User.php
index 199dd1dc..f6df7e03 100644
--- a/includes/User.php
+++ b/includes/User.php
@@ -3630,11 +3630,14 @@ class User implements IDBAccessObject {
$this->clearInstanceCache( 'defaults' );
$this->getRequest()->setSessionData( 'wsUserID', 0 );
+ $this->getRequest()->setSessionData( 'wsEditToken', null );
$this->clearCookie( 'UserID' );
$this->clearCookie( 'Token' );
$this->clearCookie( 'forceHTTPS', false, array( 'prefix' => '' ) );
+ wfResetSessionID();
+
// Remember when user logged out, to prevent seeing cached pages
$this->setCookie( 'LoggedOut', time(), time() + 86400 );
}
diff --git a/includes/WebStart.php b/includes/WebStart.php
index f5a4f93b..e75e97fd 100644
--- a/includes/WebStart.php
+++ b/includes/WebStart.php
@@ -40,6 +40,9 @@ if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) {
. 'for help on how to disable magic quotes.' );
}
+if ( ini_get( 'mbstring.func_overload' ) ) {
+ die( 'MediaWiki does not support installations where mbstring.func_overload is non-zero.' );
+}
# bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
# We're adding it here so that it's *always* set, even for alternate entry
diff --git a/includes/actions/RawAction.php b/includes/actions/RawAction.php
index b71b0e9e..6b99258f 100644
--- a/includes/actions/RawAction.php
+++ b/includes/actions/RawAction.php
@@ -90,6 +90,12 @@ class RawAction extends FormlessAction {
}
}
+ // Set standard Vary headers so cache varies on cookies and such (T125283)
+ $response->header( $this->getOutput()->getVaryHeader() );
+ if ( $config->get( 'UseXVO' ) ) {
+ $response->header( $this->getOutput()->getXVO() );
+ }
+
$response->header( 'Content-type: ' . $contentType . '; charset=UTF-8' );
// Output may contain user-specific data;
// vary generated content for open sessions on private wikis
diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php
index d53797bc..4f40499c 100644
--- a/includes/api/ApiBase.php
+++ b/includes/api/ApiBase.php
@@ -421,7 +421,13 @@ abstract class ApiBase extends ContextSource {
* @return bool
*/
public function lacksSameOriginSecurity() {
- return $this->getMain()->getRequest()->getVal( 'callback' ) !== null;
+ // Main module has this method overridden
+ // Safety - avoid infinite loop:
+ if ( $this->isMain() ) {
+ ApiBase::dieDebug( __METHOD__, 'base method was called on main module.' );
+ }
+
+ return $this->getMain()->lacksSameOriginSecurity();
}
/**
diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php
index be1b12c3..baba5b2d 100644
--- a/includes/api/ApiFormatJson.php
+++ b/includes/api/ApiFormatJson.php
@@ -102,9 +102,9 @@ class ApiFormatJson extends ApiFormatBase {
// Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in
// Flash, but what it does isn't friendly for the API, so we need to
// work around it.
- if ( preg_match( '/\<\s*cross-domain-policy\s*\>/i', $json ) ) {
+ if ( preg_match( '/\<\s*cross-domain-policy(?=\s|\>)/i', $json ) ) {
$json = preg_replace(
- '/\<(\s*cross-domain-policy\s*)\>/i', '\\u003C$1\\u003E', $json
+ '/\<(\s*cross-domain-policy(?=\s|\>))/i', '\\u003C$1', $json
);
}
diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php
index 6420a5b5..643379c7 100644
--- a/includes/api/ApiFormatPhp.php
+++ b/includes/api/ApiFormatPhp.php
@@ -65,7 +65,7 @@ class ApiFormatPhp extends ApiFormatBase {
// just be broken in a useful manner.
if ( $this->getConfig()->get( 'MangleFlashPolicy' ) &&
in_array( 'wfOutputHandler', ob_list_handlers(), true ) &&
- preg_match( '/\<\s*cross-domain-policy\s*\>/i', $text )
+ preg_match( '/\<\s*cross-domain-policy(?=\s|\>)/i', $text )
) {
$this->dieUsage(
'This response cannot be represented using format=php. ' .
diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php
index d943c86b..1f0aebb6 100644
--- a/includes/api/ApiMain.php
+++ b/includes/api/ApiMain.php
@@ -145,6 +145,9 @@ class ApiMain extends ApiBase {
private $mCacheControl = array();
private $mParamsUsed = array();
+ /** @var bool|null Cached return value from self::lacksSameOriginSecurity() */
+ private $lacksSameOriginSecurity = null;
+
/**
* Constructs an instance of ApiMain that utilizes the module and format specified by $request.
*
@@ -243,6 +246,36 @@ class ApiMain extends ApiBase {
}
/**
+ * Get the security flag for the current request
+ * @return bool
+ */
+ public function lacksSameOriginSecurity() {
+ if ( $this->lacksSameOriginSecurity !== null ) {
+ return $this->lacksSameOriginSecurity;
+ }
+
+ $request = $this->getRequest();
+
+ // JSONP mode
+ if ( $request->getVal( 'callback' ) !== null ) {
+ $this->lacksSameOriginSecurity = true;
+ return true;
+ }
+
+ // Header to be used from XMLHTTPRequest when the request might
+ // otherwise be used for XSS.
+ if ( $request->getHeader( 'Treat-as-Untrusted' ) !== false ) {
+ $this->lacksSameOriginSecurity = true;
+ return true;
+ }
+
+ // Allow extensions to override.
+ $this->lacksSameOriginSecurity = !Hooks::run( 'RequestHasSameOriginSecurity', array( $request ) );
+ return $this->lacksSameOriginSecurity;
+ }
+
+
+ /**
* Get the ApiErrorFormatter object associated with current request
* @return ApiErrorFormatter
*/
@@ -717,6 +750,8 @@ class ApiMain extends ApiBase {
$response = $this->getRequest()->response();
$out = $this->getOutput();
+ $out->addVaryHeader( 'Treat-as-Untrusted' );
+
$config = $this->getConfig();
if ( $config->get( 'VaryOnXFP' ) ) {
diff --git a/includes/api/ApiMove.php b/includes/api/ApiMove.php
index aca43784..dc50594c 100644
--- a/includes/api/ApiMove.php
+++ b/includes/api/ApiMove.php
@@ -72,6 +72,11 @@ class ApiMove extends ApiBase {
}
}
+ // Rate limit
+ if ( $user->pingLimiter( 'move' ) ) {
+ $this->dieUsageMsg( 'actionthrottledtext' );
+ }
+
// Move the page
$toTitleExists = $toTitle->exists();
$status = $this->movePage( $fromTitle, $toTitle, $params['reason'], !$params['noredirect'] );
diff --git a/includes/db/DatabasePostgres.php b/includes/db/DatabasePostgres.php
index 56a5b2cf..d5143689 100644
--- a/includes/db/DatabasePostgres.php
+++ b/includes/db/DatabasePostgres.php
@@ -486,6 +486,9 @@ class DatabasePostgres extends DatabaseBase {
if ( function_exists( 'mb_convert_encoding' ) ) {
$sql = mb_convert_encoding( $sql, 'UTF-8' );
}
+ while ( $res = pg_get_result( $this->mConn ) ) {
+ pg_free_result( $res );
+ }
$this->mTransactionState->check();
if ( pg_send_query( $this->mConn, $sql ) === false ) {
throw new DBUnexpectedError( $this, "Unable to post new query to PostgreSQL\n" );
diff --git a/includes/diff/DairikiDiff.php b/includes/diff/DairikiDiff.php
index d327433f..7bdc6543 100644
--- a/includes/diff/DairikiDiff.php
+++ b/includes/diff/DairikiDiff.php
@@ -296,9 +296,9 @@ class DiffEngine {
$this->xchanged = $this->ychanged = array();
$this->xv = $this->yv = array();
$this->xind = $this->yind = array();
- unset( $this->seq );
- unset( $this->in_seq );
- unset( $this->lcs );
+ $this->seq = array();
+ $this->in_seq = array();
+ $this->lcs = 0;
// Skip leading common lines.
for ( $skip = 0; $skip < $n_from && $skip < $n_to; $skip++ ) {
diff --git a/includes/diff/DifferenceEngine.php b/includes/diff/DifferenceEngine.php
index c138eec2..fa1cd79d 100644
--- a/includes/diff/DifferenceEngine.php
+++ b/includes/diff/DifferenceEngine.php
@@ -511,7 +511,7 @@ class DifferenceEngine extends ContextSource {
$this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown(
$this->mNewPage,
$this->msg( 'markaspatrolleddiff' )->escaped(),
- array(),
+ array( 'class' => 'mw-patrollink' ),
array(
'action' => 'markpatrolled',
'rcid' => $rcid,
@@ -823,6 +823,35 @@ class DifferenceEngine extends ContextSource {
* @return bool|string
*/
public function generateTextDiffBody( $otext, $ntext ) {
+ $self = $this;
+ $diff = function() use ( $self, $otext, $ntext ) {
+ return $self->textDiff( $otext, $ntext );
+ };
+
+ $error = function( $status ) {
+ throw new FatalError( $status->getWikiText() );
+ };
+
+ // Use PoolCounter if the diff looks like it can be expensive
+ if ( strlen( $otext ) + strlen( $ntext ) > 20000 ) {
+ $work = new PoolCounterWorkViaCallback( 'diff',
+ md5( $otext ) . md5( $ntext ),
+ array( 'doWork' => $diff, 'error' => $error )
+ );
+ return $work->execute();
+ }
+
+ return $diff();
+ }
+
+ /**
+ * Generates diff, to be wrapped internally in a logging/instrumentation
+ *
+ * @param string $otext Old text, must be already segmented
+ * @param string $ntext New text, must be already segmented
+ * @return bool|string
+ */
+ public function textDiff( $otext, $ntext ) {
global $wgExternalDiffEngine, $wgContLang;
$otext = str_replace( "\r\n", "\n", $otext );
diff --git a/includes/page/Article.php b/includes/page/Article.php
index 56b9520a..97412dc7 100644
--- a/includes/page/Article.php
+++ b/includes/page/Article.php
@@ -1168,7 +1168,7 @@ class Article implements Page {
$link = Linker::linkKnown(
$this->getTitle(),
wfMessage( 'markaspatrolledtext' )->escaped(),
- array(),
+ array( 'class' => 'mw-patrollink' ),
array(
'action' => 'markpatrolled',
'rcid' => $rcid,
diff --git a/includes/parser/CoreTagHooks.php b/includes/parser/CoreTagHooks.php
index 9755ea93..3f4f54a3 100644
--- a/includes/parser/CoreTagHooks.php
+++ b/includes/parser/CoreTagHooks.php
@@ -56,9 +56,14 @@ class CoreTagHooks {
$content = StringUtils::delimiterReplace( '<nowiki>', '</nowiki>', '$1', $text, 'i' );
$attribs = Sanitizer::validateTagAttributes( $attribs, 'pre' );
- return Xml::openElement( 'pre', $attribs ) .
- Xml::escapeTagsOnly( $content ) .
- '</pre>';
+ // We need to let both '"' and '&' through,
+ // for strip markers and entities respectively.
+ $content = str_replace(
+ array( '>', '<' ),
+ array( '&gt;', '&lt;' ),
+ $content
+ );
+ return Html::rawElement( 'pre', $attribs, $content );
}
/**
@@ -98,8 +103,17 @@ class CoreTagHooks {
* @return array
*/
public static function nowiki( $content, $attributes, $parser ) {
- $content = strtr( $content, array( '-{' => '-&#123;', '}-' => '&#125;-' ) );
- return array( Xml::escapeTagsOnly( $content ), 'markerType' => 'nowiki' );
+ $content = strtr( $content, array(
+ // lang converter
+ '-{' => '-&#123;',
+ '}-' => '&#125;-',
+ // html tags
+ '<' => '&lt;',
+ '>' => '&gt;'
+ // Note: Both '"' and '&' are not converted.
+ // This allows strip markers and entities through.
+ ) );
+ return array( $content, 'markerType' => 'nowiki' );
}
/**
diff --git a/includes/parser/Parser.php b/includes/parser/Parser.php
index c07a08ac..12953167 100644
--- a/includes/parser/Parser.php
+++ b/includes/parser/Parser.php
@@ -129,9 +129,14 @@ class Parser {
*
* Must not consist of all title characters, or else it will change
* the behavior of <nowiki> in a link.
+ *
+ * Must have a character that needs escaping in attributes, otherwise
+ * someone could put a strip marker in an attribute, to get around
+ * escaping quote marks, and break out of the attribute. Thus we add
+ * `'".
*/
- const MARKER_SUFFIX = "-QINU\x7f";
- const MARKER_PREFIX = "\x7fUNIQ-";
+ const MARKER_SUFFIX = "-QINU`\"'\x7f";
+ const MARKER_PREFIX = "\x7f'\"`UNIQ-";
# Markers used for wrapping the table of contents
const TOC_START = '<mw:toc>';
@@ -1862,11 +1867,22 @@ class Parser {
*/
public function getExternalLinkAttribs( $url = false ) {
$attribs = array();
- $attribs['rel'] = self::getExternalLinkRel( $url, $this->mTitle );
-
- if ( $this->mOptions->getExternalLinkTarget() ) {
- $attribs['target'] = $this->mOptions->getExternalLinkTarget();
+ $rel = self::getExternalLinkRel( $url, $this->mTitle );
+
+ $target = $this->mOptions->getExternalLinkTarget();
+ if ( $target ) {
+ $attribs['target'] = $target;
+ if ( !in_array( $target, array( '_self', '_parent', '_top' ) ) ) {
+ // T133507. New windows can navigate parent cross-origin.
+ // Including noreferrer due to lacking browser
+ // support of noopener. Eventually noreferrer should be removed.
+ if ( $rel !== '' ) {
+ $rel .= ' ';
+ }
+ $rel .= 'noreferrer noopener';
+ }
}
+ $attribs['rel'] = $rel;
return $attribs;
}
diff --git a/includes/password/MWOldPassword.php b/includes/password/MWOldPassword.php
index afa5cacc..43c13553 100644
--- a/includes/password/MWOldPassword.php
+++ b/includes/password/MWOldPassword.php
@@ -44,5 +44,9 @@ class MWOldPassword extends ParameterizedPassword {
$this->args = array();
$this->hash = md5( $plaintext );
}
+
+ if ( !is_string( $this->hash ) || strlen( $this->hash ) < 32 ) {
+ throw new PasswordError( 'Error when hashing password.' );
+ }
}
}
diff --git a/includes/password/MWSaltedPassword.php b/includes/password/MWSaltedPassword.php
index 6c6895a2..40d2b6a1 100644
--- a/includes/password/MWSaltedPassword.php
+++ b/includes/password/MWSaltedPassword.php
@@ -42,5 +42,9 @@ class MWSaltedPassword extends ParameterizedPassword {
}
$this->hash = md5( $this->args[0] . '-' . md5( $plaintext ) );
+
+ if ( !is_string( $this->hash ) || strlen( $this->hash ) < 32 ) {
+ throw new PasswordError( 'Error when hashing password.' );
+ }
}
}
diff --git a/includes/password/Pbkdf2Password.php b/includes/password/Pbkdf2Password.php
index 080e3b0d..808fd8a6 100644
--- a/includes/password/Pbkdf2Password.php
+++ b/includes/password/Pbkdf2Password.php
@@ -55,8 +55,15 @@ class Pbkdf2Password extends ParameterizedPassword {
(int)$this->params['length'],
true
);
+ if ( !is_string( $hash ) ) {
+ throw new PasswordError( 'Error when hashing password.' );
+ }
} else {
- $hashLen = strlen( hash( $this->params['algo'], '', true ) );
+ $hashLenHash = hash( $this->params['algo'], '', true );
+ if ( !is_string( $hashLenHash ) ) {
+ throw new PasswordError( 'Error when hashing password.' );
+ }
+ $hashLen = strlen( $hashLenHash );
$blockCount = ceil( $this->params['length'] / $hashLen );
$hash = '';
diff --git a/includes/resourceloader/ResourceLoaderFileModule.php b/includes/resourceloader/ResourceLoaderFileModule.php
index 7fbc1cb4..8060f6e1 100644
--- a/includes/resourceloader/ResourceLoaderFileModule.php
+++ b/includes/resourceloader/ResourceLoaderFileModule.php
@@ -279,7 +279,6 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
break;
// Single strings
case 'position':
- $this->isPositionDefined = true;
case 'group':
case 'skipFunction':
$this->{$member} = (string)$option;
diff --git a/includes/resourceloader/ResourceLoaderImageModule.php b/includes/resourceloader/ResourceLoaderImageModule.php
index 8de87f2e..e2da28bb 100644
--- a/includes/resourceloader/ResourceLoaderImageModule.php
+++ b/includes/resourceloader/ResourceLoaderImageModule.php
@@ -184,7 +184,6 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
break;
case 'position':
- $this->isPositionDefined = true;
case 'prefix':
case 'selectorWithoutVariant':
case 'selectorWithVariant':
@@ -456,9 +455,4 @@ class ResourceLoaderImageModule extends ResourceLoaderModule {
$this->loadFromDefinition();
return $this->position;
}
-
- public function isPositionDefault() {
- $this->loadFromDefinition();
- return parent::isPositionDefault();
- }
}
diff --git a/includes/resourceloader/ResourceLoaderModule.php b/includes/resourceloader/ResourceLoaderModule.php
index 1d3ffb55..9b6702aa 100644
--- a/includes/resourceloader/ResourceLoaderModule.php
+++ b/includes/resourceloader/ResourceLoaderModule.php
@@ -67,10 +67,6 @@ abstract class ResourceLoaderModule {
// In-object cache for module content
protected $contents = array();
- // Whether the position returned by getPosition() is defined in the module configuration
- // and not a default value
- protected $isPositionDefined = false;
-
/**
* @var Config
*/
@@ -292,19 +288,6 @@ abstract class ResourceLoaderModule {
}
/**
- * Whether the position returned by getPosition() is a default value or comes from the module
- * definition. This method is meant to be short-lived, and is only useful until classes added
- * via addModuleStyles with a default value define an explicit position. See getModuleStyles()
- * in OutputPage for the related migration warning.
- *
- * @return bool
- * @since 1.26
- */
- public function isPositionDefault() {
- return !$this->isPositionDefined;
- }
-
- /**
* Whether this module's JS expects to work without the client-side ResourceLoader module.
* Returning true from this function will prevent mw.loader.state() call from being
* appended to the bottom of the script.
diff --git a/includes/resourceloader/ResourceLoaderWikiModule.php b/includes/resourceloader/ResourceLoaderWikiModule.php
index 0023de27..693bcee4 100644
--- a/includes/resourceloader/ResourceLoaderWikiModule.php
+++ b/includes/resourceloader/ResourceLoaderWikiModule.php
@@ -72,8 +72,6 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
foreach ( $options as $member => $option ) {
switch ( $member ) {
case 'position':
- $this->isPositionDefined = true;
- // Don't break since we need the member set as well
case 'styles':
case 'scripts':
case 'group':
diff --git a/includes/specials/SpecialUserlogin.php b/includes/specials/SpecialUserlogin.php
index 21f1194f..2d6737bd 100644
--- a/includes/specials/SpecialUserlogin.php
+++ b/includes/specials/SpecialUserlogin.php
@@ -628,7 +628,7 @@ class LoginForm extends SpecialPage {
"allowed account creation w/o throttle\n" );
} else {
if ( ( $wgAccountCreationThrottle && $currentUser->isPingLimitable() ) ) {
- $key = wfMemcKey( 'acctcreate', 'ip', $ip );
+ $key = wfGlobalCacheKey( 'acctcreate', 'ip', $ip );
$value = $wgMemc->get( $key );
if ( !$value ) {
$wgMemc->set( $key, 0, 86400 );
@@ -862,11 +862,12 @@ class LoginForm extends SpecialPage {
*/
public static function incLoginThrottle( $username ) {
global $wgPasswordAttemptThrottle, $wgMemc, $wgRequest;
- $username = trim( $username ); // sanity
+ $canUsername = User::getCanonicalName( $username, 'usable' );
+ $username = $canUsername !== false ? $canUsername : $username;
$throttleCount = 0;
if ( is_array( $wgPasswordAttemptThrottle ) ) {
- $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
+ $throttleKey = wfGlobalCacheKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
$count = $wgPasswordAttemptThrottle['count'];
$period = $wgPasswordAttemptThrottle['seconds'];
@@ -890,9 +891,10 @@ class LoginForm extends SpecialPage {
*/
public static function clearLoginThrottle( $username ) {
global $wgMemc, $wgRequest;
- $username = trim( $username ); // sanity
+ $canUsername = User::getCanonicalName( $username, 'usable' );
+ $username = $canUsername !== false ? $canUsername : $username;
- $throttleKey = wfMemcKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
+ $throttleKey = wfGlobalCacheKey( 'password-throttle', $wgRequest->getIP(), md5( $username ) );
$wgMemc->delete( $throttleKey );
}
@@ -1608,7 +1610,8 @@ class LoginForm extends SpecialPage {
if ( $wgSecureLogin && !$this->mStickHTTPS ) {
$wgCookieSecure = false;
}
-
+ // Always make sure edit token is regenerated. (T114419)
+ $this->getRequest()->setSessionData( 'wsEditToken', null );
wfResetSessionID();
}
diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php
index e9e1f658..02192904 100644
--- a/includes/upload/UploadBase.php
+++ b/includes/upload/UploadBase.php
@@ -1399,7 +1399,7 @@ abstract class UploadBase {
&& strpos( $value, '#' ) !== 0
) {
if ( !( $strippedElement === 'a'
- && preg_match( '!^https?://!im', $value ) )
+ && preg_match( '!^https?://!i', $value ) )
) {
wfDebug( __METHOD__ . ": Found href attribute <$strippedElement "
. "'$attrib'='$value' in uploaded file.\n" );