Make non-existent messages be html safe regardless of output format
If you have a non-existent message in the output, chances are its user-controlled. If the message has the ->plain() or ->text() format, the output used to be not safe for html. Obviously people should not be using those format types where html is being outputted, but sometimes that happens. I think we should prioritize always being safe over the fallback content not potentially being double escaped. Additionally switch the enclosing brackets to be fancy unicode characters, to sidestep the escaping issue on the enclosing brackets. So previously, wfMessage( 'script>alert(1)</script' )->text() would have outputted <script>alert(1)</script>. Now it outputs ⧼script>alert(1)</script⧽. No sane message key will include < or >, so this would really only come up if the user can control the message key name. This goes somewhat against T68199. Change-Id: Ic8a60892b8e847e6021494c10968814aac391731
This commit is contained in:
parent
62638d7845
commit
184658eb32
4 changed files with 26 additions and 21 deletions
|
|
@ -802,10 +802,13 @@ class Message implements MessageSpecifier, Serializable {
|
||||||
$string = $this->fetchMessage();
|
$string = $this->fetchMessage();
|
||||||
|
|
||||||
if ( $string === false ) {
|
if ( $string === false ) {
|
||||||
if ( $this->format === 'plain' || $this->format === 'text' ) {
|
// Err on the side of safety, ensure that the output
|
||||||
return '<' . $this->key . '>';
|
// is always html safe in the event the message key is
|
||||||
}
|
// missing, since in that case its highly likely the
|
||||||
return '<' . htmlspecialchars( $this->key ) . '>';
|
// message key is user-controlled.
|
||||||
|
// '⧼' is used instead of '<' to side-step any
|
||||||
|
// double-escaping issues.
|
||||||
|
return '⧼' . htmlspecialchars( $this->key ) . '⧽';
|
||||||
}
|
}
|
||||||
|
|
||||||
# Replace $* with a list of parameters for &uselang=qqx.
|
# Replace $* with a list of parameters for &uselang=qqx.
|
||||||
|
|
|
||||||
|
|
@ -11013,7 +11013,7 @@ int keyword - non-existing message
|
||||||
!! wikitext
|
!! wikitext
|
||||||
{{int:var}}
|
{{int:var}}
|
||||||
!! html
|
!! html
|
||||||
<p><var>
|
<p>⧼var⧽
|
||||||
</p>
|
</p>
|
||||||
!! end
|
!! end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -223,13 +223,13 @@ class MessageTest extends MediaWikiLangTestCase {
|
||||||
*/
|
*/
|
||||||
public function testToStringKey() {
|
public function testToStringKey() {
|
||||||
$this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() );
|
$this->assertEquals( 'Main Page', wfMessage( 'mainpage' )->text() );
|
||||||
$this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->text() );
|
$this->assertEquals( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->text() );
|
||||||
$this->assertEquals( '<i<dont>exist-evar>', wfMessage( 'i<dont>exist-evar' )->text() );
|
$this->assertEquals( '⧼i<dont>exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->text() );
|
||||||
$this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->plain() );
|
$this->assertEquals( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->plain() );
|
||||||
$this->assertEquals( '<i<dont>exist-evar>', wfMessage( 'i<dont>exist-evar' )->plain() );
|
$this->assertEquals( '⧼i<dont>exist-evar⧽', wfMessage( 'i<dont>exist-evar' )->plain() );
|
||||||
$this->assertEquals( '<i-dont-exist-evar>', wfMessage( 'i-dont-exist-evar' )->escaped() );
|
$this->assertEquals( '⧼i-dont-exist-evar⧽', wfMessage( 'i-dont-exist-evar' )->escaped() );
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
'<i<dont>exist-evar>',
|
'⧼i<dont>exist-evar⧽',
|
||||||
wfMessage( 'i<dont>exist-evar' )->escaped()
|
wfMessage( 'i<dont>exist-evar' )->escaped()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -237,8 +237,10 @@ class MessageTest extends MediaWikiLangTestCase {
|
||||||
public static function provideToString() {
|
public static function provideToString() {
|
||||||
return [
|
return [
|
||||||
[ 'mainpage', 'Main Page' ],
|
[ 'mainpage', 'Main Page' ],
|
||||||
[ 'i-dont-exist-evar', '<i-dont-exist-evar>' ],
|
[ 'i-dont-exist-evar', '⧼i-dont-exist-evar⧽' ],
|
||||||
[ 'i-dont-exist-evar', '<i-dont-exist-evar>', 'escaped' ],
|
[ 'i-dont-exist-evar', '⧼i-dont-exist-evar⧽', 'escaped' ],
|
||||||
|
[ 'script>alert(1)</script', '⧼script>alert(1)</script⧽', 'escaped' ],
|
||||||
|
[ 'script>alert(1)</script', '⧼script>alert(1)</script⧽' ],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -376,9 +376,9 @@ class StatusTest extends MediaWikiLangTestCase {
|
||||||
$status->warning( 'fooBar!' );
|
$status->warning( 'fooBar!' );
|
||||||
$testCases['1StringWarning'] = [
|
$testCases['1StringWarning'] = [
|
||||||
$status,
|
$status,
|
||||||
"<fooBar!>",
|
"⧼fooBar!⧽",
|
||||||
"(wrap-short: (fooBar!))",
|
"(wrap-short: (fooBar!))",
|
||||||
"<p><fooBar!>\n</p>",
|
"<p>⧼fooBar!⧽\n</p>",
|
||||||
"<p>(wrap-short: (fooBar!))\n</p>",
|
"<p>(wrap-short: (fooBar!))\n</p>",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -387,9 +387,9 @@ class StatusTest extends MediaWikiLangTestCase {
|
||||||
$status->warning( 'fooBar2!' );
|
$status->warning( 'fooBar2!' );
|
||||||
$testCases['2StringWarnings'] = [
|
$testCases['2StringWarnings'] = [
|
||||||
$status,
|
$status,
|
||||||
"* <fooBar!>\n* <fooBar2!>\n",
|
"* ⧼fooBar!⧽\n* ⧼fooBar2!⧽\n",
|
||||||
"(wrap-long: * (fooBar!)\n* (fooBar2!)\n)",
|
"(wrap-long: * (fooBar!)\n* (fooBar2!)\n)",
|
||||||
"<ul><li> <fooBar!></li>\n<li> <fooBar2!></li></ul>\n",
|
"<ul><li> ⧼fooBar!⧽</li>\n<li> ⧼fooBar2!⧽</li></ul>\n",
|
||||||
"<p>(wrap-long: * (fooBar!)\n</p>\n<ul><li> (fooBar2!)</li></ul>\n<p>)\n</p>",
|
"<p>(wrap-long: * (fooBar!)\n</p>\n<ul><li> (fooBar2!)</li></ul>\n<p>)\n</p>",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -397,9 +397,9 @@ class StatusTest extends MediaWikiLangTestCase {
|
||||||
$status->warning( new Message( 'fooBar!', [ 'foo', 'bar' ] ) );
|
$status->warning( new Message( 'fooBar!', [ 'foo', 'bar' ] ) );
|
||||||
$testCases['1MessageWarning'] = [
|
$testCases['1MessageWarning'] = [
|
||||||
$status,
|
$status,
|
||||||
"<fooBar!>",
|
"⧼fooBar!⧽",
|
||||||
"(wrap-short: (fooBar!: foo, bar))",
|
"(wrap-short: (fooBar!: foo, bar))",
|
||||||
"<p><fooBar!>\n</p>",
|
"<p>⧼fooBar!⧽\n</p>",
|
||||||
"<p>(wrap-short: (fooBar!: foo, bar))\n</p>",
|
"<p>(wrap-short: (fooBar!: foo, bar))\n</p>",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -408,9 +408,9 @@ class StatusTest extends MediaWikiLangTestCase {
|
||||||
$status->warning( new Message( 'fooBar2!' ) );
|
$status->warning( new Message( 'fooBar2!' ) );
|
||||||
$testCases['2MessageWarnings'] = [
|
$testCases['2MessageWarnings'] = [
|
||||||
$status,
|
$status,
|
||||||
"* <fooBar!>\n* <fooBar2!>\n",
|
"* ⧼fooBar!⧽\n* ⧼fooBar2!⧽\n",
|
||||||
"(wrap-long: * (fooBar!: foo, bar)\n* (fooBar2!)\n)",
|
"(wrap-long: * (fooBar!: foo, bar)\n* (fooBar2!)\n)",
|
||||||
"<ul><li> <fooBar!></li>\n<li> <fooBar2!></li></ul>\n",
|
"<ul><li> ⧼fooBar!⧽</li>\n<li> ⧼fooBar2!⧽</li></ul>\n",
|
||||||
"<p>(wrap-long: * (fooBar!: foo, bar)\n</p>\n<ul><li> (fooBar2!)</li></ul>\n<p>)\n</p>",
|
"<p>(wrap-long: * (fooBar!: foo, bar)\n</p>\n<ul><li> (fooBar2!)</li></ul>\n<p>)\n</p>",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue