diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6e81829c..756393a6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -8,7 +8,7 @@ jobs: strategy: fail-fast: false matrix: - php: [ '8.1', '8.2', '8.3', '8.4' ] + php: [ '8.2', '8.3', '8.4' ] imagemagick: [ '6.9.13-25', '7.1.1-47' ] imagick: [ '3.8.0' ] diff --git a/Dockerfile b/Dockerfile index 66dbb7ca..d57f486e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.1-cli +FROM php:8.2-cli # install dependencies RUN apt update \ diff --git a/composer.json b/composer.json index d07eb284..7c9cc732 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ } ], "require": { - "php": "^8.1", + "php": "^8.2", "ext-mbstring": "*", "intervention/gif": "^4.2" }, diff --git a/src/Drivers/AbstractDecoder.php b/src/Drivers/AbstractDecoder.php index c0710f38..ed2988aa 100644 --- a/src/Drivers/AbstractDecoder.php +++ b/src/Drivers/AbstractDecoder.php @@ -63,15 +63,27 @@ abstract class AbstractDecoder implements DecoderInterface } /** - * Determine if given input is base64 encoded data + * Decodes given base 64 encoded data + * + * @throws DecoderException */ - protected function isValidBase64(mixed $input): bool + protected function decodeBase64Data(mixed $input): string { if (!is_string($input)) { - return false; + throw new DecoderException('Input is not Base64-encoded data.'); } - return base64_encode(base64_decode($input)) === str_replace(["\n", "\r"], '', $input); + $decoded = base64_decode($input); + + if ($decoded === false) { + throw new DecoderException('Input can not be Base64-decoded.'); + } + + if (base64_encode($decoded) !== str_replace(["\n", "\r"], '', $input)) { + throw new DecoderException('Input is not Base64-decoded data.'); + } + + return $decoded; } /** diff --git a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php index 2c1e053b..03d7457a 100644 --- a/src/Drivers/Gd/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Gd/Decoders/Base64ImageDecoder.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Intervention\Image\Drivers\Gd\Decoders; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\DecoderInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -18,10 +17,6 @@ class Base64ImageDecoder extends BinaryImageDecoder implements DecoderInterface */ public function decode(mixed $input): ImageInterface|ColorInterface { - if (!$this->isValidBase64($input)) { - throw new DecoderException('Unable to decode input'); - } - - return parent::decode(base64_decode((string) $input)); + return parent::decode($this->decodeBase64Data($input)); } } diff --git a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php index ea199580..31f7dd53 100644 --- a/src/Drivers/Gd/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Gd/Decoders/BinaryImageDecoder.php @@ -21,8 +21,8 @@ class BinaryImageDecoder extends NativeObjectDecoder implements DecoderInterface */ public function decode(mixed $input): ImageInterface|ColorInterface { - if (!is_string($input)) { - throw new DecoderException('Unable to decode input'); + if (!is_string($input) || empty($input)) { + throw new DecoderException('Input does not contain binary image data.'); } return match ($this->isGifFormat($input)) { @@ -41,7 +41,7 @@ class BinaryImageDecoder extends NativeObjectDecoder implements DecoderInterface $gd = @imagecreatefromstring($input); if ($gd === false) { - throw new DecoderException('Unable to decode input'); + throw new DecoderException('Unsupported image format.'); } // create image instance diff --git a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php index 429d7d69..6b85be4a 100644 --- a/src/Drivers/Gd/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Gd/Decoders/FilePathImageDecoder.php @@ -28,7 +28,7 @@ class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterfa // detect media (mime) type $mediaType = $this->getMediaTypeByFilePath($path); } catch (Throwable) { - throw new DecoderException('Unable to decode input - file contains unsupported file type.'); + throw new DecoderException('File contains unsupported image format.'); } $image = match ($mediaType->format()) { @@ -41,7 +41,7 @@ class FilePathImageDecoder extends NativeObjectDecoder implements DecoderInterfa Format::PNG => @imagecreatefrompng($path), Format::AVIF => @imagecreatefromavif($path), Format::BMP => @imagecreatefrombmp($path), - default => throw new DecoderException('Unable to decode input'), + default => throw new DecoderException('File contains unsupported image format.'), }), }; diff --git a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php index ea150627..79f9709e 100644 --- a/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/Base64ImageDecoder.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Intervention\Image\Drivers\Imagick\Decoders; -use Intervention\Image\Exceptions\DecoderException; use Intervention\Image\Interfaces\ColorInterface; use Intervention\Image\Interfaces\ImageInterface; @@ -17,10 +16,6 @@ class Base64ImageDecoder extends BinaryImageDecoder */ public function decode(mixed $input): ImageInterface|ColorInterface { - if (!$this->isValidBase64($input)) { - throw new DecoderException('Unable to decode input'); - } - - return parent::decode(base64_decode((string) $input)); + return parent::decode($this->decodeBase64Data($input)); } } diff --git a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php index 96850b3c..90f30b42 100644 --- a/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/BinaryImageDecoder.php @@ -20,15 +20,15 @@ class BinaryImageDecoder extends NativeObjectDecoder */ public function decode(mixed $input): ImageInterface|ColorInterface { - if (!is_string($input)) { - throw new DecoderException('Unable to decode input'); + if (!is_string($input) || empty($input)) { + throw new DecoderException('Input does not contain binary image data.'); } try { $imagick = new Imagick(); $imagick->readImageBlob($input); } catch (ImagickException) { - throw new DecoderException('Unable to decode input'); + throw new DecoderException('Unsupported image format.'); } // decode image diff --git a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php index fd0d88e5..9b7d2214 100644 --- a/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php +++ b/src/Drivers/Imagick/Decoders/FilePathImageDecoder.php @@ -26,7 +26,7 @@ class FilePathImageDecoder extends NativeObjectDecoder $imagick = new Imagick(); $imagick->readImage($path); } catch (ImagickException) { - throw new DecoderException('Unable to decode input'); + throw new DecoderException('Unsupported image format.'); } // decode image diff --git a/tests/Unit/Drivers/AbstractDecoderTest.php b/tests/Unit/Drivers/AbstractDecoderTest.php index 388d24e7..4297f2b3 100644 --- a/tests/Unit/Drivers/AbstractDecoderTest.php +++ b/tests/Unit/Drivers/AbstractDecoderTest.php @@ -16,6 +16,7 @@ use Intervention\Image\Tests\BaseTestCase; use Mockery; use PHPUnit\Framework\Attributes\DataProvider; use stdClass; +use Throwable; #[CoversClass(AbstractDecoder::class)] final class AbstractDecoderTest extends BaseTestCase @@ -95,7 +96,12 @@ final class AbstractDecoderTest extends BaseTestCase { public function isValid(mixed $input): bool { - return parent::isValidBase64($input); + try { + parent::decodeBase64Data($input); + } catch (Throwable) { + return false; + } + return true; } public function decode(mixed $input): ImageInterface|ColorInterface