Просмотр исходного кода

Fix escaping of JSON dot notation categories (#9489)

convertJsonToRss() HTML-escapes item fields before rendering the RSS,
but the is_string() guard skipped 'tags', which is always an array.
An unescaped & in a JSON category then produced invalid XML, so
SimplePie rejected the whole document and the feed showed as empty.

Minz_Helper::htmlspecialchars_utf8() already handles arrays,
so the guard is dropped. Add a test for array and string categories.

fix https://github.com/FreshRSS/FreshRSS/issues/9488

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Ron Richter 10 часов назад
Родитель
Сommit
22c3128b23
3 измененных файлов с 28 добавлено и 1 удалено
  1. 1 0
      CREDITS.md
  2. 1 1
      app/Utils/dotNotationUtil.php
  3. 26 0
      tests/app/Utils/dotNotationUtilTest.php

+ 1 - 0
CREDITS.md

@@ -312,6 +312,7 @@ People are sorted by name so please keep this order.
 * [robertdahlem](https://github.com/robertdahlem): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:robertdahlem)
 * [rocka](https://github.com/rocka): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:rocka)
 * [romibi](https://github.com/romibi): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:romibi)
+* [Ron Richter](https://github.com/eMPai-git): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:eMPai-git)
 * [Rosemary Le Faive](https://github.com/rosiel): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:rosiel)
 * [Rufubi](https://github.com/Rufubi): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:Rufubi)
 * [Rui Melo](https://github.com/ViPeR5000): [contributions](https://github.com/FreshRSS/FreshRSS/pulls?q=is:pr+author:ViPeR5000)

+ 1 - 1
app/Utils/dotNotationUtil.php

@@ -225,7 +225,7 @@ final class FreshRSS_dotNotation_Util
 			if ($rssItem['title'] != '' || $rssItem['content'] != '' || $rssItem['link'] != '') {
 				// HTML-encoding/escaping of the relevant fields (all except 'content')
 				foreach (['author', 'guid', 'link', 'thumbnail', 'timestamp', 'tags', 'title'] as $key) {
-					if (!empty($rssItem[$key]) && is_string($rssItem[$key])) {
+					if (!empty($rssItem[$key])) {
 						$rssItem[$key] = Minz_Helper::htmlspecialchars_utf8($rssItem[$key]);
 					}
 				}

+ 26 - 0
tests/app/Utils/dotNotationUtilTest.php

@@ -46,4 +46,30 @@ final class dotNotationUtilTest extends \PHPUnit\Framework\TestCase {
 		$value = FreshRSS_dotNotation_Util::get($array, $key);
 		self::assertSame($expected, $value);
 	}
+
+	public static function testConvertJsonToRssEscapesCategories(): void {
+		FreshRSS_Context::initSystem();
+		Minz_Translate::init('en');
+		$json = <<<json
+		{
+			"items": [
+				{"title": "First", "url": "https://example.net/1", "categories": ["News & Politics", "Arts & Culture"]},
+				{"title": "Second", "url": "https://example.net/2", "categories": "Science & Tech"}
+			]
+		}
+		json;
+		$jf = json_decode($json, true);
+		self::assertIsArray($jf);
+		$rss = FreshRSS_dotNotation_Util::convertJsonToRss($jf, 'https://example.net/', [
+			'item' => 'items',
+			'itemTitle' => 'title',
+			'itemUri' => 'url',
+			'itemCategories' => 'categories',
+		], 'Test');
+		self::assertIsString($rss);
+		self::assertStringContainsString('<category>News &amp; Politics</category>', $rss);
+		self::assertStringContainsString('<category>Arts &amp; Culture</category>', $rss);
+		self::assertStringContainsString('<category>Science &amp; Tech</category>', $rss);
+		self::assertNotFalse(simplexml_load_string($rss), 'Output must be valid XML');
+	}
 }