check / check (push) Waiting to run
Service.Warm, the ImageCache interface, both GenerateSignedURL methods, signature.ParseParams, imagerequest.ParseImagePath, four symbols of internal/magic, the image processor's lists of supported formats, IsEmpty and Count of the allowlist, ssrfSafeDialer, CacheStale, ErrCacheMiss, ErrNegativeCache, ImageResponse.LastModified and the Load methods of ContentStorage and VariantStorage had no caller outside tests. They are gone, with the tests that tested only them. The handlers tests sign their image URLs with generateSignedURL, a test helper that builds the URL as the removed generator did. The SSRF dialer tests call dialSSRFSafe with no operator blocklist, which is all ssrfSafeDialer did. The tests of ParseImagePath and ContentStorage.Load call ParseImageURL and LoadWithSize instead. MIMEToImageFormat, ContentStorage.Store and CleanExpired stay, as pixa calls them. Model: opus-5-5
137 lines
3.7 KiB
Go
137 lines
3.7 KiB
Go
package signature_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/pixa/internal/imagerequest"
|
|
"sneak.berlin/go/pixa/internal/signature"
|
|
)
|
|
|
|
// goldenExpiresUnix is the fixed expiration timestamp used by all golden
|
|
// vectors: 2024-01-01T00:00:00Z.
|
|
const goldenExpiresUnix int64 = 1704067200
|
|
|
|
// goldenSigningKey is the fixed signing key used by all golden vectors.
|
|
const goldenSigningKey = "golden-test-key"
|
|
|
|
type goldenVector struct {
|
|
name string
|
|
req imagerequest.Request
|
|
// wantSignature is the exact base64url (RFC 4648 URL-safe,
|
|
// padded) HMAC-SHA256 signature for the request with Expires
|
|
// set to goldenExpiresUnix.
|
|
wantSignature string
|
|
}
|
|
|
|
// goldenVectors returns the known-answer vectors. The expected values
|
|
// were computed once and are hardcoded here.
|
|
func goldenVectors() []goldenVector {
|
|
return []goldenVector{
|
|
{
|
|
name: "resized without query",
|
|
req: imagerequest.Request{
|
|
Source: imagerequest.Source{
|
|
Host: testHost,
|
|
Path: testPath,
|
|
Query: "",
|
|
},
|
|
Transform: imagerequest.Transform{
|
|
Size: imagerequest.Size{Width: 800, Height: 600},
|
|
Format: testFormatWebP,
|
|
Quality: 85,
|
|
FitMode: testFitCover,
|
|
},
|
|
},
|
|
// Signed data:
|
|
// "cdn.example.com:/photos/cat.jpg::800:600:webp:1704067200:85:cover"
|
|
wantSignature: "kdqeGoW2SX7qnaYtoB970wEnLydn0UnIgQYQLfAnjXQ=",
|
|
},
|
|
{
|
|
name: "resized with query string",
|
|
req: imagerequest.Request{
|
|
Source: imagerequest.Source{
|
|
Host: testHost,
|
|
Path: testPath,
|
|
Query: "token=abc&v=2",
|
|
},
|
|
Transform: imagerequest.Transform{
|
|
Size: imagerequest.Size{Width: 800, Height: 600},
|
|
Format: testFormatWebP,
|
|
Quality: 85,
|
|
FitMode: testFitCover,
|
|
},
|
|
},
|
|
// Signed data:
|
|
// "cdn.example.com:/photos/cat.jpg:token=abc&v=2:800:600:webp:1704067200:85:cover"
|
|
wantSignature: "pKgVBOTd_Q_EikI7MNQLC9Q8Hurdxzyv3EIYvVhqc2I=",
|
|
},
|
|
{
|
|
name: "original size without query",
|
|
req: imagerequest.Request{
|
|
Source: imagerequest.Source{
|
|
Host: testHost,
|
|
Path: testPath,
|
|
Query: "",
|
|
},
|
|
Transform: imagerequest.Transform{
|
|
Size: imagerequest.Size{Width: 0, Height: 0},
|
|
Format: testFormatPNG,
|
|
Quality: 85,
|
|
FitMode: testFitCover,
|
|
},
|
|
},
|
|
// Signed data:
|
|
// "cdn.example.com:/photos/cat.jpg::0:0:png:1704067200:85:cover"
|
|
wantSignature: "6_rZ0yyVbGZRs8kG7n7HLgLi5Jt8vjiWQljIEL1jbIs=",
|
|
},
|
|
{
|
|
name: "non-default quality and fit",
|
|
req: imagerequest.Request{
|
|
Source: imagerequest.Source{
|
|
Host: testHost,
|
|
Path: testPath,
|
|
Query: "",
|
|
},
|
|
Transform: imagerequest.Transform{
|
|
Size: imagerequest.Size{Width: 800, Height: 600},
|
|
Format: testFormatWebP,
|
|
Quality: 40,
|
|
FitMode: testFitContain,
|
|
},
|
|
},
|
|
// Signed data:
|
|
// "cdn.example.com:/photos/cat.jpg::800:600:webp:1704067200:40:contain"
|
|
wantSignature: "pGaXpPUbI3A7nMx-4T9bfq9bYWBNL0kY4bxlcv3g1F8=",
|
|
},
|
|
}
|
|
}
|
|
|
|
// TestSigner_GoldenVectors pins the exact HMAC-SHA256 signature output for
|
|
// fully-specified requests with a hardcoded signing key.
|
|
//
|
|
// If any of these assertions fail, the signed byte format
|
|
// ("host:path:query:width:height:format:expiration:quality:fit") or the
|
|
// base64url encoding has changed. Update these constants only when that
|
|
// change is intended.
|
|
func TestSigner_GoldenVectors(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
signer := signature.New(goldenSigningKey)
|
|
|
|
for _, tt := range goldenVectors() {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
signReq := tt.req
|
|
signReq.Expires = time.Unix(goldenExpiresUnix, 0)
|
|
|
|
gotSignature := signer.Sign(&signReq)
|
|
if gotSignature != tt.wantSignature {
|
|
t.Errorf("Sign() = %q, want %q (signed byte format changed?)",
|
|
gotSignature, tt.wantSignature)
|
|
}
|
|
})
|
|
}
|
|
}
|