Version in base suite: 5.4.23+dfsg-1+deb12u2 Base version: symfony_5.4.23+dfsg-1+deb12u2 Target version: symfony_5.4.23+dfsg-1+deb12u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/symfony/symfony_5.4.23+dfsg-1+deb12u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/symfony/symfony_5.4.23+dfsg-1+deb12u3.dsc changelog | 13 patches/Do-not-read-from-argv-on-non-CLI-SAPIs.patch | 102 ++ patches/ErrorHandler-Extend-test-expectation.patch | 21 patches/HttpClient-Filter-private-IPs-before-connecting-when-Host.patch | 85 ++ patches/HttpClient-Temporary-test-hack.patch | 27 patches/HttpFoundation-Reject-URIs-that-contain-invalid-character.patch | 104 ++ patches/PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch | 119 ++ patches/PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch | 192 ++++ patches/Runtime-fix-tests.patch | 22 patches/Validator-Add-D-regex-modifier-in-relevant-validators.patch | 414 ++++++++++ patches/fix-tests.patch | 51 + patches/series | 11 patches/skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch | 45 + 13 files changed, 1206 insertions(+) diff -Nru symfony-5.4.23+dfsg/debian/changelog symfony-5.4.23+dfsg/debian/changelog --- symfony-5.4.23+dfsg/debian/changelog 2024-02-13 21:01:20.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/changelog 2024-11-09 09:22:58.000000000 +0000 @@ -1,3 +1,16 @@ +symfony (5.4.23+dfsg-1+deb12u3) bookworm-security; urgency=medium + + * Backport security fixes from Symfony 5.4.46 + - [Validator] Add D regex modifier in relevant validators [CVE-2024-50343] + - Do not read from argv on non-CLI SAPIs [CVE-2024-50340] + - [HttpClient] Filter private IPs before connecting when Host == IP + [CVE-2024-50342] + - [HttpFoundation] Reject URIs that contain invalid characters + [CVE-2024-50345] + * Backport fixes to test suite + + -- David Prévot Sat, 09 Nov 2024 10:22:58 +0100 + symfony (5.4.23+dfsg-1+deb12u2) bookworm; urgency=medium * make sure that the submitted year is an accepted choice (Closes: #1061033) diff -Nru symfony-5.4.23+dfsg/debian/patches/Do-not-read-from-argv-on-non-CLI-SAPIs.patch symfony-5.4.23+dfsg/debian/patches/Do-not-read-from-argv-on-non-CLI-SAPIs.patch --- symfony-5.4.23+dfsg/debian/patches/Do-not-read-from-argv-on-non-CLI-SAPIs.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/Do-not-read-from-argv-on-non-CLI-SAPIs.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,102 @@ +From: Wouter de Jong +Date: Tue, 15 Oct 2024 10:18:46 +0200 +Subject: Do not read from argv on non-CLI SAPIs + +Origin: upstream, https://github.com/symfony/symfony/commit/a77b308c3f179ed7c8a8bc295f82b2d6ee3493fa +Bug: https://github.com/symfony/symfony/security/advisories/GHSA-x8vp-gf4q-mw5j +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2024-50340 +--- + src/Symfony/Component/Runtime/SymfonyRuntime.php | 6 +++++- + src/Symfony/Component/Runtime/Tests/phpt/kernel.php | 8 +++++--- + src/Symfony/Component/Runtime/Tests/phpt/kernel.phpt | 2 +- + .../Runtime/Tests/phpt/kernel_register_argc_argv.phpt | 18 ++++++++++++++++++ + 4 files changed, 29 insertions(+), 5 deletions(-) + create mode 100644 src/Symfony/Component/Runtime/Tests/phpt/kernel_register_argc_argv.phpt + +diff --git a/src/Symfony/Component/Runtime/SymfonyRuntime.php b/src/Symfony/Component/Runtime/SymfonyRuntime.php +index 0ca9713..5612b3e 100644 +--- a/src/Symfony/Component/Runtime/SymfonyRuntime.php ++++ b/src/Symfony/Component/Runtime/SymfonyRuntime.php +@@ -95,7 +95,7 @@ class SymfonyRuntime extends GenericRuntime + + if (isset($options['env'])) { + $_SERVER[$envKey] = $options['env']; +- } elseif (isset($_SERVER['argv']) && class_exists(ArgvInput::class)) { ++ } elseif (empty($_GET) && isset($_SERVER['argv']) && class_exists(ArgvInput::class)) { + $this->options = $options; + $this->getInput(); + } +@@ -216,6 +216,10 @@ class SymfonyRuntime extends GenericRuntime + + private function getInput(): ArgvInput + { ++ if (!empty($_GET) && filter_var(ini_get('register_argc_argv'), \FILTER_VALIDATE_BOOL)) { ++ throw new \Exception('CLI applications cannot be run safely on non-CLI SAPIs with register_argc_argv=On.'); ++ } ++ + if (null !== $this->input) { + return $this->input; + } +diff --git a/src/Symfony/Component/Runtime/Tests/phpt/kernel.php b/src/Symfony/Component/Runtime/Tests/phpt/kernel.php +index ba29d34..b7c43c5 100644 +--- a/src/Symfony/Component/Runtime/Tests/phpt/kernel.php ++++ b/src/Symfony/Component/Runtime/Tests/phpt/kernel.php +@@ -17,19 +17,21 @@ require __DIR__.'/autoload.php'; + + class TestKernel implements HttpKernelInterface + { ++ private $env; + private $var; + +- public function __construct(string $var) ++ public function __construct(string $env, string $var) + { ++ $this->env = $env; + $this->var = $var; + } + + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true): Response + { +- return new Response('OK Kernel '.$this->var); ++ return new Response('OK Kernel (env='.$this->env.') '.$this->var); + } + } + + return function (array $context) { +- return new TestKernel($context['SOME_VAR']); ++ return new TestKernel($context['APP_ENV'], $context['SOME_VAR']); + }; +diff --git a/src/Symfony/Component/Runtime/Tests/phpt/kernel.phpt b/src/Symfony/Component/Runtime/Tests/phpt/kernel.phpt +index e739eb0..e7df91e 100644 +--- a/src/Symfony/Component/Runtime/Tests/phpt/kernel.phpt ++++ b/src/Symfony/Component/Runtime/Tests/phpt/kernel.phpt +@@ -9,4 +9,4 @@ require $_SERVER['SCRIPT_FILENAME'] = __DIR__.'/kernel.php'; + + ?> + --EXPECTF-- +-OK Kernel foo_bar ++OK Kernel (env=dev) foo_bar +diff --git a/src/Symfony/Component/Runtime/Tests/phpt/kernel_register_argc_argv.phpt b/src/Symfony/Component/Runtime/Tests/phpt/kernel_register_argc_argv.phpt +new file mode 100644 +index 0000000..4da82d2 +--- /dev/null ++++ b/src/Symfony/Component/Runtime/Tests/phpt/kernel_register_argc_argv.phpt +@@ -0,0 +1,18 @@ ++--TEST-- ++Test HttpKernelInterface with register_argc_argv=1 ++--INI-- ++display_errors=1 ++register_argc_argv=1 ++--FILE-- ++ ++--EXPECTF-- ++OK Kernel (env=dev) foo_bar diff -Nru symfony-5.4.23+dfsg/debian/patches/ErrorHandler-Extend-test-expectation.patch symfony-5.4.23+dfsg/debian/patches/ErrorHandler-Extend-test-expectation.patch --- symfony-5.4.23+dfsg/debian/patches/ErrorHandler-Extend-test-expectation.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/ErrorHandler-Extend-test-expectation.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,21 @@ +From: =?utf-8?q?David_Pr=C3=A9vot?= +Date: Sat, 9 Nov 2024 18:45:01 +0100 +Subject: [ErrorHandler] Extend test expectation + +--- + src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php +index 8f57bb5..9738c88 100644 +--- a/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php ++++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorHandlerTest.php +@@ -381,7 +381,7 @@ class ErrorHandlerTest extends TestCase + restore_exception_handler(); + } + +- $this->assertSame('User Warning: foo stdClass@anonymous bar', $e->getMessage()); ++ $this->assertStringMatchesFormat('User Warning: foo stdClass@anonymous%A bar', $e->getMessage()); + } + + public function testHandleDeprecation() diff -Nru symfony-5.4.23+dfsg/debian/patches/HttpClient-Filter-private-IPs-before-connecting-when-Host.patch symfony-5.4.23+dfsg/debian/patches/HttpClient-Filter-private-IPs-before-connecting-when-Host.patch --- symfony-5.4.23+dfsg/debian/patches/HttpClient-Filter-private-IPs-before-connecting-when-Host.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/HttpClient-Filter-private-IPs-before-connecting-when-Host.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,85 @@ +From: Nicolas Grekas +Date: Fri, 25 Oct 2024 10:13:01 +0200 +Subject: [HttpClient] Filter private IPs before connecting when Host == IP + +Origin: upstream, https://symfony.com/blog/cve-2024-50342-internal-address-and-port-enumeration-allowed-by-noprivatenetworkhttpclient +Bug: https://github.com/symfony/symfony/security/advisories/GHSA-9c3x-r3wp-mgxm +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2024-50342 +--- + .../HttpClient/NoPrivateNetworkHttpClient.php | 13 ++++++++++- + .../Tests/NoPrivateNetworkHttpClientTest.php | 27 ++++++++++++++++++++-- + 2 files changed, 37 insertions(+), 3 deletions(-) + +diff --git a/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php +index 911cce9..fcbf833 100644 +--- a/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php ++++ b/src/Symfony/Component/HttpClient/NoPrivateNetworkHttpClient.php +@@ -77,9 +77,20 @@ final class NoPrivateNetworkHttpClient implements HttpClientInterface, LoggerAwa + } + + $subnets = $this->subnets; ++ $lastUrl = ''; + $lastPrimaryIp = ''; + +- $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastPrimaryIp): void { ++ $options['on_progress'] = function (int $dlNow, int $dlSize, array $info) use ($onProgress, $subnets, &$lastUrl, &$lastPrimaryIp): void { ++ if ($info['url'] !== $lastUrl) { ++ $host = trim(parse_url($info['url'], PHP_URL_HOST) ?: '', '[]'); ++ ++ if ($host && IpUtils::checkIp($host, $subnets ?? self::PRIVATE_SUBNETS)) { ++ throw new TransportException(sprintf('Host "%s" is blocked for "%s".', $host, $info['url'])); ++ } ++ ++ $lastUrl = $info['url']; ++ } ++ + if ($info['primary_ip'] !== $lastPrimaryIp) { + if ($info['primary_ip'] && IpUtils::checkIp($info['primary_ip'], $subnets ?? self::PRIVATE_SUBNETS)) { + throw new TransportException(sprintf('IP "%s" is blocked for "%s".', $info['primary_ip'], $info['url'])); +diff --git a/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php +index 8c51e9e..7130c09 100755 +--- a/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php ++++ b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php +@@ -65,10 +65,10 @@ class NoPrivateNetworkHttpClientTest extends TestCase + /** + * @dataProvider getExcludeData + */ +- public function testExclude(string $ipAddr, $subnets, bool $mustThrow) ++ public function testExcludeByIp(string $ipAddr, $subnets, bool $mustThrow) + { + $content = 'foo'; +- $url = sprintf('http://%s/', 0 < substr_count($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr); ++ $url = sprintf('http://%s/', strtr($ipAddr, '.:', '--')); + + if ($mustThrow) { + $this->expectException(TransportException::class); +@@ -85,6 +85,29 @@ class NoPrivateNetworkHttpClientTest extends TestCase + } + } + ++ /** ++ * @dataProvider getExcludeData ++ */ ++ public function testExcludeByHost(string $ipAddr, $subnets, bool $mustThrow) ++ { ++ $content = 'foo'; ++ $url = sprintf('http://%s/', str_contains($ipAddr, ':') ? sprintf('[%s]', $ipAddr) : $ipAddr); ++ ++ if ($mustThrow) { ++ $this->expectException(TransportException::class); ++ $this->expectExceptionMessage(sprintf('Host "%s" is blocked for "%s".', $ipAddr, $url)); ++ } ++ ++ $previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content); ++ $client = new NoPrivateNetworkHttpClient($previousHttpClient, $subnets); ++ $response = $client->request('GET', $url); ++ ++ if (!$mustThrow) { ++ $this->assertEquals($content, $response->getContent()); ++ $this->assertEquals(200, $response->getStatusCode()); ++ } ++ } ++ + public function testCustomOnProgressCallback() + { + $ipAddr = '104.26.14.6'; diff -Nru symfony-5.4.23+dfsg/debian/patches/HttpClient-Temporary-test-hack.patch symfony-5.4.23+dfsg/debian/patches/HttpClient-Temporary-test-hack.patch --- symfony-5.4.23+dfsg/debian/patches/HttpClient-Temporary-test-hack.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/HttpClient-Temporary-test-hack.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,27 @@ +From: =?utf-8?q?David_Pr=C3=A9vot?= +Date: Sun, 10 Nov 2024 08:56:23 +0100 +Subject: [HttpClient] Temporary test hack +MIME-Version: 1.0 +Content-Type: text/plain; charset="utf-8" +Content-Transfer-Encoding: 8bit + +Since php-symfony-http-client is part of the Build-Dependency chain, the +“old” version is used at build time, so the “new” error message is not +yet available. This patch can be dropped for the next upload. +--- + .../Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php +index 7130c09..9fdf110 100755 +--- a/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php ++++ b/src/Symfony/Component/HttpClient/Tests/NoPrivateNetworkHttpClientTest.php +@@ -95,7 +95,7 @@ class NoPrivateNetworkHttpClientTest extends TestCase + + if ($mustThrow) { + $this->expectException(TransportException::class); +- $this->expectExceptionMessage(sprintf('Host "%s" is blocked for "%s".', $ipAddr, $url)); ++ $this->expectExceptionMessage(sprintf('"%s" is blocked for "%s".', $ipAddr, $url)); + } + + $previousHttpClient = $this->getHttpClientMock($url, $ipAddr, $content); diff -Nru symfony-5.4.23+dfsg/debian/patches/HttpFoundation-Reject-URIs-that-contain-invalid-character.patch symfony-5.4.23+dfsg/debian/patches/HttpFoundation-Reject-URIs-that-contain-invalid-character.patch --- symfony-5.4.23+dfsg/debian/patches/HttpFoundation-Reject-URIs-that-contain-invalid-character.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/HttpFoundation-Reject-URIs-that-contain-invalid-character.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,104 @@ +From: Nicolas Grekas +Date: Tue, 22 Oct 2024 10:31:42 +0200 +Subject: [HttpFoundation] Reject URIs that contain invalid characters + +Origin: backport, https://github.com/symfony/symfony/commit/5a9b08e5740af795854b1b639b7d45b9cbfe8819 +Bug: https://github.com/symfony/symfony/security/advisories/GHSA-mrqx-rp3w-jpjp +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2024-50345 +--- + src/Symfony/Component/HttpFoundation/Request.php | 18 +++++++++++++ + .../Component/HttpFoundation/Tests/RequestTest.php | 30 ++++++++++++++++++++-- + 2 files changed, 46 insertions(+), 2 deletions(-) + +diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php +index 28cebad..acad3c9 100644 +--- a/src/Symfony/Component/HttpFoundation/Request.php ++++ b/src/Symfony/Component/HttpFoundation/Request.php +@@ -11,6 +11,7 @@ + + namespace Symfony\Component\HttpFoundation; + ++use Symfony\Component\HttpFoundation\Exception\BadRequestException; + use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; + use Symfony\Component\HttpFoundation\Exception\JsonException; + use Symfony\Component\HttpFoundation\Exception\SessionNotFoundException; +@@ -330,6 +331,8 @@ class Request + * @param string|resource|null $content The raw body data + * + * @return static ++ * ++ * @throws BadRequestException When the URI is invalid + */ + public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null) + { +@@ -353,6 +356,21 @@ class Request + $server['REQUEST_METHOD'] = strtoupper($method); + + $components = parse_url($uri); ++ ++ if (false === $components) { ++ throw new BadRequestException('Invalid URI.'); ++ } ++ ++ if (false !== ($i = strpos($uri, '\\')) && $i < strcspn($uri, '?#')) { ++ throw new BadRequestException('Invalid URI: A URI cannot contain a backslash.'); ++ } ++ if (\strlen($uri) !== strcspn($uri, "\r\n\t")) { ++ throw new BadRequestException('Invalid URI: A URI cannot contain CR/LF/TAB characters.'); ++ } ++ if ('' !== $uri && (\ord($uri[0]) <= 32 || \ord($uri[-1]) <= 32)) { ++ throw new BadRequestException('Invalid URI: A URI must not start nor end with ASCII control characters or spaces.'); ++ } ++ + if (isset($components['host'])) { + $server['SERVER_NAME'] = $components['host']; + $server['HTTP_HOST'] = $components['host']; +diff --git a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +index e536080..993d95c 100644 +--- a/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php ++++ b/src/Symfony/Component/HttpFoundation/Tests/RequestTest.php +@@ -13,6 +13,7 @@ namespace Symfony\Component\HttpFoundation\Tests; + + use PHPUnit\Framework\TestCase; + use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; ++use Symfony\Component\HttpFoundation\Exception\BadRequestException; + use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException; + use Symfony\Component\HttpFoundation\Exception\JsonException; + use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException; +@@ -286,9 +287,34 @@ class RequestTest extends TestCase + $this->assertTrue($request->isSecure()); + + // Fragment should not be included in the URI +- $request = Request::create('http://test.com/foo#bar'); +- $request->server->set('REQUEST_URI', 'http://test.com/foo#bar'); ++ $request = Request::create('http://test.com/foo#bar\\baz'); ++ $request->server->set('REQUEST_URI', 'http://test.com/foo#bar\\baz'); + $this->assertEquals('http://test.com/foo', $request->getUri()); ++ ++ $request = Request::create('http://test.com/foo?bar=f\\o'); ++ $this->assertEquals('http://test.com/foo?bar=f%5Co', $request->getUri()); ++ $this->assertEquals('/foo', $request->getPathInfo()); ++ $this->assertEquals('bar=f%5Co', $request->getQueryString()); ++ } ++ ++ /** ++ * @testWith ["http://foo.com\\bar"] ++ * ["\\\\foo.com/bar"] ++ * ["a\rb"] ++ * ["a\nb"] ++ * ["a\tb"] ++ * ["\u0000foo"] ++ * ["foo\u0000"] ++ * [" foo"] ++ * ["foo "] ++ * [":"] ++ */ ++ public function testCreateWithBadRequestUri(string $uri) ++ { ++ $this->expectException(BadRequestException::class); ++ $this->expectExceptionMessage('Invalid URI'); ++ ++ Request::create($uri); + } + + /** diff -Nru symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch --- symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,119 @@ +From: Alexandre Daubois +Date: Tue, 7 May 2024 10:04:19 +0200 +Subject: [PasswordHasher] Make bcrypt nul byte hash test tolerant to PHP + related failures + +Origin: upstream, https://github.com/symfony/symfony/commit/30de18b659e331828df1618b6734fe4348a9ae63 +--- + .../Tests/Hasher/NativePasswordHasherTest.php | 36 +++++++++++++++++--- + .../Tests/Hasher/SodiumPasswordHasherTest.php | 38 +++++++++++++++++++--- + 2 files changed, 65 insertions(+), 9 deletions(-) + +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +index 4cf708b..9895910 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +@@ -98,16 +98,44 @@ class NativePasswordHasherTest extends TestCase + $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + +- public function testBcryptWithNulByte() ++ /** ++ * @requires PHP < 8.4 ++ */ ++ public function testBcryptWithNulByteWithNativePasswordHash() + { + $hasher = new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT); + $plainPassword = "a\0b"; + +- if (\PHP_VERSION_ID < 80218 || \PHP_VERSION_ID >= 80300 && \PHP_VERSION_ID < 80305) { +- // password_hash() does not accept passwords containing NUL bytes since PHP 8.2.18 and 8.3.5 +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ try { ++ $hash = password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]); ++ } catch (\Throwable $throwable) { ++ // we skip the test in case the current PHP version does not support NUL bytes in passwords ++ // with bcrypt ++ // ++ // @see https://github.com/php/php-src/commit/11f2568767660ffe92fbc6799800e01203aad73a ++ if (str_contains($throwable->getMessage(), 'Bcrypt password must not contain null character')) { ++ $this->markTestSkipped('password_hash() does not accept passwords containing NUL bytes.'); ++ } ++ ++ throw $throwable; + } + ++ if (null === $hash) { ++ // we also skip the test in case password_hash() returns null as ++ // implemented in security patches backports ++ // ++ // @see https://github.com/shivammathur/php-src-backports/commit/d22d9ebb29dce86edd622205dd1196a2796c08c7 ++ $this->markTestSkipped('password_hash() does not accept passwords containing NUL bytes.'); ++ } ++ ++ $this->assertTrue($hasher->verify($hash, $plainPassword)); ++ } ++ ++ public function testPasswordNulByteGracefullyHandled() ++ { ++ $hasher = new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT); ++ $plainPassword = "a\0b"; ++ + $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +index 101c09f..2931635 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +@@ -73,17 +73,45 @@ class SodiumPasswordHasherTest extends TestCase + $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword)); + } + +- public function testBcryptWithNulByte() ++ /** ++ * @requires PHP < 8.4 ++ */ ++ public function testBcryptWithNulByteWithNativePasswordHash() + { + $hasher = new SodiumPasswordHasher(null, null); + $plainPassword = "a\0b"; + +- if (\PHP_VERSION_ID < 80218 || \PHP_VERSION_ID >= 80300 && \PHP_VERSION_ID < 80305) { +- // password_hash() does not accept passwords containing NUL bytes since PHP 8.2.18 and 8.3.5 +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ try { ++ $hash = password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]); ++ } catch (\Throwable $throwable) { ++ // we skip the test in case the current PHP version does not support NUL bytes in passwords ++ // with bcrypt ++ // ++ // @see https://github.com/php/php-src/commit/11f2568767660ffe92fbc6799800e01203aad73a ++ if (str_contains($throwable->getMessage(), 'Bcrypt password must not contain null character')) { ++ $this->markTestSkipped('password_hash() does not accept passwords containing NUL bytes.'); ++ } ++ ++ throw $throwable; + } + +- $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword)); ++ if (null === $hash) { ++ // we also skip the test in case password_hash() returns null as ++ // implemented in security patches backports ++ // ++ // @see https://github.com/shivammathur/php-src-backports/commit/d22d9ebb29dce86edd622205dd1196a2796c08c7 ++ $this->markTestSkipped('password_hash() does not accept passwords containing NUL bytes.'); ++ } ++ ++ $this->assertTrue($hasher->verify($hash, $plainPassword)); ++ } ++ ++ public function testPasswordNulByteGracefullyHandled() ++ { ++ $hasher = new SodiumPasswordHasher(null, null); ++ $plainPassword = "a\0b"; ++ ++ $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + + public function testUserProvidedSaltIsNotUsed() diff -Nru symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch --- symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,192 @@ +From: Oskar Stark +Date: Thu, 2 Nov 2023 11:18:11 +0100 +Subject: [PasswordHasher][Tests] Do not invoke methods with additional + arguments in tests + +Origin: backport, https://github.com/symfony/symfony/commit/fdaefc4e8580f1dafb7b5487e7957ea4af2e42d8 +--- + .../Tests/Hasher/NativePasswordHasherTest.php | 32 ++++++++-------- + .../Tests/Hasher/SodiumPasswordHasherTest.php | 44 +++++++++++----------- + 2 files changed, 38 insertions(+), 38 deletions(-) + +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +index 2b7bd78..5dc3019 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +@@ -51,25 +51,25 @@ class NativePasswordHasherTest extends TestCase + { + $hasher = new NativePasswordHasher(); + $result = $hasher->hash('password', null); +- $this->assertTrue($hasher->verify($result, 'password', null)); +- $this->assertFalse($hasher->verify($result, 'anotherPassword', null)); +- $this->assertFalse($hasher->verify($result, '', null)); ++ $this->assertTrue($hasher->verify($result, 'password')); ++ $this->assertFalse($hasher->verify($result, 'anotherPassword')); ++ $this->assertFalse($hasher->verify($result, '')); + } + + public function testNonArgonValidation() + { + $hasher = new NativePasswordHasher(); +- $this->assertTrue($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password', null)); +- $this->assertFalse($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword', null)); +- $this->assertTrue($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password', null)); +- $this->assertFalse($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword', null)); ++ $this->assertTrue($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password')); ++ $this->assertFalse($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword')); ++ $this->assertTrue($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password')); ++ $this->assertFalse($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword')); + } + + public function testConfiguredAlgorithm() + { + $hasher = new NativePasswordHasher(null, null, null, \PASSWORD_BCRYPT); +- $result = $hasher->hash('password', null); +- $this->assertTrue($hasher->verify($result, 'password', null)); ++ $result = $hasher->hash('password'); ++ $this->assertTrue($hasher->verify($result, 'password')); + $this->assertStringStartsWith('$2', $result); + } + +@@ -84,8 +84,8 @@ class NativePasswordHasherTest extends TestCase + public function testConfiguredAlgorithmWithLegacyConstValue() + { + $hasher = new NativePasswordHasher(null, null, null, '1'); +- $result = $hasher->hash('password', null); +- $this->assertTrue($hasher->verify($result, 'password', null)); ++ $result = $hasher->hash('password'); ++ $this->assertTrue($hasher->verify($result, 'password')); + $this->assertStringStartsWith('$2', $result); + } + +@@ -94,8 +94,8 @@ class NativePasswordHasherTest extends TestCase + $hasher = new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT); + $plainPassword = str_repeat('a', 100); + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword, 'salt')); +- $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword, 'salt')); ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + + public function testBcryptWithNulByte() +@@ -103,8 +103,8 @@ class NativePasswordHasherTest extends TestCase + $hasher = new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT); + $plainPassword = "a\0b"; + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword, 'salt')); +- $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword, 'salt')); ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + + public function testNeedsRehash() +@@ -113,7 +113,7 @@ class NativePasswordHasherTest extends TestCase + + $this->assertTrue($hasher->needsRehash('dummyhash')); + +- $hash = $hasher->hash('foo', 'salt'); ++ $hash = $hasher->hash('foo'); + $this->assertFalse($hasher->needsRehash($hash)); + + $hasher = new NativePasswordHasher(5, 11000, 5); +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +index 67210de..3dc97c7 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +@@ -28,65 +28,65 @@ class SodiumPasswordHasherTest extends TestCase + public function testValidation() + { + $hasher = new SodiumPasswordHasher(); +- $result = $hasher->hash('password', null); +- $this->assertTrue($hasher->verify($result, 'password', null)); +- $this->assertFalse($hasher->verify($result, 'anotherPassword', null)); +- $this->assertFalse($hasher->verify($result, '', null)); ++ $result = $hasher->hash('password'); ++ $this->assertTrue($hasher->verify($result, 'password')); ++ $this->assertFalse($hasher->verify($result, 'anotherPassword')); ++ $this->assertFalse($hasher->verify($result, '')); + } + + public function testBcryptValidation() + { + $hasher = new SodiumPasswordHasher(); +- $this->assertTrue($hasher->verify('$2y$04$M8GDODMoGQLQRpkYCdoJh.lbiZPee3SZI32RcYK49XYTolDGwoRMm', 'abc', null)); ++ $this->assertTrue($hasher->verify('$2y$04$M8GDODMoGQLQRpkYCdoJh.lbiZPee3SZI32RcYK49XYTolDGwoRMm', 'abc')); + } + + public function testNonArgonValidation() + { + $hasher = new SodiumPasswordHasher(); +- $this->assertTrue($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password', null)); +- $this->assertFalse($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword', null)); +- $this->assertTrue($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password', null)); +- $this->assertFalse($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword', null)); ++ $this->assertTrue($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'password')); ++ $this->assertFalse($hasher->verify('$5$abcdefgh$ZLdkj8mkc2XVSrPVjskDAgZPGjtj1VGVaa1aUkrMTU/', 'anotherPassword')); ++ $this->assertTrue($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'password')); ++ $this->assertFalse($hasher->verify('$6$abcdefgh$yVfUwsw5T.JApa8POvClA1pQ5peiq97DUNyXCZN5IrF.BMSkiaLQ5kvpuEm/VQ1Tvh/KV2TcaWh8qinoW5dhA1', 'anotherPassword')); + } + + public function testHashLength() + { + $this->expectException(InvalidPasswordException::class); + $hasher = new SodiumPasswordHasher(); +- $hasher->hash(str_repeat('a', 4097), 'salt'); ++ $hasher->hash(str_repeat('a', 4097)); + } + + public function testCheckPasswordLength() + { + $hasher = new SodiumPasswordHasher(); +- $result = $hasher->hash(str_repeat('a', 4096), null); +- $this->assertFalse($hasher->verify($result, str_repeat('a', 4097), null)); +- $this->assertTrue($hasher->verify($result, str_repeat('a', 4096), null)); ++ $result = $hasher->hash(str_repeat('a', 4096)); ++ $this->assertFalse($hasher->verify($result, str_repeat('a', 4097))); ++ $this->assertTrue($hasher->verify($result, str_repeat('a', 4096))); + } + + public function testBcryptWithLongPassword() + { +- $hasher = new SodiumPasswordHasher(null, null, 4); ++ $hasher = new SodiumPasswordHasher(null, null); + $plainPassword = str_repeat('a', 100); + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword, 'salt')); +- $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword, 'salt')); ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword)); + } + + public function testBcryptWithNulByte() + { +- $hasher = new SodiumPasswordHasher(null, null, 4); ++ $hasher = new SodiumPasswordHasher(null, null); + $plainPassword = "a\0b"; + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword, 'salt')); +- $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword, 'salt')); ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword)); + } + + public function testUserProvidedSaltIsNotUsed() + { + $hasher = new SodiumPasswordHasher(); +- $result = $hasher->hash('password', 'salt'); +- $this->assertTrue($hasher->verify($result, 'password', 'anotherSalt')); ++ $result = $hasher->hash('password'); ++ $this->assertTrue($hasher->verify($result, 'password')); + } + + public function testNeedsRehash() +@@ -95,7 +95,7 @@ class SodiumPasswordHasherTest extends TestCase + + $this->assertTrue($hasher->needsRehash('dummyhash')); + +- $hash = $hasher->hash('foo', 'salt'); ++ $hash = $hasher->hash('foo'); + $this->assertFalse($hasher->needsRehash($hash)); + + $hasher = new SodiumPasswordHasher(5, 11000); diff -Nru symfony-5.4.23+dfsg/debian/patches/Runtime-fix-tests.patch symfony-5.4.23+dfsg/debian/patches/Runtime-fix-tests.patch --- symfony-5.4.23+dfsg/debian/patches/Runtime-fix-tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/Runtime-fix-tests.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,22 @@ +From: Nicolas Grekas +Date: Wed, 6 Nov 2024 09:58:41 +0100 +Subject: [Runtime] fix tests + +Origin: upstream, https://github.com/symfony/symfony/commit/65678fe67c71eddde04f297a7e469ad0264bdcf3 +--- + src/Symfony/Component/Runtime/Tests/phpt/kernel-loop.phpt | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/Symfony/Component/Runtime/Tests/phpt/kernel-loop.phpt b/src/Symfony/Component/Runtime/Tests/phpt/kernel-loop.phpt +index 966007c..0b31e61 100644 +--- a/src/Symfony/Component/Runtime/Tests/phpt/kernel-loop.phpt ++++ b/src/Symfony/Component/Runtime/Tests/phpt/kernel-loop.phpt +@@ -11,6 +11,6 @@ require __DIR__.'/kernel-loop.php'; + + ?> + --EXPECTF-- +-OK Kernel foo_bar +-OK Kernel foo_bar ++OK Kernel (env=dev) foo_bar ++OK Kernel (env=dev) foo_bar + 0 diff -Nru symfony-5.4.23+dfsg/debian/patches/Validator-Add-D-regex-modifier-in-relevant-validators.patch symfony-5.4.23+dfsg/debian/patches/Validator-Add-D-regex-modifier-in-relevant-validators.patch --- symfony-5.4.23+dfsg/debian/patches/Validator-Add-D-regex-modifier-in-relevant-validators.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/Validator-Add-D-regex-modifier-in-relevant-validators.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,414 @@ +From: Alexandre Daubois +Date: Tue, 13 Aug 2024 09:02:30 +0200 +Subject: [Validator] Add `D` regex modifier in relevant validators + +Origin: upstream, https://github.com/symfony/symfony/commit/7d1032bbead9a4229b32fa6ebca32681c80cb76f +Bug: https://github.com/symfony/symfony/security/advisories/GHSA-g3rh-rrhp-jhh9 +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2024-50343 +--- + .../Validator/Constraints/CardSchemeValidator.php | 38 +++++++++++----------- + .../Validator/Constraints/CssColorValidator.php | 24 +++++++------- + .../Validator/Constraints/DateValidator.php | 2 +- + .../Validator/Constraints/EmailValidator.php | 4 +-- + .../Validator/Constraints/TimeValidator.php | 2 +- + .../Validator/Constraints/UrlValidator.php | 2 +- + .../Tests/Constraints/CardSchemeValidatorTest.php | 30 +++++++++++++++++ + .../Tests/Constraints/CssColorValidatorTest.php | 13 ++++++++ + .../Tests/Constraints/DateValidatorTest.php | 13 ++++++++ + .../Tests/Constraints/EmailValidatorTest.php | 13 ++++++++ + .../Tests/Constraints/IbanValidatorTest.php | 13 ++++++++ + .../Tests/Constraints/TimeValidatorTest.php | 13 ++++++++ + .../Tests/Constraints/UrlValidatorTest.php | 31 ++++++++++++++++++ + 13 files changed, 162 insertions(+), 36 deletions(-) + +diff --git a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +index faef7ec..9425e9b 100644 +--- a/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/CardSchemeValidator.php +@@ -29,65 +29,65 @@ class CardSchemeValidator extends ConstraintValidator + protected $schemes = [ + // American Express card numbers start with 34 or 37 and have 15 digits. + CardScheme::AMEX => [ +- '/^3[47][0-9]{13}$/', ++ '/^3[47][0-9]{13}$/D', + ], + // China UnionPay cards start with 62 and have between 16 and 19 digits. + // Please note that these cards do not follow Luhn Algorithm as a checksum. + CardScheme::CHINA_UNIONPAY => [ +- '/^62[0-9]{14,17}$/', ++ '/^62[0-9]{14,17}$/D', + ], + // Diners Club card numbers begin with 300 through 305, 36 or 38. All have 14 digits. + // There are Diners Club cards that begin with 5 and have 16 digits. + // These are a joint venture between Diners Club and MasterCard, and should be processed like a MasterCard. + CardScheme::DINERS => [ +- '/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/', ++ '/^3(?:0[0-5]|[68][0-9])[0-9]{11}$/D', + ], + // Discover card numbers begin with 6011, 622126 through 622925, 644 through 649 or 65. + // All have 16 digits. + CardScheme::DISCOVER => [ +- '/^6011[0-9]{12}$/', +- '/^64[4-9][0-9]{13}$/', +- '/^65[0-9]{14}$/', +- '/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/', ++ '/^6011[0-9]{12}$/D', ++ '/^64[4-9][0-9]{13}$/D', ++ '/^65[0-9]{14}$/D', ++ '/^622(12[6-9]|1[3-9][0-9]|[2-8][0-9][0-9]|91[0-9]|92[0-5])[0-9]{10}$/D', + ], + // InstaPayment cards begin with 637 through 639 and have 16 digits. + CardScheme::INSTAPAYMENT => [ +- '/^63[7-9][0-9]{13}$/', ++ '/^63[7-9][0-9]{13}$/D', + ], + // JCB cards beginning with 2131 or 1800 have 15 digits. + // JCB cards beginning with 35 have 16 digits. + CardScheme::JCB => [ +- '/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/', ++ '/^(?:2131|1800|35[0-9]{3})[0-9]{11}$/D', + ], + // Laser cards begin with either 6304, 6706, 6709 or 6771 and have between 16 and 19 digits. + CardScheme::LASER => [ +- '/^(6304|670[69]|6771)[0-9]{12,15}$/', ++ '/^(6304|670[69]|6771)[0-9]{12,15}$/D', + ], + // Maestro international cards begin with 675900..675999 and have between 12 and 19 digits. + // Maestro UK cards begin with either 500000..509999 or 560000..699999 and have between 12 and 19 digits. + CardScheme::MAESTRO => [ +- '/^(6759[0-9]{2})[0-9]{6,13}$/', +- '/^(50[0-9]{4})[0-9]{6,13}$/', +- '/^5[6-9][0-9]{10,17}$/', +- '/^6[0-9]{11,18}$/', ++ '/^(6759[0-9]{2})[0-9]{6,13}$/D', ++ '/^(50[0-9]{4})[0-9]{6,13}$/D', ++ '/^5[6-9][0-9]{10,17}$/D', ++ '/^6[0-9]{11,18}$/D', + ], + // All MasterCard numbers start with the numbers 51 through 55. All have 16 digits. + // October 2016 MasterCard numbers can also start with 222100 through 272099. + CardScheme::MASTERCARD => [ +- '/^5[1-5][0-9]{14}$/', +- '/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/', ++ '/^5[1-5][0-9]{14}$/D', ++ '/^2(22[1-9][0-9]{12}|2[3-9][0-9]{13}|[3-6][0-9]{14}|7[0-1][0-9]{13}|720[0-9]{12})$/D', + ], + // Payment system MIR numbers start with 220, then 1 digit from 0 to 4, then between 12 and 15 digits + CardScheme::MIR => [ +- '/^220[0-4][0-9]{12,15}$/', ++ '/^220[0-4][0-9]{12,15}$/D', + ], + // All UATP card numbers start with a 1 and have a length of 15 digits. + CardScheme::UATP => [ +- '/^1[0-9]{14}$/', ++ '/^1[0-9]{14}$/D', + ], + // All Visa card numbers start with a 4 and have a length of 13, 16, or 19 digits. + CardScheme::VISA => [ +- '/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/', ++ '/^4([0-9]{12}|[0-9]{15}|[0-9]{18})$/D', + ], + ]; + +diff --git a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php +index b34ef93..be377d8 100644 +--- a/src/Symfony/Component/Validator/Constraints/CssColorValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/CssColorValidator.php +@@ -21,21 +21,21 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; + */ + class CssColorValidator extends ConstraintValidator + { +- private const PATTERN_HEX_LONG = '/^#[0-9a-f]{6}$/i'; +- private const PATTERN_HEX_LONG_WITH_ALPHA = '/^#[0-9a-f]{8}$/i'; +- private const PATTERN_HEX_SHORT = '/^#[0-9a-f]{3}$/i'; +- private const PATTERN_HEX_SHORT_WITH_ALPHA = '/^#[0-9a-f]{4}$/i'; ++ private const PATTERN_HEX_LONG = '/^#[0-9a-f]{6}$/iD'; ++ private const PATTERN_HEX_LONG_WITH_ALPHA = '/^#[0-9a-f]{8}$/iD'; ++ private const PATTERN_HEX_SHORT = '/^#[0-9a-f]{3}$/iD'; ++ private const PATTERN_HEX_SHORT_WITH_ALPHA = '/^#[0-9a-f]{4}$/iD'; + // List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Basic_Colors +- private const PATTERN_BASIC_NAMED_COLORS = '/^(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)$/i'; ++ private const PATTERN_BASIC_NAMED_COLORS = '/^(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)$/iD'; + // List comes from https://www.w3.org/wiki/CSS/Properties/color/keywords#Extended_colors +- private const PATTERN_EXTENDED_NAMED_COLORS = '/^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/i'; ++ private const PATTERN_EXTENDED_NAMED_COLORS = '/^(aliceblue|antiquewhite|aqua|aquamarine|azure|beige|bisque|black|blanchedalmond|blue|blueviolet|brown|burlywood|cadetblue|chartreuse|chocolate|coral|cornflowerblue|cornsilk|crimson|cyan|darkblue|darkcyan|darkgoldenrod|darkgray|darkgreen|darkgrey|darkkhaki|darkmagenta|darkolivegreen|darkorange|darkorchid|darkred|darksalmon|darkseagreen|darkslateblue|darkslategray|darkslategrey|darkturquoise|darkviolet|deeppink|deepskyblue|dimgray|dimgrey|dodgerblue|firebrick|floralwhite|forestgreen|fuchsia|gainsboro|ghostwhite|gold|goldenrod|gray|green|greenyellow|grey|honeydew|hotpink|indianred|indigo|ivory|khaki|lavender|lavenderblush|lawngreen|lemonchiffon|lightblue|lightcoral|lightcyan|lightgoldenrodyellow|lightgray|lightgreen|lightgrey|lightpink|lightsalmon|lightseagreen|lightskyblue|lightslategray|lightslategrey|lightsteelblue|lightyellow|lime|limegreen|linen|magenta|maroon|mediumaquamarine|mediumblue|mediumorchid|mediumpurple|mediumseagreen|mediumslateblue|mediumspringgreen|mediumturquoise|mediumvioletred|midnightblue|mintcream|mistyrose|moccasin|navajowhite|navy|oldlace|olive|olivedrab|orange|orangered|orchid|palegoldenrod|palegreen|paleturquoise|palevioletred|papayawhip|peachpuff|peru|pink|plum|powderblue|purple|red|rosybrown|royalblue|saddlebrown|salmon|sandybrown|seagreen|seashell|sienna|silver|skyblue|slateblue|slategray|slategrey|snow|springgreen|steelblue|tan|teal|thistle|tomato|turquoise|violet|wheat|white|whitesmoke|yellow|yellowgreen)$/iD'; + // List comes from https://drafts.csswg.org/css-color/#css-system-colors +- private const PATTERN_SYSTEM_COLORS = '/^(Canvas|CanvasText|LinkText|VisitedText|ActiveText|ButtonFace|ButtonText|ButtonBorder|Field|FieldText|Highlight|HighlightText|SelectedItem|SelectedItemText|Mark|MarkText|GrayText)$/i'; +- private const PATTERN_KEYWORDS = '/^(transparent|currentColor)$/i'; +- private const PATTERN_RGB = '/^rgb\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\s*\)$/i'; +- private const PATTERN_RGBA = '/^rgba\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|0?\.\d+|1(\.0)?)\s*\)$/i'; +- private const PATTERN_HSL = '/^hsl\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%\s*\)$/i'; +- private const PATTERN_HSLA = '/^hsla\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%,\s*(0|0?\.\d+|1(\.0)?)\s*\)$/i'; ++ private const PATTERN_SYSTEM_COLORS = '/^(Canvas|CanvasText|LinkText|VisitedText|ActiveText|ButtonFace|ButtonText|ButtonBorder|Field|FieldText|Highlight|HighlightText|SelectedItem|SelectedItemText|Mark|MarkText|GrayText)$/iD'; ++ private const PATTERN_KEYWORDS = '/^(transparent|currentColor)$/iD'; ++ private const PATTERN_RGB = '/^rgb\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d)\s*\)$/iD'; ++ private const PATTERN_RGBA = '/^rgba\(\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|255|25[0-4]|2[0-4]\d|1\d\d|0?\d?\d),\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD'; ++ private const PATTERN_HSL = '/^hsl\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%\s*\)$/iD'; ++ private const PATTERN_HSLA = '/^hsla\(\s*(0|360|35\d|3[0-4]\d|[12]\d\d|0?\d?\d),\s*(0|100|\d{1,2})%,\s*(0|100|\d{1,2})%,\s*(0|0?\.\d+|1(\.0)?)\s*\)$/iD'; + + private const COLOR_PATTERNS = [ + CssColor::HEX_LONG => self::PATTERN_HEX_LONG, +diff --git a/src/Symfony/Component/Validator/Constraints/DateValidator.php b/src/Symfony/Component/Validator/Constraints/DateValidator.php +index 5a5f22e..4a1fb7d 100644 +--- a/src/Symfony/Component/Validator/Constraints/DateValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/DateValidator.php +@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; + */ + class DateValidator extends ConstraintValidator + { +- public const PATTERN = '/^(?\d{4})-(?\d{2})-(?\d{2})$/'; ++ public const PATTERN = '/^(?\d{4})-(?\d{2})-(?\d{2})$/D'; + + /** + * Checks whether a date is valid. +diff --git a/src/Symfony/Component/Validator/Constraints/EmailValidator.php b/src/Symfony/Component/Validator/Constraints/EmailValidator.php +index 11fc7be..a073ab3 100644 +--- a/src/Symfony/Component/Validator/Constraints/EmailValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/EmailValidator.php +@@ -25,8 +25,8 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; + */ + class EmailValidator extends ConstraintValidator + { +- private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/'; +- private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/'; ++ private const PATTERN_HTML5 = '/^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/D'; ++ private const PATTERN_LOOSE = '/^.+\@\S+\.\S+$/D'; + + private const EMAIL_PATTERNS = [ + Email::VALIDATION_MODE_LOOSE => self::PATTERN_LOOSE, +diff --git a/src/Symfony/Component/Validator/Constraints/TimeValidator.php b/src/Symfony/Component/Validator/Constraints/TimeValidator.php +index 855f320..0065fc9 100644 +--- a/src/Symfony/Component/Validator/Constraints/TimeValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/TimeValidator.php +@@ -21,7 +21,7 @@ use Symfony\Component\Validator\Exception\UnexpectedValueException; + */ + class TimeValidator extends ConstraintValidator + { +- public const PATTERN = '/^(\d{2}):(\d{2}):(\d{2})$/'; ++ public const PATTERN = '/^(\d{2}):(\d{2}):(\d{2})$/D'; + + /** + * Checks whether a time is valid. +diff --git a/src/Symfony/Component/Validator/Constraints/UrlValidator.php b/src/Symfony/Component/Validator/Constraints/UrlValidator.php +index dff0a99..df3ccfd 100644 +--- a/src/Symfony/Component/Validator/Constraints/UrlValidator.php ++++ b/src/Symfony/Component/Validator/Constraints/UrlValidator.php +@@ -43,7 +43,7 @@ class UrlValidator extends ConstraintValidator + (?:/ (?:[\pL\pN\-._\~!$&\'()*+,;=:@]|%%[0-9A-Fa-f]{2})* )* # a path + (?:\? (?:[\pL\pN\-._\~!$&\'\[\]()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a query (optional) + (?:\# (?:[\pL\pN\-._\~!$&\'()*+,;=:@/?]|%%[0-9A-Fa-f]{2})* )? # a fragment (optional) +- $~ixu'; ++ $~ixuD'; + + /** + * {@inheritdoc} +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php +index dcb40c9..9a6bc55 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/CardSchemeValidatorTest.php +@@ -46,6 +46,36 @@ class CardSchemeValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @requires PHP 8 ++ * ++ * @dataProvider getValidNumbers ++ */ ++ public function testValidNumbersWithNewLine($scheme, $number) ++ { ++ $this->validator->validate($number."\n", new CardScheme(['schemes' => $scheme, 'message' => 'myMessage'])); ++ ++ $this->buildViolation('myMessage') ++ ->setParameter('{{ value }}', '"'.$number."\n\"") ++ ->setCode(CardScheme::INVALID_FORMAT_ERROR) ++ ->assertRaised(); ++ } ++ ++ /** ++ * @requires PHP < 8 ++ * ++ * @dataProvider getValidNumbers ++ */ ++ public function testValidNumbersWithNewLinePriorToPhp8($scheme, $number) ++ { ++ $this->validator->validate($number."\n", new CardScheme(['schemes' => $scheme, 'message' => 'myMessage'])); ++ ++ $this->buildViolation('myMessage') ++ ->setParameter('{{ value }}', '"'.$number."\n\"") ++ ->setCode(CardScheme::NOT_NUMERIC_ERROR) ++ ->assertRaised(); ++ } ++ + public function testValidNumberWithOrderedArguments() + { + $this->validator->validate( +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php +index 95b0b6f..e87f588 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/CssColorValidatorTest.php +@@ -52,6 +52,19 @@ final class CssColorValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidAnyColor ++ */ ++ public function testValidAnyColorWithNewLine($cssColor) ++ { ++ $this->validator->validate($cssColor."\n", new CssColor([], 'myMessage')); ++ ++ $this->buildViolation('myMessage') ++ ->setParameter('{{ value }}', '"'.$cssColor."\n\"") ++ ->setCode(CssColor::INVALID_FORMAT_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidAnyColor(): array + { + return [ +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +index b2e9fdf..23725d5 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/DateValidatorTest.php +@@ -53,6 +53,19 @@ class DateValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidDates ++ */ ++ public function testValidDatesWithNewLine(string $date) ++ { ++ $this->validator->validate($date."\n", new Date(['message' => 'myMessage'])); ++ ++ $this->buildViolation('myMessage') ++ ->setParameter('{{ value }}', '"'.$date."\n\"") ++ ->setCode(Date::INVALID_FORMAT_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidDates() + { + return [ +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +index 0904622..b116da6 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/EmailValidatorTest.php +@@ -70,6 +70,19 @@ class EmailValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidEmails ++ */ ++ public function testValidEmailsWithNewLine($email) ++ { ++ $this->validator->validate($email."\n", new Email()); ++ ++ $this->buildViolation('This value is not a valid email address.') ++ ->setParameter('{{ value }}', '"'.$email."\n\"") ++ ->setCode(Email::INVALID_FORMAT_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidEmails() + { + return [ +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php +index 70994f5..ce43893 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/IbanValidatorTest.php +@@ -48,6 +48,19 @@ class IbanValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidIbans ++ */ ++ public function testValidIbansWithNewLine(string $iban) ++ { ++ $this->validator->validate($iban."\n", new Iban()); ++ ++ $this->buildViolation('This is not a valid International Bank Account Number (IBAN).') ++ ->setParameter('{{ value }}', '"'.$iban."\n\"") ++ ->setCode(Iban::INVALID_CHARACTERS_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidIbans() + { + return [ +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php +index 80d21d5..56d8abc 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/TimeValidatorTest.php +@@ -53,6 +53,19 @@ class TimeValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidTimes ++ */ ++ public function testValidTimesWithNewLine(string $time) ++ { ++ $this->validator->validate($time."\n", new Time()); ++ ++ $this->buildViolation('This value is not a valid time.') ++ ->setParameter('{{ value }}', '"'.$time."\n".'"') ++ ->setCode(Time::INVALID_FORMAT_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidTimes() + { + return [ +diff --git a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +index e7bd83d..5441477 100644 +--- a/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php ++++ b/src/Symfony/Component/Validator/Tests/Constraints/UrlValidatorTest.php +@@ -60,6 +60,19 @@ class UrlValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidUrls ++ */ ++ public function testValidUrlsWithNewLine($url) ++ { ++ $this->validator->validate($url."\n", new Url()); ++ ++ $this->buildViolation('This value is not a valid URL.') ++ ->setParameter('{{ value }}', '"'.$url."\n".'"') ++ ->setCode(Url::INVALID_URL_ERROR) ++ ->assertRaised(); ++ } ++ + /** + * @dataProvider getValidUrlsWithWhitespaces + */ +@@ -85,6 +98,24 @@ class UrlValidatorTest extends ConstraintValidatorTestCase + $this->assertNoViolation(); + } + ++ /** ++ * @dataProvider getValidRelativeUrls ++ * @dataProvider getValidUrls ++ */ ++ public function testValidRelativeUrlWithNewLine(string $url) ++ { ++ $constraint = new Url([ ++ 'relativeProtocol' => true, ++ ]); ++ ++ $this->validator->validate($url."\n", $constraint); ++ ++ $this->buildViolation('This value is not a valid URL.') ++ ->setParameter('{{ value }}', '"'.$url."\n".'"') ++ ->setCode(Url::INVALID_URL_ERROR) ++ ->assertRaised(); ++ } ++ + public static function getValidRelativeUrls() + { + return [ diff -Nru symfony-5.4.23+dfsg/debian/patches/fix-tests.patch symfony-5.4.23+dfsg/debian/patches/fix-tests.patch --- symfony-5.4.23+dfsg/debian/patches/fix-tests.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/fix-tests.patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,51 @@ +From: Christian Flothmann +Date: Wed, 17 Jan 2024 11:57:24 +0100 +Subject: fix tests + +Origin: upstream, https://github.com/symfony/symfony/commit/c3f0dc4ebb824afaeb78950b4f253b0062a19856 +--- + .../VarDumper/Tests/Caster/PdoCasterTest.php | 24 +++++++++++++++++++++- + 1 file changed, 23 insertions(+), 1 deletion(-) + +diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/PdoCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/PdoCasterTest.php +index 564c8a0..c6a96ec 100644 +--- a/src/Symfony/Component/VarDumper/Tests/Caster/PdoCasterTest.php ++++ b/src/Symfony/Component/VarDumper/Tests/Caster/PdoCasterTest.php +@@ -43,7 +43,28 @@ class PdoCasterTest extends TestCase + $this->assertSame('NATURAL', $attr['CASE']->class); + $this->assertSame('BOTH', $attr['DEFAULT_FETCH_MODE']->class); + +- $xDump = <<<'EODUMP' ++ if (\PHP_VERSION_ID >= 80215 && \PHP_VERSION_ID < 80300 || \PHP_VERSION_ID >= 80302) { ++ $xDump = <<<'EODUMP' ++array:2 [ ++ "\x00~\x00inTransaction" => false ++ "\x00~\x00attributes" => array:10 [ ++ "CASE" => NATURAL ++ "ERRMODE" => EXCEPTION ++ "PERSISTENT" => false ++ "DRIVER_NAME" => "sqlite" ++ "ORACLE_NULLS" => NATURAL ++ "CLIENT_VERSION" => "%s" ++ "SERVER_VERSION" => "%s" ++ "STATEMENT_CLASS" => array:%d [ ++ 0 => "PDOStatement"%A ++ ] ++ "STRINGIFY_FETCHES" => false ++ "DEFAULT_FETCH_MODE" => BOTH ++ ] ++] ++EODUMP; ++ } else { ++ $xDump = <<<'EODUMP' + array:2 [ + "\x00~\x00inTransaction" => false + "\x00~\x00attributes" => array:9 [ +@@ -61,6 +82,7 @@ array:2 [ + ] + ] + EODUMP; ++ } + + $this->assertDumpMatchesFormat($xDump, $cast); + } diff -Nru symfony-5.4.23+dfsg/debian/patches/series symfony-5.4.23+dfsg/debian/patches/series --- symfony-5.4.23+dfsg/debian/patches/series 2024-02-13 21:01:20.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/series 2024-11-09 09:22:58.000000000 +0000 @@ -34,3 +34,14 @@ TwigBridge-Ensure-CodeExtension-s-filters-properly-escape.patch Security-Fix-possible-session-fixation-when-only-the-toke.patch make-sure-that-the-submitted-year-is-an-accepted-choice.patch +Validator-Add-D-regex-modifier-in-relevant-validators.patch +Do-not-read-from-argv-on-non-CLI-SAPIs.patch +HttpClient-Filter-private-IPs-before-connecting-when-Host.patch +HttpFoundation-Reject-URIs-that-contain-invalid-character.patch +PasswordHasher-Tests-Do-not-invoke-methods-with-additiona.patch +skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch +PasswordHasher-Make-bcrypt-nul-byte-hash-test-tolerant-to.patch +fix-tests.patch +Runtime-fix-tests.patch +ErrorHandler-Extend-test-expectation.patch +HttpClient-Temporary-test-hack.patch diff -Nru symfony-5.4.23+dfsg/debian/patches/skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch symfony-5.4.23+dfsg/debian/patches/skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch --- symfony-5.4.23+dfsg/debian/patches/skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-5.4.23+dfsg/debian/patches/skip-test-assertions-that-are-no-longer-valid-with-PHP-8..patch 2024-11-09 09:22:58.000000000 +0000 @@ -0,0 +1,45 @@ +From: Christian Flothmann +Date: Sat, 13 Apr 2024 09:04:03 +0200 +Subject: skip test assertions that are no longer valid with PHP >= + 8.2.18/8.3.5 + +Origin: upstream, https://github.com/symfony/symfony/commit/210e9e49089d065e2e2f3f4ee90c75155e94ac86 +--- + .../PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php | 6 +++++- + .../PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php | 6 +++++- + 2 files changed, 10 insertions(+), 2 deletions(-) + +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +index 5dc3019..4cf708b 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/NativePasswordHasherTest.php +@@ -103,7 +103,11 @@ class NativePasswordHasherTest extends TestCase + $hasher = new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT); + $plainPassword = "a\0b"; + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ if (\PHP_VERSION_ID < 80218 || \PHP_VERSION_ID >= 80300 && \PHP_VERSION_ID < 80305) { ++ // password_hash() does not accept passwords containing NUL bytes since PHP 8.2.18 and 8.3.5 ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ } ++ + $this->assertTrue($hasher->verify($hasher->hash($plainPassword), $plainPassword)); + } + +diff --git a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +index 3dc97c7..101c09f 100644 +--- a/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php ++++ b/src/Symfony/Component/PasswordHasher/Tests/Hasher/SodiumPasswordHasherTest.php +@@ -78,7 +78,11 @@ class SodiumPasswordHasherTest extends TestCase + $hasher = new SodiumPasswordHasher(null, null); + $plainPassword = "a\0b"; + +- $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ if (\PHP_VERSION_ID < 80218 || \PHP_VERSION_ID >= 80300 && \PHP_VERSION_ID < 80305) { ++ // password_hash() does not accept passwords containing NUL bytes since PHP 8.2.18 and 8.3.5 ++ $this->assertFalse($hasher->verify(password_hash($plainPassword, \PASSWORD_BCRYPT, ['cost' => 4]), $plainPassword)); ++ } ++ + $this->assertTrue($hasher->verify((new NativePasswordHasher(null, null, 4, \PASSWORD_BCRYPT))->hash($plainPassword), $plainPassword)); + } +