From d2f6322af9444ac5cd1ef3ac6f280dbef7f9d1fb Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 3 Mar 2022 11:39:01 +0100 Subject: [PATCH 1/9] [HttpKernel] Remove private headers before storing responses with HttpCache --- .../Component/HttpKernel/HttpCache/Store.php | 20 ++++++++++++++++--- .../HttpKernel/Tests/HttpCache/StoreTest.php | 13 ++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php index eeb7a6ef948b1..43bd7c808252c 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php @@ -26,19 +26,29 @@ class Store implements StoreInterface { protected $root; private $keyCache; - private $locks; + private $locks = []; + private $options; /** + * Constructor. + * + * The available options are: + * + * * private_headers Set of response headers that should not be stored + * when a response is cached. (default: Set-Cookie) + * * @throws \RuntimeException */ - public function __construct(string $root) + public function __construct(string $root, array $options = []) { $this->root = $root; if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); } $this->keyCache = new \SplObjectStorage(); - $this->locks = []; + $this->options = array_merge([ + 'private_headers' => ['Set-Cookie'], + ], $options); } /** @@ -215,6 +225,10 @@ public function write(Request $request, Response $response) $headers = $this->persistResponse($response); unset($headers['age']); + foreach ($this->options['private_headers'] as $h) { + unset($headers[strtolower($h)]); + } + array_unshift($entries, [$storedEnv, $headers]); if (!$this->save($key, serialize($entries))) { diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php index da1f649127405..239361bc8c337 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php @@ -12,8 +12,10 @@ namespace Symfony\Component\HttpKernel\Tests\HttpCache; use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Cookie; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\HttpCache; use Symfony\Component\HttpKernel\HttpCache\Store; class StoreTest extends TestCase @@ -317,6 +319,17 @@ public function testPurgeHttpAndHttps() $this->assertEmpty($this->getStoreMetadata($requestHttps)); } + public function testDoesNotStorePrivateHeaders() + { + $request = Request::create('https://example.com/foo'); + $response = new Response('foo'); + $response->headers->setCookie(Cookie::fromString('foo=bar')); + + $this->store->write($request, $response); + $this->assertArrayNotHasKey('set-cookie', $this->getStoreMetadata($request)[0][1]); + $this->assertNotEmpty($response->headers->getCookies()); + } + protected function storeSimpleEntry($path = null, $headers = []) { if (null === $path) { From 5909d74ecee359ea4982fcf4331aaf2e489a1fd4 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Mon, 23 Jan 2023 19:43:46 +0100 Subject: [PATCH 2/9] [Security/Http] Remove CSRF tokens from storage on successful login --- .../SecurityBundle/Resources/config/security.xml | 1 + .../Tests/Functional/CsrfFormLoginTest.php | 6 ++++++ .../SecurityBundle/Tests/Functional/LogoutTest.php | 4 +--- src/Symfony/Bundle/SecurityBundle/composer.json | 2 +- .../Http/Session/SessionAuthenticationStrategy.php | 14 +++++++++++--- .../Session/SessionAuthenticationStrategyTest.php | 13 +++++++++++++ 6 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml index 3491383b8bba6..eabe5e547fada 100644 --- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml @@ -63,6 +63,7 @@ %security.authentication.session_strategy.strategy% + diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php index 1a672d70f8335..08ea67a6416fa 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php @@ -19,12 +19,15 @@ class CsrfFormLoginTest extends AbstractWebTestCase public function testFormLoginAndLogoutWithCsrfTokens($config) { $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]); + static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[username]'] = 'johannes'; $form['user_login[password]'] = 'test'; $client->submit($form); + $this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo')); + $this->assertRedirect($client->getResponse(), '/profile'); $crawler = $client->followRedirect(); @@ -48,11 +51,14 @@ public function testFormLoginAndLogoutWithCsrfTokens($config) public function testFormLoginWithInvalidCsrfToken($config) { $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]); + static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); $form = $client->request('GET', '/login')->selectButton('login')->form(); $form['user_login[_token]'] = ''; $client->submit($form); + $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo')); + $this->assertRedirect($client->getResponse(), '/login'); $text = $client->followRedirect()->text(null, true); diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php index cb7868f3256ef..465027f42f0c8 100644 --- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php @@ -36,15 +36,13 @@ public function testSessionLessRememberMeLogout() public function testCsrfTokensAreClearedOnLogout() { $client = $this->createClient(['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']); - static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); $client->request('POST', '/login', [ '_username' => 'johannes', '_password' => 'test', ]); - $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo')); - $this->assertSame('bar', static::$container->get('security.csrf.token_storage')->getToken('foo')); + static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); $client->request('GET', '/logout'); diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json index 1106acfa008c6..4061646f399ff 100644 --- a/src/Symfony/Bundle/SecurityBundle/composer.json +++ b/src/Symfony/Bundle/SecurityBundle/composer.json @@ -25,7 +25,7 @@ "symfony/security-core": "^4.4", "symfony/security-csrf": "^4.2|^5.0", "symfony/security-guard": "^4.2|^5.0", - "symfony/security-http": "^4.4.5" + "symfony/security-http": "^4.4.50" }, "require-dev": { "doctrine/annotations": "^1.10.4", diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php index a4bb88818d452..73691058d0ce6 100644 --- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php +++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php @@ -13,6 +13,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; /** * The default session strategy implementation. @@ -31,10 +32,15 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte public const INVALIDATE = 'invalidate'; private $strategy; + private $csrfTokenStorage = null; - public function __construct(string $strategy) + public function __construct(string $strategy, ClearableTokenStorageInterface $csrfTokenStorage = null) { $this->strategy = $strategy; + + if (self::MIGRATE === $strategy) { + $this->csrfTokenStorage = $csrfTokenStorage; + } } /** @@ -47,10 +53,12 @@ public function onAuthentication(Request $request, TokenInterface $token) return; case self::MIGRATE: - // Note: this logic is duplicated in several authentication listeners - // until Symfony 5.0 due to a security fix with BC compat $request->getSession()->migrate(true); + if ($this->csrfTokenStorage) { + $this->csrfTokenStorage->clear(); + } + return; case self::INVALIDATE: diff --git a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php index 010bb0edf2cfd..2051f34239144 100644 --- a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; use Symfony\Component\Security\Http\Tests\Fixtures\TokenInterface; @@ -57,6 +58,18 @@ public function testSessionIsInvalidated() $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); } + public function testCsrfTokensAreCleared() + { + $session = $this->createMock(SessionInterface::class); + $session->expects($this->once())->method('migrate')->with($this->equalTo(true)); + + $csrfStorage = $this->createMock(ClearableTokenStorageInterface::class); + $csrfStorage->expects($this->once())->method('clear'); + + $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE, $csrfStorage); + $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); + } + private function getRequest($session = null) { $request = $this->createMock(Request::class); From aa82db9b1ad07c553efe13b83ed757ead6c0487d Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 24 Jan 2023 14:41:59 +0100 Subject: [PATCH 3/9] Bump Symfony version to 5.4.20 --- src/Symfony/Component/HttpKernel/Kernel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index d354205eb14a8..3126ebd31ab1f 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,12 +78,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static $freshCache = []; - public const VERSION = '5.4.19'; - public const VERSION_ID = 50419; + public const VERSION = '5.4.20-DEV'; + public const VERSION_ID = 50420; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 4; - public const RELEASE_VERSION = 19; - public const EXTRA_VERSION = ''; + public const RELEASE_VERSION = 20; + public const EXTRA_VERSION = 'DEV'; public const END_OF_MAINTENANCE = '11/2024'; public const END_OF_LIFE = '11/2025'; From 538d660c6aa02b0bbc020e3bee708c16171b96aa Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Jan 2023 14:58:30 +0100 Subject: [PATCH 4/9] [Security/Http] Fix compat of persistent remember-me with legacy tokens --- .../RememberMe/PersistentRememberMeHandler.php | 2 -- .../Http/RememberMe/RememberMeDetails.php | 3 +++ .../PersistentRememberMeHandlerTest.php | 15 +++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php index 75fd6b582a6a4..6e43dbf5feec7 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentRememberMeHandler.php @@ -34,7 +34,6 @@ final class PersistentRememberMeHandler extends AbstractRememberMeHandler { private $tokenProvider; private $tokenVerifier; - private $secret; public function __construct(TokenProviderInterface $tokenProvider, string $secret, UserProviderInterface $userProvider, RequestStack $requestStack, array $options, LoggerInterface $logger = null, TokenVerifierInterface $tokenVerifier = null) { @@ -45,7 +44,6 @@ public function __construct(TokenProviderInterface $tokenProvider, string $secre } $this->tokenProvider = $tokenProvider; $this->tokenVerifier = $tokenVerifier; - $this->secret = $secret; } /** diff --git a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php index fea0955ca80e1..6aa65ec4d2187 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php +++ b/src/Symfony/Component/Security/Http/RememberMe/RememberMeDetails.php @@ -36,6 +36,9 @@ public function __construct(string $userFqcn, string $userIdentifier, int $expir public static function fromRawCookie(string $rawCookie): self { + if (!str_contains($rawCookie, self::COOKIE_DELIMITER)) { + $rawCookie = base64_decode($rawCookie); + } $cookieParts = explode(self::COOKIE_DELIMITER, $rawCookie, 4); if (4 !== \count($cookieParts)) { throw new AuthenticationException('The cookie contains invalid data.'); diff --git a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php index da4f26eaaf6d4..76472b1d5733c 100644 --- a/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/RememberMe/PersistentRememberMeHandlerTest.php @@ -156,4 +156,19 @@ public function testConsumeRememberMeCookieExpired() $this->handler->consumeRememberMeCookie(new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue')); } + + public function testBase64EncodedTokens() + { + $this->tokenProvider->expects($this->any()) + ->method('loadTokenBySeries') + ->with('series1') + ->willReturn(new PersistentToken(InMemoryUser::class, 'wouter', 'series1', 'tokenvalue', new \DateTime('-10 min'))) + ; + + $this->tokenProvider->expects($this->once())->method('updateToken')->with('series1'); + + $rememberMeDetails = new RememberMeDetails(InMemoryUser::class, 'wouter', 360, 'series1:tokenvalue'); + $rememberMeDetails = RememberMeDetails::fromRawCookie(base64_encode($rememberMeDetails->toString())); + $this->handler->consumeRememberMeCookie($rememberMeDetails); + } } From 3cb1d70f215f52ff281a4935eef01bf2775f6edd Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Wed, 25 Jan 2023 16:03:53 +0100 Subject: [PATCH 5/9] [HttpClient] Fix collecting data non-late for the profiler --- .../DataCollector/HttpClientDataCollector.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php index cd065961b936e..edd9d1c201be7 100644 --- a/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php +++ b/src/Symfony/Component/HttpClient/DataCollector/HttpClientDataCollector.php @@ -38,22 +38,28 @@ public function registerClient(string $name, TraceableHttpClient $client) */ public function collect(Request $request, Response $response, \Throwable $exception = null) { + $this->lateCollect(); } public function lateCollect() { - $this->reset(); + $this->data['request_count'] = 0; + $this->data['error_count'] = 0; + $this->data += ['clients' => []]; foreach ($this->clients as $name => $client) { [$errorCount, $traces] = $this->collectOnClient($client); - $this->data['clients'][$name] = [ - 'traces' => $traces, - 'error_count' => $errorCount, + $this->data['clients'] += [ + $name => [ + 'traces' => [], + 'error_count' => 0, + ], ]; + $this->data['clients'][$name]['traces'] = array_merge($this->data['clients'][$name]['traces'], $traces); $this->data['request_count'] += \count($traces); - $this->data['error_count'] += $errorCount; + $this->data['error_count'] += $this->data['clients'][$name]['error_count'] += $errorCount; $client->reset(); } From 45d614df79856e57496f3d6ef926c07bcbe903a9 Mon Sep 17 00:00:00 2001 From: Nicolas Grekas Date: Thu, 26 Jan 2023 18:36:15 +0100 Subject: [PATCH 6/9] [DependencyInjection] Fix order of arguments when mixing positional and named ones --- .../Compiler/AutowirePass.php | 6 +++++- .../Compiler/ResolveBindingsPass.php | 16 ++++++++++++++- .../Tests/Compiler/AutowirePassTest.php | 15 ++++++++++++++ .../Compiler/ResolveBindingsPassTest.php | 20 +++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php index c2b80770c880f..5418268756fe6 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/AutowirePass.php @@ -240,6 +240,10 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a foreach ($parameters as $index => $parameter) { $this->defaultArgument->names[$index] = $parameter->name; + if (\array_key_exists($parameter->name, $arguments)) { + $arguments[$index] = $arguments[$parameter->name]; + unset($arguments[$parameter->name]); + } if (\array_key_exists($index, $arguments) && '' !== $arguments[$index]) { continue; } @@ -341,7 +345,7 @@ private function autowireMethod(\ReflectionFunctionAbstract $reflectionMethod, a // it's possible index 1 was set, then index 0, then 2, etc // make sure that we re-order so they're injected as expected - ksort($arguments); + ksort($arguments, \SORT_NATURAL); return $arguments; } diff --git a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php index 5bc379153a19e..5f0d93711af24 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/ResolveBindingsPass.php @@ -177,10 +177,17 @@ protected function processValue($value, bool $isRoot = false) } } + $names = []; + foreach ($reflectionMethod->getParameters() as $key => $parameter) { + $names[$key] = $parameter->name; + if (\array_key_exists($key, $arguments) && '' !== $arguments[$key]) { continue; } + if (\array_key_exists($parameter->name, $arguments) && '' !== $arguments[$parameter->name]) { + continue; + } $typeHint = ProxyHelper::getTypeHint($reflectionMethod, $parameter); $name = Target::parseName($parameter); @@ -210,8 +217,15 @@ protected function processValue($value, bool $isRoot = false) } } + foreach ($names as $key => $name) { + if (\array_key_exists($name, $arguments) && (0 === $key || \array_key_exists($key - 1, $arguments))) { + $arguments[$key] = $arguments[$name]; + unset($arguments[$name]); + } + } + if ($arguments !== $call[1]) { - ksort($arguments); + ksort($arguments, \SORT_NATURAL); $calls[$i][1] = $arguments; } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php index bdac6781072d6..6dcc9d220efde 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/AutowirePassTest.php @@ -1204,4 +1204,19 @@ public function testDecorationWithServiceAndAliasedInterface() static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorInterface::class)); static::assertInstanceOf(DecoratedDecorator::class, $container->get(DecoratorImpl::class)); } + + public function testAutowireWithNamedArgs() + { + $container = new ContainerBuilder(); + + $container->register('foo', MultipleArgumentsOptionalScalar::class) + ->setArguments(['foo' => 'abc']) + ->setAutowired(true) + ->setPublic(true); + $container->register(A::class, A::class); + + (new AutowirePass())->process($container); + + $this->assertEquals([new TypedReference(A::class, A::class), 'abc'], $container->getDefinition('foo')->getArguments()); + } } diff --git a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php index fc48bf0723312..600c8e036c4cd 100644 --- a/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php +++ b/src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveBindingsPassTest.php @@ -249,4 +249,24 @@ public function testBindWithTarget() $this->assertSame('bar', (string) $container->getDefinition('with_target')->getArgument(0)); } + + public function testBindWithNamedArgs() + { + $container = new ContainerBuilder(); + + $bindings = [ + '$apiKey' => new BoundArgument('K'), + ]; + + $definition = $container->register(NamedArgumentsDummy::class, NamedArgumentsDummy::class); + $definition->setArguments(['c' => 'C', 'hostName' => 'H']); + $definition->setBindings($bindings); + + $container->register('foo', CaseSensitiveClass::class); + + $pass = new ResolveBindingsPass(); + $pass->process($container); + + $this->assertEquals(['C', 'K', 'H'], $definition->getArguments()); + } } From f694aa82c20348cda857be29c1427abc446ed1d2 Mon Sep 17 00:00:00 2001 From: Tristan Kretzer Date: Fri, 27 Jan 2023 23:00:03 +0100 Subject: [PATCH 7/9] [HttpFoundation] Fix bad return type in IpUtils::checkIp4() --- .../Component/HttpFoundation/IpUtils.php | 2 +- .../HttpFoundation/Tests/IpUtilsTest.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/HttpFoundation/IpUtils.php b/src/Symfony/Component/HttpFoundation/IpUtils.php index 9a1afa7bf498a..2f31284e36c69 100644 --- a/src/Symfony/Component/HttpFoundation/IpUtils.php +++ b/src/Symfony/Component/HttpFoundation/IpUtils.php @@ -86,7 +86,7 @@ public static function checkIp4(?string $requestIp, string $ip) [$address, $netmask] = explode('/', $ip, 2); if ('0' === $netmask) { - return self::$checkedIps[$cacheKey] = filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4); + return self::$checkedIps[$cacheKey] = false !== filter_var($address, \FILTER_VALIDATE_IP, \FILTER_FLAG_IPV4); } if ($netmask < 0 || $netmask > 32) { diff --git a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php index d6d3728db1401..33d67303a831d 100644 --- a/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php +++ b/src/Symfony/Component/HttpFoundation/Tests/IpUtilsTest.php @@ -164,4 +164,21 @@ public function anonymizedIpData() ['::123.234.235.236', '::123.234.235.0'], // deprecated IPv4-compatible IPv6 address ]; } + + /** + * @dataProvider getIp4SubnetMaskZeroData + */ + public function testIp4SubnetMaskZero($matches, $remoteAddr, $cidr) + { + $this->assertSame($matches, IpUtils::checkIp4($remoteAddr, $cidr)); + } + + public function getIp4SubnetMaskZeroData() + { + return [ + [true, '1.2.3.4', '0.0.0.0/0'], + [true, '1.2.3.4', '192.168.1.0/0'], + [false, '1.2.3.4', '256.256.256/0'], // invalid CIDR notation + ]; + } } From 017be93e375ca78027c097046d1d9190f425e32b Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Feb 2023 09:18:30 +0100 Subject: [PATCH 8/9] Update CHANGELOG for 5.4.20 --- CHANGELOG-5.4.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG-5.4.md b/CHANGELOG-5.4.md index 8c687fa6ef99e..98dbf9b1e7c86 100644 --- a/CHANGELOG-5.4.md +++ b/CHANGELOG-5.4.md @@ -7,6 +7,15 @@ in 5.4 minor versions. To get the diff for a specific change, go to https://github.com/symfony/symfony/commit/XXX where XXX is the change hash To get the diff between two versions, go to https://github.com/symfony/symfony/compare/v5.4.0...v5.4.1 +* 5.4.20 (2023-02-01) + + * bug #49141 [HttpFoundation] Fix bad return type in IpUtils::checkIp4() (tristankretzer) + * bug #49126 [DependencyInjection] Fix order of arguments when mixing positional and named ones (nicolas-grekas) + * bug #49104 [HttpClient] Fix collecting data non-late for the profiler (nicolas-grekas) + * bug #49103 [Security/Http] Fix compat of persistent remember-me with legacy tokens (nicolas-grekas) + * security #cve-2022-24895 [Security/Http] Remove CSRF tokens from storage on successful login (nicolas-grekas) + * security #cve-2022-24894 [HttpKernel] Remove private headers before storing responses with HttpCache (nicolas-grekas) + * 5.4.19 (2023-01-24) * bug #49078 [Security/Http] Check tokens before loading users from providers (nicolas-grekas) From 5361b1227c3027f5e2c8bea7f1e3ebf0703dccb6 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Wed, 1 Feb 2023 09:18:48 +0100 Subject: [PATCH 9/9] Update VERSION for 5.4.20 --- src/Symfony/Component/HttpKernel/Kernel.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 3126ebd31ab1f..b2ccc7d95a6b8 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -78,12 +78,12 @@ abstract class Kernel implements KernelInterface, RebootableInterface, Terminabl */ private static $freshCache = []; - public const VERSION = '5.4.20-DEV'; + public const VERSION = '5.4.20'; public const VERSION_ID = 50420; public const MAJOR_VERSION = 5; public const MINOR_VERSION = 4; public const RELEASE_VERSION = 20; - public const EXTRA_VERSION = 'DEV'; + public const EXTRA_VERSION = ''; public const END_OF_MAINTENANCE = '11/2024'; public const END_OF_LIFE = '11/2025';