mirror of
https://github.com/imgproxy/imgproxy.git
synced 2026-06-18 17:43:40 +02:00
Get rid of security.Options. Use security.Checker directly with options.Options
This commit is contained in:
1 parent
ef237b0585
commit
3f2b019721
17 files changed
+134
-141
No files matched your search
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+9
-9
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
+19
-2
@@ -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())
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
+14
-21
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
+43
-23
@@ -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
|
||||
}
|
||||
+20
-16
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in new issue
Block a user