CVE-2024-32871: Allocation of Resources Without Limits or Throttling (CWE-770)
value: Pimcore is an Open Source Data & Experience Management Platform. The Pimcore thumbnail generation can be used to flood the server with large files. By changing the file extension or scaling factor of the requested thumbnail, attackers can create files that are much larger in file size than the original. This vulnerability is fixed in 11.2.4.
CWE description: The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
CVSS v3 base score: 7.5
Published: 2024-06-04T15:15Z
Repository: https://github.com/pimcore/pimcore
Language: PHP
Files changed: bundles/CoreBundle/src/DependencyInjection/Configuration.php, doc/04_Assets/03_Working_with_Thumbnails/01_Image_Thumbnails.md, doc/04_Assets/03_Working_with_Thumbnails/README.md, doc/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md, models/Asset/Document/ImageThumbnail.php, models/Asset/Image/Thumbnail.php, models/Asset/Thumbnail/ImageThumbnailTrait.php, models/Asset/Video/ImageThumbnail.php, models/Exception/ThumbnailFormatNotSupportedException.php, models/Exception/ThumbnailMaxScalingFactorException.php
Commit date: 2024-05-03 13:09:04 +0200
Commit message: Merge pull request from GHSA-277c-5vvj-9pwx

* Introduce allowed formats and max scale factor

* Add documentation, upgrade notes and use floatNode for max_scale_factor

* Update to max_scaling_factor

* Cast format to lower

* Fix typo
Diff: diff --git a/bundles/CoreBundle/src/DependencyInjection/Configuration.php b/bundles/CoreBundle/src/DependencyInjection/Configuration.php
index 507c5ce3a90f..00b96b9344b0 100644
--- a/bundles/CoreBundle/src/DependencyInjection/Configuration.php
+++ b/bundles/CoreBundle/src/DependencyInjection/Configuration.php
@@ -350,10 +350,22 @@ final class Configuration implements ConfigurationInterface
         $assetsNode = $rootNode
             ->children()
                 ->arrayNode('assets')
                 ->addDefaultsIfNotSet()
                 ->children()
+                    ->arrayNode('thumbnails')
+                    ->addDefaultsIfNotSet()
+                        ->children()
+                            ->arrayNode('allowed_formats')
+                                ->defaultValue(['avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'])
+                                ->scalarPrototype()->end()
+                            ->end()
+                            ->floatNode('max_scaling_factor')
+                                ->defaultValue(5.0)
+                            ->end()
+                        ->end()
+                    ->end()
                     ->arrayNode('frontend_prefixes')
                         ->addDefaultsIfNotSet()
                         ->children()
                             ->scalarNode('source')
                                 ->defaultValue('')
diff --git a/doc/04_Assets/03_Working_with_Thumbnails/01_Image_Thumbnails.md b/doc/04_Assets/03_Working_with_Thumbnails/01_Image_Thumbnails.md
index 426b6fef40e3..37325a4bcb07 100644
--- a/doc/04_Assets/03_Working_with_Thumbnails/01_Image_Thumbnails.md
+++ b/doc/04_Assets/03_Working_with_Thumbnails/01_Image_Thumbnails.md
@@ -7,11 +7,12 @@ With this service every image which is stored as an asset can be transformed. Pi
 which are not stored as an asset inside Pimcore.
 
 > **IMPORTANT**  
 > Use Imagick PECL extension for best results, GDlib is just a fallback with limited functionality
 > (only PNG, JPG, GIF) and less quality!
-> Using ImageMagick Pimcore supports hundreds of formats including: AI, EPS, TIFF, PNG, JPG, GIF, PSD, ...
+> Using ImageMagick Pimcore can support hundreds of formats including: AI, EPS, TIFF, PNG, JPG, GIF, PSD, etc.
+> Not all formats are allowed out of the box. To extend the list [see](./README.md#allowed-formats).
 
 To use the thumbnailing service of Pimcore, you have to create a transformation pipeline first. To do so, open
 _Settings_ > _Thumbnails_ and click on _Add Thumbnail_ to create a new configuration.
 The fields name, description, format and quality should be self-explanatory, the interesting part are now the transformations.
 Click on _+_ to add a new transformation, so that it look like that for example:
@@ -327,10 +328,19 @@ $asset->getThumbnail("myConfig")->getPath(['deferredAllowed' => false]);
 
 This is a special functionality to allow embedding high resolution (ppi/dpi) images.
 The following is only necessary in special use-cases like Web-to-Print, in typical web-based cases, Pimcore
 automatically adds the `srcset` attribute to `<img>` and `<picture>` tags automatically, so no manual work is necessary.
 
+The high resolution scaling factor is limited to `5.0` eg. `@5x`. Float values are supported.
+If you need to scale an image more than that, you can use the `max_scaling_factor` option in the configuration.
+```yaml
+  pimcore:
+    assets:
+      thumbnails:
+        max_scaling_factor: 6.0
+```
+
 ### Use in the Thumbnail Configuration:
 
 ![High Resolution](../../img/thumbnail_high_resolution.png)
 The above configuration will generate a thumbnail with 500px width.
 
diff --git a/doc/04_Assets/03_Working_with_Thumbnails/README.md b/doc/04_Assets/03_Working_with_Thumbnails/README.md
index 02d38f5b2b78..58802ab5093c 100644
--- a/doc/04_Assets/03_Working_with_Thumbnails/README.md
+++ b/doc/04_Assets/03_Working_with_Thumbnails/README.md
@@ -2,11 +2,36 @@
 
 Pimcore provides a sophisticated thumbnail processing engine for calculating thumbnails based on source assets. So for 
 different output channels Pimcore can calculate and provide optimized images in terms of dimensions, file sizes, formats
 and much more.
 
-This functionality allows true single source publishing with Pimcore. 
+This functionality allows true single source publishing with Pimcore.
+
+### Allowed formats
+Pimcore allows the following formats for thumbnails out of the box:
+`'avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'`.
+
+If you want to use a different format, you can easily extend the list of supported formats.
+Keep in mind that you must copy the whole list of formats and add your desired format to it.
+```yaml
+pimcore:
+    assets:
+        thumbnails:
+            allowed_formats:
+                - 'avif'
+                - 'eps'
+                - 'gif'
+                - 'jpeg'
+                - 'jpg'
+                - 'pjpeg'
+                - 'png'
+                - 'svg'
+                - 'tiff'
+                - 'webm'
+                - 'webp'
+                - 'pdf' # Add your desired format here
+```
 
 ##### Thumbnails are available for following file types: 
 * [Image Thumbnails](./01_Image_Thumbnails.md)
 * [Video Thumbnails](./03_Video_Thumbnails.md)
 * [Asset Document Thumbnails](./05_Document_Thumbnails.md)
diff --git a/doc/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md b/doc/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md
index 2e57d5882a54..ec8f12199e7c 100644
--- a/doc/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md
+++ b/doc/23_Installation_and_Upgrade/09_Upgrade_Notes/README.md
@@ -1,7 +1,37 @@
 # Upgrade Notes
 
+## Pimcore 11.2.4
+### Assets Thumbnails
+- Thumbnail generation for Assets, Documents and Videos now only support the following formats out of the box: `'avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'`.
+- You can extend this list by adding your formats on the bottom: 
+```yaml
+  pimcore:
+    assets:
+      thumbnails:
+        allowed_formats:
+          - 'avif'
+          - 'eps'
+          - 'gif'
+          - 'jpeg'
+          - 'jpg'
+          - 'pjpeg'
+          - 'png'
+          - 'svg'
+          - 'tiff'
+          - 'webm'
+          - 'webp'
+          - 'pdf' # Add your desired format here
+```
+- High resolution scaling factor for image thumbnails has now been limited to a maximum of `5.0`. If you need to scale an image more than that, you can use the `max_scaling_factor` option in the configuration.
+```yaml
+  pimcore:
+    assets:
+      thumbnails:
+        max_scaling_factor: 6.0
+```
+
 ## Pimcore 11.2.0
 ### Elements
 #### [Documents]:
 - Using `outputFormat` config for `Pimcore\Model\Document\Editable\Date` editable is deprecated, use `outputIsoFormat` config instead.
 - Service `Pimcore\Document\Renderer\DocumentRenderer` is deprecated, use `Pimcore\Document\Renderer\DocumentRendererInterface` instead.
diff --git a/models/Asset/Document/ImageThumbnail.php b/models/Asset/Document/ImageThumbnail.php
index 5b81dac7f81e..d9b4b7f00061 100644
--- a/models/Asset/Document/ImageThumbnail.php
+++ b/models/Asset/Document/ImageThumbnail.php
@@ -22,10 +22,11 @@ use Pimcore\File;
 use Pimcore\Helper\TemporaryFileHelperTrait;
 use Pimcore\Logger;
 use Pimcore\Model;
 use Pimcore\Model\Asset\Image;
 use Pimcore\Model\Exception\NotFoundException;
+use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
 use Pimcore\Tool\Storage;
 use Symfony\Component\EventDispatcher\GenericEvent;
 use Symfony\Component\Lock\LockFactory;
 
 /**
@@ -68,12 +69,19 @@ final class ImageThumbnail implements ImageThumbnailInterface
         $path = $event->getArgument('frontendPath');
 
         return $path;
     }
 
+    /**
+     * @throws ThumbnailFormatNotSupportedException
+     */
     public function generate(bool $deferredAllowed = true): void
     {
+        if (!$this->checkAllowedFormats($this->config->getFormat())) {
+            throw new ThumbnailFormatNotSupportedException();
+        }
+
         $deferred = $deferredAllowed && $this->deferred;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
             $config = $this->getConfig();
diff --git a/models/Asset/Image/Thumbnail.php b/models/Asset/Image/Thumbnail.php
index b679ebfda85e..d609d39e8c1b 100644
--- a/models/Asset/Image/Thumbnail.php
+++ b/models/Asset/Image/Thumbnail.php
@@ -22,10 +22,12 @@ use Pimcore\Logger;
 use Pimcore\Model\Asset;
 use Pimcore\Model\Asset\Image;
 use Pimcore\Model\Asset\Image\Thumbnail\Config;
 use Pimcore\Model\Asset\Thumbnail\ImageThumbnailTrait;
 use Pimcore\Model\Exception\NotFoundException;
+use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
+use Pimcore\Model\Exception\ThumbnailMaxScalingFactorException;
 use Pimcore\Tool;
 use Symfony\Component\EventDispatcher\GenericEvent;
 
 final class Thumbnail implements ThumbnailInterface
 {
@@ -101,14 +103,24 @@ final class Thumbnail implements ThumbnailInterface
     {
         return $this->getConfig() && preg_match("@\.svgz?$@", $filename) && !$this->getConfig()->isRasterizeSVG();
     }
 
     /**
+     * @throws ThumbnailFormatNotSupportedException
+     * @throws ThumbnailMaxScalingFactorException
      * @internal
      */
     public function generate(bool $deferredAllowed = true): void
     {
+        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
+            throw new ThumbnailFormatNotSupportedException();
+        }
+
+        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
+            throw new ThumbnailMaxScalingFactorException();
+        }
+
         $deferred = false;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
             // if no correct thumbnail config is given use the original image as thumbnail
diff --git a/models/Asset/Thumbnail/ImageThumbnailTrait.php b/models/Asset/Thumbnail/ImageThumbnailTrait.php
index fcdb14cd502d..9ed6b9e6bd74 100644
--- a/models/Asset/Thumbnail/ImageThumbnailTrait.php
+++ b/models/Asset/Thumbnail/ImageThumbnailTrait.php
@@ -14,10 +14,11 @@ declare(strict_types=1);
  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  */
 
 namespace Pimcore\Model\Asset\Thumbnail;
 
+use Pimcore\Config as PimcoreConfig;
 use Pimcore\Helper\TemporaryFileHelperTrait;
 use Pimcore\Model\Asset;
 use Pimcore\Model\Asset\Image;
 use Pimcore\Model\Asset\Image\Thumbnail\Config;
 use Pimcore\Tool;
@@ -426,6 +427,30 @@ trait ImageThumbnailTrait
         $thumb->config = $config;
         $thumb->reset();
 
         return $thumb;
     }
+
+    private function checkAllowedFormats(string $format, ?Asset $asset = null): bool
+    {
+        $format = strtolower($format);
+        if($asset) {
+            $original = pathinfo($asset->getRealFullPath(),  PATHINFO_EXTENSION);
+            if ($format === $original || $format === 'source') {
+                return true;
+            }
+        }
+
+        $assetConfig = PimcoreConfig::getSystemConfiguration('assets');
+        return in_array(
+            $format,
+            $assetConfig['thumbnails']['allowed_formats'],
+            true
+        );
+    }
+
+    private function checkMaxScalingFactor(float $scalingFactor): bool
+    {
+        $assetConfig = PimcoreConfig::getSystemConfiguration('assets');
+        return $scalingFactor <= $assetConfig['thumbnails']['max_scaling_factor'];
+    }
 }
diff --git a/models/Asset/Video/ImageThumbnail.php b/models/Asset/Video/ImageThumbnail.php
index 129b97288ad7..992aaca917ab 100644
--- a/models/Asset/Video/ImageThumbnail.php
+++ b/models/Asset/Video/ImageThumbnail.php
@@ -14,16 +14,18 @@ declare(strict_types=1);
  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  */
 
 namespace Pimcore\Model\Asset\Video;
 
+use Exception;
 use Pimcore\Event\AssetEvents;
 use Pimcore\Event\FrontendEvents;
 use Pimcore\File;
 use Pimcore\Logger;
 use Pimcore\Model;
 use Pimcore\Model\Asset\Image;
+use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
 use Pimcore\Tool\Storage;
 use Symfony\Component\EventDispatcher\GenericEvent;
 use Symfony\Component\Lock\LockFactory;
 
 /**
@@ -73,16 +75,20 @@ final class ImageThumbnail implements ImageThumbnailInterface
 
         return $path;
     }
 
     /**
-     * @throws \Exception
+     * @throws Exception
      *
      * @internal
      */
     public function generate(bool $deferredAllowed = true): void
     {
+        if (!$this->checkAllowedFormats($this->config->getFormat())) {
+            throw new ThumbnailFormatNotSupportedException();
+        }
+
         $deferred = $deferredAllowed && $this->deferred;
         $generated = false;
 
         if ($this->asset && empty($this->pathReference)) {
             $cs = $this->asset->getCustomSetting('image_thumbnail_time');
diff --git a/models/Exception/ThumbnailFormatNotSupportedException.php b/models/Exception/ThumbnailFormatNotSupportedException.php
new file mode 100644
index 000000000000..542df8f7389b
--- /dev/null
+++ b/models/Exception/ThumbnailFormatNotSupportedException.php
@@ -0,0 +1,23 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Pimcore
+ *
+ * This source file is available under two different licenses:
+ * - GNU General Public License version 3 (GPLv3)
+ * - Pimcore Commercial License (PCL)
+ * Full copyright and license information is available in
+ * LICENSE.md which is distributed with this source code.
+ *
+ *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
+ *  @license    http://www.pimcore.org/license     GPLv3 and PCL
+ */
+
+namespace Pimcore\Model\Exception;
+
+use Exception;
+
+final class ThumbnailFormatNotSupportedException extends Exception
+{
+}
diff --git a/models/Exception/ThumbnailMaxScalingFactorException.php b/models/Exception/ThumbnailMaxScalingFactorException.php
new file mode 100644
index 000000000000..5e0858b930b8
--- /dev/null
+++ b/models/Exception/ThumbnailMaxScalingFactorException.php
@@ -0,0 +1,23 @@
+<?php
+declare(strict_types=1);
+
+/**
+ * Pimcore
+ *
+ * This source file is available under two different licenses:
+ * - GNU General Public License version 3 (GPLv3)
+ * - Pimcore Commercial License (PCL)
+ * Full copyright and license information is available in
+ * LICENSE.md which is distributed with this source code.
+ *
+ *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
+ *  @license    http://www.pimcore.org/license     GPLv3 and PCL
+ */
+
+namespace Pimcore\Model\Exception;
+
+use Exception;
+
+final class ThumbnailMaxScalingFactorException extends Exception
+{
+}

Vulnerable code: > Using ImageMagick Pimcore supports hundreds of formats including: AI, EPS, TIFF, PNG, JPG, GIF, PSD, ...
This functionality allows true single source publishing with Pimcore. 
     * @throws \Exception
Fixed code:                     ->arrayNode('thumbnails')
                    ->addDefaultsIfNotSet()
                        ->children()
                            ->arrayNode('allowed_formats')
                                ->defaultValue(['avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'])
                                ->scalarPrototype()->end()
                            ->end()
                            ->floatNode('max_scaling_factor')
                                ->defaultValue(5.0)
                            ->end()
                        ->end()
                    ->end()
> Using ImageMagick Pimcore can support hundreds of formats including: AI, EPS, TIFF, PNG, JPG, GIF, PSD, etc.
> Not all formats are allowed out of the box. To extend the list [see](./README.md#allowed-formats).
The high resolution scaling factor is limited to `5.0` eg. `@5x`. Float values are supported.
If you need to scale an image more than that, you can use the `max_scaling_factor` option in the configuration.
```yaml
  pimcore:
    assets:
      thumbnails:
        max_scaling_factor: 6.0
```

This functionality allows true single source publishing with Pimcore.

### Allowed formats
Pimcore allows the following formats for thumbnails out of the box:
`'avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'`.

If you want to use a different format, you can easily extend the list of supported formats.
Keep in mind that you must copy the whole list of formats and add your desired format to it.
```yaml
pimcore:
    assets:
        thumbnails:
            allowed_formats:
                - 'avif'
                - 'eps'
                - 'gif'
                - 'jpeg'
                - 'jpg'
                - 'pjpeg'
                - 'png'
                - 'svg'
                - 'tiff'
                - 'webm'
                - 'webp'
                - 'pdf' # Add your desired format here
```
## Pimcore 11.2.4
### Assets Thumbnails
- Thumbnail generation for Assets, Documents and Videos now only support the following formats out of the box: `'avif', 'eps', 'gif', 'jpeg', 'jpg', 'pjpeg', 'png', 'svg', 'tiff', 'webm', 'webp'`.
- You can extend this list by adding your formats on the bottom: 
```yaml
  pimcore:
    assets:
      thumbnails:
        allowed_formats:
          - 'avif'
          - 'eps'
          - 'gif'
          - 'jpeg'
          - 'jpg'
          - 'pjpeg'
          - 'png'
          - 'svg'
          - 'tiff'
          - 'webm'
          - 'webp'
          - 'pdf' # Add your desired format here
```
- High resolution scaling factor for image thumbnails has now been limited to a maximum of `5.0`. If you need to scale an image more than that, you can use the `max_scaling_factor` option in the configuration.
```yaml
  pimcore:
    assets:
      thumbnails:
        max_scaling_factor: 6.0
```

use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
    /**
     * @throws ThumbnailFormatNotSupportedException
     */
        if (!$this->checkAllowedFormats($this->config->getFormat())) {
            throw new ThumbnailFormatNotSupportedException();
        }

use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
use Pimcore\Model\Exception\ThumbnailMaxScalingFactorException;
     * @throws ThumbnailFormatNotSupportedException
     * @throws ThumbnailMaxScalingFactorException
        if (!$this->checkAllowedFormats($this->config->getFormat(), $this->asset)) {
            throw new ThumbnailFormatNotSupportedException();
        }

        if (!$this->checkMaxScalingFactor($this->config->getHighResolution())) {
            throw new ThumbnailMaxScalingFactorException();
        }

use Pimcore\Config as PimcoreConfig;

    private function checkAllowedFormats(string $format, ?Asset $asset = null): bool
    {
        $format = strtolower($format);
        if($asset) {
            $original = pathinfo($asset->getRealFullPath(),  PATHINFO_EXTENSION);
            if ($format === $original || $format === 'source') {
                return true;
            }
        }

        $assetConfig = PimcoreConfig::getSystemConfiguration('assets');
        return in_array(
            $format,
            $assetConfig['thumbnails']['allowed_formats'],
            true
        );
    }

    private function checkMaxScalingFactor(float $scalingFactor): bool
    {
        $assetConfig = PimcoreConfig::getSystemConfiguration('assets');
        return $scalingFactor <= $assetConfig['thumbnails']['max_scaling_factor'];
    }
use Exception;
use Pimcore\Model\Exception\ThumbnailFormatNotSupportedException;
     * @throws Exception
        if (!$this->checkAllowedFormats($this->config->getFormat())) {
            throw new ThumbnailFormatNotSupportedException();
        }

<?php
declare(strict_types=1);

/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Commercial License (PCL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
 *  @license    http://www.pimcore.org/license     GPLv3 and PCL
 */

namespace Pimcore\Model\Exception;

use Exception;

final class ThumbnailFormatNotSupportedException extends Exception
{
}
<?php
declare(strict_types=1);

/**
 * Pimcore
 *
 * This source file is available under two different licenses:
 * - GNU General Public License version 3 (GPLv3)
 * - Pimcore Commercial License (PCL)
 * Full copyright and license information is available in
 * LICENSE.md which is distributed with this source code.
 *
 *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
 *  @license    http://www.pimcore.org/license     GPLv3 and PCL
 */

namespace Pimcore\Model\Exception;

use Exception;

final class ThumbnailMaxScalingFactorException extends Exception
{
}