// been changed somehow, and keep it if so.
$anonPO = ParserOptions::newFromAnon();
$anonPO->setEditSection( false );
+ $anonPO->setAllowUnsafeRawHtml( false );
if ( !$options->matches( $anonPO ) ) {
wfLogWarning( __METHOD__ . ': Setting a changed bogus ParserOptions: ' . wfGetAllCallers( 5 ) );
$options->isBogus = false;
// either.
$po = ParserOptions::newFromAnon();
$po->setEditSection( false );
+ $po->setAllowUnsafeRawHtml( false );
$po->isBogus = true;
if ( $options !== null ) {
$this->mParserOptions = empty( $options->isBogus ) ? $options : null;
$this->mParserOptions = ParserOptions::newFromContext( $this->getContext() );
$this->mParserOptions->setEditSection( false );
+ $this->mParserOptions->setAllowUnsafeRawHtml( false );
}
if ( $options !== null && !empty( $options->isBogus ) ) {
// either.
$po = ParserOptions::newFromAnon();
$po->setEditSection( false );
+ $po->setAllowUnsafeRawHtml( false );
return $po;
}
$this->mParserOptions = new ParserOptions;
$this->mParserOptions->setEditSection( false );
+ // Messages may take parameters that could come
+ // from malicious sources. As a precaution, disable
+ // the <html> parser tag when parsing messages.
+ $this->mParserOptions->setAllowUnsafeRawHtml( false );
}
return $this->mParserOptions;
* @param array $attributes
* @param Parser $parser
* @throws MWException
- * @return array
+ * @return array|string Output of tag hook
*/
public static function html( $content, $attributes, $parser ) {
global $wgRawHtml;
if ( $wgRawHtml ) {
- return [ $content, 'markerType' => 'nowiki' ];
+ if ( $parser->getOptions()->getAllowUnsafeRawHtml() ) {
+ return [ $content, 'markerType' => 'nowiki' ];
+ } else {
+ // In a system message where raw html is
+ // not allowed (but it is allowed in other
+ // contexts).
+ return Html::rawElement(
+ 'span',
+ [ 'class' => 'error' ],
+ // Using ->text() not ->parse() as
+ // a paranoia measure against a loop.
+ wfMessage( 'rawhtml-notallowed' )->escaped()
+ );
+ }
} else {
throw new MWException( '<html> extension tag encountered unexpectedly' );
}
*/
private $redirectTarget = null;
+ /**
+ * If the wiki is configured to allow raw html ($wgRawHtml = true)
+ * is it allowed in the specific case of parsing this page.
+ *
+ * This is meant to disable unsafe parser tags in cases where
+ * a malicious user may control the input to the parser.
+ *
+ * @note This is expected to be true for normal pages even if the
+ * wiki has $wgRawHtml disabled in general. The setting only
+ * signifies that raw html would be unsafe in the current context
+ * provided that raw html is allowed at all.
+ * @var boolean
+ */
+ private $allowUnsafeRawHtml = true;
+
public function getInterwikiMagic() {
return $this->mInterwikiMagic;
}
public function getMagicRFCLinks() {
return $this->mMagicRFCLinks;
}
+
+ /**
+ * @since 1.29
+ * @return bool
+ */
+ public function getAllowUnsafeRawHtml() {
+ return $this->allowUnsafeRawHtml;
+ }
+
public function setInterwikiMagic( $x ) {
return wfSetVar( $this->mInterwikiMagic, $x );
}
return wfSetVar( $this->mIsPrintable, $x );
}
+ /**
+ * @param bool|null Value to set or null to get current value
+ * @return bool Current value for allowUnsafeRawHtml
+ * @since 1.29
+ */
+ public function setAllowUnsafeRawHtml( $x ) {
+ return wfSetVar( $this->allowUnsafeRawHtml, $x );
+ }
+
/**
* Set the redirect target.
*
"restrictionsfield-label": "Allowed IP ranges:",
"restrictionsfield-help": "One IP address or CIDR range per line. To enable everything, use:<pre>0.0.0.0/0\n::/0</pre>",
"revid": "revision $1",
- "pageid": "page ID $1"
+ "pageid": "page ID $1",
+ "rawhtml-notallowed": "<html> tags cannot be used outside of normal pages."
}
"restrictionsfield-label": "Field label shown for restriction fields (e.g. on Special:BotPassword).",
"restrictionsfield-help": "Placeholder text displayed in restriction fields (e.g. on Special:BotPassword).",
"revid": "Used to format a revision ID number in text. Parameters:\n* $1 - Revision ID number.\n{{Identical|Revision}}",
- "pageid": "Used to format a page ID number in text. Parameters:\n* $1 - Page ID number."
+ "pageid": "Used to format a page ID number in text. Parameters:\n* $1 - Page ID number.",
+ "rawhtml-notallowed": "Error message given when $wgRawHtml = true; is set and a user uses an <html> tag in a system message or somewhere other than a normal page."
}
<?php
+use MediaWiki\MediaWikiServices;
class MessageTest extends MediaWikiLangTestCase {
$this->assertSame( 'example &', $msg->escaped() );
}
+ public function testRawHtmlInMsg() {
+ global $wgParserConf;
+ $this->setMwGlobals( 'wgRawHtml', true );
+ // We have to reset the core hook registration.
+ // to register the html hook
+ MessageCache::destroyInstance();
+ $this->setMwGlobals( 'wgParser',
+ ObjectFactory::constructClassInstance( $wgParserConf['class'], [ $wgParserConf ] )
+ );
+
+ $msg = new RawMessage( '<html><script>alert("xss")</script></html>' );
+ $txt = '<span class="error"><html> tags cannot be' .
+ ' used outside of normal pages.</span>';
+ $this->assertSame( $txt, $msg->parse() );
+ }
+
/**
* @covers Message::params
* @covers Message::toString