Prechádzať zdrojové kódy

feat: Implement AlwaysRasterizeSvg (#1257)

Implement AlwaysRasterizeSvg
Add sanitize to skip processing
Add tests
Refactoring skipProcessing
Frezyy 1 rok pred
rodič
commit
923b32a1b4
3 zmenil súbory, kde vykonal 69 pridanie a 24 odobranie
  1. 4 2
      config/config.go
  2. 18 21
      processing_handler.go
  3. 47 1
      processing_handler_test.go

+ 4 - 2
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)
 		}

+ 18 - 21
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()
 
-			respondWithImage(reqID, r, rw, statusCode, originData, po, imageURL, originData)
-			return
-		}
+			originData = sanitized
 
-		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) {

+ 47 - 1
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))
 }