From 4579fc1691d826cefd0a4a2ed2f105b3de9477bd Mon Sep 17 00:00:00 2001
From: Lucas Werkmeister <lucas.werkmeister@wikimedia.de>
Date: Tue, 22 Nov 2022 15:44:20 +0100
Subject: [PATCH] SECURITY: HTML-escape inner formatter in HtmlTimeFormatter
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

HtmlTimeFormatter assumed that the inner formatter returned HTML, but we
were actually using it with MwTimeIsoFormatter, which returns plain
text. Given that we weren’t actually using the HTML feature, let’s just
have HtmlTimeFormatter HTML-escape the inner formatter’s result.

Bug: T323592
Change-Id: I5051a6b96af65a981bab33f63e789fadb41a65a5
---
 lib/includes/Formatters/HtmlTimeFormatter.php         |  5 +++--
 .../phpunit/Formatters/HtmlTimeFormatterTest.php      | 11 +++++++++++
 2 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/lib/includes/Formatters/HtmlTimeFormatter.php b/lib/includes/Formatters/HtmlTimeFormatter.php
index f5671707de..4fd7999320 100644
--- a/lib/includes/Formatters/HtmlTimeFormatter.php
+++ b/lib/includes/Formatters/HtmlTimeFormatter.php
@@ -41,7 +41,8 @@ class HtmlTimeFormatter implements ValueFormatter {
 	/**
 	 * @param FormatterOptions|null $options
 	 * @param ValueFormatter $dateTimeFormatter A value formatter that accepts TimeValue objects and
-	 *  returns the formatted date and time, but not the calendar model. Must return HTML.
+	 *  returns the formatted date and time, but not the calendar model.
+	 *  The formatter is assumed to return plain text (its output will be HTML-escaped).
 	 */
 	public function __construct(
 		?FormatterOptions $options,
@@ -66,7 +67,7 @@ public function format( $value ) {
 			throw new InvalidArgumentException( 'Data value type mismatch. Expected a TimeValue.' );
 		}
 
-		$formatted = $this->dateTimeFormatter->format( $value );
+		$formatted = htmlspecialchars( $this->dateTimeFormatter->format( $value ) );
 
 		if ( $this->calendarNameNeeded( $value ) ) {
 			$formatted .= '<sup class="wb-calendar-name">'
diff --git a/lib/tests/phpunit/Formatters/HtmlTimeFormatterTest.php b/lib/tests/phpunit/Formatters/HtmlTimeFormatterTest.php
index 16b3b8edc5..ee95eba2df 100644
--- a/lib/tests/phpunit/Formatters/HtmlTimeFormatterTest.php
+++ b/lib/tests/phpunit/Formatters/HtmlTimeFormatterTest.php
@@ -254,6 +254,17 @@ public function timeFormatProvider() {
 		return $testCases;
 	}
 
+	public function testEscapesHtml(): void {
+		$dateTimeFormatter = $this->createMock( ValueFormatter::class );
+		$dateTimeFormatter->method( 'format' )
+			->willReturn( '<script>' );
+		$formatter = new HtmlTimeFormatter( null, $dateTimeFormatter );
+
+		$value = $this->getTimeValue( 'MOCKTIME', TimeValue::PRECISION_DAY, 'calendar' );
+		$this->assertSame( '&lt;script&gt;<sup class="wb-calendar-name">calendar</sup>',
+			$formatter->format( $value ) );
+	}
+
 	/**
 	 * @dataProvider invalidValueProvider
 	 */
-- 
2.37.2

