diff --git a/.github/workflows/test-phpunit.yml b/.github/workflows/test-phpunit.yml index ee16efa..3d1f6b2 100644 --- a/.github/workflows/test-phpunit.yml +++ b/.github/workflows/test-phpunit.yml @@ -31,7 +31,7 @@ jobs: if: (! contains(github.event.head_commit.message, '[ci skip]')) strategy: matrix: - php-versions: ['8.1', '8.2', '8.3', '8.4', '8.5'] + php-versions: ['8.2', '8.3', '8.4', '8.5'] steps: - name: Checkout diff --git a/composer.json b/composer.json index cbb6acd..84ac93c 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ ], "homepage": "https://github.com/PHPDevsr/PHPSpreadsheet-Secure", "require": { - "php": "^8.1 || ^8.2 || ^8.3 || ^8.4 || ^8.5", + "php": "^8.2 || ^8.3 || ^8.4 || ^8.5", "ext-openssl": "*", "ext-hash": "*", "ext-simplexml": "*", @@ -29,13 +29,13 @@ "pear/pear-core-minimal": "^v1.10.15" }, "require-dev": { - "codeigniter/coding-standard": "^1.8.1 || ^1.9.1", + "codeigniter/coding-standard": "^1.9.1", "nexusphp/tachycardia": "^2.4", "phpstan/extension-installer": "^1.4", "phpstan/phpstan": "^2.1.40", "phpstan/phpstan-strict-rules": "^2.0.10", - "phpunit/phpcov": "^9.0.2 || ^10.0 || ^11.0", - "phpunit/phpunit": "^10.5.16 || ^11.5", + "phpunit/phpcov": "^10.0 || ^11.0", + "phpunit/phpunit": "^11.5", "rector/rector": "2.3.8" }, "minimum-stability": "dev", diff --git a/phpstan-baseline.php b/phpstan-baseline.php index 331add7..2d20b6a 100644 --- a/phpstan-baseline.php +++ b/phpstan-baseline.php @@ -13,36 +13,12 @@ 'count' => 1, 'path' => __DIR__ . '/src/Secure.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_convertPasswordToKey\\(\\) has parameter \\$hashAlgorithm with no type specified\\.$#', - 'identifier' => 'missingType.parameter', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_convertPasswordToKey\\(\\) has parameter \\$keyBits with no type specified\\.$#', - 'identifier' => 'missingType.parameter', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; -$ignoreErrors[] = [ - 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_convertPasswordToKey\\(\\) has parameter \\$password with no type specified\\.$#', - 'identifier' => 'missingType.parameter', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; $ignoreErrors[] = [ 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_convertPasswordToKey\\(\\) has parameter \\$saltValue with no type specified\\.$#', 'identifier' => 'missingType.parameter', 'count' => 1, 'path' => __DIR__ . '/src/Secure.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_convertPasswordToKey\\(\\) has parameter \\$spinCount with no type specified\\.$#', - 'identifier' => 'missingType.parameter', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; $ignoreErrors[] = [ 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:_createIV\\(\\) has no return type specified\\.$#', 'identifier' => 'missingType.return', @@ -79,12 +55,6 @@ 'count' => 1, 'path' => __DIR__ . '/src/Secure.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Method PHPDevsr\\\\Spreadsheet\\\\Secure\\:\\:output\\(\\) should return string but returns string\\|false\\.$#', - 'identifier' => 'return.type', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; $ignoreErrors[] = [ 'message' => '#^Offset \'name\' might not exist on array\\\\|string\\.$#', 'identifier' => 'offsetAccess.notFound', @@ -127,12 +97,6 @@ 'count' => 1, 'path' => __DIR__ . '/src/Secure.php', ]; -$ignoreErrors[] = [ - 'message' => '#^Parameter \\#1 \\$string of function bin2hex expects string, array\\|string given\\.$#', - 'identifier' => 'argument.type', - 'count' => 1, - 'path' => __DIR__ . '/src/Secure.php', -]; $ignoreErrors[] = [ 'message' => '#^Parameter \\#1 \\$value of function count expects array\\|Countable, mixed given\\.$#', 'identifier' => 'argument.type', diff --git a/rector.php b/rector.php index ca2054e..21a9704 100644 --- a/rector.php +++ b/rector.php @@ -79,8 +79,8 @@ $rectorConfig->sets([ SetList::DEAD_CODE, SetList::CODE_QUALITY, - SetList::STRICT_BOOLEANS, - LevelSetList::UP_TO_PHP_80, + SetList::CODING_STYLE, + LevelSetList::UP_TO_PHP_82, ]); // The paths to refactor (can also be supplied with CLI arguments) @@ -99,7 +99,7 @@ } // Set the target version for refactoring - $rectorConfig->phpVersion(PhpVersion::PHP_82); + $rectorConfig->phpVersion(PhpVersion::PHP_83); // Auto-import fully qualified class names $rectorConfig->importNames(); diff --git a/src/Secure.php b/src/Secure.php index f342b56..f70acb1 100644 --- a/src/Secure.php +++ b/src/Secure.php @@ -101,6 +101,7 @@ public function setFile($data = '') while (! feof($fp)) { yield (array) unpack('C*', (string) fread($fp, 4096)); } + fclose($fp); unset($fp); }); @@ -305,6 +306,7 @@ public function output(?string $filePath = null) $OLE2 = new OLE_PPS_File(OLE::Asc2Ucs('EncryptedPackage')); $OLE2->init(); + $filesize = (int) filesize($encryptedPackage['tmpFile']); for ($i = 0; $i < ($filesize / 4096); $i++) { @@ -324,7 +326,7 @@ public function output(?string $filePath = null) $root->save($filePath); - return file_get_contents($filePath); + return (string) file_get_contents($filePath); } /** @@ -427,21 +429,18 @@ private function arrayToXml(array $array = []) /** * Crypt method * - * @param string $cipherAlgorithm - * @param string $cipherChaining * @param array $key - * @param bool $encrypt * @param array $iv * @param array $input */ - private function _crypt($encrypt, $cipherAlgorithm, $cipherChaining, $key, $iv, $input) + private function _crypt(bool $encrypt, string $cipherAlgorithm, string $cipherChaining, $key, $iv, $input) { $algorithm = $cipherAlgorithm . '-' . (count($key) * 8); if ($cipherChaining === 'ChainingModeCBC') { $algorithm .= '-cbc'; } else { - throw new Exception("Unknown cipher chaining: {$cipherChaining}"); + throw new Exception('Unknown cipher chaining: ' . $cipherChaining); } $cipher = []; @@ -463,11 +462,9 @@ private function _crypt($encrypt, $cipherAlgorithm, $cipherChaining, $key, $iv, /** * Hashing * - * @param string $algorithm - * * @return array */ - private function _hash($algorithm, ...$buffers) + private function _hash(string $algorithm, ...$buffers) { try { $ctx = hash_init($algorithm); @@ -489,7 +486,7 @@ private function _hash($algorithm, ...$buffers) * * @return array */ - private function _hmac($algorithm, $key, $fileName) + private function _hmac(string $algorithm, $key, string $fileName) { return (array) unpack('C*', hash_hmac_file( $algorithm, @@ -501,19 +498,16 @@ private function _hmac($algorithm, $key, $fileName) /** * Create Unsigned Integer 32-bit Buffer - * - * @param int $value - * @param int $bufferSize */ - private function _createUInt32LEBuffer($value, $bufferSize = 4) + private function _createUInt32LEBuffer(int $value, int $bufferSize = 4) { return array_pad(array_values((array) unpack('C*', pack('V', $value))), $bufferSize, 0); } - private function _convertPasswordToKey($password, $hashAlgorithm, $saltValue, $spinCount, $keyBits, $blockKey) + private function _convertPasswordToKey(string $password, string $hashAlgorithm, $saltValue, int $spinCount, int $keyBits, $blockKey) { // Password must be in unicode buffer - $passwordBuffer = array_map('hexdec', str_split(bin2hex(mb_convert_encoding($password, 'UTF-16LE', 'utf-8')), 2)); + $passwordBuffer = array_map(hexdec(...), str_split(bin2hex(mb_convert_encoding($password, 'UTF-16LE', 'utf-8')), 2)); // Generate the initial hash $key = $this->_hash($hashAlgorithm, $saltValue, $passwordBuffer); @@ -547,12 +541,10 @@ private function _convertPasswordToKey($password, $hashAlgorithm, $saltValue, $s /** * Create initialization vector * - * @param string $hashAlgorithm * @param array $saltValue - * @param int $blockSize * @param mixed $blockKey */ - private function _createIV($hashAlgorithm, $saltValue, $blockSize, $blockKey) + private function _createIV(string $hashAlgorithm, $saltValue, int $blockSize, $blockKey) { // Create the block key from the current index if (is_int($blockKey)) { @@ -574,30 +566,26 @@ private function _createIV($hashAlgorithm, $saltValue, $blockSize, $blockKey) /** * Package Crypt * - * @param bool $encrypt - * @param string $cipherAlgorithm - * @param string $cipherChaining - * @param string $hashAlgorithm - * @param int $blockSize * @param array $saltValue - * @param mixed $input + * @param Closure|null $input * @param array $key */ private function _cryptPackage( - $encrypt, - $cipherAlgorithm, - $cipherChaining, - $hashAlgorithm, - $blockSize, + bool $encrypt, + string $cipherAlgorithm, + string $cipherChaining, + string $hashAlgorithm, + int $blockSize, $saltValue, $input, $key = [], ) { - $tmpOutputChunk = (string) tempnam(sys_get_temp_dir(), 'outputChunk'); - $tmpFileHeaderLength = (string) tempnam(sys_get_temp_dir(), 'fileHeaderLength'); - $tmpFile = (string) tempnam(sys_get_temp_dir(), 'file'); + $sysTempDir = sys_get_temp_dir(); + $tmpOutputChunk = (string) tempnam($sysTempDir, 'outputChunk'); + $tmpFileHeaderLength = (string) tempnam($sysTempDir, 'fileHeaderLength'); + $tmpFile = (string) tempnam($sysTempDir, 'file'); - if (is_callable($input) && is_a($in = $input(), 'Generator')) { + if ($input !== null && is_a($in = $input(), 'Generator')) { $inputCount = 0; foreach ($in as $i => $inputChunk) { @@ -605,9 +593,11 @@ private function _cryptPackage( // Grab the next chunk $inputCount += $lengthInputChunk; $remainder = $lengthInputChunk % $blockSize; + if ($remainder !== 0) { $inputChunk = array_pad($inputChunk, $lengthInputChunk + (16 - $remainder), 0); } + // Create the initialization vector $iv = $this->_createIV($hashAlgorithm, $saltValue, $blockSize, $i); @@ -632,6 +622,8 @@ private function _cryptPackage( 'tmpFile' => $tmpFile, ]; } + + return null; } /** @@ -657,6 +649,7 @@ private function build(SimpleXMLElement $rootNode, array $data = []) $rootNode->addAttribute($kk, $vv); } } + if ($k === 'children') { $isNamespace = count(explode(':', $vv['name'])) === 2; $r = $isNamespace ? $rootNode->addChild('xmlns:' . $vv['name'], '') : $rootNode->addChild($vv['name'], '');