diff --git a/composer.json b/composer.json index cc3eb6a57..492a42067 100644 --- a/composer.json +++ b/composer.json @@ -79,6 +79,7 @@ "symfony/property-access": "^6.4 || ^7.4 || ^8.0", "symfony/property-info": "^6.4 || ^7.4 || ^8.0", "symfony/routing": "^6.4 || ^7.4 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/security-core": "^6.4 || ^7.4 || ^8.0", "symfony/security-http": "^6.4 || ^7.4 || ^8.0", "symfony/serializer": "^6.4 || ^7.4 || ^8.0", diff --git a/composer.lock b/composer.lock index eb53d45fa..f135c3531 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9f7e3923989783c1e1c0fbfa51012650", + "content-hash": "2a326b7adbd5bb2d7624c82921172673", "packages": [ { "name": "async-aws/core", @@ -7275,6 +7275,118 @@ ], "time": "2026-08-17T13:12:36+00:00" }, + { + "name": "symfony/security-bundle", + "version": "v7.4.18", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-bundle.git", + "reference": "caaa8b92f2657a6b99057f4da36ee89f3baab506" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-bundle/zipball/caaa8b92f2657a6b99057f4da36ee89f3baab506", + "reference": "caaa8b92f2657a6b99057f4da36ee89f3baab506", + "shasum": "" + }, + "require": { + "composer-runtime-api": ">=2.1", + "ext-xml": "*", + "php": ">=8.2", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^7.4|^8.0", + "symfony/dependency-injection": "^6.4.11|^7.1.4|^8.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4.13|^7.1.6|^8.0", + "symfony/password-hasher": "^6.4|^7.0|^8.0", + "symfony/security-core": "^7.4|^8.0", + "symfony/security-csrf": "^6.4|^7.0|^8.0", + "symfony/security-http": "^7.4|^8.0", + "symfony/service-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/browser-kit": "<6.4", + "symfony/console": "<6.4", + "symfony/framework-bundle": "<6.4", + "symfony/http-client": "<6.4", + "symfony/ldap": "<6.4", + "symfony/serializer": "<6.4", + "symfony/twig-bundle": "<6.4", + "symfony/validator": "<6.4" + }, + "require-dev": { + "symfony/asset": "^6.4|^7.0|^8.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/css-selector": "^6.4|^7.0|^8.0", + "symfony/dom-crawler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/form": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4.13|^7.1.6|^8.0", + "symfony/http-client": "^6.4|^7.0|^8.0", + "symfony/ldap": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/rate-limiter": "^6.4|^7.0|^8.0", + "symfony/runtime": "^6.4.13|^7.1.6|^8.0", + "symfony/serializer": "^6.4|^7.0|^8.0", + "symfony/translation": "^6.4|^7.0|^8.0", + "symfony/twig-bridge": "^6.4|^7.0|^8.0", + "symfony/twig-bundle": "^6.4|^7.0|^8.0", + "symfony/validator": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "twig/twig": "^3.15|^4.0", + "web-token/jwt-library": "^3.3.2|^4.0" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "Symfony\\Bundle\\SecurityBundle\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a tight integration of the Security component into the Symfony full-stack framework", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-bundle/tree/v7.4.18" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-08-30T17:23:54+00:00" + }, { "name": "symfony/security-core", "version": "v7.4.18", @@ -7367,6 +7479,80 @@ ], "time": "2026-08-30T00:47:26+00:00" }, + { + "name": "symfony/security-csrf", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-csrf.git", + "reference": "16b3aa2f67d02fb0dbd013a8759bbe90daaa9c5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-csrf/zipball/16b3aa2f67d02fb0dbd013a8759bbe90daaa9c5d", + "reference": "16b3aa2f67d02fb0dbd013a8759bbe90daaa9c5d", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/security-core": "^6.4|^7.0|^8.0" + }, + "conflict": { + "symfony/http-foundation": "<6.4" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Csrf\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - CSRF Library", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/security-csrf/tree/v7.4.8" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2026-03-24T13:12:05+00:00" + }, { "name": "symfony/security-http", "version": "v7.4.18", diff --git a/documentation/components/bridges/symfony-telemetry-bundle.md b/documentation/components/bridges/symfony-telemetry-bundle.md index c3b7862a3..53b5d14d7 100644 --- a/documentation/components/bridges/symfony-telemetry-bundle.md +++ b/documentation/components/bridges/symfony-telemetry-bundle.md @@ -1274,6 +1274,10 @@ already-authenticated requests and mid-request logins are covered. Each field is configurable; `email` is taken from the configured getter and skipped when the method is missing or returns a non-scalar. Anonymous requests are left untouched. +The token is read from `security.untracked_token_storage` and only when the request carries a span, so instrumentation +never marks the session as used and never turns a cacheable response into `Cache-Control: private` behind a lazy +firewall. + To attach application-specific attributes, implement `UserSpanAttributeProvider` and tag the service (the tag is autoconfigured). Returned attributes are merged onto the request span and win on key collision: diff --git a/src/bridge/symfony/telemetry-bundle/composer.json b/src/bridge/symfony/telemetry-bundle/composer.json index d22e098d7..545c85e3c 100644 --- a/src/bridge/symfony/telemetry-bundle/composer.json +++ b/src/bridge/symfony/telemetry-bundle/composer.json @@ -37,6 +37,7 @@ "symfony/framework-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/messenger": "^6.4 || ^7.4 || ^8.0", "symfony/routing": "^6.4 || ^7.4 || ^8.0", + "symfony/security-bundle": "^6.4 || ^7.4 || ^8.0", "symfony/security-core": "^6.4 || ^7.4 || ^8.0", "symfony/security-http": "^6.4 || ^7.4 || ^8.0", "symfony/serializer": "^6.4 || ^7.4 || ^8.0", diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Instrumentation/Security/SecuritySpanSubscriber.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Instrumentation/Security/SecuritySpanSubscriber.php index e40757419..45cffc38f 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Instrumentation/Security/SecuritySpanSubscriber.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Instrumentation/Security/SecuritySpanSubscriber.php @@ -8,7 +8,6 @@ use Flow\Telemetry\Tracer\Span; use SensitiveParameter; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\ControllerEvent; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -39,27 +38,36 @@ public static function getSubscribedEvents(): array public function onController(ControllerEvent $event): void { + // @mago-expect analysis:mixed-assignment + if ( + !($span = $event->getRequest()->attributes->get(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE)) instanceof Span + ) { + return; + } + $token = $this->tokenStorage->getToken(); if ($token === null) { return; } - $this->decorate($event->getRequest(), $token); + $this->decorate($span, $token); } public function onLoginSuccess(LoginSuccessEvent $event): void - { - $this->decorate($event->getRequest(), $event->getAuthenticatedToken()); - } - - private function decorate(Request $request, #[SensitiveParameter] TokenInterface $token): void { // @mago-expect analysis:mixed-assignment - if (!($span = $request->attributes->get(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE)) instanceof Span) { + if ( + !($span = $event->getRequest()->attributes->get(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE)) instanceof Span + ) { return; } + $this->decorate($span, $event->getAuthenticatedToken()); + } + + private function decorate(Span $span, #[SensitiveParameter] TokenInterface $token): void + { foreach ($this->resolver->resolve($token) as $key => $value) { $span->setAttribute($key, $value); } diff --git a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Resources/config/instrumentation/security.php b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Resources/config/instrumentation/security.php index 08d65813d..841dd6d3f 100644 --- a/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Resources/config/instrumentation/security.php +++ b/src/bridge/symfony/telemetry-bundle/src/Flow/Bridge/Symfony/TelemetryBundle/Resources/config/instrumentation/security.php @@ -23,7 +23,7 @@ $services ->set('flow.telemetry.security.span_subscriber', SecuritySpanSubscriber::class) ->args([ - service('security.token_storage'), + service('security.untracked_token_storage'), service('flow.telemetry.security.user_attribute_resolver'), ]) ->tag('kernel.event_subscriber'); diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Controller/TestController.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Controller/TestController.php index b0f5ae943..742f16ed4 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Controller/TestController.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Controller/TestController.php @@ -11,6 +11,15 @@ final class TestController { + public function cacheable(): Response + { + $response = new JsonResponse(['status' => 'ok']); + $response->setPublic(); + $response->setMaxAge(3600); + + return $response; + } + public function error(): Response { return new JsonResponse(['error' => 'not found'], 404); diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Security/SpyTokenStorage.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Security/SpyTokenStorage.php new file mode 100644 index 000000000..7978df414 --- /dev/null +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Fixtures/Security/SpyTokenStorage.php @@ -0,0 +1,36 @@ +reads++; + + return $this->token; + } + + public function reads(): int + { + return $this->reads; + } + + public function setToken(#[SensitiveParameter] ?TokenInterface $token): void + { + $this->token = $token; + } +} diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Security/SecuritySpanSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Security/SecuritySpanSubscriberTest.php index b0caf61f3..18ced6112 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Security/SecuritySpanSubscriberTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Integration/Instrumentation/Security/SecuritySpanSubscriberTest.php @@ -20,6 +20,7 @@ use Override; use PHPUnit\Framework\Attributes\CoversClass; use Symfony\Bundle\FrameworkBundle\FrameworkBundle; +use Symfony\Bundle\SecurityBundle\SecurityBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\HttpFoundation\Request; @@ -75,7 +76,7 @@ public function test_decorates_request_span_with_authenticated_user_and_provider ]); $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { $container - ->setDefinition('security.token_storage', new Definition(TokenStorage::class)) + ->setDefinition('security.untracked_token_storage', new Definition(TokenStorage::class)) ->setPublic(true); $container ->setDefinition('test.user_attributes', new Definition(StaticUserAttributeProvider::class)) @@ -94,7 +95,7 @@ public function test_decorates_request_span_with_authenticated_user_and_provider ])); /** @var TokenStorageInterface $tokenStorage */ - $tokenStorage = $container->get('security.token_storage'); + $tokenStorage = $container->get('security.untracked_token_storage'); $user = new TestSecurityUser('alice', 'alice@example.com', ['ROLE_USER', 'ROLE_ADMIN']); $tokenStorage->setToken(new UsernamePasswordToken($user, 'main', $user->getRoles())); @@ -115,6 +116,58 @@ public function test_decorates_request_span_with_authenticated_user_and_provider static::assertSame('acme', $attributes['app.tenant_id']); } + public function test_does_not_make_a_public_response_private_behind_a_lazy_firewall(): void + { + $kernel = $this->bootKernel([ + 'config' => static function (TestKernel $kernel): void { + $kernel->addTestBundle(FrameworkBundle::class); + $kernel->addTestBundle(SecurityBundle::class); + $kernel->addTestExtensionConfig('framework', [ + 'router' => [ + 'utf8' => true, + 'resource' => __DIR__ . '/../../../Fixtures/config/routes.php', + ], + 'session' => [ + 'enabled' => true, + 'storage_factory_id' => 'session.storage.factory.mock_file', + 'handler_id' => null, + 'cookie_secure' => 'auto', + 'cookie_samesite' => 'lax', + ], + 'http_method_override' => false, + 'handle_all_throwables' => true, + ]); + $kernel->addTestExtensionConfig('security', [ + 'providers' => ['in_memory' => ['memory' => ['users' => []]]], + 'firewalls' => ['main' => ['lazy' => true, 'provider' => 'in_memory']], + ]); + $kernel->addTestExtensionConfig('flow_telemetry', [ + 'resource' => [], + 'exporters' => ['memory' => ['memory' => null], 'void' => ['void' => null]], + 'tracer_provider' => ['processor' => ['type' => 'memory', 'exporter' => 'memory']], + 'instrumentation' => [ + 'http_kernel' => ['enabled' => true], + 'console' => ['enabled' => false], + 'messenger' => false, + 'security' => ['enabled' => true], + ], + ]); + }, + ]); + + /** @var Router $router */ + $router = $this->getContainer()->get('router'); + $router->getRouteCollection()->add('test_cacheable', new Route('/cacheable', [ + '_controller' => TestController::class . '::cacheable', + ])); + + $request = Request::create('/cacheable', 'GET'); + $response = $kernel->handle($request); + $kernel->terminate($request, $response); + + static::assertSame('max-age=3600, public', $response->headers->get('Cache-Control')); + } + public function test_decorates_request_span_on_login_success(): void { $this->bootKernel([ @@ -132,7 +185,7 @@ public function test_decorates_request_span_on_login_success(): void ]); $kernel->addTestContainerConfigurator(static function (ContainerBuilder $container): void { $container - ->setDefinition('security.token_storage', new Definition(TokenStorage::class)) + ->setDefinition('security.untracked_token_storage', new Definition(TokenStorage::class)) ->setPublic(true); }); }, diff --git a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Security/SecuritySpanSubscriberTest.php b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Security/SecuritySpanSubscriberTest.php index 683fa45e3..7cf13f747 100644 --- a/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Security/SecuritySpanSubscriberTest.php +++ b/src/bridge/symfony/telemetry-bundle/tests/Flow/Bridge/Symfony/TelemetryBundle/Tests/Unit/Instrumentation/Security/SecuritySpanSubscriberTest.php @@ -7,6 +7,8 @@ use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\HttpKernel\HttpKernelSpanSubscriber; use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\Security\SecuritySpanSubscriber; use Flow\Bridge\Symfony\TelemetryBundle\Instrumentation\Security\UserAttributeResolver; +use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Security\FakeAuthenticator; +use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Security\SpyTokenStorage; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Fixtures\Security\TestSecurityUser; use Flow\Bridge\Symfony\TelemetryBundle\Tests\Mother\TelemetryMother; use Flow\Telemetry\Provider\Memory\MemoryExporter; @@ -19,6 +21,9 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; +use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; +use Symfony\Component\Security\Http\Authenticator\Passport\SelfValidatingPassport; +use Symfony\Component\Security\Http\Event\LoginSuccessEvent; #[CoversClass(SecuritySpanSubscriber::class)] final class SecuritySpanSubscriberTest extends TestCase @@ -48,10 +53,35 @@ public function test_does_not_decorate_when_there_is_no_token(): void static::assertArrayNotHasKey('user.id', $span->attributes()); } - public function test_does_nothing_when_request_has_no_span(): void + public function test_does_not_decorate_on_login_success_when_request_has_no_span(): void { - $tokenStorage = new TokenStorage(); - $tokenStorage->setToken(new UsernamePasswordToken(new TestSecurityUser('alice'), 'main')); + $user = new TestSecurityUser('bob'); + $token = new UsernamePasswordToken($user, 'main'); + + $request = Request::create('/test', 'GET'); + + $subscriber = new SecuritySpanSubscriber( + new TokenStorage(), + new UserAttributeResolver([], 'user.id', null, null, 'getEmail'), + ); + + $subscriber->onLoginSuccess( + new LoginSuccessEvent( + new FakeAuthenticator(), + new SelfValidatingPassport(new UserBadge('bob', static fn(): TestSecurityUser => $user)), + $token, + $request, + null, + 'main', + ), + ); + + static::assertFalse($request->attributes->has(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE)); + } + + public function test_does_not_read_the_token_when_request_has_no_span(): void + { + $tokenStorage = new SpyTokenStorage(new UsernamePasswordToken(new TestSecurityUser('alice'), 'main')); $request = Request::create('/test', 'GET'); $request->attributes->set(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE, 'not-a-span'); @@ -70,6 +100,7 @@ public function test_does_nothing_when_request_has_no_span(): void ), ); + static::assertSame(0, $tokenStorage->reads()); static::assertSame('not-a-span', $request->attributes->get(HttpKernelSpanSubscriber::SPAN_ATTRIBUTE)); } } diff --git a/tools/box/composer.lock b/tools/box/composer.lock index 2024f8bb5..f69c122e6 100644 --- a/tools/box/composer.lock +++ b/tools/box/composer.lock @@ -2767,16 +2767,16 @@ }, { "name": "symfony/console", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0" + "reference": "3a1973458b153f566a8d7499bc06ece323d60063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", + "url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063", + "reference": "3a1973458b153f566a8d7499bc06ece323d60063", "shasum": "" }, "require": { @@ -2841,7 +2841,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.18" + "source": "https://github.com/symfony/console/tree/v7.4.19" }, "funding": [ { @@ -2861,7 +2861,7 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:18:37+00:00" + "time": "2026-09-13T10:38:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3086,16 +3086,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.17", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe", + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe", "shasum": "" }, "require": { @@ -3130,7 +3130,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.17" + "source": "https://github.com/symfony/finder/tree/v7.4.19" }, "funding": [ { @@ -3150,7 +3150,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T12:09:28+00:00" + "time": "2026-09-10T19:14:10+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3653,16 +3653,16 @@ }, { "name": "symfony/process", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "058d17fc284cce14efb2385783b55014a461b176" + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176", - "reference": "058d17fc284cce14efb2385783b55014a461b176", + "url": "https://api.github.com/repos/symfony/process/zipball/ed0ae095b86994d370d5791612e55984f15aa30e", + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e", "shasum": "" }, "require": { @@ -3694,7 +3694,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.18" + "source": "https://github.com/symfony/process/tree/v7.4.19" }, "funding": [ { @@ -3714,7 +3714,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T17:40:08+00:00" + "time": "2026-09-02T12:38:34+00:00" }, { "name": "symfony/service-contracts", @@ -3805,16 +3805,16 @@ }, { "name": "symfony/string", - "version": "v7.4.15", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "url": "https://api.github.com/repos/symfony/string/zipball/02731ce2bd26514bec9efc0baef2cd0b0cf67f46", + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46", "shasum": "" }, "require": { @@ -3872,7 +3872,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.15" + "source": "https://github.com/symfony/string/tree/v7.4.19" }, "funding": [ { @@ -3892,7 +3892,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:33:02+00:00" + "time": "2026-09-11T14:46:58+00:00" }, { "name": "symfony/var-dumper", diff --git a/tools/infection/composer.lock b/tools/infection/composer.lock index bb47be8aa..4fd6ff6c5 100644 --- a/tools/infection/composer.lock +++ b/tools/infection/composer.lock @@ -1096,16 +1096,16 @@ }, { "name": "sanmai/di-container", - "version": "0.1.23", + "version": "0.2", "source": { "type": "git", "url": "https://github.com/sanmai/di-container.git", - "reference": "8cf59c091f33297389d0a5a27ea0d688df15c376" + "reference": "c14ca816bb5527e2361e3dc554a20829674c7ee1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sanmai/di-container/zipball/8cf59c091f33297389d0a5a27ea0d688df15c376", - "reference": "8cf59c091f33297389d0a5a27ea0d688df15c376", + "url": "https://api.github.com/repos/sanmai/di-container/zipball/c14ca816bb5527e2361e3dc554a20829674c7ee1", + "reference": "c14ca816bb5527e2361e3dc554a20829674c7ee1", "shasum": "" }, "require": { @@ -1163,7 +1163,7 @@ ], "support": { "issues": "https://github.com/sanmai/di-container/issues", - "source": "https://github.com/sanmai/di-container/tree/0.1.23" + "source": "https://github.com/sanmai/di-container/tree/0.2" }, "funding": [ { @@ -1171,7 +1171,7 @@ "type": "github" } ], - "time": "2026-08-11T00:58:41+00:00" + "time": "2026-08-11T06:42:18+00:00" }, { "name": "sanmai/duoclock", @@ -1449,16 +1449,16 @@ }, { "name": "symfony/console", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0" + "reference": "3a1973458b153f566a8d7499bc06ece323d60063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", + "url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063", + "reference": "3a1973458b153f566a8d7499bc06ece323d60063", "shasum": "" }, "require": { @@ -1523,7 +1523,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.18" + "source": "https://github.com/symfony/console/tree/v7.4.19" }, "funding": [ { @@ -1543,7 +1543,7 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:18:37+00:00" + "time": "2026-09-13T10:38:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1688,16 +1688,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.17", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe", + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe", "shasum": "" }, "require": { @@ -1732,7 +1732,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.17" + "source": "https://github.com/symfony/finder/tree/v7.4.19" }, "funding": [ { @@ -1752,7 +1752,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T12:09:28+00:00" + "time": "2026-09-10T19:14:10+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2171,16 +2171,16 @@ }, { "name": "symfony/process", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "058d17fc284cce14efb2385783b55014a461b176" + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176", - "reference": "058d17fc284cce14efb2385783b55014a461b176", + "url": "https://api.github.com/repos/symfony/process/zipball/ed0ae095b86994d370d5791612e55984f15aa30e", + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e", "shasum": "" }, "require": { @@ -2212,7 +2212,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.18" + "source": "https://github.com/symfony/process/tree/v7.4.19" }, "funding": [ { @@ -2232,7 +2232,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T17:40:08+00:00" + "time": "2026-09-02T12:38:34+00:00" }, { "name": "symfony/service-contracts", @@ -2323,16 +2323,16 @@ }, { "name": "symfony/string", - "version": "v7.4.15", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "url": "https://api.github.com/repos/symfony/string/zipball/02731ce2bd26514bec9efc0baef2cd0b0cf67f46", + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46", "shasum": "" }, "require": { @@ -2390,7 +2390,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.15" + "source": "https://github.com/symfony/string/tree/v7.4.19" }, "funding": [ { @@ -2410,7 +2410,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:33:02+00:00" + "time": "2026-09-11T14:46:58+00:00" }, { "name": "thecodingmachine/safe", diff --git a/tools/monorepo/composer.lock b/tools/monorepo/composer.lock index ee727f476..b194b2af7 100644 --- a/tools/monorepo/composer.lock +++ b/tools/monorepo/composer.lock @@ -462,16 +462,16 @@ }, { "name": "symfony/console", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0" + "reference": "3a1973458b153f566a8d7499bc06ece323d60063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", + "url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063", + "reference": "3a1973458b153f566a8d7499bc06ece323d60063", "shasum": "" }, "require": { @@ -536,7 +536,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.18" + "source": "https://github.com/symfony/console/tree/v7.4.19" }, "funding": [ { @@ -556,7 +556,7 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:18:37+00:00" + "time": "2026-09-13T10:38:41+00:00" }, { "name": "symfony/dependency-injection", @@ -1032,16 +1032,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.17", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe", + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe", "shasum": "" }, "require": { @@ -1076,7 +1076,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.17" + "source": "https://github.com/symfony/finder/tree/v7.4.19" }, "funding": [ { @@ -1096,20 +1096,20 @@ "type": "tidelift" } ], - "time": "2026-08-21T12:09:28+00:00" + "time": "2026-09-10T19:14:10+00:00" }, { "name": "symfony/http-foundation", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/http-foundation.git", - "reference": "d070b716a32fbe3bf04204db0f58ace73b86d133" + "reference": "f7b953be7801974a5a0685e1228bbc3b12b35936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d070b716a32fbe3bf04204db0f58ace73b86d133", - "reference": "d070b716a32fbe3bf04204db0f58ace73b86d133", + "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f7b953be7801974a5a0685e1228bbc3b12b35936", + "reference": "f7b953be7801974a5a0685e1228bbc3b12b35936", "shasum": "" }, "require": { @@ -1158,7 +1158,7 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v7.4.18" + "source": "https://github.com/symfony/http-foundation/tree/v7.4.19" }, "funding": [ { @@ -1178,20 +1178,20 @@ "type": "tidelift" } ], - "time": "2026-08-30T20:10:52+00:00" + "time": "2026-09-14T17:41:17+00:00" }, { "name": "symfony/http-kernel", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "275d2d2d24530f2a0eaf17704a3a93860a036351" + "reference": "683507424cc84ca85ac1551adb609b6b98216667" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/275d2d2d24530f2a0eaf17704a3a93860a036351", - "reference": "275d2d2d24530f2a0eaf17704a3a93860a036351", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/683507424cc84ca85ac1551adb609b6b98216667", + "reference": "683507424cc84ca85ac1551adb609b6b98216667", "shasum": "" }, "require": { @@ -1277,7 +1277,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.4.18" + "source": "https://github.com/symfony/http-kernel/tree/v7.4.19" }, "funding": [ { @@ -1297,7 +1297,7 @@ "type": "tidelift" } ], - "time": "2026-08-30T21:24:29+00:00" + "time": "2026-09-15T07:09:35+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1716,16 +1716,16 @@ }, { "name": "symfony/process", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "058d17fc284cce14efb2385783b55014a461b176" + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176", - "reference": "058d17fc284cce14efb2385783b55014a461b176", + "url": "https://api.github.com/repos/symfony/process/zipball/ed0ae095b86994d370d5791612e55984f15aa30e", + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e", "shasum": "" }, "require": { @@ -1757,7 +1757,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.18" + "source": "https://github.com/symfony/process/tree/v7.4.19" }, "funding": [ { @@ -1777,7 +1777,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T17:40:08+00:00" + "time": "2026-09-02T12:38:34+00:00" }, { "name": "symfony/service-contracts", @@ -1868,16 +1868,16 @@ }, { "name": "symfony/string", - "version": "v7.4.15", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "url": "https://api.github.com/repos/symfony/string/zipball/02731ce2bd26514bec9efc0baef2cd0b0cf67f46", + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46", "shasum": "" }, "require": { @@ -1935,7 +1935,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.15" + "source": "https://github.com/symfony/string/tree/v7.4.19" }, "funding": [ { @@ -1955,7 +1955,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:33:02+00:00" + "time": "2026-09-11T14:46:58+00:00" }, { "name": "symfony/var-dumper", diff --git a/tools/phpbench/composer.lock b/tools/phpbench/composer.lock index eee4602c8..acf41e03d 100644 --- a/tools/phpbench/composer.lock +++ b/tools/phpbench/composer.lock @@ -529,16 +529,16 @@ }, { "name": "symfony/console", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0" + "reference": "3a1973458b153f566a8d7499bc06ece323d60063" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", - "reference": "23d6f88a29f6d0eac45bd77d70307adf83ba7ab0", + "url": "https://api.github.com/repos/symfony/console/zipball/3a1973458b153f566a8d7499bc06ece323d60063", + "reference": "3a1973458b153f566a8d7499bc06ece323d60063", "shasum": "" }, "require": { @@ -603,7 +603,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.18" + "source": "https://github.com/symfony/console/tree/v7.4.19" }, "funding": [ { @@ -623,7 +623,7 @@ "type": "tidelift" } ], - "time": "2026-08-25T14:18:37+00:00" + "time": "2026-09-13T10:38:41+00:00" }, { "name": "symfony/deprecation-contracts", @@ -768,16 +768,16 @@ }, { "name": "symfony/finder", - "version": "v7.4.17", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6" + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", - "reference": "5ce28827081f6d1f0c32eaf3882750f19cb5bbe6", + "url": "https://api.github.com/repos/symfony/finder/zipball/1b900bc6ae5ba60c5eee69c11ba44566190576fe", + "reference": "1b900bc6ae5ba60c5eee69c11ba44566190576fe", "shasum": "" }, "require": { @@ -812,7 +812,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.4.17" + "source": "https://github.com/symfony/finder/tree/v7.4.19" }, "funding": [ { @@ -832,7 +832,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T12:09:28+00:00" + "time": "2026-09-10T19:14:10+00:00" }, { "name": "symfony/options-resolver", @@ -1242,16 +1242,16 @@ }, { "name": "symfony/process", - "version": "v7.4.18", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "058d17fc284cce14efb2385783b55014a461b176" + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/058d17fc284cce14efb2385783b55014a461b176", - "reference": "058d17fc284cce14efb2385783b55014a461b176", + "url": "https://api.github.com/repos/symfony/process/zipball/ed0ae095b86994d370d5791612e55984f15aa30e", + "reference": "ed0ae095b86994d370d5791612e55984f15aa30e", "shasum": "" }, "require": { @@ -1283,7 +1283,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.18" + "source": "https://github.com/symfony/process/tree/v7.4.19" }, "funding": [ { @@ -1303,7 +1303,7 @@ "type": "tidelift" } ], - "time": "2026-08-21T17:40:08+00:00" + "time": "2026-09-02T12:38:34+00:00" }, { "name": "symfony/service-contracts", @@ -1394,16 +1394,16 @@ }, { "name": "symfony/string", - "version": "v7.4.15", + "version": "v7.4.19", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b" + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/e394af32256bf9e7bf80849d95e589167c10097b", - "reference": "e394af32256bf9e7bf80849d95e589167c10097b", + "url": "https://api.github.com/repos/symfony/string/zipball/02731ce2bd26514bec9efc0baef2cd0b0cf67f46", + "reference": "02731ce2bd26514bec9efc0baef2cd0b0cf67f46", "shasum": "" }, "require": { @@ -1461,7 +1461,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.15" + "source": "https://github.com/symfony/string/tree/v7.4.19" }, "funding": [ { @@ -1481,7 +1481,7 @@ "type": "tidelift" } ], - "time": "2026-07-28T07:33:02+00:00" + "time": "2026-09-11T14:46:58+00:00" }, { "name": "webmozart/glob",