From 923b32a1b4e5dd73d7658e43d03332648efcb2f7 Mon Sep 17 00:00:00 2001 From: Frezyy Date: Mon, 25 Mar 2024 19:53:43 +0500 Subject: [PATCH] feat: Implement AlwaysRasterizeSvg (#1257) Implement AlwaysRasterizeSvg Add sanitize to skip processing Add tests Refactoring skipProcessing --- config/config.go | 6 +++-- processing_handler.go | 39 ++++++++++++++----------------- processing_handler_test.go | 48 +++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 24 deletions(-) diff --git a/config/config.go b/config/config.go index 58f87bff..741ac720 100644 --- a/config/config.go +++ b/config/config.go @@ -93,7 +93,8 @@ var ( AllowLinkLocalSourceAddresses bool AllowPrivateSourceAddresses bool - SanitizeSvg bool + SanitizeSvg bool + AlwaysRasterizeSvg bool CookiePassthrough bool CookieBaseURL string @@ -288,6 +289,7 @@ func Reset() { AllowPrivateSourceAddresses = true SanitizeSvg = true + AlwaysRasterizeSvg = false CookiePassthrough = false CookieBaseURL = "" @@ -429,6 +431,7 @@ func Configure() error { configurators.Bool(&AllowPrivateSourceAddresses, "IMGPROXY_ALLOW_PRIVATE_SOURCE_ADDRESSES") configurators.Bool(&SanitizeSvg, "IMGPROXY_SANITIZE_SVG") + configurators.Bool(&AlwaysRasterizeSvg, "IMGPROXY_ALWAYS_RASTERIZE_SVG") configurators.Bool(&AllowSecurityOptions, "IMGPROXY_ALLOW_SECURITY_OPTIONS") @@ -680,7 +683,6 @@ func Configure() error { if LocalFileSystemRoot != "" { stat, err := os.Stat(LocalFileSystemRoot) - if err != nil { return fmt.Errorf("Cannot use local directory: %s", err) } diff --git a/processing_handler.go b/processing_handler.go index 410fbaad..eb2f9a22 100644 --- a/processing_handler.go +++ b/processing_handler.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "slices" "strconv" "strings" "time" @@ -372,32 +373,28 @@ func handleProcessing(reqID string, rw http.ResponseWriter, r *http.Request) { checkErr(ctx, "timeout", router.CheckTimeout(ctx)) - if originData.Type == po.Format || po.Format == imagetype.Unknown { - // Don't process SVG - if originData.Type == imagetype.SVG { - if config.SanitizeSvg { - sanitized, svgErr := svg.Sanitize(originData) - checkErr(ctx, "svg_processing", svgErr) + // Skip processing svg with unknown or the same destination imageType + // if it's not forced by AlwaysRasterizeSvg option + // Also skip processing if the format is in SkipProcessingFormats + shouldSkipProcessing := (originData.Type == po.Format || po.Format == imagetype.Unknown) && + (slices.Contains(po.SkipProcessingFormats, originData.Type) || + originData.Type == imagetype.SVG && !config.AlwaysRasterizeSvg) - // Since we'll replace origin data, it's better to close it to return - // it's buffer to the pool - originData.Close() + if shouldSkipProcessing { + if originData.Type == imagetype.SVG && config.SanitizeSvg { + sanitized, svgErr := svg.Sanitize(originData) + checkErr(ctx, "svg_processing", svgErr) - originData = sanitized - } + // Since we'll replace origin data, it's better to close it to return + // it's buffer to the pool + originData.Close() + + originData = sanitized - respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData) - return } - if len(po.SkipProcessingFormats) > 0 { - for _, f := range po.SkipProcessingFormats { - if f == originData.Type { - respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData) - return - } - } - } + respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData) + return } if !vips.SupportsLoad(originData.Type) { diff --git a/processing_handler_test.go b/processing_handler_test.go index d702b94e..1b9d7313 100644 --- a/processing_handler_test.go +++ b/processing_handler_test.go @@ -608,7 +608,6 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedD require.Equal(s.T(), "", modifiedSince) rw.WriteHeader(200) rw.Write(data) - })) defer ts.Close() @@ -619,6 +618,7 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedD require.Equal(s.T(), 200, res.StatusCode) } + func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqExactMatchLastModifiedEnabled() { config.LastModifiedEnabled = true lastModified := "Wed, 21 Oct 2015 07:28:00 GMT" @@ -657,6 +657,7 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastMo require.Equal(s.T(), 200, res.StatusCode) } + func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastModifiedEnabled() { config.LastModifiedEnabled = true ts := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { @@ -678,6 +679,7 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareMoreRecentLastMo require.Equal(s.T(), 304, res.StatusCode) } + func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedDisabled() { config.LastModifiedEnabled = false data := s.readTestFile("test1.png") @@ -698,6 +700,7 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifi require.Equal(s.T(), 200, res.StatusCode) } + func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifiedEnabled() { config.LastModifiedEnabled = true data := s.readTestFile("test1.png") @@ -721,6 +724,49 @@ func (s *ProcessingHandlerTestSuite) TestModifiedSinceReqCompareTooOldLastModifi require.Equal(s.T(), 200, res.StatusCode) } + +func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvg() { + config.AlwaysRasterizeSvg = true + + rw := s.send("/unsafe/rs:fill:40:40/plain/local:///test1.svg") + res := rw.Result() + + require.Equal(s.T(), 200, res.StatusCode) + require.Equal(s.T(), "image/png", res.Header.Get("Content-Type")) +} + +func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithEnforceAvif() { + config.AlwaysRasterizeSvg = true + config.EnforceWebp = true + + rw := s.send("/unsafe/plain/local:///test1.svg", http.Header{"Accept": []string{"image/webp"}}) + res := rw.Result() + + require.Equal(s.T(), 200, res.StatusCode) + require.Equal(s.T(), "image/webp", res.Header.Get("Content-Type")) +} + +func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgDisabled() { + config.AlwaysRasterizeSvg = false + config.EnforceWebp = true + + rw := s.send("/unsafe/plain/local:///test1.svg") + res := rw.Result() + + require.Equal(s.T(), 200, res.StatusCode) + require.Equal(s.T(), "image/svg+xml", res.Header.Get("Content-Type")) +} + +func (s *ProcessingHandlerTestSuite) TestAlwaysRasterizeSvgWithFormat() { + config.AlwaysRasterizeSvg = true + config.SkipProcessingFormats = []imagetype.Type{imagetype.SVG} + rw := s.send("/unsafe/plain/local:///test1.svg@svg") + res := rw.Result() + + require.Equal(s.T(), 200, res.StatusCode) + require.Equal(s.T(), "image/svg+xml", res.Header.Get("Content-Type")) +} + func TestProcessingHandler(t *testing.T) { suite.Run(t, new(ProcessingHandlerTestSuite)) }