diff --git a/.docker/php/8.1/Dockerfile b/.docker/php/8.1/Dockerfile index e4586be..9951001 100644 --- a/.docker/php/8.1/Dockerfile +++ b/.docker/php/8.1/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.1.0RC2-fpm-alpine +FROM php:8.1-fpm-alpine ENV PHP_CONF_DIR=/usr/local/etc/php-fpm.d COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ec06aa9..df9ef5c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,7 +69,7 @@ jobs: uses: mheap/phpunit-matcher-action@v1 - name: Run unit tests on PHP ${{ matrix.php }} - run: make -s "test-php-${{ matrix.php }}" + run: make -s -e "PHPUNIT_OPTIONS=--teamcity" "test-php-${{ matrix.php }}" env: COMPOSE_INTERACTIVE_NO_CLI: 1 PHPUNIT_OPTIONS: "--teamcity" diff --git a/.phive/phars.xml b/.phive/phars.xml index 02d10c5..8a0ee75 100644 --- a/.phive/phars.xml +++ b/.phive/phars.xml @@ -1,8 +1,8 @@ - - - - - + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index 77a220f..79a7456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/) and [Keep a CHANGELOG](http://keepachangelog.com). +## [3.1.7] - 2021-12-07 + +* Make sure length values are within valid bounds + ## [3.1.6] - 2021-09-23 ### Added @@ -410,6 +414,8 @@ on [Pierrick Charron](https://github.com/adoy)'s [PHP-FastCGI-Client](https://gi socket connection) * Method `Client->getValues()` +[3.1.7]: https://github.com/hollodotme/fast-cgi-client/compare/v3.1.6...v3.1.7 + [3.1.6]: https://github.com/hollodotme/fast-cgi-client/compare/v3.1.5...v3.1.6 [3.1.5]: https://github.com/hollodotme/fast-cgi-client/compare/v3.1.4...v3.1.5 diff --git a/Makefile b/Makefile index 59fcdb9..cbcba88 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ composer-update: .PHONY: composer-update ## Run all tests on all PHP versions -tests: composer-validate phplint test-php-7.1 test-php-7.2 test-php-7.3 test-php-7.4 test-php-8.0 test-php-8.1 phpstan +tests: composer-validate phplint test-php-7.1 test-php-7.2 test-php-7.3 test-php-7.4 test-php-8.0 test-php-8.1 dcdown phpstan .PHONY: tests INTEGRATION_WORKER_DIR = ./tests/Integration/Workers @@ -91,6 +91,7 @@ make-integration-workers-accessible: .PHONY: make-integration-workers-accessible PHP_OPTIONS = -d error_reporting=-1 -dmemory_limit=-1 -d xdebug.mode=coverage -d auto_prepend_file=tests/xdebug-filter.php +PHPUNIT_OPTIONS = --testdox ## Run PHP linting phplint: diff --git a/phpstan.neon b/phpstan.neon index cb2d4fb..27024df 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,6 +2,6 @@ parameters: paths: - src - tests - level: max + level: 8 bootstrapFiles: - /repo/.tools/phpunit-7.phar \ No newline at end of file diff --git a/src/Encoders/NameValuePairEncoder.php b/src/Encoders/NameValuePairEncoder.php index fcc3fe2..eae6276 100644 --- a/src/Encoders/NameValuePairEncoder.php +++ b/src/Encoders/NameValuePairEncoder.php @@ -1,4 +1,4 @@ - $pairs + * @param array $pairs * * @return string */ @@ -105,25 +105,25 @@ public function decodePairs( string $data, int $length = -1 ) : array while ( $p !== $length ) { - $nameLength = ord( $data[$p++] ); + $nameLength = ord( $data[ $p++ ] ); if ( $nameLength >= 128 ) { $nameLength &= (0x7F << 24); - $nameLength |= (ord( $data[$p++] ) << 16); - $nameLength |= (ord( $data[$p++] ) << 8); - $nameLength |= ord( $data[$p++] ); + $nameLength |= (ord( $data[ $p++ ] ) << 16); + $nameLength |= (ord( $data[ $p++ ] ) << 8); + $nameLength |= ord( $data[ $p++ ] ); } - $valueLength = ord( $data[$p++] ); + $valueLength = ord( $data[ $p++ ] ); if ( $valueLength >= 128 ) { $valueLength = ($nameLength & 0x7F << 24); - $valueLength |= (ord( $data[$p++] ) << 16); - $valueLength |= (ord( $data[$p++] ) << 8); - $valueLength |= ord( $data[$p++] ); + $valueLength |= (ord( $data[ $p++ ] ) << 16); + $valueLength |= (ord( $data[ $p++ ] ) << 8); + $valueLength |= ord( $data[ $p++ ] ); } $array[ substr( $data, $p, $nameLength ) ] = substr( $data, $p + $nameLength, $valueLength ); - $p += ($nameLength + $valueLength); + $p += ($nameLength + $valueLength); } return $array; diff --git a/src/Interfaces/EncodesNameValuePair.php b/src/Interfaces/EncodesNameValuePair.php index cf0492b..5471d29 100644 --- a/src/Interfaces/EncodesNameValuePair.php +++ b/src/Interfaces/EncodesNameValuePair.php @@ -30,7 +30,7 @@ interface EncodesNameValuePair { /** - * @param array $pairs + * @param array $pairs * * @return string */ diff --git a/src/RequestContents/JsonData.php b/src/RequestContents/JsonData.php index 7870306..929afa5 100644 --- a/src/RequestContents/JsonData.php +++ b/src/RequestContents/JsonData.php @@ -5,6 +5,7 @@ use hollodotme\FastCGI\Interfaces\ComposesRequestContent; use RuntimeException; use function json_encode; +use const PHP_INT_MAX; final class JsonData implements ComposesRequestContent { @@ -14,19 +15,19 @@ final class JsonData implements ComposesRequestContent /** @var int */ private $encodingOptions; - /** @var int */ + /** @var int<1, max> */ private $encodingDepth; /** * @param mixed $data * @param int $options - * @param int $depth + * @param int<1, max> $depth */ public function __construct( $data, int $options = 0, int $depth = 512 ) { $this->data = $data; $this->encodingOptions = $options; - $this->encodingDepth = $depth; + $this->encodingDepth = max( 1, min( $depth, PHP_INT_MAX ) ); } public function getContentType() : string diff --git a/src/Sockets/Socket.php b/src/Sockets/Socket.php index 361f1f7..979b6d5 100644 --- a/src/Sockets/Socket.php +++ b/src/Sockets/Socket.php @@ -44,7 +44,9 @@ use function fread; use function fwrite; use function is_resource; +use function max; use function microtime; +use function min; use function ord; use function str_repeat; use function stream_get_meta_data; @@ -54,14 +56,13 @@ use function stream_socket_shutdown; use function strlen; use function substr; +use const PHP_INT_MAX; use const STREAM_SHUT_RDWR; final class Socket { private const BEGIN_REQUEST = 1; - private const ABORT_REQUEST = 2; - private const END_REQUEST = 3; private const PARAMS = 4; @@ -314,11 +315,11 @@ private function handleFailedResource( ?int $errorNumber, ?string $errorString ) if ( null !== $lastError ) { $lastErrorException = new ErrorException( - $lastError['message'] ?? '[No message available]', + $lastError['message'], 0, - $lastError['type'] ?? E_ERROR, - $lastError['file'] ?? '[No file available]', - $lastError['line'] ?? '[No line available]' + $lastError['type'], + $lastError['file'], + $lastError['line'] ); } @@ -502,11 +503,11 @@ private function readPacket() : ?array if ( $packet['contentLength'] ) { - $length = $packet['contentLength']; + $length = $this->getValidLength( (int)$packet['contentLength'] ); while ( $length && ($buffer = fread( $this->resource, $length )) !== false ) { - $length -= strlen( (string)$buffer ); + $length = $this->getValidLength( $length - strlen( (string)$buffer ) ); $packet['content'] .= $buffer; } } @@ -514,7 +515,7 @@ private function readPacket() : ?array if ( $packet['paddingLength'] ) { /** @noinspection UnusedFunctionResultInspection */ - fread( $this->resource, (int)$packet['paddingLength'] ); + fread( $this->resource, $this->getValidLength( (int)$packet['paddingLength'] ) ); } return $packet; @@ -523,6 +524,16 @@ private function readPacket() : ?array return null; } + /** + * @param int $value + * + * @return int<0, max> + */ + private function getValidLength( int $value ) : int + { + return (int)max( 0, min( $value, PHP_INT_MAX ) ); + } + private function notifyPassThroughCallbacks( string $outputBuffer, string $errorBuffer ) : void { foreach ( $this->passThroughCallbacks as $passThroughCallback )