Browse Source

Add IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION config

DarthSim 2 years ago
parent
commit
1ededbb11f
6 changed files with 30 additions and 12 deletions
  1. 3 0
      CHANGELOG.md
  2. 8 5
      config/config.go
  3. 4 2
      docs/configuration.md
  4. 1 1
      imagedata/read.go
  5. 1 1
      processing/processing.go
  6. 13 3
      security/image_size.go

+ 3 - 0
CHANGELOG.md

@@ -1,6 +1,9 @@
 # Changelog
 # Changelog
 
 
 ## [Unreleased]
 ## [Unreleased]
+### Add
+- Add `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION` config.
+
 ### Change
 ### Change
 - Change `IMGPROXY_FORMAT_QUALITY` default value to `avif=65`.
 - Change `IMGPROXY_FORMAT_QUALITY` default value to `avif=65`.
 - Change `IMGPROXY_AVIF_SPEED` default value to `8`.
 - Change `IMGPROXY_AVIF_SPEED` default value to `8`.

+ 8 - 5
config/config.go

@@ -35,11 +35,12 @@ var (
 
 
 	PathPrefix string
 	PathPrefix string
 
 
-	MaxSrcResolution   int
-	MaxSrcFileSize     int
-	MaxAnimationFrames int
-	MaxSvgCheckBytes   int
-	MaxRedirects       int
+	MaxSrcResolution            int
+	MaxSrcFileSize              int
+	MaxAnimationFrames          int
+	MaxAnimationFrameResolution int
+	MaxSvgCheckBytes            int
+	MaxRedirects                int
 
 
 	JpegProgressive       bool
 	JpegProgressive       bool
 	PngInterlaced         bool
 	PngInterlaced         bool
@@ -217,6 +218,7 @@ func Reset() {
 	MaxSrcResolution = 16800000
 	MaxSrcResolution = 16800000
 	MaxSrcFileSize = 0
 	MaxSrcFileSize = 0
 	MaxAnimationFrames = 1
 	MaxAnimationFrames = 1
+	MaxAnimationFrameResolution = 0
 	MaxSvgCheckBytes = 32 * 1024
 	MaxSvgCheckBytes = 32 * 1024
 	MaxRedirects = 10
 	MaxRedirects = 10
 
 
@@ -386,6 +388,7 @@ func Configure() error {
 	configurators.Int(&MaxSvgCheckBytes, "IMGPROXY_MAX_SVG_CHECK_BYTES")
 	configurators.Int(&MaxSvgCheckBytes, "IMGPROXY_MAX_SVG_CHECK_BYTES")
 
 
 	configurators.Int(&MaxAnimationFrames, "IMGPROXY_MAX_ANIMATION_FRAMES")
 	configurators.Int(&MaxAnimationFrames, "IMGPROXY_MAX_ANIMATION_FRAMES")
+	configurators.MegaInt(&MaxAnimationFrameResolution, "IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION")
 
 
 	configurators.Int(&MaxRedirects, "IMGPROXY_MAX_REDIRECTS")
 	configurators.Int(&MaxRedirects, "IMGPROXY_MAX_REDIRECTS")
 
 

+ 4 - 2
docs/configuration.md

@@ -60,13 +60,15 @@ echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
 imgproxy protects you from so-called image bombs. Here's how you can specify the maximum image resolution which you consider reasonable:
 imgproxy protects you from so-called image bombs. Here's how you can specify the maximum image resolution which you consider reasonable:
 
 
 * `IMGPROXY_MAX_SRC_RESOLUTION`: the maximum resolution of the source image, in megapixels. Images with larger actual size will be rejected. Default: `16.8`
 * `IMGPROXY_MAX_SRC_RESOLUTION`: the maximum resolution of the source image, in megapixels. Images with larger actual size will be rejected. Default: `16.8`
+
+**⚠️Warning:** When the source image is animated, imgproxy summarizes all its frames' resolutions while checking the source image resolution unless `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION` is greater than zero.
+
 * `IMGPROXY_MAX_SRC_FILE_SIZE`: the maximum size of the source image, in bytes. Images with larger file size will be rejected. When set to `0`, file size check is disabled. Default: `0`
 * `IMGPROXY_MAX_SRC_FILE_SIZE`: the maximum size of the source image, in bytes. Images with larger file size will be rejected. When set to `0`, file size check is disabled. Default: `0`
 
 
 imgproxy can process animated images (GIF, WebP), but since this operation is pretty memory heavy, only one frame is processed by default. You can increase the maximum animation frames that can be processed number of with the following variable:
 imgproxy can process animated images (GIF, WebP), but since this operation is pretty memory heavy, only one frame is processed by default. You can increase the maximum animation frames that can be processed number of with the following variable:
 
 
 * `IMGPROXY_MAX_ANIMATION_FRAMES`: the maximum number of animated image frames that may be processed. Default: `1`
 * `IMGPROXY_MAX_ANIMATION_FRAMES`: the maximum number of animated image frames that may be processed. Default: `1`
-
-**📝Note:** imgproxy summarizes all frame resolutions while checking the source image resolution.
+* `IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION`: the maximum resolution of the animated source image frame, in megapixels. Images with larger actual frame size will be rejected. When set to `0`, imgproxy will test the whole animated image resolution against `IMGPROXY_MAX_SRC_RESOLUTION` summarising all the frames' resolutions. Default: `0`
 
 
 To check if the source image is SVG, imgproxy reads some amount of bytes; by default it reads a maximum of 32KB. However, you can change this value using the following variable:
 To check if the source image is SVG, imgproxy reads some amount of bytes; by default it reads a maximum of 32KB. However, you can change this value using the following variable:
 
 

+ 1 - 1
imagedata/read.go

@@ -67,7 +67,7 @@ func readAndCheckImage(r io.Reader, contentLength int) (*ImageData, error) {
 		return nil, checkTimeoutErr(err)
 		return nil, checkTimeoutErr(err)
 	}
 	}
 
 
-	if err = security.CheckDimensions(meta.Width(), meta.Height()); err != nil {
+	if err = security.CheckDimensions(meta.Width(), meta.Height(), 1); err != nil {
 		buf.Reset()
 		buf.Reset()
 		cancel()
 		cancel()
 		return nil, err
 		return nil, err

+ 1 - 1
processing/processing.go

@@ -119,7 +119,7 @@ func transformAnimated(ctx context.Context, img *vips.Image, po *options.Process
 	framesCount := imath.Min(img.Height()/frameHeight, config.MaxAnimationFrames)
 	framesCount := imath.Min(img.Height()/frameHeight, config.MaxAnimationFrames)
 
 
 	// Double check dimensions because animated image has many frames
 	// Double check dimensions because animated image has many frames
-	if err = security.CheckDimensions(imgWidth, frameHeight*framesCount); err != nil {
+	if err = security.CheckDimensions(imgWidth, frameHeight, framesCount); err != nil {
 		return err
 		return err
 	}
 	}
 
 

+ 13 - 3
security/image_size.go

@@ -3,13 +3,23 @@ package security
 import (
 import (
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/config"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
 	"github.com/imgproxy/imgproxy/v3/ierrors"
+	"github.com/imgproxy/imgproxy/v3/imath"
 )
 )
 
 
 var ErrSourceResolutionTooBig = ierrors.New(422, "Source image resolution is too big", "Invalid source image")
 var ErrSourceResolutionTooBig = ierrors.New(422, "Source image resolution is too big", "Invalid source image")
+var ErrSourceFrameResolutionTooBig = ierrors.New(422, "Source image frame resolution is too big", "Invalid source image")
 
 
-func CheckDimensions(width, height int) error {
-	if width*height > config.MaxSrcResolution {
-		return ErrSourceResolutionTooBig
+func CheckDimensions(width, height, frames int) error {
+	frames = imath.Max(frames, 1)
+
+	if frames > 1 && config.MaxAnimationFrameResolution > 0 {
+		if width*height > config.MaxAnimationFrameResolution {
+			return ErrSourceFrameResolutionTooBig
+		}
+	} else {
+		if width*height*frames > config.MaxSrcResolution {
+			return ErrSourceResolutionTooBig
+		}
 	}
 	}
 
 
 	return nil
 	return nil