package imageprocessor import ( "bytes" "encoding/binary" "errors" "os" "testing" ) // TestImageProcessor_RefusesAVIFAboveMaxPixels verifies that an AVIF output // of more than MaxAVIFOutputPixels is refused with ErrAVIFOutputTooLarge // before anything is decoded, whatever size and fit give it, the source's own // size included, and that an AVIF output of at most that many pixels, or one // in another format, is not. One input is a 64x32 PNG cut off in its pixel // data. The other is testdata/red.avif, a 4x4 AVIF, with the size in its // header changed to 4097x4096. libvips reads each input's size from its // header but fails when it decodes its pixel data, so a request that gets // that failure was not refused, and a refusal came before any decoding. func TestImageProcessor_RefusesAVIFAboveMaxPixels(t *testing.T) { t.Parallel() whole := createTestPNG(t, 64, 32) cutOff := whole[:len(whole)/2] largeAVIF, err := os.ReadFile("testdata/red.avif") if err != nil { t.Fatalf("failed to read test AVIF: %v", err) } // The ispe box gives the width and then the height, 4 bytes each, after // the box's type and its 4 bytes of version and flags ispe := bytes.Index(largeAVIF, []byte("ispe")) binary.BigEndian.PutUint32(largeAVIF[ispe+8:], 4097) binary.BigEndian.PutUint32(largeAVIF[ispe+12:], 4096) tests := []struct { name string input []byte format Format width, height int fit FitMode refused bool }{ {"AVIF at the limit", cutOff, FormatAVIF, 4096, 4096, FitCover, false}, {"AVIF above the limit", cutOff, FormatAVIF, 4096, 4097, FitCover, true}, {"WebP above the AVIF limit", cutOff, FormatWebP, 4096, 4097, FitCover, false}, // FitInside does not enlarge, so the output stays 64x32 {"AVIF inside a larger size", cutOff, FormatAVIF, 8192, 8192, FitInside, false}, // FitOutside scales 64x32 by 128 to cover 8192x1: 8192x4096 {"AVIF outside a smaller size", cutOff, FormatAVIF, 8192, 1, FitOutside, true}, // With no size, the output keeps the source's 4097x4096 {"AVIF of the source's size", largeAVIF, FormatAVIF, 0, 0, FitCover, true}, {"orig of an AVIF source", largeAVIF, FormatOriginal, 0, 0, FitCover, true}, {"JPEG of the source's size", largeAVIF, FormatJPEG, 0, 0, FitCover, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() _, err := New(Params{}).Process(t.Context(), bytes.NewReader(tt.input), &Request{ Size: Size{Width: tt.width, Height: tt.height}, Format: tt.format, FitMode: tt.fit, }) t.Logf("Process() error = %v", err) if tt.refused { if !errors.Is(err, ErrAVIFOutputTooLarge) { t.Errorf("Process() error = %v, want %v", err, ErrAVIFOutputTooLarge) } return } if err == nil || errors.Is(err, ErrAVIFOutputTooLarge) { t.Errorf("Process() error = %v, want the failure to decode "+ "the input", err) } }) } }