*/ public static function provideMaliciousHtml(): Traversable { yield 'script tag' => ['Hello', ' ['', 'onerror']; yield 'JavaScript URL' => ['click', 'href="javascript:']; yield 'style tag' => ['Hello', 'Hello world

'); self::assertSame('

Hello world

', $result); } public static function test_sanitizeHTML_whenUnsafeAttributeIsRemoved_keepsAllowedTag(): void { self::assertSame('Hello
', FreshRSS_SimplePieCustom::sanitizeHTML('Hello
')); self::assertSame('Hello
', FreshRSS_SimplePieCustom::sanitizeHTML('Hello
', maxLength: 100)); } public static function test_sanitizeHTML_whenMaxLengthIsZeroOrNegative_returnsEmptyString(): void { self::assertSame('', FreshRSS_SimplePieCustom::sanitizeHTML('

Hello world

', maxLength: 0)); self::assertSame('', FreshRSS_SimplePieCustom::sanitizeHTML('

Hello world

', maxLength: -1)); } public static function test_sanitizeHTML_whenResultFitsWithinMaxLength_isUnaffected(): void { $result = FreshRSS_SimplePieCustom::sanitizeHTML('

Hello world

', maxLength: 100); self::assertSame('

Hello world

', $result); } public static function test_sanitizeHTML_whenUnsafePrefixExceedsMaxLength_keepsSafeText(): void { self::assertSame('text', FreshRSS_SimplePieCustom::sanitizeHTML('

text', maxLength: 5)); } /** * Sanitizing can grow a truncated fragment (e.g. `

He` gets sanitized into `

He

`) */ #[DataProvider('provideMaxLengthInputs')] public static function test_sanitizeHTML_whenMaxLengthForcesReSanitizing_terminatesWithinBound(string $input, int $maxLength): void { $result = FreshRSS_SimplePieCustom::sanitizeHTML($input, maxLength: $maxLength); self::assertLessThanOrEqual($maxLength, strlen($result)); } /** @return Traversable */ public static function provideMaxLengthInputs(): Traversable { yield 'unclosed tag' => ['

Hello world

', 5]; yield 'repeated short tags' => [str_repeat('x ', 50), 20]; yield 'single-character budget' => ['

Hello world

', 1]; } #[DataProvider('provideIncompleteTagsOrEntities')] public static function test_sanitizeHTML_Cases(string $input, int $maxLength, string $expected): void { $result = FreshRSS_SimplePieCustom::sanitizeHTML($input, maxLength: $maxLength); self::assertLessThanOrEqual($maxLength, strlen($result)); self::assertSame(trim($expected), trim($result)); } /** @return Traversable */ public static function provideIncompleteTagsOrEntities(): Traversable { yield 'unclosed tag not fitting' => ['Hello World', 31, 'Hello']; yield 'unclosed entity' => ['Hello…', 9, 'Hello']; yield 'double unclosed tag' => [' x', 10, 'x']; yield 'triple unclosed tag' => [' y', 20, 'y']; } }