diff --git a/handlers/processing/handler.go b/handlers/processing/handler.go index d8faf858..a0dea04b 100644 --- a/handlers/processing/handler.go +++ b/handlers/processing/handler.go @@ -88,7 +88,6 @@ func (h *Handler) Execute( r.req = req r.rw = rw r.config = h.config - r.secops = h.Security().NewOptions(r.opts) return r.execute(ctx) } diff --git a/handlers/processing/request.go b/handlers/processing/request.go index bfb26503..d4b6d709 100644 --- a/handlers/processing/request.go +++ b/handlers/processing/request.go @@ -13,7 +13,6 @@ import ( "github.com/imgproxy/imgproxy/v3/monitoring" "github.com/imgproxy/imgproxy/v3/options" "github.com/imgproxy/imgproxy/v3/options/keys" - "github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/server" "github.com/imgproxy/imgproxy/v3/vips" ) @@ -27,7 +26,6 @@ type request struct { rw server.ResponseWriter config *Config opts *options.Options - secops security.Options imageURL string path string monitoringMeta monitoring.Meta diff --git a/handlers/processing/request_methods.go b/handlers/processing/request_methods.go index f1d8574a..8edaa084 100644 --- a/handlers/processing/request_methods.go +++ b/handlers/processing/request_methods.go @@ -70,7 +70,7 @@ func (r *request) makeDownloadOptions( return imagedata.DownloadOptions{ Header: h, - MaxSrcFileSize: r.secops.MaxSrcFileSize, + MaxSrcFileSize: r.Security().MaxSrcFileSize(r.opts), CookieJar: jar, }, nil } @@ -160,7 +160,7 @@ func (r *request) getFallbackImage(ctx context.Context) (imagedata.ImageData, ht // processImage calls actual image processing func (r *request) processImage(ctx context.Context, originData imagedata.ImageData) (*processing.Result, error) { defer r.Monitoring().StartProcessingSegment(ctx, r.monitoringMeta.Filter(monitoring.MetaOptions))() - return r.Processor().ProcessImage(ctx, originData, r.opts, r.secops) + return r.Processor().ProcessImage(ctx, originData, r.opts) } // writeDebugHeaders writes debug headers (X-Origin-*, X-Result-*) to the response diff --git a/imgproxy.go b/imgproxy.go index 70a5525a..853dc4e7 100644 --- a/imgproxy.go +++ b/imgproxy.go @@ -47,7 +47,7 @@ type Imgproxy struct { imageDataFactory *imagedata.Factory clientFeaturesDetector *clientfeatures.Detector handlers ImgproxyHandlers - security *security.Checker + securityChecker *security.Checker optionsParser *optionsparser.Parser processor *processing.Processor cookies *cookies.Cookies @@ -62,6 +62,11 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) { return nil, err } + securityChecker, err := security.New(&config.Security) + if err != nil { + return nil, err + } + fetcher, err := fetcher.New(&config.Fetcher) if err != nil { return nil, err @@ -86,12 +91,7 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) { return nil, err } - security, err := security.New(&config.Security) - if err != nil { - return nil, err - } - - processor, err := processing.New(&config.Processing, watermarkImage) + processor, err := processing.New(&config.Processing, securityChecker, watermarkImage) if err != nil { return nil, err } @@ -124,7 +124,7 @@ func New(ctx context.Context, config *Config) (*Imgproxy, error) { imageDataFactory: idf, clientFeaturesDetector: clientFeaturesDetector, config: config, - security: security, + securityChecker: securityChecker, optionsParser: optionsParser, processor: processor, cookies: cookies, @@ -262,7 +262,7 @@ func (i *Imgproxy) ClientFeaturesDetector() *clientfeatures.Detector { } func (i *Imgproxy) Security() *security.Checker { - return i.security + return i.securityChecker } func (i *Imgproxy) OptionsParser() *optionsparser.Parser { diff --git a/integration/matrix_test.go b/integration/matrix_test.go index 8abceb87..f79ea5a9 100644 --- a/integration/matrix_test.go +++ b/integration/matrix_test.go @@ -44,7 +44,7 @@ func (s *MatrixTestSuite) SetupTest() { s.testImagesPath = s.TestData.Path("test-images") s.matcher = testutil.NewImageHashMatcher(s.TestData) - s.Config().Security.DefaultOptions.MaxAnimationFrames = 999 + s.Config().Security.MaxAnimationFrames = 999 s.Config().Server.DevelopmentErrorsMode = true s.Config().Fetcher.Transport.Local.Root = s.testImagesPath } diff --git a/integration/processing_handler_test.go b/integration/processing_handler_test.go index cc67a4ce..a832e692 100644 --- a/integration/processing_handler_test.go +++ b/integration/processing_handler_test.go @@ -9,7 +9,6 @@ import ( "time" "github.com/imgproxy/imgproxy/v3/config/configurators" - "github.com/imgproxy/imgproxy/v3/fetcher" "github.com/imgproxy/imgproxy/v3/httpheaders" "github.com/imgproxy/imgproxy/v3/imagedata" "github.com/imgproxy/imgproxy/v3/imagetype" @@ -205,14 +204,7 @@ func (s *ProcessingHandlerTestSuite) TestSkipProcessingSVG() { s.Require().Equal(http.StatusOK, res.StatusCode) - c := fetcher.NewDefaultConfig() - f, err := fetcher.New(&c) - s.Require().NoError(err) - - idf := imagedata.NewFactory(f) - - data, err := idf.NewFromBytes(s.TestData.Read("test1.svg")) - s.Require().NoError(err) + data := imagedata.NewFromBytesWithFormat(imagetype.SVG, s.TestData.Read("test1.svg")) cfg := svg.NewDefaultConfig() svg := svg.New(&cfg) @@ -508,7 +500,7 @@ func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithFormat() { } func (s *ProcessingHandlerTestSuite) TestMaxSrcFileSizeGlobal() { - s.Config().Security.DefaultOptions.MaxSrcFileSize = 1 + s.Config().Security.MaxSrcFileSize = 1 ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(200) diff --git a/processing/options.go b/processing/options.go index 1ea637c0..986735b4 100644 --- a/processing/options.go +++ b/processing/options.go @@ -6,6 +6,7 @@ import ( "github.com/imgproxy/imgproxy/v3/imagetype" "github.com/imgproxy/imgproxy/v3/options" "github.com/imgproxy/imgproxy/v3/options/keys" + "github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/vips/color" ) @@ -16,12 +17,16 @@ type ProcessingOptions struct { // Config that contains default values for some options. config *Config + + // Security checker to obtain security-related limits. + securityChecker *security.Checker } func (p *Processor) NewProcessingOptions(o *options.Options) ProcessingOptions { return ProcessingOptions{ - Options: o, - config: p.config, + Options: o, + config: p.config, + securityChecker: p.securityChecker, } } @@ -287,3 +292,15 @@ func (po ProcessingOptions) KeepCopyright() bool { func (po ProcessingOptions) StripColorProfile() bool { return po.Main().GetBool(keys.StripColorProfile, po.config.StripColorProfile) } + +func (po ProcessingOptions) MaxSrcResolution() int { + return po.securityChecker.MaxSrcResolution(po.Main()) +} + +func (po ProcessingOptions) MaxAnimationFrames() int { + return po.securityChecker.MaxAnimationFrames(po.Main()) +} + +func (po ProcessingOptions) MaxResultDimension() int { + return po.securityChecker.MaxResultDimension(po.Main()) +} diff --git a/processing/pipeline.go b/processing/pipeline.go index 0a0219fc..420e27c9 100644 --- a/processing/pipeline.go +++ b/processing/pipeline.go @@ -4,7 +4,6 @@ import ( "context" "github.com/imgproxy/imgproxy/v3/imagedata" - "github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/server" "github.com/imgproxy/imgproxy/v3/vips" ) @@ -19,9 +18,6 @@ type Context struct { // Processing options this pipeline runs with PO ProcessingOptions - // Security options this pipeline runs with - SecOps security.Options - // Original image data ImgData imagedata.ImageData @@ -80,10 +76,9 @@ func (p Pipeline) Run( ctx context.Context, img *vips.Image, po ProcessingOptions, - secops security.Options, imgdata imagedata.ImageData, ) error { - pctx := p.newContext(ctx, img, po, secops, imgdata) + pctx := p.newContext(ctx, img, po, imgdata) pctx.CalcParams() for _, step := range p { @@ -105,14 +100,12 @@ func (p Pipeline) newContext( ctx context.Context, img *vips.Image, po ProcessingOptions, - secops security.Options, imgdata imagedata.ImageData, ) Context { pctx := Context{ Ctx: ctx, Img: img, PO: po, - SecOps: secops, ImgData: imgdata, WScale: 1.0, diff --git a/processing/prepare.go b/processing/prepare.go index d01e448f..dd746ae5 100644 --- a/processing/prepare.go +++ b/processing/prepare.go @@ -221,7 +221,7 @@ func (c *Context) calcSizes(widthToScale, heightToScale int, po ProcessingOption } func (c *Context) limitScale(widthToScale, heightToScale int, po ProcessingOptions) { - maxresultDim := c.SecOps.MaxResultDimension + maxresultDim := c.PO.MaxResultDimension() if maxresultDim <= 0 { return diff --git a/processing/processing.go b/processing/processing.go index db069a23..ca568204 100644 --- a/processing/processing.go +++ b/processing/processing.go @@ -10,7 +10,6 @@ import ( "github.com/imgproxy/imgproxy/v3/imagedata" "github.com/imgproxy/imgproxy/v3/imagetype" "github.com/imgproxy/imgproxy/v3/options" - "github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/server" "github.com/imgproxy/imgproxy/v3/vips" ) @@ -63,7 +62,6 @@ func (p *Processor) ProcessImage( ctx context.Context, imgdata imagedata.ImageData, o *options.Options, - secops security.Options, ) (*Result, error) { runtime.LockOSThread() defer runtime.UnlockOSThread() @@ -84,14 +82,13 @@ func (p *Processor) ProcessImage( // Let's check if we should skip standard processing if p.shouldSkipStandardProcessing(imgdata.Format(), po) { - return p.skipStandardProcessing(img, imgdata, po, secops) + return p.skipStandardProcessing(img, imgdata, po) } // Check if we expect image to be processed as animated. // If MaxAnimationFrames is 1, we never process as animated since we can only // process a single frame. - animated := secops.MaxAnimationFrames > 1 && - img.IsAnimated() + animated := po.MaxAnimationFrames() > 1 && img.IsAnimated() // Determine output format and check if it's supported. // The determined format is stored in po[KeyFormat]. @@ -108,24 +105,24 @@ func (p *Processor) ProcessImage( // and remove animation-related data if not animated. // Don't reload if we initially loaded a thumbnail. if !thumbnailLoaded { - if err = p.reloadImageForProcessing(img, imgdata, po, secops, animated); err != nil { + if err = p.reloadImageForProcessing(img, imgdata, po, animated); err != nil { return nil, err } } // Check image dimensions and number of frames for security reasons - originWidth, originHeight, err := p.checkImageSize(img, imgdata.Format(), secops) + originWidth, originHeight, err := p.checkImageSize(img, imgdata.Format(), po) if err != nil { return nil, err } // Transform the image (resize, crop, etc) - if err = p.transformImage(ctx, img, po, secops, imgdata, animated); err != nil { + if err = p.transformImage(ctx, img, po, imgdata, animated); err != nil { return nil, err } // Finalize the image (colorspace conversion, metadata stripping, etc) - if err = p.finalizePipeline().Run(ctx, img, po, secops, imgdata); err != nil { + if err = p.finalizePipeline().Run(ctx, img, po, imgdata); err != nil { return nil, err } @@ -170,13 +167,12 @@ func (p *Processor) reloadImageForProcessing( img *vips.Image, imgdata imagedata.ImageData, po ProcessingOptions, - secops security.Options, asAnimated bool, ) error { // If we are going to process the image as animated, we need to load all frames // up to MaxAnimationFrames if asAnimated { - frames := min(img.Pages(), secops.MaxAnimationFrames) + frames := min(img.Pages(), po.MaxAnimationFrames()) return img.Load(imgdata, 1.0, 0, frames) } @@ -189,7 +185,7 @@ func (p *Processor) reloadImageForProcessing( func (p *Processor) checkImageSize( img *vips.Image, imgtype imagetype.Type, - secops security.Options, + po ProcessingOptions, ) (int, int, error) { width, height, frames := p.getImageSize(img) @@ -198,7 +194,7 @@ func (p *Processor) checkImageSize( return width, height, nil } - err := secops.CheckDimensions(width, height, frames) + err := p.securityChecker.CheckDimensions(po.Options, width, height, frames) return width, height, err } @@ -258,11 +254,10 @@ func (p *Processor) skipStandardProcessing( img *vips.Image, imgdata imagedata.ImageData, po ProcessingOptions, - secops security.Options, ) (*Result, error) { // Even if we skip standard processing, we still need to check image dimensions // to not send an image bomb to the client - originWidth, originHeight, err := p.checkImageSize(img, imgdata.Format(), secops) + originWidth, originHeight, err := p.checkImageSize(img, imgdata.Format(), po) if err != nil { return nil, err } @@ -368,22 +363,20 @@ func (p *Processor) transformImage( ctx context.Context, img *vips.Image, po ProcessingOptions, - secops security.Options, imgdata imagedata.ImageData, asAnimated bool, ) error { if asAnimated { - return p.transformAnimated(ctx, img, po, secops) + return p.transformAnimated(ctx, img, po) } - return p.mainPipeline().Run(ctx, img, po, secops, imgdata) + return p.mainPipeline().Run(ctx, img, po, imgdata) } func (p *Processor) transformAnimated( ctx context.Context, img *vips.Image, po ProcessingOptions, - secops security.Options, ) error { if po.TrimEnabled() { slog.Warn("Trim is not supported for animated images") @@ -440,7 +433,7 @@ func (p *Processor) transformAnimated( // Transform the frame using the main pipeline. // We don't provide imgdata here to prevent scale-on-load. // Watermarking is disabled for individual frames (see above) - if err = p.mainPipeline().Run(ctx, frame, po, secops, nil); err != nil { + if err = p.mainPipeline().Run(ctx, frame, po, nil); err != nil { return err } @@ -475,7 +468,7 @@ func (p *Processor) transformAnimated( // Set watermark opacity back po.SetWatermarkOpacity(watermarkOpacity) - if err = p.applyWatermark(ctx, img, po, secops, dprScale, framesCount); err != nil { + if err = p.applyWatermark(ctx, img, po, dprScale, framesCount); err != nil { return err } } diff --git a/processing/processing_test.go b/processing/processing_test.go index 76461c23..334450d2 100644 --- a/processing/processing_test.go +++ b/processing/processing_test.go @@ -48,10 +48,10 @@ func (s *ProcessingTestSuite) SetupSuite() { s.securityConfig, _ = testutil.NewLazySuiteObj(s, func() (*security.Config, error) { c := security.NewDefaultConfig() - c.DefaultOptions.MaxSrcResolution = 10 * 1024 * 1024 - c.DefaultOptions.MaxSrcFileSize = 10 * 1024 * 1024 - c.DefaultOptions.MaxAnimationFrames = 100 - c.DefaultOptions.MaxAnimationFrameResolution = 10 * 1024 * 1024 + c.MaxSrcResolution = 10 * 1024 * 1024 + c.MaxSrcFileSize = 10 * 1024 * 1024 + c.MaxAnimationFrames = 100 + c.MaxAnimationFrameResolution = 10 * 1024 * 1024 return &c, nil }) @@ -66,7 +66,7 @@ func (s *ProcessingTestSuite) SetupSuite() { }) s.processor, _ = testutil.NewLazySuiteObj(s, func() (*Processor, error) { - return New(s.config(), nil) + return New(s.config(), s.security(), nil) }) } @@ -96,9 +96,7 @@ func (s *ProcessingTestSuite) processImageAndCheck( o *options.Options, expectedWidth, expectedHeight int, ) { - secops := s.security().NewOptions(o) - - result, err := s.processor().ProcessImage(s.T().Context(), imgdata, o, secops) + result, err := s.processor().ProcessImage(s.T().Context(), imgdata, o) s.Require().NoError(err) s.Require().NotNil(result) @@ -955,7 +953,7 @@ func (s *ProcessingTestSuite) TestImageResolutionTooLarge() { o.Set(keys.MaxSrcResolution, 1) imgdata := s.openFile("test2.jpg") - _, err := s.processor().ProcessImage(s.T().Context(), imgdata, o, s.security().NewOptions(o)) + _, err := s.processor().ProcessImage(s.T().Context(), imgdata, o) s.Require().Error(err) s.Require().Equal(422, ierrors.Wrap(err, 0).StatusCode()) diff --git a/processing/processor.go b/processing/processor.go index 9f9a99da..d1ad78de 100644 --- a/processing/processor.go +++ b/processing/processor.go @@ -3,23 +3,30 @@ package processing import ( "github.com/imgproxy/imgproxy/v3/auximageprovider" "github.com/imgproxy/imgproxy/v3/processing/svg" + "github.com/imgproxy/imgproxy/v3/security" ) // Processor is responsible for processing images according to the given configuration. type Processor struct { config *Config + securityChecker *security.Checker watermarkProvider auximageprovider.Provider svg *svg.Processor } // New creates a new Processor instance with the given configuration and watermark provider -func New(config *Config, watermark auximageprovider.Provider) (*Processor, error) { +func New( + config *Config, + securityChecker *security.Checker, + watermark auximageprovider.Provider, +) (*Processor, error) { if err := config.Validate(); err != nil { return nil, err } return &Processor{ config: config, + securityChecker: securityChecker, watermarkProvider: watermark, svg: svg.New(&config.Svg), }, nil diff --git a/processing/vector_guard_scale.go b/processing/vector_guard_scale.go index 7a061fc8..f5024bb1 100644 --- a/processing/vector_guard_scale.go +++ b/processing/vector_guard_scale.go @@ -11,8 +11,10 @@ func (p *Processor) vectorGuardScale(c *Context) error { return nil } - if resolution := c.Img.Width() * c.Img.Height(); resolution > c.SecOps.MaxSrcResolution { - shrink := math.Sqrt(float64(resolution) / float64(c.SecOps.MaxSrcResolution)) + maxSrcRes := c.PO.MaxSrcResolution() + + if resolution := c.Img.Width() * c.Img.Height(); resolution > maxSrcRes { + shrink := math.Sqrt(float64(resolution) / float64(maxSrcRes)) c.VectorBaseShrink = shrink if err := c.Img.Load(c.ImgData, shrink, 0, 1); err != nil { diff --git a/processing/watermark.go b/processing/watermark.go index 95b67fe5..6bf3a63c 100644 --- a/processing/watermark.go +++ b/processing/watermark.go @@ -8,7 +8,6 @@ import ( "github.com/imgproxy/imgproxy/v3/imath" "github.com/imgproxy/imgproxy/v3/options" "github.com/imgproxy/imgproxy/v3/options/keys" - "github.com/imgproxy/imgproxy/v3/security" "github.com/imgproxy/imgproxy/v3/vips" ) @@ -34,7 +33,6 @@ func (p *Processor) prepareWatermark( wm *vips.Image, wmData imagedata.ImageData, po ProcessingOptions, - secops security.Options, imgWidth, imgHeight int, offsetScale float64, framesCount int, @@ -80,7 +78,7 @@ func (p *Processor) prepareWatermark( wmPo.Set(keys.PaddingBottom, padY-padY/2) } - if err := p.watermarkPipeline().Run(ctx, wm, wmPo, secops, wmData); err != nil { + if err := p.watermarkPipeline().Run(ctx, wm, wmPo, wmData); err != nil { return err } @@ -104,7 +102,6 @@ func (p *Processor) applyWatermark( ctx context.Context, img *vips.Image, po ProcessingOptions, - secops security.Options, offsetScale float64, framesCount int, ) error { @@ -129,7 +126,7 @@ func (p *Processor) applyWatermark( frameHeight := height / framesCount if err := p.prepareWatermark( - ctx, wm, wmData, po, secops, width, frameHeight, offsetScale, framesCount, + ctx, wm, wmData, po, width, frameHeight, offsetScale, framesCount, ); err != nil { return err } @@ -216,5 +213,5 @@ func (p *Processor) watermark(c *Context) error { return nil } - return p.applyWatermark(c.Ctx, c.Img, c.PO, c.SecOps, c.DprScale, 1) + return p.applyWatermark(c.Ctx, c.Img, c.PO, c.DprScale, 1) } diff --git a/security/checker.go b/security/checker.go index d4902ada..639f655d 100644 --- a/security/checker.go +++ b/security/checker.go @@ -21,30 +21,50 @@ func New(config *Config) (*Checker, error) { }, nil } -// NewOptions creates a new [security.Options] instance -// filling it from [options.Options]. -// If opts is nil, it returns default [security.Options]. -func (s *Checker) NewOptions(opts *options.Options) (secops Options) { - secops = s.config.DefaultOptions - if opts == nil { - return +// MaxSrcResolution returns the maximum allowed source image resolution +func (s *Checker) MaxSrcResolution(o *options.Options) int { + return o.GetInt(keys.MaxSrcResolution, s.config.MaxSrcResolution) +} + +// MaxSrcFileSize returns the maximum allowed source file size +func (s *Checker) MaxSrcFileSize(o *options.Options) int { + return o.GetInt(keys.MaxSrcFileSize, s.config.MaxSrcFileSize) +} + +// MaxAnimationFrames returns the maximum allowed animation frames +func (s *Checker) MaxAnimationFrames(o *options.Options) int { + return o.GetInt(keys.MaxAnimationFrames, s.config.MaxAnimationFrames) +} + +// MaxAnimationFrameResolution returns the maximum allowed animation frame resolution +func (s *Checker) MaxAnimationFrameResolution(o *options.Options) int { + return o.GetInt( + keys.MaxAnimationFrameResolution, + s.config.MaxAnimationFrameResolution, + ) +} + +// MaxResultDimension returns the maximum allowed result image dimension +func (s *Checker) MaxResultDimension(o *options.Options) int { + return o.GetInt(keys.MaxResultDimension, s.config.MaxResultDimension) +} + +// CheckDimensions checks if the given dimensions are within the allowed limits +func (s *Checker) CheckDimensions(o *options.Options, width, height, frames int) error { + frames = max(frames, 1) + + maxFrameRes := s.MaxAnimationFrameResolution(o) + + if frames > 1 && maxFrameRes > 0 { + if width*height > maxFrameRes { + return newImageResolutionError("Source image frame resolution is too big") + } + return nil } - secops.MaxSrcResolution = opts.GetInt( - keys.MaxSrcResolution, secops.MaxSrcResolution, - ) - secops.MaxSrcFileSize = opts.GetInt( - keys.MaxSrcFileSize, secops.MaxSrcFileSize, - ) - secops.MaxAnimationFrames = opts.GetInt( - keys.MaxAnimationFrames, secops.MaxAnimationFrames, - ) - secops.MaxAnimationFrameResolution = opts.GetInt( - keys.MaxAnimationFrameResolution, secops.MaxAnimationFrameResolution, - ) - secops.MaxResultDimension = opts.GetInt( - keys.MaxResultDimension, secops.MaxResultDimension, - ) + if width*height*frames > s.MaxSrcResolution(o) { + return newImageResolutionError("Source image resolution is too big") + } - return + return nil } diff --git a/security/config.go b/security/config.go index 0640dd20..4de2c46f 100644 --- a/security/config.go +++ b/security/config.go @@ -32,21 +32,25 @@ type Config struct { Salts [][]byte // List of the HMAC salts SignatureSize int // Size of the HMAC signature in bytes TrustedSignatures []string // List of trusted signature sources - DefaultOptions Options // Default security options + + MaxSrcResolution int // Maximum allowed source image resolution + MaxSrcFileSize int // Maximum allowed source image file size in bytes + MaxAnimationFrames int // Maximum allowed animation frames + MaxAnimationFrameResolution int // Maximum allowed resolution per animation frame + MaxResultDimension int // Maximum allowed result image dimension (width or height) } // NewDefaultConfig returns a new Config instance with default values. func NewDefaultConfig() Config { return Config{ - DefaultOptions: Options{ - MaxSrcResolution: 50_000_000, - MaxSrcFileSize: 0, - MaxAnimationFrames: 1, - MaxAnimationFrameResolution: 0, - MaxResultDimension: 0, - }, AllowSecurityOptions: false, SignatureSize: 32, + + MaxSrcResolution: 50_000_000, + MaxSrcFileSize: 0, + MaxAnimationFrames: 1, + MaxAnimationFrameResolution: 0, + MaxResultDimension: 0, } } @@ -60,11 +64,11 @@ func LoadConfigFromEnv(c *Config) (*Config, error) { env.Int(&c.SignatureSize, IMGPROXY_SIGNATURE_SIZE), env.StringSlice(&c.TrustedSignatures, IMGPROXY_TRUSTED_SIGNATURES), - env.MegaInt(&c.DefaultOptions.MaxSrcResolution, IMGPROXY_MAX_SRC_RESOLUTION), - env.Int(&c.DefaultOptions.MaxSrcFileSize, IMGPROXY_MAX_SRC_FILE_SIZE), - env.Int(&c.DefaultOptions.MaxAnimationFrames, IMGPROXY_MAX_ANIMATION_FRAMES), - env.MegaInt(&c.DefaultOptions.MaxAnimationFrameResolution, IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION), - env.Int(&c.DefaultOptions.MaxResultDimension, IMGPROXY_MAX_RESULT_DIMENSION), + env.MegaInt(&c.MaxSrcResolution, IMGPROXY_MAX_SRC_RESOLUTION), + env.Int(&c.MaxSrcFileSize, IMGPROXY_MAX_SRC_FILE_SIZE), + env.Int(&c.MaxAnimationFrames, IMGPROXY_MAX_ANIMATION_FRAMES), + env.MegaInt(&c.MaxAnimationFrameResolution, IMGPROXY_MAX_ANIMATION_FRAME_RESOLUTION), + env.Int(&c.MaxResultDimension, IMGPROXY_MAX_RESULT_DIMENSION), env.HexSlice(&c.Keys, IMGPROXY_KEYS), env.HexSlice(&c.Salts, IMGPROXY_SALTS), @@ -75,15 +79,15 @@ func LoadConfigFromEnv(c *Config) (*Config, error) { // Validate validates the configuration func (c *Config) Validate() error { - if c.DefaultOptions.MaxSrcResolution <= 0 { + if c.MaxSrcResolution <= 0 { return IMGPROXY_MAX_SRC_RESOLUTION.ErrorZeroOrNegative() } - if c.DefaultOptions.MaxSrcFileSize < 0 { + if c.MaxSrcFileSize < 0 { return IMGPROXY_MAX_SRC_FILE_SIZE.ErrorNegative() } - if c.DefaultOptions.MaxAnimationFrames <= 0 { + if c.MaxAnimationFrames <= 0 { return IMGPROXY_MAX_ANIMATION_FRAMES.ErrorZeroOrNegative() } diff --git a/security/options.go b/security/options.go deleted file mode 100644 index 8aae2eff..00000000 --- a/security/options.go +++ /dev/null @@ -1,27 +0,0 @@ -package security - -// Security options (part of processing options) -type Options struct { - MaxSrcResolution int - MaxSrcFileSize int - MaxAnimationFrames int - MaxAnimationFrameResolution int - MaxResultDimension int -} - -// CheckDimensions checks if the given dimensions are within the allowed limits -func (o *Options) CheckDimensions(width, height, frames int) error { - frames = max(frames, 1) - - if frames > 1 && o.MaxAnimationFrameResolution > 0 { - if width*height > o.MaxAnimationFrameResolution { - return newImageResolutionError("Source image frame resolution is too big") - } - } else { - if width*height*frames > o.MaxSrcResolution { - return newImageResolutionError("Source image resolution is too big") - } - } - - return nil -}