check / check (push) Canceled after 0s
seal.DeriveKeys derives the session hash and block keys, the encrypted URL key and the CSRF key from the signing key, with their salts declared beside it, and returns them as one seal.Keys. internal/providers calls it once at startup and provides the value to the fx graph. The session manager, the encrypted URL generator and the CSRF protection each take their own key. session.NewManager, encurl.NewGenerator, newCSRFProtect and handlers.New return no error, as nothing in them can fail now. Each key is derived as before, with seal.DeriveKey and the same salt. The signing key itself stays the HMAC key of signed URLs and the login password. README.md says under signing_key what it protects. Model: opus-5-5
79 lines
2.5 KiB
Go
79 lines
2.5 KiB
Go
package server
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/pixa/internal/encurl"
|
|
"sneak.berlin/go/pixa/internal/imagerequest"
|
|
"sneak.berlin/go/pixa/internal/seal"
|
|
)
|
|
|
|
// 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)
|
|
|
|
keys, err := seal.DeriveKeys(testSigningKey)
|
|
if err != nil {
|
|
t.Fatalf("seal.DeriveKeys: %v", err)
|
|
}
|
|
|
|
generator := encurl.NewGenerator(keys.EncryptedURLKey)
|
|
|
|
encryptedURL := func(format imagerequest.Format) string {
|
|
token, err := generator.Generate(&imagerequest.Request{
|
|
Source: imagerequest.Source{Host: upstreamHost, Path: "/drawing.avif"},
|
|
Transform: imagerequest.Transform{
|
|
Size: imagerequest.Size{Width: 32, Height: 24},
|
|
Format: format,
|
|
},
|
|
}, time.Time{})
|
|
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(imagerequest.FormatPNG),
|
|
encryptedURL(imagerequest.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)
|
|
}
|
|
}
|
|
}
|