Bound imageprocessor.Process input read to prevent unbounded memory use (#37)
All checks were successful
check / check (push) Successful in 4s
All checks were successful
check / check (push) Successful in 4s
closes #31 ## Problem `ImageProcessor.Process` used `io.ReadAll(input)` without any size limit, allowing arbitrarily large inputs to exhaust all available memory. This is a DoS vector — even though the upstream fetcher has a `MaxResponseSize` limit (50 MiB), the processor interface accepts any `io.Reader` and should defend itself independently. Additionally, the service layer's `processFromSourceOrFetch` read cached source content with `io.ReadAll` without a bound, so an unexpectedly large cached file could also cause unbounded memory consumption. ## Changes ### Processor (`processor.go`) - Added `maxInputBytes` field to `ImageProcessor` (configurable, defaults to 50 MiB via `DefaultMaxInputBytes`) - `NewImageProcessor` now accepts a `maxInputBytes` parameter (0 or negative uses the default) - `Process` now wraps the input reader with `io.LimitReader` and rejects inputs exceeding the limit with `ErrInputDataTooLarge` - Added `DefaultMaxInputBytes` and `ErrInputDataTooLarge` exported constants/errors ### Service (`service.go`) - `NewService` now wires the fetcher's `MaxResponseSize` through to the processor - Extracted `loadCachedSource` helper method to flatten nesting in `processFromSourceOrFetch` - Cached source reads are now bounded by `maxResponseSize` — oversized cached files are discarded and re-fetched ### Tests (`processor_test.go`) - `TestImageProcessor_RejectsOversizedInputData` — verifies that inputs exceeding `maxInputBytes` are rejected with `ErrInputDataTooLarge` - `TestImageProcessor_AcceptsInputWithinLimit` — verifies that inputs within the limit are processed normally - `TestImageProcessor_DefaultMaxInputBytes` — verifies that 0 and negative values use the default - All existing tests updated to use `NewImageProcessor(0)` (default limit) Co-authored-by: user <user@Mac.lan guest wan> Co-authored-by: clawbot <clawbot@eeqj.de> Reviewed-on: #37 Co-authored-by: clawbot <clawbot@noreply.example.org> Co-committed-by: clawbot <clawbot@noreply.example.org>
This commit was merged in pull request #37.
This commit is contained in:
@@ -199,36 +199,6 @@ type FetchResult struct {
|
||||
TLSCipherSuite string
|
||||
}
|
||||
|
||||
// Processor handles image transformation (resize, format conversion)
|
||||
type Processor interface {
|
||||
// Process transforms an image according to the request
|
||||
Process(ctx context.Context, input io.Reader, req *ImageRequest) (*ProcessResult, error)
|
||||
// SupportedInputFormats returns MIME types this processor can read
|
||||
SupportedInputFormats() []string
|
||||
// SupportedOutputFormats returns formats this processor can write
|
||||
SupportedOutputFormats() []ImageFormat
|
||||
}
|
||||
|
||||
// ProcessResult contains the result of image processing
|
||||
type ProcessResult struct {
|
||||
// Content is the processed image data
|
||||
Content io.ReadCloser
|
||||
// ContentLength is the size in bytes
|
||||
ContentLength int64
|
||||
// ContentType is the MIME type of the output
|
||||
ContentType string
|
||||
// Width is the output image width
|
||||
Width int
|
||||
// Height is the output image height
|
||||
Height int
|
||||
// InputWidth is the original image width before processing
|
||||
InputWidth int
|
||||
// InputHeight is the original image height before processing
|
||||
InputHeight int
|
||||
// InputFormat is the detected input format (e.g., "jpeg", "png")
|
||||
InputFormat string
|
||||
}
|
||||
|
||||
// Storage handles persistent storage of cached content
|
||||
type Storage interface {
|
||||
// Store saves content and returns its hash
|
||||
|
||||
Reference in New Issue
Block a user