1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-09-16 09:36:18 +02:00

Add IMGPROXY_ALLOWED_PROCESSING_OPTIONS config

This commit is contained in:
DarthSim
2025-06-09 18:15:24 +03:00
parent a5a587eb39
commit 2864bab12d
6 changed files with 59 additions and 4 deletions

View File

@@ -3,6 +3,7 @@
## [Unreleased]
### Added
- Add [IMGPROXY_MAX_RESULT_DIMENSION](https://docs.imgproxy.net/latest/configuration/options#IMGPROXY_MAX_RESULT_DIMENSION) config and [max_result_dimension](https://docs.imgproxy.net/latest/usage/processing#max-result-dimension) processing option.
- Add [IMGPROXY_ALLOWED_PROCESSING_OPTIONS](https://docs.imgproxy.net/latest/configuration/options#IMGPROXY_ALLOWED_PROCESSING_OPTIONS) config.
- Add `imgproxy.source_image_origin` attribute to New Relic, DataDog, and OpenTelemetry traces.
- Add `imgproxy.source_image_url` and `imgproxy.source_image_origin` attributes to `downloading_image` spans in New Relic, DataDog, and OpenTelemetry traces.
- Add `imgproxy.processing_options` attribute to `processing_image` spans in New Relic, DataDog, and OpenTelemetry traces.

View File

@@ -48,6 +48,7 @@ var (
PngUnlimited bool
SvgUnlimited bool
MaxResultDimension int
AllowedProcessiongOptions []string
AllowSecurityOptions bool
JpegProgressive bool
@@ -254,6 +255,7 @@ func Reset() {
PngUnlimited = false
SvgUnlimited = false
MaxResultDimension = 0
AllowedProcessiongOptions = make([]string, 0)
AllowSecurityOptions = false
JpegProgressive = false
@@ -486,6 +488,7 @@ func Configure() error {
configurators.Bool(&SvgUnlimited, "IMGPROXY_SVG_UNLIMITED")
configurators.Int(&MaxResultDimension, "IMGPROXY_MAX_RESULT_DIMENSION")
configurators.StringSlice(&AllowedProcessiongOptions, "IMGPROXY_ALLOWED_PROCESSING_OPTIONS")
configurators.Bool(&AllowSecurityOptions, "IMGPROXY_ALLOW_SECURITY_OPTIONS")

View File

@@ -35,6 +35,16 @@ func newUnknownOptionError(kind, opt string) error {
)
}
func newForbiddenOptionError(kind, opt string) error {
return ierrors.Wrap(
UnknownOptionError(fmt.Sprintf("Forbidden %s option %s", kind, opt)),
1,
ierrors.WithStatusCode(http.StatusNotFound),
ierrors.WithPublicMessage("Invalid URL"),
ierrors.WithShouldReport(false),
)
}
func (e UnknownOptionError) Error() string { return string(e) }
func newOptionArgumentError(format string, args ...interface{}) error {

View File

@@ -59,7 +59,7 @@ func parsePreset(presetStr string) error {
func ValidatePresets() error {
for name, opts := range presets {
po := NewProcessingOptions()
if err := applyURLOptions(po, opts, name); err != nil {
if err := applyURLOptions(po, opts, true, name); err != nil {
return fmt.Errorf("Error in preset `%s`: %s", name, err)
}
}

View File

@@ -691,7 +691,7 @@ func applyPresetOption(po *ProcessingOptions, args []string, usedPresets ...stri
po.UsedPresets = append(po.UsedPresets, preset)
if err := applyURLOptions(po, p, append(usedPresets, preset)...); err != nil {
if err := applyURLOptions(po, p, true, append(usedPresets, preset)...); err != nil {
return err
}
} else {
@@ -1081,8 +1081,14 @@ func applyURLOption(po *ProcessingOptions, name string, args []string, usedPrese
return newUnknownOptionError("processing", name)
}
func applyURLOptions(po *ProcessingOptions, options urlOptions, usedPresets ...string) error {
func applyURLOptions(po *ProcessingOptions, options urlOptions, allowAll bool, usedPresets ...string) error {
allowAll = allowAll || len(config.AllowedProcessiongOptions) == 0
for _, opt := range options {
if !allowAll && !slices.Contains(config.AllowedProcessiongOptions, opt.Name) {
return newForbiddenOptionError("processing", opt.Name)
}
if err := applyURLOption(po, opt.Name, opt.Args, usedPresets...); err != nil {
return err
}
@@ -1154,7 +1160,7 @@ func parsePathOptions(parts []string, headers http.Header) (*ProcessingOptions,
options, urlParts := parseURLOptions(parts)
if err = applyURLOptions(po, options); err != nil {
if err = applyURLOptions(po, options, false); err != nil {
return nil, "", err
}

View File

@@ -6,6 +6,7 @@ import (
"net/http"
"net/url"
"regexp"
"strings"
"testing"
"github.com/stretchr/testify/suite"
@@ -644,6 +645,40 @@ func (s *ProcessingOptionsTestSuite) TestParseBase64URLOnlyPresets() {
s.Require().Equal(originURL, imageURL)
}
func (s *ProcessingOptionsTestSuite) TestParseAllowedOptions() {
config.AllowedProcessiongOptions = []string{"w", "h", "pr"}
presets["test1"] = urlOptions{
urlOption{Name: "blur", Args: []string{"0.2"}},
}
originURL := "http://images.dev/lorem/ipsum.jpg?param=value"
testCases := []struct {
options string
expectedError string
}{
{options: "w:100/h:200", expectedError: ""},
{options: "w:100/h:200/blur:10", expectedError: "Forbidden processing option blur"},
{options: "w:100/h:200/pr:test1", expectedError: ""},
{options: "w:100/h:200/pr:test1/blur:10", expectedError: "Forbidden processing option blur"},
}
for _, tc := range testCases {
s.Run(strings.ReplaceAll(tc.options, "/", "_"), func() {
path := fmt.Sprintf("/%s/%s.png", tc.options, base64.RawURLEncoding.EncodeToString([]byte(originURL)))
_, _, err := ParsePath(path, make(http.Header))
if len(tc.expectedError) > 0 {
s.Require().Error(err)
s.Require().Equal(tc.expectedError, err.Error())
} else {
s.Require().NoError(err)
}
})
}
}
func TestProcessingOptions(t *testing.T) {
suite.Run(t, new(ProcessingOptionsTestSuite))
}