From 66937d394b73fddbe887409b1eacc23dd6ce35aa Mon Sep 17 00:00:00 2001 From: Rob Wigginton <8102829+bob2021@users.noreply.github.com> Date: Wed, 6 May 2026 17:17:14 +0100 Subject: [PATCH 1/2] Upgrade Saloon to v4 to resolve security advisories. Bumps saloonphp/saloon to ^4.0 and saloonphp/rate-limit-plugin to ^2.5, along with the generator dev dependency. Resolves CVE-2026-33182 and CVE-2026-33183. Refs #9. --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 76c866b0..b092e9a4 100644 --- a/composer.json +++ b/composer.json @@ -9,11 +9,11 @@ ], "license": "Apache-2.0", "require": { - "saloonphp/saloon": "^3.8", - "saloonphp/rate-limit-plugin": "^2.1" + "saloonphp/saloon": "^4.0", + "saloonphp/rate-limit-plugin": "^2.5" }, "require-dev": { - "highsidelabs/saloon-sdk-generator": "^2.1", + "highsidelabs/saloon-sdk-generator": "^2.1.8", "psy/psysh": "^0.12.3", "symfony/console": "^7.0", "phpcompatibility/php-compatibility": "dev-develop", From d709c7c80c553a619d15af17e19b94be95371892 Mon Sep 17 00:00:00 2001 From: Rob Wigginton <8102829+bob2021@users.noreply.github.com> Date: Wed, 6 May 2026 17:18:01 +0100 Subject: [PATCH 2/2] Add TokenSerializer helper for caching access tokens. Replaces the AccessTokenAuthenticator::serialize/unserialize methods that were removed in Saloon v4. Centralizes the unserialize allowed_classes whitelist so consumers don't have to know which classes are safe to instantiate. --- src/Auth/TokenSerializer.php | 37 +++++++++++++++++++++++++++ tests/TokenSerializerTest.php | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 src/Auth/TokenSerializer.php create mode 100644 tests/TokenSerializerTest.php diff --git a/src/Auth/TokenSerializer.php b/src/Auth/TokenSerializer.php new file mode 100644 index 00000000..c6467485 --- /dev/null +++ b/src/Auth/TokenSerializer.php @@ -0,0 +1,37 @@ + [ + AccessTokenAuthenticator::class, + DateTimeImmutable::class, + ], + ]); + } catch (Throwable) { + return null; + } + + return $token instanceof AccessTokenAuthenticator ? $token : null; + } +} diff --git a/tests/TokenSerializerTest.php b/tests/TokenSerializerTest.php new file mode 100644 index 00000000..2fae0023 --- /dev/null +++ b/tests/TokenSerializerTest.php @@ -0,0 +1,47 @@ +assertEquals($authenticator, $restored); + } + + public function testRoundTripsAnAuthenticatorWithNullRefreshAndExpiry(): void + { + $authenticator = new AccessTokenAuthenticator(accessToken: 'access-123'); + + $serialized = TokenSerializer::serialize($authenticator); + $restored = TokenSerializer::unserialize($serialized); + + $this->assertEquals($authenticator, $restored); + } + + public function testReturnsNullForGarbageInput(): void + { + $this->assertNull(TokenSerializer::unserialize('not-a-serialized-string')); + $this->assertNull(TokenSerializer::unserialize('')); + } + + public function testReturnsNullForNonAuthenticatorObject(): void + { + $serialized = serialize(new DateTimeImmutable); + + $this->assertNull(TokenSerializer::unserialize($serialized)); + } +}