Page MenuHomePhabricator

[Clonable] replace wfMessage()->rawParams() with wfMessage()->plaintextParams() where applicable
Open, LowPublic

Description

Background

The MediaWiki i18n system is used to ensure that our software can be translated into many languages. Any thing in the interface gets a key (e.g. notloggedin). In the code we do wfMessage( 'notloggedin' )->text() and then the user sees Not logged in.

Messages can also contain different format types which we use depending on context. If we're outputting to a page as HTML, we use ->parse(). This converts things like [[foo]] into links and allows stuff like <big> to make text big, but bans unsafe html like <script>. If for some reason we are outputting to a page but don't want to allow html formatting in the message, we use ->escaped(). This turns <big> into &lt;big&gt; so that the user sees the word <big> and not just big text. If the message is going to be escaped later on, we use ->text(). This does only minimal processing (It processes things like {{PLURAL:...}} and {{GENDER:..}}, and does not html escapes. This ensures that if someone does code like htmlspecialchars( wfMessage( 'myMsg' )->text() ), and the message contains <big> its displayed as <big> to the user, as opposed to htmlspecialchars( wfMessage('myMsg')->escaped() ) [or ->parse()], where <big> would be converted to &amp;lt;big&amp;gt; which is shown to the user as &lt;big&gt; which is wrong (Called double escaping).

Sometimes we need to insert things into messages. For example the message cannotdelete-title is given if there is an error in deleting a page. We would like to include the name of the page in the error message. The contents of this message are Cannot delete page "$1". We can then specify a parameter that get's inserted where the $1 is - wfMessage( 'cannotdelete-title' )->params( "some page" )->text(); gives us Cannot delete page "some page"

There are also different parameter types. For this task, the important ones are ->params(), ->rawParams(), and ->plaintextParams(). ->params() is just a normal parameter. It gets inserted into where the $1 is, and then the message is escaped or parsed. ->rawParams() is meant to be used when you want to insert some unsafe html into a message, so that the ->parse() format specifier doesn't remove the html for being unsafe. This is very commonly used with Linker::makeLink and $linkRenderer->makeLink() to insert links into message. ->plaintextParams() is used when you want to insert a parameter into a message, and ensure that it isn't processed (That is, {{GENDER:... and other wikitext is not converted), but the parameter should still be html escaped.

For more information on how wfMessage() works see https://www.mediawiki.org/wiki/Manual:Messages_API. The purpose of this task is that often developers use ->rawParams() when they should be using ->plaintextParams(), and that should be fixed. To complete this task you have to replace one incorrect usage of ->rawParams()

The task

There are three cases

Case 1 ( non-html rawParams() in a ->text() message)

->rawParams() only makes sense to use when the format specifier is ->parse(), ->parseAsBlock() or ->escaped() . If ->rawParams() is used with ->text() or ->plain() format specifiers, then something is wrong. Most likely, the parameter should use ->plaintextParams() instead.

This is the case whenever the parameter in question does not contain any html (For example, it does not contain Linker::makeLink or $linkRenderer->makeLink or similar link related stuff)

For example

$this->context->msg( 'newsectionsummary' )
	->rawParams( $cleanSectionTitle )
	->inContentLanguage()
	->text();

Can be safely replaced with plaintextParams:

$this->context->msg( 'newsectionsummary' )
	->plaintextParams( $cleanSectionTitle )
	->inContentLanguage()
	->text();

Because ->text() is a format that is not ->parse(), ->parseAsBlock() or ->escaped() and the $cleanSectionTitle is not Linker::makeLink.

Case 2 ( ->rawParams() with ->text() when the param contains html)

Sometimes developers use ->text() when they are supposed to use ->parse(). If you see something like

wfMessage( 'foo' )
     ->rawParams( Linker::makeLink( $title ) )
     ->text();

Where the raw parameter is something with arbitrary html in it, this means the developer used the wrong message format type. It also means that wiki admins could insert malicious html by editing the page MediaWiki:<message key> (So in the above example, an admin can put <img src=x onerror=alert(1)> in the page MediaWiki:Foo, and then a pop up box appears). This is bad, so in this case, you should change the format to be ->parse() while keeping the ->rawParams() the same.

Case 3 (Using ->rawParams( htmlspecialchars( $foo ) instead of plaintextParams directly)

[This case is harder to recognize] If ->rawParams() contains some sort of value that is html escaped, and the non-html escaped version is available, you can replace rawParams with using the non-html escaped version and plaintextParams. e.g.

$param = htmlspecialchars( $this->getSomething() );
wfMessage( 'foo' )->rawParams( $param )->parse();

can be replaced with

wfMessage( 'foo' )->plaintextParams( $this->getSomething() )->parse();

Once you've changed a ->rawParams to a ->plaintextParams, you should test your changes, to ensure that the output displayed to the user is still the same. If you can control the input to the message, you should especially test that things like <script>alert(1)</script> are handled properly (That is <script>alert(1)</script> is actually shown to the user, no popup box happens, and the user is not shown something like &lt;script&gt;alert(1)&lt;/script&gt;


This task applies to both MediaWiki core, as well as any extension in Wikimedia's version control. To complete this task, you only have to fix one instance.

Event Timeline

Change 395916 had a related patch set uploaded (by Ryan10145; owner: Ryan10145):
[mediawiki/core@master] Changed Instances of rawParams() to plaintextParams()

https://gerrit.wikimedia.org/r/395916

Change 395916 merged by jenkins-bot:
[mediawiki/core@master] Changed Instances of rawParams() to plaintextParams()

https://gerrit.wikimedia.org/r/395916

Change 396548 had a related patch set uploaded (by Divadsn; owner: Divadsn):
[mediawiki/extensions/Newsletter@master] Replace instances of rawParams() to plaintextParams()

https://gerrit.wikimedia.org/r/396548

Change 396548 merged by jenkins-bot:
[mediawiki/extensions/Newsletter@master] Replace instances of rawParams() to plaintextParams()

https://gerrit.wikimedia.org/r/396548

Bawolff updated the task description. (Show Details)

Change 396590 had a related patch set uploaded (by Phantom42; owner: Phantom42):
[mediawiki/extensions/Newsletter@master] Using plaintextParams instead of rawParams for newsletter name

https://gerrit.wikimedia.org/r/396590

The change I just submitted makes Newsletter special pages use plaintextParams instead of rawParams with escaped newsletter name. So that's for case #3. I have tested this of course: after change output displayed to user is still the same. I also tested <script>alert(1)</script> thing.

If something is wrong, please tell and I will fix it.

Change 396590 merged by jenkins-bot:
[mediawiki/extensions/Newsletter@master] Using plaintextParams instead of rawParams for newsletter name

https://gerrit.wikimedia.org/r/396590

As an aside, T141670 is also basically about this issue.

btw, some hints for where to look

(The following is generated by an automated tool. the tool may have bugs, or these things may be caused by issues other then the issue this task is for)

Abuse Filter extension

./includes/Views/AbuseFilterViewEdit.php:609 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewEdit::buildFilterEditor that outputs using tainted argument $[arg #1].
./includes/Views/AbuseFilterViewEdit.php:609 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewEdit::buildFilterEditor that outputs using tainted argument $[arg #3].
./includes/Views/AbuseFilterViewEdit.php:609 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewEdit::buildFilterEditor that outputs using tainted argument $[arg #4].
./includes/Views/AbuseFilterViewEdit.php:609 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewEdit::buildFilterEditor that outputs using tainted argument $userName. (Caused by: ./includes/Views/AbuseFilterViewEdit.php +607)
./includes/Views/AbuseFilterViewList.php:333 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterPager::formatValue that outputs using tainted argument $user. (Caused by: ./includes/Views/AbuseFilterViewList.php +332)
./includes/Views/AbuseFilterViewRevert.php:64 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewRevert::show that outputs using tainted argument $[arg #1]. (Caused by: ./includes/Views/AbuseFilterViewRevert.php +59)
./includes/Views/AbuseFilterViewRevert.php:64 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewRevert::show that outputs using tainted argument $[arg #3]. (Caused by: ./includes/Views/AbuseFilterViewRevert.php +59)
./includes/Views/AbuseFilterViewRevert.php:64 SecurityCheck-XSS Calling method \Message::rawParams() in \AbuseFilterViewRevert::show that outputs using tainted argument $[arg #5]. (Caused by: ./includes/Views/AbuseFilterViewRevert.php +60)
./includes/special/SpecialAbuseLog.php:819 SecurityCheck-XSS Calling method \Message::rawParams() in \SpecialAbuseLog::formatRow that outputs using tainted argument $actions_taken. (Caused by: ./includes/special/SpecialAbuseLog.php +750; ./includes/special/SpecialAbuseLog.php +758; ./includes/special/SpecialAbuseLog.php +819)
./includes/special/SpecialAbuseLog.php:835 SecurityCheck-XSS Calling method \Message::rawParams() in \SpecialAbuseLog::formatRow that outputs using tainted argument $actions_taken. (Caused by: ./includes/special/SpecialAbuseLog.php +750; ./includes/special/SpecialAbuseLog.php +758; ./includes/special/SpecialAbuseLog.php +819; ./includes/special/SpecialAbuseLog.php +835)

Change 398775 had a related patch set uploaded (by Pppery; owner: Pppery):
[mediawiki/extensions/MobileFrontend@master] Fix escaping of 'mobile-frontend-diffview-comma' message

https://gerrit.wikimedia.org/r/398775

Change 398775 merged by jenkins-bot:
[mediawiki/extensions/MobileFrontend@master] Fix escaping of 'mobile-frontend-diffview-comma' message

https://gerrit.wikimedia.org/r/398775

Change 401585 had a related patch set uploaded (by LukBukkit; owner: LukBukkit):
[mediawiki/core@master] Replaced ->text() with ->parse() because of ->rawParams()

https://gerrit.wikimedia.org/r/401585

Change 401585 abandoned by LukBukkit:
Replaced ->text() with ->parse() because of ->rawParams()

https://gerrit.wikimedia.org/r/401585

Change 401616 had a related patch set uploaded (by LukBukkit; owner: LukBukkit):
[mediawiki/extensions/Wikibase@master] Replaced ->rawParams( htmlspecialchars() ) with ->plaintextParams

https://gerrit.wikimedia.org/r/401616

Change 401616 merged by jenkins-bot:
[mediawiki/extensions/Wikibase@master] Replaced ->rawParams with ->plaintextParams

https://gerrit.wikimedia.org/r/401616

I am proposing this task Mini-MWT at VVIT (Feb 2019), I have to prepare subtask for it for newbies student. Please give up it until 23 Feb.

Change 561320 had a related patch set uploaded (by JeremyNguyenGCI; owner: JeremyNguyenGCI):
[mediawiki/extensions/RevisionCommentSupplement@master] Special: Updated rawParams( htmlspecialchars()) with plaintextParams()

https://gerrit.wikimedia.org/r/561320

Change 561320 merged by jenkins-bot:
[mediawiki/extensions/RevisionCommentSupplement@master] Special: Updated rawParams( htmlspecialchars()) with plaintextParams()

https://gerrit.wikimedia.org/r/561320

Hasn't this been repaired several times? Why hasn't it been fixed yet?

@I: This task is not resolved yet because there are likely places that still require you or someone else to provide patches in order to update remaining code. (Please ask general questions in support forum instead - thanks.)

The vast majority of these seem to have been fixed over the years? A quick code search reveals a few rawParams(...)->text(...) calls. Sourcegraph finds some others with its structural search of our github mirrors. A few of these are likely true positives, maybe, though the rest have either been noted as exceptions via // @phan-suppress-next-line SecurityCheck-XSS comments or are in unit test files.