From 0db8b96c8427f1607891a140ae56da18ae166cae Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Dec 2025 06:23:55 +0000 Subject: [PATCH 1/3] Add Psalm taint annotations for session/cookie security Mark session and cookie providers as taint sources since $_COOKIE contains user-controlled data that could be manipulated. - SessionProvider::get() - returns Session initialized with $_COOKIE - CookieProvider::get() - returns $_COOKIE directly Also apply code style fixes (phpcbf). --- src/Annotation/Cookie.php | 3 ++- src/Annotation/DeleteCookie.php | 3 ++- src/AuraSessionInject.php | 6 ++---- src/AuraSessionModule.php | 2 +- src/CookieProvider.php | 3 ++- src/DeleteCookieInvoker.php | 2 +- src/SessionProvider.php | 15 ++++++++------- 7 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Annotation/Cookie.php b/src/Annotation/Cookie.php index 1565f7a..e4254c2 100644 --- a/src/Annotation/Cookie.php +++ b/src/Annotation/Cookie.php @@ -7,7 +7,8 @@ use Attribute; use Ray\Di\Di\Qualifier; -#[Attribute, Qualifier] +#[Attribute] +#[Qualifier] final class Cookie { } diff --git a/src/Annotation/DeleteCookie.php b/src/Annotation/DeleteCookie.php index 4d411a9..2e69ef6 100644 --- a/src/Annotation/DeleteCookie.php +++ b/src/Annotation/DeleteCookie.php @@ -7,7 +7,8 @@ use Attribute; use Ray\Di\Di\Qualifier; -#[Attribute, Qualifier] +#[Attribute] +#[Qualifier] final class DeleteCookie { } diff --git a/src/AuraSessionInject.php b/src/AuraSessionInject.php index d64c78a..b3e6687 100644 --- a/src/AuraSessionInject.php +++ b/src/AuraSessionInject.php @@ -6,15 +6,13 @@ use Aura\Session\Session; -/** - * @deprecated Use PHP 8.0: Class constructor property promotion instead - */ +/** @deprecated Use PHP 8.0: Class constructor property promotion instead */ trait AuraSessionInject { /** @var Session */ protected $session; - public function setSession(Session $session) + public function setSession(Session $session): void { $this->session = $session; } diff --git a/src/AuraSessionModule.php b/src/AuraSessionModule.php index bd40b4d..06bc2f3 100644 --- a/src/AuraSessionModule.php +++ b/src/AuraSessionModule.php @@ -22,7 +22,7 @@ class AuraSessionModule extends AbstractModule { /** - * {@inheritdoc} + * {@inheritDoc} */ protected function configure() { diff --git a/src/CookieProvider.php b/src/CookieProvider.php index d7c6fe9..d0e1320 100644 --- a/src/CookieProvider.php +++ b/src/CookieProvider.php @@ -13,9 +13,10 @@ class CookieProvider implements ProviderInterface { /** - * {@inheritdoc} + * {@inheritDoc} * * @SuppressWarnings(PHPMD.Superglobals) + * @psalm-taint-source input */ public function get() { diff --git a/src/DeleteCookieInvoker.php b/src/DeleteCookieInvoker.php index 2bf2658..52251b4 100644 --- a/src/DeleteCookieInvoker.php +++ b/src/DeleteCookieInvoker.php @@ -25,7 +25,7 @@ public function __invoke(string $name, array $params): void '', time() - 42000, $params['path'], - $params['domain'] + $params['domain'], ); } } diff --git a/src/SessionProvider.php b/src/SessionProvider.php index 9997031..37c195f 100644 --- a/src/SessionProvider.php +++ b/src/SessionProvider.php @@ -1,26 +1,27 @@ newInstance($_COOKIE); + return (new SessionFactory())->newInstance($_COOKIE); } } From c3d7679ae159c4a13203df711b335697157de49a Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Wed, 24 Dec 2025 00:25:19 +0900 Subject: [PATCH 2/3] Add tests to achieve 100% code coverage - Add SessionProviderTest for SessionProvider class - Add AuraSessionInjectTest for AuraSessionInject trait - Add DeleteCookieInvokerTest with function overrides for testing - Remove use function statements from DeleteCookieInvoker for testability --- src/DeleteCookieInvoker.php | 3 -- tests/AuraSessionInjectTest.php | 30 +++++++++++++++ tests/DeleteCookieInvokerTest.php | 63 +++++++++++++++++++++++++++++++ tests/SessionProviderTest.php | 18 +++++++++ 4 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 tests/AuraSessionInjectTest.php create mode 100644 tests/DeleteCookieInvokerTest.php create mode 100644 tests/SessionProviderTest.php diff --git a/src/DeleteCookieInvoker.php b/src/DeleteCookieInvoker.php index 52251b4..50dc75b 100644 --- a/src/DeleteCookieInvoker.php +++ b/src/DeleteCookieInvoker.php @@ -8,9 +8,6 @@ namespace Ray\AuraSessionModule; -use function setcookie; -use function time; - final class DeleteCookieInvoker { /** diff --git a/tests/AuraSessionInjectTest.php b/tests/AuraSessionInjectTest.php new file mode 100644 index 0000000..af39244 --- /dev/null +++ b/tests/AuraSessionInjectTest.php @@ -0,0 +1,30 @@ +session; + } +} + +class AuraSessionInjectTest extends TestCase +{ + public function testSetSession(): void + { + $session = $this->createMock(Session::class); + $consumer = new FakeSessionConsumer(); + $consumer->setSession($session); + + $this->assertSame($session, $consumer->getSession()); + } +} diff --git a/tests/DeleteCookieInvokerTest.php b/tests/DeleteCookieInvokerTest.php new file mode 100644 index 0000000..540f5a3 --- /dev/null +++ b/tests/DeleteCookieInvokerTest.php @@ -0,0 +1,63 @@ + */ +$setCookieCalls = []; + +/** + * Override time function for testing + */ +function time(): int +{ + return 1000000; +} + +/** + * Override setcookie function for testing + * + * @return bool + */ +function setcookie(string $name, string $value = '', int $expires = 0, string $path = '', string $domain = '') +{ + global $setCookieCalls; + + $setCookieCalls[] = [ + 'name' => $name, + 'value' => $value, + 'expires' => $expires, + 'path' => $path, + 'domain' => $domain, + ]; + + return true; +} + +class DeleteCookieInvokerTest extends TestCase +{ + protected function setUp(): void + { + global $setCookieCalls; + + $setCookieCalls = []; + } + + public function testInvoke(): void + { + global $setCookieCalls; + + $invoker = new DeleteCookieInvoker(); + $invoker('test_cookie', ['path' => '/app', 'domain' => 'example.com']); + + $this->assertCount(1, $setCookieCalls); + $this->assertSame('test_cookie', $setCookieCalls[0]['name']); + $this->assertSame('', $setCookieCalls[0]['value']); + $this->assertSame(1000000 - 42000, $setCookieCalls[0]['expires']); + $this->assertSame('/app', $setCookieCalls[0]['path']); + $this->assertSame('example.com', $setCookieCalls[0]['domain']); + } +} diff --git a/tests/SessionProviderTest.php b/tests/SessionProviderTest.php new file mode 100644 index 0000000..ed50373 --- /dev/null +++ b/tests/SessionProviderTest.php @@ -0,0 +1,18 @@ +get(); + $this->assertInstanceOf(Session::class, $session); + } +} From 7a17a6d5dc8746307e902cd7f04339d47afb5048 Mon Sep 17 00:00:00 2001 From: Akihito Koriyama Date: Fri, 22 May 2026 08:31:49 +0900 Subject: [PATCH 3/3] Extract cookie expiration offset constant --- src/DeleteCookieInvoker.php | 4 +++- tests/DeleteCookieInvokerTest.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/DeleteCookieInvoker.php b/src/DeleteCookieInvoker.php index 50dc75b..1d121fe 100644 --- a/src/DeleteCookieInvoker.php +++ b/src/DeleteCookieInvoker.php @@ -10,6 +10,8 @@ final class DeleteCookieInvoker { + public const EXPIRE_OFFSET = 42000; + /** * Delete a cookie by setting its expiration time to a past value * @@ -20,7 +22,7 @@ public function __invoke(string $name, array $params): void setcookie( $name, '', - time() - 42000, + time() - self::EXPIRE_OFFSET, $params['path'], $params['domain'], ); diff --git a/tests/DeleteCookieInvokerTest.php b/tests/DeleteCookieInvokerTest.php index 540f5a3..b475ec6 100644 --- a/tests/DeleteCookieInvokerTest.php +++ b/tests/DeleteCookieInvokerTest.php @@ -56,7 +56,7 @@ public function testInvoke(): void $this->assertCount(1, $setCookieCalls); $this->assertSame('test_cookie', $setCookieCalls[0]['name']); $this->assertSame('', $setCookieCalls[0]['value']); - $this->assertSame(1000000 - 42000, $setCookieCalls[0]['expires']); + $this->assertSame(1000000 - DeleteCookieInvoker::EXPIRE_OFFSET, $setCookieCalls[0]['expires']); $this->assertSame('/app', $setCookieCalls[0]['path']); $this->assertSame('example.com', $setCookieCalls[0]['domain']); }