fix: Variablenwerte für Links/Bilder lehnen //host, /\host und Steuerzeichen ab

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-19 14:41:06 +02:00
co-authored by Claude Sonnet 5.5
parent 5064409f9a
commit c6daae131d
2 changed files with 13 additions and 3 deletions
@@ -27,7 +27,10 @@ final class VariableValue
/** Control characters except the line feed. */ /** Control characters except the line feed. */
private const CONTROL = '/[\x00-\x09\x0B-\x1F\x7F]/'; private const CONTROL = '/[\x00-\x09\x0B-\x1F\x7F]/';
private const URL = '/^(?:https?:\/\/[^\s<>"]+|\/[^\s<>"]*|mailto:[^\s<>"]+|tel:[^\s<>"]+)$/iD'; /** Every control character: Divi's esc_url_raw strips them, so the read-back would differ. */
private const URL_CONTROL = '/[\x00-\x1F\x7F]/';
private const URL = '/^(?:https?:\/\/[^\s<>"]+|\/(?![\/\\\\])[^\s<>"]*|mailto:[^\s<>"]+|tel:[^\s<>"]+)$/iD';
private const WEB_URL = '/^https?:\/\/[^\s<>"]+$/iD'; private const WEB_URL = '/^https?:\/\/[^\s<>"]+$/iD';
@@ -74,7 +77,8 @@ final class VariableValue
private static function links(string $value): ?string private static function links(string $value): ?string
{ {
if (mb_strlen($value) > self::URL_MAX || preg_match(self::URL, $value) !== 1) { if (mb_strlen($value) > self::URL_MAX || preg_match(self::URL, $value) !== 1
|| preg_match(self::URL_CONTROL, $value) === 1) {
return 'Link: eine http(s)://- oder /-URL, mailto: oder tel: mit höchstens 2000 Zeichen.'; return 'Link: eine http(s)://- oder /-URL, mailto: oder tel: mit höchstens 2000 Zeichen.';
} }
@@ -84,7 +88,8 @@ final class VariableValue
private static function images(string $value): ?string private static function images(string $value): ?string
{ {
$valid = preg_match('/^\d+$/D', $value) === 1 $valid = preg_match('/^\d+$/D', $value) === 1
|| (mb_strlen($value) <= self::URL_MAX && preg_match(self::WEB_URL, $value) === 1); || (mb_strlen($value) <= self::URL_MAX && preg_match(self::WEB_URL, $value) === 1
&& preg_match(self::URL_CONTROL, $value) !== 1);
return $valid ? null : 'Bild: eine Anhang-ID (Ziffern) oder eine http(s)://-URL mit höchstens 2000 Zeichen.'; return $valid ? null : 'Bild: eine Anhang-ID (Ziffern) oder eine http(s)://-URL mit höchstens 2000 Zeichen.';
} }
@@ -60,6 +60,11 @@ final class VariableValueTest extends TestCase
'links relative without slash' => ['links', 'kontakt/'], 'links relative without slash' => ['links', 'kontakt/'],
'links with space' => ['links', 'https://example.com/a b'], 'links with space' => ['links', 'https://example.com/a b'],
'links mailto empty' => ['links', 'mailto:'], 'links mailto empty' => ['links', 'mailto:'],
'links protocol-relative' => ['links', '//evil.example/x'],
'links backslash host' => ['links', '/\\evil.example/x'],
'links control character' => ['links', "https://example.com/a\x01b"],
'links delete character' => ['links', "/kontakt\x7F"],
'images control character' => ['images', "https://example.com/a\x00.jpg"],
'links too long' => ['links', 'https://example.com/' . str_repeat('a', 2000)], 'links too long' => ['links', 'https://example.com/' . str_repeat('a', 2000)],
'images negative' => ['images', '-5'], 'images negative' => ['images', '-5'],
'images relative path' => ['images', '/wp-content/a.jpg'], 'images relative path' => ['images', '/wp-content/a.jpg'],