diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 2b4481e..fc5f29a 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -18,15 +18,44 @@ jobs: strategy: matrix: php-version: + - "8.5" - "8.4" - "8.3" - - "8.2" - - "8.1" steps: - uses: actions/checkout@v5 - run: composer install - - run: ./vendor/bin/phpunit + - run: composer test + + Psalm: + name: Psalm Static Analyzer + runs-on: ubuntu-latest + permissions: + # for github/codeql-action/upload-sarif to upload SARIF results + security-events: write + container: + image: byjg/php:8.4-cli + options: --user root --privileged + + steps: + - name: Git checkout + uses: actions/checkout@v4 + + - name: Composer + run: composer install + + - name: Psalm + # Note: Ignoring error code 2, which just signals that some + # flaws were found, not that Psalm itself failed to run. + run: ./vendor/bin/psalm + --show-info=true + --report=psalm-results.sarif || [ $? = 2 ] + + - name: Upload Analysis results to GitHub + uses: github/codeql-action/upload-sarif@v4 + if: github.ref == 'refs/heads/master' + with: + sarif_file: psalm-results.sarif Documentation: if: github.ref == 'refs/heads/master' diff --git a/README.md b/README.md index c92de21..421c5cd 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ -# JwtSession +# JWT Session Handler -[![Build Status](https://github.com/byjg/jwt-session/actions/workflows/phpunit.yml/badge.svg?branch=master)](https://github.com/byjg/jwt-session/actions/workflows/phpunit.yml) -[![Opensource ByJG](https://img.shields.io/badge/opensource-byjg-success.svg)](http://opensource.byjg.com) -[![GitHub source](https://img.shields.io/badge/Github-source-informational?logo=github)](https://github.com/byjg/jwt-session/) -[![GitHub license](https://img.shields.io/github/license/byjg/jwt-session.svg)](https://opensource.byjg.com/opensource/licensing.html) +[![Sponsor](https://img.shields.io/badge/Sponsor-%23ea4aaa?logo=githubsponsors&logoColor=white&labelColor=0d1117)](https://github.com/sponsors/byjg) +[![Build Status](https://github.com/byjg/jwt-session/actions/workflows/phpunit.yml/badge.svg?branch=master)](https://github.com/byjg/jwt-session/actions/workflows/phpunit.yml) +[![Opensource ByJG](https://img.shields.io/badge/opensource-byjg-success.svg)](http://opensource.byjg.com) +[![GitHub source](https://img.shields.io/badge/Github-source-informational?logo=github)](https://github.com/byjg/jwt-session/) +[![GitHub license](https://img.shields.io/github/license/byjg/jwt-session.svg)](https://opensource.byjg.com/opensource/licensing.html) [![GitHub release](https://img.shields.io/github/release/byjg/jwt-session.svg)](https://github.com/byjg/jwt-session/releases/) -JwtSession is a PHP session replacement. Instead of use FileSystem, just use JWT TOKEN. -The implementation following the SessionHandlerInterface. +A PHP session replacement that stores session data in JWT tokens instead of the filesystem. This implementation follows the SessionHandlerInterface standard, enabling stateless sessions without the need for dedicated session servers like Redis or Memcached. Perfect for distributed applications and microservices architectures. # How to use: diff --git a/composer.json b/composer.json index 6344eb8..ac59705 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "byjg/jwt-session", - "description": "JwtSession is a PHP session replacement. Instead of use FileSystem, just use JWT TOKEN. The implementation following the SessionHandlerInterface.", + "description": "A PHP session replacement that stores session data in JWT tokens instead of the filesystem. This implementation follows the SessionHandlerInterface standard, enabling stateless sessions without the need for dedicated session servers like Redis or Memcached. Perfect for distributed applications and microservices architectures.", "autoload": { "psr-4": { "ByJG\\Session\\": "src/" @@ -9,16 +9,16 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": ">=8.1 <8.5", + "php": ">=8.3 <8.6", "byjg/jwt-wrapper": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10|^11", - "vimeo/psalm": "^5.9|^6.12" + "phpunit/phpunit": "^10.5|^11.5", + "vimeo/psalm": "^5.9|^6.13" }, "scripts": { "test": "vendor/bin/phpunit", - "psalm": "vendor/bin/psalm" + "psalm": "vendor/bin/psalm --threads=1" }, "license": "MIT" } diff --git a/psalm.xml b/psalm.xml index cba57d1..241b742 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ sessionConfig->getSessionContext()])) { + $key = $this->sessionConfig->getKey(); + if ($key === null) { + return ''; + } $jwt = new JwtWrapper( $this->sessionConfig->getServerName(), - $this->sessionConfig->getKey() + $key ); $data = $jwt->extractData($_COOKIE[self::COOKIE_PREFIX . $this->sessionConfig->getSessionContext()]); @@ -192,9 +196,13 @@ public function read(string $id): string #[\Override] public function write(string $id, string $data): bool { + $key = $this->sessionConfig->getKey(); + if ($key === null) { + return false; + } $jwt = new JwtWrapper( $this->sessionConfig->getServerName(), - $this->sessionConfig->getKey() + $key ); $session_data = $jwt->createJwtData(['data' => $data], $this->sessionConfig->getTimeoutMinutes() * 60, 0, null); $token = $jwt->generateToken($session_data); @@ -239,6 +247,9 @@ public function unSerializeSessionData($session_data): array while ($offset < strlen($session_data)) { if (!str_contains(substr($session_data, $offset), "|")) throw new JwtSessionException("invalid data, remaining: " . substr($session_data, $offset)); $pos = strpos($session_data, "|", $offset); + if ($pos === false) { + throw new JwtSessionException("invalid data, pipe not found"); + } $num = $pos - $offset; $varname = substr($session_data, $offset, $num); $offset += $num + 1; diff --git a/tests/JwtSessionTest.php b/tests/JwtSessionTest.php index 5387a17..47fbb82 100644 --- a/tests/JwtSessionTest.php +++ b/tests/JwtSessionTest.php @@ -22,7 +22,7 @@ class JwtSessionTest extends TestCase */ protected SessionConfig $sessionConfig; - const SESSION_ID = "sessionid"; + const string SESSION_ID = "sessionid"; /** * @throws JwtSessionException