diff --git a/system/Encryption/EncrypterInterface.php b/system/Encryption/EncrypterInterface.php index 9c1b0d36d1be..1c916e8b390c 100644 --- a/system/Encryption/EncrypterInterface.php +++ b/system/Encryption/EncrypterInterface.php @@ -26,8 +26,8 @@ interface EncrypterInterface /** * Encrypt - convert plaintext into ciphertext * - * @param string $data Input data - * @param array|string|null $params Overridden parameters, specifically the key + * @param string $data Input data + * @param array|string|null $params Overridden parameters, specifically the key * * @return string * @@ -38,8 +38,8 @@ public function encrypt(#[SensitiveParameter] $data, #[SensitiveParameter] $para /** * Decrypt - convert ciphertext into plaintext * - * @param string $data Encrypted data - * @param array|string|null $params Overridden parameters, specifically the key + * @param string $data Encrypted data + * @param array|string|null $params Overridden parameters, specifically the key * * @return string * diff --git a/system/Encryption/Encryption.php b/system/Encryption/Encryption.php index 47a6ec649e1d..d24a6754efab 100644 --- a/system/Encryption/Encryption.php +++ b/system/Encryption/Encryption.php @@ -70,7 +70,7 @@ class Encryption /** * Map of drivers to handler classes, in preference order * - * @var array + * @var list */ protected $drivers = [ 'OpenSSL', @@ -138,8 +138,14 @@ public function initialize(?EncryptionConfig $config = null) $handlerName = 'CodeIgniter\\Encryption\\Handlers\\' . $this->driver . 'Handler'; $this->encrypter = new $handlerName($config); - if (($config->previousKeys ?? []) !== []) { - $this->encrypter = new KeyRotationDecorator($this->encrypter, $config->previousKeys); + // (array) '' is [''], not [], so the unset default must be filtered out here. + $previousKeys = array_values(array_filter( + (array) ($config->previousKeys ?? []), + static fn ($key): bool => $key !== '', + )); + + if ($previousKeys !== []) { + $this->encrypter = new KeyRotationDecorator($this->encrypter, $previousKeys); } return $this->encrypter; @@ -162,7 +168,7 @@ public static function createKey($length = 32) * * @param string $key Property name * - * @return array|string|null + * @return list|string|null */ public function __get($key) { diff --git a/system/Encryption/Handlers/OpenSSLHandler.php b/system/Encryption/Handlers/OpenSSLHandler.php index 26c10a258536..d649a66bf060 100644 --- a/system/Encryption/Handlers/OpenSSLHandler.php +++ b/system/Encryption/Handlers/OpenSSLHandler.php @@ -19,6 +19,9 @@ /** * Encryption handling for OpenSSL library * + * @property-read string $cipher + * @property-read string $key + * * @see \CodeIgniter\Encryption\Handlers\OpenSSLHandlerTest */ class OpenSSLHandler extends BaseHandler @@ -33,7 +36,7 @@ class OpenSSLHandler extends BaseHandler /** * List of supported HMAC algorithms * - * @var array [name => digest size] + * @var array [name => digest size] */ protected array $digestSize = [ 'SHA224' => 28, diff --git a/system/Encryption/Handlers/SodiumHandler.php b/system/Encryption/Handlers/SodiumHandler.php index c759bdbf518f..0fa1bd235247 100644 --- a/system/Encryption/Handlers/SodiumHandler.php +++ b/system/Encryption/Handlers/SodiumHandler.php @@ -20,6 +20,9 @@ /** * SodiumHandler uses libsodium in encryption. * + * @property-read int $blockSize + * @property-read string|null $key + * * @see https://github.com/jedisct1/libsodium/issues/392 * @see \CodeIgniter\Encryption\Handlers\SodiumHandlerTest */ @@ -124,7 +127,7 @@ public function decrypt($data, #[SensitiveParameter] $params = null) /** * Parse the $params before doing assignment. * - * @param array|string|null $params + * @param array|string|null $params * * @return void * diff --git a/system/Encryption/KeyRotationDecorator.php b/system/Encryption/KeyRotationDecorator.php index 7202bcc06764..46846dead1bb 100644 --- a/system/Encryption/KeyRotationDecorator.php +++ b/system/Encryption/KeyRotationDecorator.php @@ -22,6 +22,9 @@ * Wraps any EncrypterInterface implementation to provide automatic * fallback to previous encryption keys during decryption. This enables * seamless key rotation without requiring re-encryption of existing data. + * + * @property-read string|null $cipher + * @property-read string|null $key */ class KeyRotationDecorator implements EncrypterInterface { diff --git a/tests/system/Encryption/EncryptionTest.php b/tests/system/Encryption/EncryptionTest.php index 48fd6dc78e73..81c4554336e0 100644 --- a/tests/system/Encryption/EncryptionTest.php +++ b/tests/system/Encryption/EncryptionTest.php @@ -15,6 +15,7 @@ use CodeIgniter\Config\Services as CodeIgniterServices; use CodeIgniter\Encryption\Exceptions\EncryptionException; +use CodeIgniter\Encryption\Handlers\OpenSSLHandler; use CodeIgniter\Superglobals; use CodeIgniter\Test\CIUnitTestCase; use Config\Encryption as EncryptionConfig; @@ -156,6 +157,7 @@ public function testServiceShared(): void $config->key = 'Abracadabra'; $encrypter = Services::encrypter($config, true); + $this->assertInstanceOf(OpenSSLHandler::class, $encrypter); $this->assertSame('anything', $encrypter->key); } @@ -166,7 +168,7 @@ public function testMagicIssetTrue(): void public function testMagicIssetFalse(): void { - $this->assertFalse(isset($this->encryption->bogus)); + $this->assertFalse(isset($this->encryption->bogus)); // @phpstan-ignore property.notFound } public function testMagicGet(): void @@ -176,7 +178,7 @@ public function testMagicGet(): void public function testMagicGetMissing(): void { - $this->assertNull($this->encryption->bogus); + $this->assertNull($this->encryption->bogus); // @phpstan-ignore property.notFound } public function testDecryptEncryptedDataByCI3AES128CBC(): void diff --git a/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php index e883c5b2fbc6..bac22a881f0a 100644 --- a/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php +++ b/tests/system/Encryption/Handlers/OpenSSLHandlerTest.php @@ -46,6 +46,7 @@ public function testSanity(): void $params->key = 'Something other than an empty string'; $encrypter = $this->encryption->initialize($params); + $this->assertInstanceOf(OpenSSLHandler::class, $encrypter); $this->assertSame('AES-256-CTR', $encrypter->cipher); $this->assertSame('Something other than an empty string', $encrypter->key); @@ -64,6 +65,7 @@ public function testSimple(): void $params->key = '\xd0\xc9\x08\xc4\xde\x52\x12\x6e\xf8\xcc\xdb\x03\xea\xa0\x3a\x5c'; // Default state (AES-256/Rijndael-256 in CTR mode) $encrypter = $this->encryption->initialize($params); + $this->assertInstanceOf(OpenSSLHandler::class, $encrypter); // Was the key properly set? $this->assertSame($params->key, $encrypter->key); @@ -145,6 +147,7 @@ public function testInternalKeyNotModifiedByParams(): void $params->key = 'original-key-value'; $encrypter = $this->encryption->initialize($params); + $this->assertInstanceOf(OpenSSLHandler::class, $encrypter); $this->assertSame('original-key-value', $encrypter->key); diff --git a/tests/system/Encryption/Handlers/SodiumHandlerTest.php b/tests/system/Encryption/Handlers/SodiumHandlerTest.php index 09b1784e4a12..47406cef00d0 100644 --- a/tests/system/Encryption/Handlers/SodiumHandlerTest.php +++ b/tests/system/Encryption/Handlers/SodiumHandlerTest.php @@ -47,10 +47,11 @@ public function testPropertiesGetter(): void $this->config->key = sodium_crypto_secretbox_keygen(); $this->config->blockSize = 256; $encrypter = $this->encryption->initialize($this->config); + $this->assertInstanceOf(SodiumHandler::class, $encrypter); $this->assertSame($this->config->key, $encrypter->key); $this->assertSame($this->config->blockSize, $encrypter->blockSize); - $this->assertNull($encrypter->driver); + $this->assertNull($encrypter->driver); // @phpstan-ignore property.notFound } public function testEmptyKeyThrowsErrorOnInitialize(): void @@ -136,6 +137,7 @@ public function testInternalKeyNotModifiedByParams(): void $this->config->key = $originalKey; $encrypter = $this->encryption->initialize($this->config); + $this->assertInstanceOf(SodiumHandler::class, $encrypter); $this->assertSame($originalKey, $encrypter->key); diff --git a/tests/system/Encryption/KeyRotationDecoratorTest.php b/tests/system/Encryption/KeyRotationDecoratorTest.php index e94ea16bcd65..1f86220739db 100644 --- a/tests/system/Encryption/KeyRotationDecoratorTest.php +++ b/tests/system/Encryption/KeyRotationDecoratorTest.php @@ -193,6 +193,7 @@ public function testPropertyAccessDelegation(): void $params->previousKeys = ['old-key']; $encrypter = $this->encryption->initialize($params); + $this->assertInstanceOf(KeyRotationDecorator::class, $encrypter); $this->assertSame('AES-128-CBC', $encrypter->cipher); $this->assertSame('test-key-very-long', $encrypter->key); diff --git a/utils/phpstan-baseline/loader.neon b/utils/phpstan-baseline/loader.neon index 61ebc04982b4..92775038f58d 100644 --- a/utils/phpstan-baseline/loader.neon +++ b/utils/phpstan-baseline/loader.neon @@ -1,4 +1,4 @@ -# total 419 errors +# total 394 errors includes: - argument.type.neon diff --git a/utils/phpstan-baseline/missingType.iterableValue.neon b/utils/phpstan-baseline/missingType.iterableValue.neon index 333eaa92ae2e..54d3caee7dbd 100644 --- a/utils/phpstan-baseline/missingType.iterableValue.neon +++ b/utils/phpstan-baseline/missingType.iterableValue.neon @@ -1,4 +1,4 @@ -# total 282 errors +# total 270 errors parameters: ignoreErrors: @@ -397,66 +397,6 @@ parameters: count: 1 path: ../../system/Email/Email.php - - - message: '#^Method CodeIgniter\\Encryption\\EncrypterInterface\:\:decrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/EncrypterInterface.php - - - - message: '#^Method CodeIgniter\\Encryption\\EncrypterInterface\:\:encrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/EncrypterInterface.php - - - - message: '#^Method CodeIgniter\\Encryption\\Encryption\:\:__get\(\) return type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Encryption.php - - - - message: '#^Property CodeIgniter\\Encryption\\Encryption\:\:\$drivers type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Encryption.php - - - - message: '#^Method CodeIgniter\\Encryption\\Handlers\\OpenSSLHandler\:\:decrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/OpenSSLHandler.php - - - - message: '#^Method CodeIgniter\\Encryption\\Handlers\\OpenSSLHandler\:\:encrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/OpenSSLHandler.php - - - - message: '#^Property CodeIgniter\\Encryption\\Handlers\\OpenSSLHandler\:\:\$digestSize type has no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/OpenSSLHandler.php - - - - message: '#^Method CodeIgniter\\Encryption\\Handlers\\SodiumHandler\:\:decrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/SodiumHandler.php - - - - message: '#^Method CodeIgniter\\Encryption\\Handlers\\SodiumHandler\:\:encrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/SodiumHandler.php - - - - message: '#^Method CodeIgniter\\Encryption\\Handlers\\SodiumHandler\:\:parseParams\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/Handlers/SodiumHandler.php - - - - message: '#^Method CodeIgniter\\Encryption\\KeyRotationDecorator\:\:decrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/KeyRotationDecorator.php - - - - message: '#^Method CodeIgniter\\Encryption\\KeyRotationDecorator\:\:encrypt\(\) has parameter \$params with no value type specified in iterable type array\.$#' - count: 1 - path: ../../system/Encryption/KeyRotationDecorator.php - - message: '#^Method CodeIgniter\\Exceptions\\PageNotFoundException\:\:lang\(\) has parameter \$args with no value type specified in iterable type array\.$#' count: 1 diff --git a/utils/phpstan-baseline/property.notFound.neon b/utils/phpstan-baseline/property.notFound.neon index f55cee7b3de4..1a8daae7b918 100644 --- a/utils/phpstan-baseline/property.notFound.neon +++ b/utils/phpstan-baseline/property.notFound.neon @@ -1,52 +1,7 @@ -# total 19 errors +# total 6 errors parameters: ignoreErrors: - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$key\.$#' - count: 1 - path: ../../tests/system/Encryption/EncryptionTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\Encryption\:\:\$bogus\.$#' - count: 2 - path: ../../tests/system/Encryption/EncryptionTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$cipher\.$#' - count: 1 - path: ../../tests/system/Encryption/Handlers/OpenSSLHandlerTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$key\.$#' - count: 3 - path: ../../tests/system/Encryption/Handlers/OpenSSLHandlerTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$blockSize\.$#' - count: 1 - path: ../../tests/system/Encryption/Handlers/SodiumHandlerTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$driver\.$#' - count: 1 - path: ../../tests/system/Encryption/Handlers/SodiumHandlerTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$key\.$#' - count: 2 - path: ../../tests/system/Encryption/Handlers/SodiumHandlerTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$cipher\.$#' - count: 1 - path: ../../tests/system/Encryption/KeyRotationDecoratorTest.php - - - - message: '#^Access to an undefined property CodeIgniter\\Encryption\\EncrypterInterface\:\:\$key\.$#' - count: 1 - path: ../../tests/system/Encryption/KeyRotationDecoratorTest.php - - message: '#^Access to an undefined property CodeIgniter\\I18n\\TimeLegacy\:\:\$foobar\.$#' count: 1