From 888eab1a076a287bddd84fdf9dd9c57154c91e3f Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Thu, 27 Nov 2014 06:08:05 +0100 Subject: Update to MediaWiki 1.22.14 --- includes/DefaultSettings.php | 23 ++++++++++- includes/EditPage.php | 18 +++++++++ includes/OutputHandler.php | 6 ++- includes/OutputPage.php | 78 ++++++++++++++++++++++---------------- includes/User.php | 1 + includes/api/ApiBase.php | 1 + includes/api/ApiEditPage.php | 3 ++ includes/api/ApiFormatJson.php | 10 +++++ includes/api/ApiFormatPhp.php | 19 +++++++++- includes/api/ApiQueryLogEvents.php | 10 +++-- includes/upload/UploadBase.php | 4 +- 11 files changed, 130 insertions(+), 43 deletions(-) (limited to 'includes') diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index df3d57b9..6feac36b 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.13'; +$wgVersion = '1.22.14'; /** * Name of the site. It must be changed in LocalSettings.php @@ -3322,6 +3322,27 @@ $wgResourceLoaderLESSImportPaths = array( "$IP/resources/mediawiki.less/", ); +/** + * Whether to allow site-wide CSS (MediaWiki:Common.css and friends) on + * restricted pages like Special:UserLogin or Special:Preferences where + * JavaScript is disabled for security reasons. As it is possible to + * execute JavaScript through CSS, setting this to true opens up a + * potential security hole. Some sites may "skin" their wiki by using + * site-wide CSS, causing restricted pages to look unstyled and different + * from the rest of the site. + * + * @since 1.25 + */ +$wgAllowSiteCSSOnRestrictedPages = false; + +/** + * When OutputHandler is used, mangle any output that contains + * . Without this, an attacker can send their own + * cross-domain policy unless it is prevented by the crossdomain.xml file at + * the domain root. + */ +$wgMangleFlashPolicy = true; + /** @} */ # End of resource loader settings } /*************************************************************************//** diff --git a/includes/EditPage.php b/includes/EditPage.php index 16d9a5a4..4dd83845 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -155,6 +155,12 @@ class EditPage { */ const AS_IMAGE_REDIRECT_LOGGED = 234; + /** + * Status: user tried to modify the content model, but is not allowed to do that + * ( User::isAllowed('editcontentmodel') == false ) + */ + const AS_NO_CHANGE_CONTENT_MODEL = 235; + /** * Status: can't parse content */ @@ -1289,6 +1295,9 @@ class EditPage { $permission = $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage'; throw new PermissionsError( $permission ); + case self::AS_NO_CHANGE_CONTENT_MODEL: + throw new PermissionsError( 'editcontentmodel' ); + default: // We don't recognize $status->value. The only way that can happen // is if an extension hook aborted from inside ArticleSave. @@ -1503,6 +1512,15 @@ class EditPage { } } + if ( $this->contentModel !== $this->mTitle->getContentModel() + && !$wgUser->isAllowed( 'editcontentmodel' ) + ) { + $status->setResult( false, self::AS_NO_CHANGE_CONTENT_MODEL ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); + return $status; + } + if ( wfReadOnly() ) { $status->fatal( 'readonlytext' ); $status->value = self::AS_READ_ONLY_PAGE; diff --git a/includes/OutputHandler.php b/includes/OutputHandler.php index 3860b8e2..65bb86e7 100644 --- a/includes/OutputHandler.php +++ b/includes/OutputHandler.php @@ -28,8 +28,10 @@ * @return string */ function wfOutputHandler( $s ) { - global $wgDisableOutputCompression, $wgValidateAllHtml; - $s = wfMangleFlashPolicy( $s ); + global $wgDisableOutputCompression, $wgValidateAllHtml, $wgMangleFlashPolicy; + if ( $wgMangleFlashPolicy ) { + $s = wfMangleFlashPolicy( $s ); + } if ( $wgValidateAllHtml ) { $headers = headers_list(); $isHTML = false; diff --git a/includes/OutputPage.php b/includes/OutputPage.php index 363f2b62..e6d4339f 100644 --- a/includes/OutputPage.php +++ b/includes/OutputPage.php @@ -151,12 +151,14 @@ class OutputPage extends ContextSource { var $mFeedLinksAppendQuery = null; - /** - * @var int - * The level of 'untrustworthiness' allowed for modules loaded on this page. + /** @var array + * What level of 'untrustworthiness' is allowed in CSS/JS modules loaded on this page? * @see ResourceLoaderModule::$origin + * ResourceLoaderModule::ORIGIN_ALL is assumed unless overridden; */ - protected $mAllowedModuleOrigin = ResourceLoaderModule::ORIGIN_ALL; + protected $mAllowedModules = array( + ResourceLoaderModule::TYPE_COMBINED => ResourceLoaderModule::ORIGIN_ALL, + ); /** * @EasterEgg I just love the name for this self documenting variable. @@ -1271,13 +1273,31 @@ class OutputPage extends ContextSource { } /** - * Restrict the page to loading modules bundled the software. + * Do not allow scripts which can be modified by wiki users to load on this page; + * only allow scripts bundled with, or generated by, the software. + * Site-wide styles are controlled by a config setting, since they can be + * used to create a custom skin/theme, but not user-specific ones. * - * Disallows the queue to contain any modules which can be modified by wiki - * users to load on this page. + * @todo this should be given a more accurate name */ public function disallowUserJs() { - $this->reduceAllowedModuleOrigin( ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL ); + global $wgAllowSiteCSSOnRestrictedPages; + $this->reduceAllowedModules( + ResourceLoaderModule::TYPE_SCRIPTS, + ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL + ); + + // Site-wide styles are controlled by a config setting, see bug 71621 + // for background on why. User styles are never allowed. + if ( $wgAllowSiteCSSOnRestrictedPages ) { + $styleOrigin = ResourceLoaderModule::ORIGIN_USER_SITEWIDE; + } else { + $styleOrigin = ResourceLoaderModule::ORIGIN_CORE_INDIVIDUAL; + } + $this->reduceAllowedModules( + ResourceLoaderModule::TYPE_STYLES, + $styleOrigin + ); } /** @@ -1295,40 +1315,31 @@ class OutputPage extends ContextSource { * Get the level of JavaScript / CSS untrustworthiness allowed on this page. * * @see ResourceLoaderModule::$origin - * @param string $type Unused: Module origin allowance used to be fragmented by - * ResourceLoaderModule TYPE_ constants. - * @return Int ResourceLoaderModule ORIGIN_ class constant + * @param string $type ResourceLoaderModule TYPE_ constant + * @return int ResourceLoaderModule ORIGIN_ class constant */ - public function getAllowedModules( $type = null ) { - return $this->mAllowedModuleOrigin; + public function getAllowedModules( $type ) { + if ( $type == ResourceLoaderModule::TYPE_COMBINED ) { + return min( array_values( $this->mAllowedModules ) ); + } else { + return isset( $this->mAllowedModules[$type] ) + ? $this->mAllowedModules[$type] + : ResourceLoaderModule::ORIGIN_ALL; + } } /** * Set the highest level of CSS/JS untrustworthiness allowed * * @deprecated since 1.24 Raising level of allowed untrusted content is no longer supported. - * Use reduceAllowedModuleOrigin() instead. - * - * @param $type String ResourceLoaderModule TYPE_ constant - * @param int $level ResourceLoaderModule ORIGIN_ constant - */ - public function setAllowedModules( $type, $level ) { - wfDeprecated( __METHOD__, '1.24' ); - $this->reduceAllowedModuleOrigin( $level ); - } - - /** - * Limit the highest level of CSS/JS untrustworthiness allowed. - * - * @deprecated since 1.24 Module allowance is no longer fragmented by content type. - * Use reduceAllowedModuleOrigin() instead. + * Use reduceAllowedModules() instead * * @param string $type ResourceLoaderModule TYPE_ constant - * @param int $level ResourceLoaderModule ORIGIN_ class constant + * @param int $level ResourceLoaderModule class constant */ - public function reduceAllowedModules( $type, $level ) { + public function setAllowedModules( $type, $level ) { wfDeprecated( __METHOD__, '1.24' ); - $this->reduceAllowedModuleOrigin( $level ); + $this->reduceAllowedModules( $type, $level ); } /** @@ -1337,10 +1348,11 @@ class OutputPage extends ContextSource { * If passed the same or a higher level than the current level of untrustworthiness set, the * level will remain unchanged. * + * @param string $type * @param int $level ResourceLoaderModule class constant */ - public function reduceAllowedModuleOrigin( $level ) { - $this->mAllowedModuleOrigin = min( $this->mAllowedModuleOrigin, $level ); + public function reduceAllowedModules( $type, $level ) { + $this->mAllowedModules[$type] = min( $this->getAllowedModules( $type ), $level ); } /** diff --git a/includes/User.php b/includes/User.php index 62324043..4c7a39df 100644 --- a/includes/User.php +++ b/includes/User.php @@ -122,6 +122,7 @@ class User { 'deletelogentry', 'deleterevision', 'edit', + 'editcontentmodel', 'editinterface', 'editprotected', 'editmyoptions', diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index ce6ecda6..c1454e76 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -1351,6 +1351,7 @@ abstract class ApiBase extends ContextSource { 'permdenied-undelete' => array( 'code' => 'permissiondenied', 'info' => "You don't have permission to restore deleted revisions" ), 'createonly-exists' => array( 'code' => 'articleexists', 'info' => "The article you tried to create has been created already" ), 'nocreate-missing' => array( 'code' => 'missingtitle', 'info' => "The article you tried to edit doesn't exist" ), + 'cantchangecontentmodel' => array( 'code' => 'cantchangecontentmodel', 'info' => "You don't have permission to change the content model of a page" ), 'nosuchrcid' => array( 'code' => 'nosuchrcid', 'info' => "There is no change with rcid \"\$1\"" ), 'protect-invalidaction' => array( 'code' => 'protect-invalidaction', 'info' => "Invalid protection type \"\$1\"" ), 'protect-invalidlevel' => array( 'code' => 'protect-invalidlevel', 'info' => "Invalid protection level \"\$1\"" ), diff --git a/includes/api/ApiEditPage.php b/includes/api/ApiEditPage.php index bd61895b..51c9efc6 100644 --- a/includes/api/ApiEditPage.php +++ b/includes/api/ApiEditPage.php @@ -423,6 +423,9 @@ class ApiEditPage extends ApiBase { case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg( 'nocreate-loggedin' ); + case EditPage::AS_NO_CHANGE_CONTENT_MODEL: + $this->dieUsageMsg( 'cantchangecontentmodel' ); + case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg( 'blankpage' ); diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php index 4140583e..47d82124 100644 --- a/includes/api/ApiFormatJson.php +++ b/includes/api/ApiFormatJson.php @@ -62,6 +62,16 @@ class ApiFormatJson extends ApiFormatBase { $this->getIsHtml(), $params['utf8'] ? FormatJson::ALL_OK : FormatJson::XMLMETA_OK ); + + // 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 ) ) { + $json = preg_replace( + '/\<(\s*cross-domain-policy\s*)\>/i', '\\u003C$1\\u003E', $json + ); + } + $callback = $params['callback']; if ( $callback !== null ) { $callback = preg_replace( "/[^][.\\'\\\"_A-Za-z0-9]/", '', $callback ); diff --git a/includes/api/ApiFormatPhp.php b/includes/api/ApiFormatPhp.php index b2d1f044..bda1c180 100644 --- a/includes/api/ApiFormatPhp.php +++ b/includes/api/ApiFormatPhp.php @@ -35,7 +35,24 @@ class ApiFormatPhp extends ApiFormatBase { } public function execute() { - $this->printText( serialize( $this->getResultData() ) ); + global $wgMangleFlashPolicy; + $text = serialize( $this->getResultData() ); + + // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in + // Flash, but what it does isn't friendly for the API. There's nothing + // we can do here that isn't actively broken in some manner, so let's + // just be broken in a useful manner. + if ( $wgMangleFlashPolicy && + in_array( 'wfOutputHandler', ob_list_handlers(), true ) && + preg_match( '/\<\s*cross-domain-policy\s*\>/i', $text ) + ) { + $this->dieUsage( + 'This response cannot be represented using format=php. See https://bugzilla.wikimedia.org/show_bug.cgi?id=66776', + 'internalerror' + ); + } + + $this->printText( $text ); } public function getDescription() { diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index 26774ef4..0e8c5e61 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -157,7 +157,7 @@ class ApiQueryLogEvents extends ApiQueryBase { $this->addOption( 'USE INDEX', $index ); // Paranoia: avoid brute force searches (bug 17342) - if ( !is_null( $title ) ) { + if ( !is_null( $title ) || !is_null( $params['action'] ) ) { $this->addWhere( $db->bitAnd( 'log_deleted', LogPage::DELETED_ACTION ) . ' = 0' ); } if ( !is_null( $user ) ) { @@ -300,10 +300,13 @@ class ApiQueryLogEvents extends ApiQueryBase { $title = Title::makeTitle( $row->log_namespace, $row->log_title ); } - if ( $this->fld_title || $this->fld_ids ) { + if ( $this->fld_title || $this->fld_ids || $this->fld_type ) { if ( LogEventsList::isDeleted( $row, LogPage::DELETED_ACTION ) ) { $vals['actionhidden'] = ''; } else { + if ( $this->fld_type ) { + $vals['action'] = $row->log_action; + } if ( $this->fld_title ) { ApiQueryBase::addTitleInfo( $vals, $title ); } @@ -313,9 +316,8 @@ class ApiQueryLogEvents extends ApiQueryBase { } } - if ( $this->fld_type || $this->fld_action ) { + if ( $this->fld_type ) { $vals['type'] = $row->log_type; - $vals['action'] = $row->log_action; } if ( $this->fld_details && $row->log_params !== '' ) { diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php index 26cc5427..8268f8e3 100644 --- a/includes/upload/UploadBase.php +++ b/includes/upload/UploadBase.php @@ -1303,8 +1303,8 @@ abstract class UploadBase { wfDebug( __METHOD__ . ": Found href attribute <$strippedElement " . "'$attrib'='$value' in uploaded file.\n" ); - return true; - } + return true; + } } # href with embedded svg as target -- cgit v1.2.3-54-g00ecf