Refuse SVG sources and never save an unknown input as JPEG (closes #68) #240
@@ -0,0 +1,46 @@
|
|||||||
|
package imageprocessor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestImageProcessor_LoadsEachSourceFormat verifies that an image in each of
|
||||||
|
// the six source formats, saved by the processor itself, is loaded and saved
|
||||||
|
// again in its own format (orig).
|
||||||
|
func TestImageProcessor_LoadsEachSourceFormat(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
proc := New(Params{})
|
||||||
|
|
||||||
|
for _, format := range []Format{
|
||||||
|
FormatJPEG, FormatPNG, FormatGIF, FormatWebP, FormatAVIF, FormatJXL,
|
||||||
|
} {
|
||||||
|
saved, err := proc.Process(t.Context(),
|
||||||
|
bytes.NewReader(createTestPNG(t, 20, 20)), &Request{Format: format})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("saving the test image as %s: %v", format, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := io.ReadAll(saved.Content)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("reading the %s image: %v", format, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := proc.Process(t.Context(), bytes.NewReader(source),
|
||||||
|
&Request{Format: FormatOriginal})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("orig of the %s image: %v", format, err)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("orig of the %s image: %s", format, result.ContentType)
|
||||||
|
|
||||||
|
if result.ContentType != FormatToMIME(format) {
|
||||||
|
t.Errorf("orig of the %s image is %s, want %s",
|
||||||
|
format, result.ContentType, FormatToMIME(format))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"sneak.berlin/go/pixa/internal/encurl"
|
||||||
|
"sneak.berlin/go/pixa/internal/imgcache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// svgServedAsAVIF is an SVG whose first bytes pass the first-bytes check for
|
||||||
|
// AVIF: its XML comment puts "ftypavis" at bytes 4 to 11, where an AVIF file
|
||||||
|
// has its ftyp box and brand. libvips would load it as SVG.
|
||||||
|
const svgServedAsAVIF = `<!--ftypavis--><svg xmlns="http://www.w3.org/2000/svg" ` +
|
||||||
|
`width="64" height="48"><rect width="64" height="48"/></svg>`
|
||||||
|
|
||||||
|
// TestImageProxyRefusesSVGServedAsAVIF requests svgServedAsAVIF, which the
|
||||||
|
// upstream answers as image/avif, through both image routes, resized as PNG
|
||||||
|
// and in its own format (orig). Each request is answered 415, as for an SVG
|
||||||
|
// source, never with an image.
|
||||||
|
func TestImageProxyRefusesSVGServedAsAVIF(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
upstream := httptest.NewServer(http.HandlerFunc(
|
||||||
|
func(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "image/avif")
|
||||||
|
_, _ = io.WriteString(w, svgServedAsAVIF)
|
||||||
|
}))
|
||||||
|
t.Cleanup(upstream.Close)
|
||||||
|
|
||||||
|
s, _, _ := startImageProxy(t, upstream)
|
||||||
|
|
||||||
|
generator, err := encurl.NewGenerator(testSigningKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("encurl.NewGenerator: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedURL := func(format imgcache.ImageFormat) string {
|
||||||
|
token, err := generator.Generate(&encurl.Payload{
|
||||||
|
SourceHost: upstreamHost, SourcePath: "/drawing.avif",
|
||||||
|
Width: 32, Height: 24, Format: format,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("encurl Generate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "/v1/e/" + token + "/drawing." + string(format)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, target := range []string{
|
||||||
|
"/v1/image/" + upstreamHost + "/drawing.avif/32x24.png",
|
||||||
|
"/v1/image/" + upstreamHost + "/drawing.avif/64x48.orig",
|
||||||
|
encryptedURL(imgcache.FormatPNG),
|
||||||
|
encryptedURL(imgcache.FormatOriginal),
|
||||||
|
} {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
s.ServeHTTP(rec, httptest.NewRequestWithContext(
|
||||||
|
t.Context(), http.MethodGet, target, nil))
|
||||||
|
t.Logf("GET %s: %d, Content-Type %s, body %q",
|
||||||
|
target, rec.Code, rec.Header().Get("Content-Type"), rec.Body.String())
|
||||||
|
|
||||||
|
if rec.Code != http.StatusUnsupportedMediaType ||
|
||||||
|
!strings.Contains(rec.Body.String(), "unsupported source format") {
|
||||||
|
t.Errorf("GET %s: %d %q, want %d unsupported source format",
|
||||||
|
target, rec.Code, rec.Body.String(), http.StatusUnsupportedMediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user