Compare commits

..
2 Commits
Author SHA1 Message Date
clawbot 0bc32bcd7b Refuse a size whose computed side exceeds the maximum (closes #241)
check / check (push) Canceled after 0s
A side given as 0 is computed from the source's proportions, and
nothing held it to 8192: 0x8192 of a 3000x600 source was resized to
40960x8192 and encoded. Process now refuses such a size with
ErrOutputTooLarge once it knows the source's dimensions, before the
resize and the encode, and both image routes answer it 400 with the
JSON error body. It is a processing error, not a failed fetch, so it
is never negative-cached. imgcache.MaxDimension is now
imageprocessor.MaxOutputDimension, so the request limit and the output
limit are one value. README.md says it under Routes and Encrypted URLs.

Model: opus-5-5
2026-10-13 07:00:02 +00:00
clawbot c8d4b9fd13 Test that a size whose computed side exceeds the maximum is refused (part of #241)
check / check (push) Canceled after 0s
Failing tests first. Through both image routes, 0x8192 of a 3000x600
source and 8192x0 of a 600x3000 source must answer 400 with the JSON
error body; orig, a size with both sides given and a 0 side whose
computed side stays within 8192 must be served as before, after the
refusals. In the image processor, a 0 side computed to exactly 8192 is
kept and one above is refused with ErrOutputTooLarge, which does not
exist yet.

Model: opus-5-5
2026-10-13 06:56:26 +00:00
2 changed files with 25 additions and 35 deletions

No files matched your search

+18 -22
View File
@@ -267,10 +267,14 @@ func (p *ImageProcessor) Process(
return nil, ErrInputTooLarge
}
// Determine target dimensions; too large ones are refused before resizing
targetWidth, targetHeight, err := targetDimensions(req.Size, origWidth, origHeight)
if err != nil {
return nil, err
// Determine target dimensions
targetWidth, targetHeight := targetDimensions(req.Size, origWidth, origHeight)
// A side given as 0 is computed from the input's proportions, so it can
// come out above MaxOutputDimension; refuse it before resizing
if targetWidth > MaxOutputDimension || targetHeight > MaxOutputDimension {
return nil, fmt.Errorf("%w: %dx%d is above %d", ErrOutputTooLarge,
targetWidth, targetHeight, MaxOutputDimension)
}
// Resize if needed
@@ -307,29 +311,21 @@ func (p *ImageProcessor) Process(
// targetDimensions calculates the output dimensions for a requested size,
// scaling proportionally when only one dimension is given and keeping the
// original dimensions when both are zero. A side scaled proportionally can
// come out above MaxOutputDimension, which is ErrOutputTooLarge.
func targetDimensions(size Size, origWidth, origHeight int) (int, int, error) {
width, height := size.Width, size.Height
// original dimensions when both are zero.
func targetDimensions(size Size, origWidth, origHeight int) (int, int) {
switch {
case width == 0 && height == 0:
case size.Width == 0 && size.Height == 0:
// Both are 0: keep original size
return origWidth, origHeight, nil
case width == 0:
return origWidth, origHeight
case size.Width == 0:
// Only height specified: calculate width proportionally
width = origWidth * height / origHeight
case height == 0:
return origWidth * size.Height / origHeight, size.Height
case size.Height == 0:
// Only width specified: calculate height proportionally
height = origHeight * width / origWidth
return size.Width, origHeight * size.Width / origWidth
default:
return size.Width, size.Height
}
if width > MaxOutputDimension || height > MaxOutputDimension {
return 0, 0, fmt.Errorf("%w: %dx%d is above %d", ErrOutputTooLarge,
width, height, MaxOutputDimension)
}
return width, height, nil
}
// MIME types for the supported image formats.
+7 -13
View File
@@ -22,12 +22,6 @@ import (
func TestImageProxyRefusesComputedSideAboveMaximum(t *testing.T) {
t.Parallel()
// The upstream paths of the two sources
const (
widePath = "/wide.png"
tallPath = "/tall.png"
)
wide := encodeTestPNG(t, 3000, 600)
tall := encodeTestPNG(t, 600, 3000)
@@ -35,7 +29,7 @@ func TestImageProxyRefusesComputedSideAboveMaximum(t *testing.T) {
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/png")
if r.URL.Path == tallPath {
if r.URL.Path == "/tall.png" {
_, _ = w.Write(tall)
return
@@ -61,12 +55,12 @@ func TestImageProxyRefusesComputedSideAboveMaximum(t *testing.T) {
// wantWidth and wantHeight are the served image's, 0 for a refusal
wantWidth, wantHeight int
}{
{widePath, "0x8192", 0, 8192, 0, 0},
{tallPath, "8192x0", 8192, 0, 0, 0},
{widePath, "orig", 0, 0, 3000, 600},
{widePath, "300x200", 300, 200, 300, 200},
{widePath, "0x300", 0, 300, 1500, 300},
{tallPath, "300x0", 300, 0, 300, 1500},
{"/wide.png", "0x8192", 0, 8192, 0, 0},
{"/tall.png", "8192x0", 8192, 0, 0, 0},
{"/wide.png", "orig", 0, 0, 3000, 600},
{"/wide.png", "300x200", 300, 200, 300, 200},
{"/wide.png", "0x300", 0, 300, 1500, 300},
{"/tall.png", "300x0", 300, 0, 300, 1500},
}
for _, tt := range tests {