*/ private static function entriesFromRss(string $rss): array { $feed = new FreshRSS_Feed('http://example.net/feed.xml', validate: false); $feed->_id(1); $simplePie = new FreshRSS_SimplePieCustom(); $simplePie->enable_cache(false); $simplePie->set_raw_data($rss); self::assertTrue($simplePie->init()); return array_values(iterator_to_array($feed->loadEntries($simplePie))); } public function test_content_dropsUnsafeEnclosureAndThumbnailUrls(): void { $rss = << Malicious feed https://example.net/ Feed with malicious enclosure URLs Victim Article https://example.net/article poc-001 Tue, 14 Nov 2023 20:13:20 +0000 Hello XML; $entries = self::entriesFromRss($rss); self::assertCount(1, $entries); $entry = $entries[0]; self::assertSame('Victim Article', $entry->title()); // The malicious `` of the item must not be stored as an attribute self::assertNull($entry->attributeArray('thumbnail')); // The malicious enclosure must not even be stored as an attribute $enclosureUrls = array_column($entry->attributeArray('enclosures') ?? [], 'url'); self::assertNotContains('javascript:alert(document.domain)//', $enclosureUrls); self::assertContains('https://example.com/podcast.mp3', $enclosureUrls); // SimplePie must absolutise protocol-relative URLs against the feed URL self::assertContains('https://cdn.example.com/podcast2.mp3', $enclosureUrls); $html = $entry->content(); self::assertStringNotContainsString('javascript:', $html); self::assertStringContainsString('href="https://example.com/podcast.mp3"', $html); self::assertStringContainsString('src="https://example.com/thumb.jpg"', $html); self::assertStringContainsString('href="https://cdn.example.com/podcast2.mp3"', $html); self::assertStringContainsString('src="https://cdn.example.com/thumb2.jpg"', $html); self::assertStringContainsString('Hello', $html); } public function test_content_dropsUnsafeUrlsFromLegacyAttributes(): void { $entry = new FreshRSS_Entry(1, 'poc-003', 'Victim Article', '', 'Hello', 'https://example.net/article'); $entry->_attributes([ 'thumbnail' => ['url' => 'javascript:alert(1)'], 'enclosures' => [ ['url' => 'javascript:alert(2)', 'title' => 'evil'], ['url' => 'https://example.com/podcast.mp3', 'thumbnails' => ['javascript:alert(3)', 'https://example.com/thumb.jpg']], ], ]); $html = $entry->content(); self::assertStringNotContainsString('javascript:', $html); self::assertStringContainsString('href="https://example.com/podcast.mp3"', $html); self::assertStringContainsString('src="https://example.com/thumb.jpg"', $html); self::assertStringContainsString('Hello', $html); } }