Browse Source

Src file size check

DarthSim 6 years ago
parent
commit
0027db5f8a
3 changed files with 41 additions and 4 deletions
  1. 6 0
      config.go
  2. 1 0
      docs/configuration.md
  3. 34 4
      download.go

+ 6 - 0
config.go

@@ -140,6 +140,7 @@ type config struct {
 
 
 	MaxSrcDimension  int
 	MaxSrcDimension  int
 	MaxSrcResolution int
 	MaxSrcResolution int
+	MaxSrcFileSize   int
 	MaxGifFrames     int
 	MaxGifFrames     int
 
 
 	JpegProgressive bool
 	JpegProgressive bool
@@ -253,6 +254,7 @@ func init() {
 
 
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	intEnvConfig(&conf.MaxSrcDimension, "IMGPROXY_MAX_SRC_DIMENSION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
 	megaIntEnvConfig(&conf.MaxSrcResolution, "IMGPROXY_MAX_SRC_RESOLUTION")
+	intEnvConfig(&conf.MaxSrcFileSize, "IMGPROXY_MAX_SRC_FILE_SIZE")
 	intEnvConfig(&conf.MaxGifFrames, "IMGPROXY_MAX_GIF_FRAMES")
 	intEnvConfig(&conf.MaxGifFrames, "IMGPROXY_MAX_GIF_FRAMES")
 
 
 	boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
 	boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE")
@@ -371,6 +373,10 @@ func init() {
 		logFatal("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
 		logFatal("Max src resolution should be greater than 0, now - %d\n", conf.MaxSrcResolution)
 	}
 	}
 
 
+	if conf.MaxSrcFileSize < 0 {
+		logFatal("Max src file size should be greater than or equal to 0, now - %d\n", conf.MaxSrcFileSize)
+	}
+
 	if conf.MaxGifFrames <= 0 {
 	if conf.MaxGifFrames <= 0 {
 		logFatal("Max GIF frames should be greater than 0, now - %d\n", conf.MaxGifFrames)
 		logFatal("Max GIF frames should be greater than 0, now - %d\n", conf.MaxGifFrames)
 	}
 	}

+ 1 - 0
docs/configuration.md

@@ -41,6 +41,7 @@ $ echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
 imgproxy protects you from so-called image bombs. Here is how you can specify maximum image resolution which you consider reasonable:
 imgproxy protects you from so-called image bombs. Here is how you can specify 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`;
+* `IMGPROXY_MAX_SRC_FILE_SIZE`: the maximum size of the source image, in bytes. Images with larger file size will be rejected. When `0`, file size check is disabled. Default: `0`;
 
 
 imgproxy can process animated GIFs, but since this operation is pretty heavy, only one frame is processed by default. You can increase the maximum of GIF frames to process with the following variable:
 imgproxy can process animated GIFs, but since this operation is pretty heavy, only one frame is processed by default. You can increase the maximum of GIF frames to process with the following variable:
 
 

+ 34 - 4
download.go

@@ -25,7 +25,8 @@ var (
 	imageDataCtxKey = ctxKey("imageData")
 	imageDataCtxKey = ctxKey("imageData")
 
 
 	errSourceDimensionsTooBig      = newError(422, "Source image dimensions are too big", "Invalid source image")
 	errSourceDimensionsTooBig      = newError(422, "Source image dimensions are too big", "Invalid source image")
-	errSourceResolutionTooBig      = newError(422, "Source image resolution are too big", "Invalid source image")
+	errSourceResolutionTooBig      = newError(422, "Source image resolution is too big", "Invalid source image")
+	errSourceFileTooBig            = newError(422, "Source image file is too big", "Invalid source image")
 	errSourceImageTypeNotSupported = newError(422, "Source image type not supported", "Invalid source image")
 	errSourceImageTypeNotSupported = newError(422, "Source image type not supported", "Invalid source image")
 )
 )
 
 
@@ -33,6 +34,26 @@ const msgSourceImageIsUnreachable = "Source image is unreachable"
 
 
 var downloadBufPool *bufPool
 var downloadBufPool *bufPool
 
 
+type limitReader struct {
+	r    io.ReadCloser
+	left int
+}
+
+func (lr *limitReader) Read(p []byte) (n int, err error) {
+	n, err = lr.r.Read(p)
+	lr.left = lr.left - n
+
+	if err == nil && lr.left < 0 {
+		err = errSourceFileTooBig
+	}
+
+	return
+}
+
+func (lr *limitReader) Close() error {
+	return lr.r.Close()
+}
+
 func initDownloading() {
 func initDownloading() {
 	transport := &http.Transport{
 	transport := &http.Transport{
 		Proxy: http.ProxyFromEnvironment,
 		Proxy: http.ProxyFromEnvironment,
@@ -76,9 +97,12 @@ func checkDimensions(width, height int) error {
 
 
 func checkTypeAndDimensions(r io.Reader) (imageType, error) {
 func checkTypeAndDimensions(r io.Reader) (imageType, error) {
 	imgconf, imgtypeStr, err := image.DecodeConfig(r)
 	imgconf, imgtypeStr, err := image.DecodeConfig(r)
-	if err != nil {
+	if err == image.ErrFormat {
 		return imageTypeUnknown, errSourceImageTypeNotSupported
 		return imageTypeUnknown, errSourceImageTypeNotSupported
 	}
 	}
+	if err != nil {
+		return imageTypeUnknown, err
+	}
 
 
 	imgtype, imgtypeOk := imageTypes[imgtypeStr]
 	imgtype, imgtypeOk := imageTypes[imgtypeStr]
 	if !imgtypeOk || !vipsTypeSupportLoad[imgtype] {
 	if !imgtypeOk || !vipsTypeSupportLoad[imgtype] {
@@ -98,7 +122,13 @@ func readAndCheckImage(ctx context.Context, res *http.Response) (context.Context
 		downloadBufPool.put(buf)
 		downloadBufPool.put(buf)
 	}
 	}
 
 
-	imgtype, err := checkTypeAndDimensions(io.TeeReader(res.Body, buf))
+	body := res.Body
+
+	if conf.MaxSrcFileSize > 0 {
+		body = &limitReader{r: body, left: conf.MaxSrcFileSize}
+	}
+
+	imgtype, err := checkTypeAndDimensions(io.TeeReader(body, buf))
 	if err != nil {
 	if err != nil {
 		return ctx, cancel, err
 		return ctx, cancel, err
 	}
 	}
@@ -116,7 +146,7 @@ func readAndCheckImage(ctx context.Context, res *http.Response) (context.Context
 		buf.Grow(contentLength - buf.Len())
 		buf.Grow(contentLength - buf.Len())
 	}
 	}
 
 
-	if _, err = buf.ReadFrom(res.Body); err != nil {
+	if _, err = buf.ReadFrom(body); err != nil {
 		return ctx, cancel, newError(404, err.Error(), msgSourceImageIsUnreachable)
 		return ctx, cancel, newError(404, err.Error(), msgSourceImageIsUnreachable)
 	}
 	}