From 016258f7eb3544a4388677c2cf3ae2f53bb2ff49 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 20 Jul 2021 19:29:18 +0600 Subject: [PATCH 1/8] Reset image resolution while stripping meta --- CHANGELOG.md | 2 ++ vips.c | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89691e14..3e2ffc10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] +### Change +- Reset DPI while stripping meta. ## [2.16.6] - 2021-07-08 ### Fix diff --git a/vips.c b/vips.c index 167349fa..368974e7 100644 --- a/vips.c +++ b/vips.c @@ -626,7 +626,14 @@ vips_arrayjoin_go(VipsImage **in, VipsImage **out, int n) { int vips_strip(VipsImage *in, VipsImage **out) { - if (vips_copy(in, out, NULL)) return 1; + static double default_resolution = 72.0 / 25.4; + + if (vips_copy( + in, out, + "xres", default_resolution, + "yres", default_resolution, + NULL + )) return 1; gchar **fields = vips_image_get_fields(in); From de65113b9790c7e34402ed43cd9557b5c5e4238b Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 20 Jul 2021 19:39:39 +0600 Subject: [PATCH 2/8] Bump version --- CHANGELOG.md | 2 ++ main.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e2ffc10..60ab2a00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] + +## [2.16.7] - 2021-07-20 ### Change - Reset DPI while stripping meta. diff --git a/main.go b/main.go index c5071c7c..1e0ecf2d 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "time" ) -const version = "2.16.6" +const version = "2.16.7" type ctxKey string From dbd19649a6e3d712aca66b3dc18ac7365915901f Mon Sep 17 00:00:00 2001 From: Christopher Hlubek Date: Mon, 9 Aug 2021 17:42:43 +0200 Subject: [PATCH 3/8] Support wildcards with * in AllowedSources (excluding slashes) (#677) * Support wildcards with * in AllowedSources (excluding slashes) * Compile allowed sources patterns when parsing config * Fix linter errors Co-authored-by: Christopher Hlubek --- config.go | 53 +++++++++++++++++++---------- docs/configuration.md | 2 +- processing_options.go | 4 +-- processing_options_test.go | 69 ++++++++++++++++++++++++++++++-------- 4 files changed, 93 insertions(+), 35 deletions(-) diff --git a/config.go b/config.go index 9707cdc2..5ada138d 100644 --- a/config.go +++ b/config.go @@ -7,6 +7,7 @@ import ( "fmt" "math" "os" + "regexp" "runtime" "strconv" "strings" @@ -36,22 +37,6 @@ func strEnvConfig(s *string, name string) { } } -func strSliceEnvConfig(s *[]string, name string) { - if env := os.Getenv(name); len(env) > 0 { - parts := strings.Split(env, ",") - - for i, p := range parts { - parts[i] = strings.TrimSpace(p) - } - - *s = parts - - return - } - - *s = []string{} -} - func boolEnvConfig(b *bool, name string) { if env, err := strconv.ParseBool(os.Getenv(name)); err == nil { *b = env @@ -197,6 +182,38 @@ func presetFileConfig(p presets, filepath string) error { return nil } +func patternsEnvConfig(s *[]*regexp.Regexp, name string) { + if env := os.Getenv(name); len(env) > 0 { + parts := strings.Split(env, ",") + result := make([]*regexp.Regexp, len(parts)) + + for i, p := range parts { + result[i] = regexpFromPattern(strings.TrimSpace(p)) + } + + *s = result + } else { + *s = []*regexp.Regexp{} + } +} + +func regexpFromPattern(pattern string) *regexp.Regexp { + var result strings.Builder + // Perform prefix matching + result.WriteString("^") + for i, part := range strings.Split(pattern, "*") { + // Add a regexp match all without slashes for each wildcard character + if i > 0 { + result.WriteString("[^/]*") + } + + // Quote other parts of the pattern + result.WriteString(regexp.QuoteMeta(part)) + } + // It is safe to use regexp.MustCompile since the expression is always valid + return regexp.MustCompile(result.String()) +} + type config struct { Network string Bind string @@ -258,7 +275,7 @@ type config struct { IgnoreSslVerification bool DevelopmentErrorsMode bool - AllowedSources []string + AllowedSources []*regexp.Regexp LocalFileSystemRoot string S3Enabled bool S3Region string @@ -384,7 +401,7 @@ func configure() error { } intEnvConfig(&conf.MaxAnimationFrames, "IMGPROXY_MAX_ANIMATION_FRAMES") - strSliceEnvConfig(&conf.AllowedSources, "IMGPROXY_ALLOWED_SOURCES") + patternsEnvConfig(&conf.AllowedSources, "IMGPROXY_ALLOWED_SOURCES") intEnvConfig(&conf.AvifSpeed, "IMGPROXY_AVIF_SPEED") boolEnvConfig(&conf.JpegProgressive, "IMGPROXY_JPEG_PROGRESSIVE") diff --git a/docs/configuration.md b/docs/configuration.md index dc2635cc..d447045f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -73,7 +73,7 @@ imgproxy does not send CORS headers by default. Specify allowed origin to enable You can limit allowed source URLs: -* `IMGPROXY_ALLOWED_SOURCES`: whitelist of source image URLs prefixes divided by comma. When blank, imgproxy allows all source image URLs. Example: `s3://,https://example.com/,local://`. Default: blank. +* `IMGPROXY_ALLOWED_SOURCES`: whitelist of source image URLs prefixes divided by comma. Wildcards can be included with `*` to match all characters except `/`. When blank, imgproxy allows all source image URLs. Example: `s3://,https://*.example.com/,local://`. Default: blank. **⚠️Warning:** Be careful when using this config to limit source URL hosts, and always add a trailing slash after the host. Bad: `http://example.com`, good: `http://example.com/`. If you don't add a trailing slash, `http://example.com@baddomain.com` will be an allowed URL but the request will be made to `baddomain.com`. diff --git a/processing_options.go b/processing_options.go index eb2767fd..bbd47f3a 100644 --- a/processing_options.go +++ b/processing_options.go @@ -993,8 +993,8 @@ func isAllowedSource(imageURL string) bool { if len(conf.AllowedSources) == 0 { return true } - for _, val := range conf.AllowedSources { - if strings.HasPrefix(imageURL, string(val)) { + for _, allowedSource := range conf.AllowedSources { + if allowedSource.MatchString(imageURL) { return true } } diff --git a/processing_options_test.go b/processing_options_test.go index 9e2687f8..e5d0b0ae 100644 --- a/processing_options_test.go +++ b/processing_options_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "net/url" + "regexp" "testing" "github.com/stretchr/testify/assert" @@ -105,22 +106,62 @@ func (s *ProcessingOptionsTestSuite) TestParsePlainURLEscapedWithBase() { assert.Equal(s.T(), imageTypePNG, getProcessingOptions(ctx).Format) } -func (s *ProcessingOptionsTestSuite) TestParseURLAllowedSource() { - conf.AllowedSources = []string{"local://", "http://images.dev/"} +func (s *ProcessingOptionsTestSuite) TestParseURLAllowedSources() { + tt := []struct { + name string + allowedSources []string + requestPath string + expectedError bool + }{ + { + name: "match http URL without wildcard", + allowedSources: []string{"local://", "http://images.dev/"}, + requestPath: "/unsafe/plain/http://images.dev/lorem/ipsum.jpg", + expectedError: false, + }, + { + name: "match http URL with wildcard in hostname single level", + allowedSources: []string{"local://", "http://*.mycdn.dev/"}, + requestPath: "/unsafe/plain/http://a-1.mycdn.dev/lorem/ipsum.jpg", + expectedError: false, + }, + { + name: "match http URL with wildcard in hostname multiple levels", + allowedSources: []string{"local://", "http://*.mycdn.dev/"}, + requestPath: "/unsafe/plain/http://a-1.b-2.mycdn.dev/lorem/ipsum.jpg", + expectedError: false, + }, + { + name: "no match s3 URL with allowed local and http URLs", + allowedSources: []string{"local://", "http://images.dev/"}, + requestPath: "/unsafe/plain/s3://images/lorem/ipsum.jpg", + expectedError: true, + }, + { + name: "no match http URL with wildcard in hostname including slash", + allowedSources: []string{"local://", "http://*.mycdn.dev/"}, + requestPath: "/unsafe/plain/http://other.dev/.mycdn.dev/lorem/ipsum.jpg", + expectedError: true, + }, + } - req := s.getRequest("/unsafe/plain/http://images.dev/lorem/ipsum.jpg") - _, err := parsePath(context.Background(), req) + for _, tc := range tt { + s.T().Run(tc.name, func(t *testing.T) { + exps := make([]*regexp.Regexp, len(tc.allowedSources)) + for i, pattern := range tc.allowedSources { + exps[i] = regexpFromPattern(pattern) + } + conf.AllowedSources = exps - require.Nil(s.T(), err) -} - -func (s *ProcessingOptionsTestSuite) TestParseURLNotAllowedSource() { - conf.AllowedSources = []string{"local://", "http://images.dev/"} - - req := s.getRequest("/unsafe/plain/s3://images/lorem/ipsum.jpg") - _, err := parsePath(context.Background(), req) - - require.Error(s.T(), err) + req := s.getRequest(tc.requestPath) + _, err := parsePath(context.Background(), req) + if tc.expectedError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } } func (s *ProcessingOptionsTestSuite) TestParsePathBasic() { From 8f062a43ee1b69b8e2f295a5447e3830e8ec8944 Mon Sep 17 00:00:00 2001 From: Hampus Kraft Date: Mon, 6 Sep 2021 11:47:10 +0200 Subject: [PATCH 4/8] Fix typo in response header name (#693) --- landing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/landing.go b/landing.go index eb992b72..8ba57cd2 100644 --- a/landing.go +++ b/landing.go @@ -14,7 +14,7 @@ var landingTmpl = []byte(` `) func handleLanding(reqID string, rw http.ResponseWriter, r *http.Request) { - rw.Header().Set("Content-Tyle", "text/html") + rw.Header().Set("Content-Type", "text/html") rw.WriteHeader(200) rw.Write(landingTmpl) } From e0f9fab772cebc6a8dea479396f37329ec3795b9 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 7 Sep 2021 17:39:24 +0600 Subject: [PATCH 5/8] Do not add base URL if the image URL already contains it --- docs/configuration.md | 2 +- processing_options.go | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index d447045f..93af3cf4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -334,7 +334,7 @@ imgproxy can send logs to syslog, but this feature is disabled by default. To en ## Miscellaneous -* `IMGPROXY_BASE_URL`: base URL prefix that will be added to every requested image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. Default: blank. +* `IMGPROXY_BASE_URL`: base URL prefix that will be added to every requested image URL. For example, if the base URL is `http://example.com/images` and `/path/to/image.png` is requested, imgproxy will download the source image from `http://example.com/images/path/to/image.png`. If the image URL already contains the prefix, it won't be added. Default: blank. * `IMGPROXY_USE_LINEAR_COLORSPACE`: when `true`, imgproxy will process images in linear colorspace. This will slow down processing. Note that images won't be fully processed in linear colorspace while shrink-on-load is enabled (see below). * `IMGPROXY_DISABLE_SHRINK_ON_LOAD`: when `true`, disables shrink-on-load for JPEG and WebP. Allows to process the whole image in linear colorspace but dramatically slows down resizing and increases memory usage when working with large images. * `IMGPROXY_STRIP_METADATA`: when `true`, imgproxy will strip all metadata (EXIF, IPTC, etc.) from JPEG and WebP output images. Default: `true`. diff --git a/processing_options.go b/processing_options.go index bbd47f3a..1bf3b575 100644 --- a/processing_options.go +++ b/processing_options.go @@ -304,6 +304,14 @@ func colorFromHex(hexcolor string) (rgbColor, error) { return c, nil } +func addBaseURL(u string) string { + if len(conf.BaseURL) == 0 || strings.HasPrefix(u, conf.BaseURL) { + return u + } + + return fmt.Sprintf("%s%s", conf.BaseURL, u) +} + func decodeBase64URL(parts []string) (string, string, error) { var format string @@ -327,9 +335,7 @@ func decodeBase64URL(parts []string) (string, string, error) { return "", "", fmt.Errorf("Invalid url encoding: %s", encoded) } - fullURL := fmt.Sprintf("%s%s", conf.BaseURL, string(imageURL)) - - return fullURL, format, nil + return addBaseURL(string(imageURL)), format, nil } func decodePlainURL(parts []string) (string, string, error) { @@ -355,9 +361,7 @@ func decodePlainURL(parts []string) (string, string, error) { return "", "", fmt.Errorf("Invalid url encoding: %s", encoded) } - fullURL := fmt.Sprintf("%s%s", conf.BaseURL, unescaped) - - return fullURL, format, nil + return addBaseURL(unescaped), format, nil } func decodeURL(parts []string) (string, string, error) { From bb9294f163f3b294047fd5a577404897c482587e Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 7 Sep 2021 17:47:30 +0600 Subject: [PATCH 6/8] Update changelog --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 60ab2a00..a7053fb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog ## [Unreleased] +### Added +- Wildcard support in `IMGPROXY_ALLOWED_SOURCES`. + +### Change +- If the source URL contains the `IMGPROXY_BASE_URL` prefix, it won't be added. + +### Fix +- (pro) Fix path prefix support in the `/info` handler. ## [2.16.7] - 2021-07-20 ### Change From ce5116b9ecfeba1ffc6f8bf78aaef20a977fd839 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 7 Sep 2021 18:00:07 +0600 Subject: [PATCH 7/8] Deprecate basic URL format --- CHANGELOG.md | 3 +++ processing_options.go | 2 ++ 2 files changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7053fb0..6a48bd75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ ### Fix - (pro) Fix path prefix support in the `/info` handler. +### Deprecated +- The [basic URL format](https://docs.imgproxy.net/generating_the_url_basic) is deprecated and can be removed in future versions. Use [advanced URL format](https://docs.imgproxy.net/generating_the_url_advanced) instead. + ## [2.16.7] - 2021-07-20 ### Change - Reset DPI while stripping meta. diff --git a/processing_options.go b/processing_options.go index 1bf3b575..837a0d75 100644 --- a/processing_options.go +++ b/processing_options.go @@ -1122,6 +1122,8 @@ func parsePathPresets(parts []string, headers *processingHeaders) (string, *proc } func parsePathBasic(parts []string, headers *processingHeaders) (string, *processingOptions, error) { + logWarning("The basic URL format is deprecated and can be removed in future versions. Use advanced URL format instead") + if len(parts) < 6 { return "", nil, fmt.Errorf("Invalid basic URL format arguments: %s", strings.Join(parts, "/")) } From 75beaf3bccf849e5602aeb5284bddf05ced15872 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 7 Sep 2021 18:01:04 +0600 Subject: [PATCH 8/8] Bump version --- CHANGELOG.md | 2 ++ main.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a48bd75..1d7db61e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Changelog ## [Unreleased] + +## [2.17.0] - 2021-09-07 ### Added - Wildcard support in `IMGPROXY_ALLOWED_SOURCES`. diff --git a/main.go b/main.go index 1e0ecf2d..a9df4938 100644 --- a/main.go +++ b/main.go @@ -11,7 +11,7 @@ import ( "time" ) -const version = "2.16.7" +const version = "2.17.0" type ctxKey string