summaryrefslogtreecommitdiff
path: root/includes
diff options
context:
space:
mode:
Diffstat (limited to 'includes')
-rw-r--r--includes/DefaultSettings.php23
-rw-r--r--includes/EditPage.php18
-rw-r--r--includes/OutputHandler.php6
-rw-r--r--includes/OutputPage.php78
-rw-r--r--includes/User.php1
-rw-r--r--includes/api/ApiBase.php1
-rw-r--r--includes/api/ApiEditPage.php3
-rw-r--r--includes/api/ApiFormatJson.php10
-rw-r--r--includes/api/ApiFormatPhp.php19
-rw-r--r--includes/api/ApiQueryLogEvents.php10
-rw-r--r--includes/upload/UploadBase.php4
11 files changed, 130 insertions, 43 deletions
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
+ * <cross-domain-policy>. 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
@@ -156,6 +156,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
*/
const AS_PARSE_ERROR = 240;
@@ -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