package signature_test import ( "errors" "testing" "time" "sneak.berlin/go/pixa/internal/signature" ) // signedQualityFitRequest returns a request signed for quality 85 and fit // mode "cover", the effective defaults the handler applies before // verification. func signedQualityFitRequest(signer *signature.Signer) *signature.Request { req := &signature.Request{ SourceHost: testHost, SourcePath: testPath, Width: 800, Height: 600, Format: testFormatWebP, Quality: 85, FitMode: "cover", Expires: time.Now().Add(1 * time.Hour), } req.Signature = signer.Sign(req) return req } // TestSigner_Verify_QualityAndFitAreSigned proves that quality and fit are // covered by the signature: a URL signed for one quality or fit mode must // not verify when replayed with a different quality or fit mode. This is the // amplification vector from the issue — one signed URL replayed across many // quality and fit values yields many unauthorized cache entries and // transcodes — so it must be rejected. func TestSigner_Verify_QualityAndFitAreSigned(t *testing.T) { t.Parallel() signer := signature.New("test-secret-key") cases := []struct { name string tamper func(r *signature.Request) }{ { name: "replayed with different quality", tamper: func(r *signature.Request) { r.Quality = 40 }, }, { name: "replayed with different fit mode", tamper: func(r *signature.Request) { r.FitMode = "contain" }, }, } for _, tt := range cases { t.Run(tt.name, func(t *testing.T) { t.Parallel() req := signedQualityFitRequest(signer) tt.tamper(req) err := signer.Verify(req) if !errors.Is(err, signature.ErrInvalid) { t.Errorf("Verify() = %v, want %v", err, signature.ErrInvalid) } }) } }