fix(media-proxy): bound animated transforms by output pixels (#3402)

This commit is contained in:
Hampus authored and GitHub committed 2026-10-11 18:46:34 +02:00
1 parent 50c28aa8fa
commit 5a2d81ebbb
8 files changed
+72 -10

No files matched your search

@@ -182,7 +182,7 @@ impl GifResizeDims {
(self.width as usize)
.saturating_mul(self.height as usize)
.saturating_mul(frames.max(1) as usize)
<= constants::MAX_GIF_RESIZE_OUTPUT_PIXELS
<= constants::MAX_ANIMATED_OUTPUT_PIXELS
}
}
@@ -13,8 +13,8 @@ use super::image_probe::{
};
use super::loaded_image::{
normalize_vips_image_to_uchar, page_height, resize_loaded_image, resize_loaded_image_by_scale,
validate_dimensions_u32, validate_jxl_decode_pixels, validate_static_output_pixels,
validate_vips_image,
static_output_pixels, validate_dimensions_u32, validate_jxl_decode_pixels,
validate_static_output_pixels, validate_vips_image,
};
use super::native_runtime::{clear_vips_error, ensure_vips_init, last_vips_error};
use super::transform_plan::{
@@ -128,6 +128,31 @@ fn ensure_decode_cost_admissible(
admitted
}
fn ensure_animated_output_admissible(
input: &[u8],
options: &ImageOptions,
media_limits: &MediaLimits,
) -> Result<(), MediaError> {
let dims = probe_image_dims(media_limits, input)?;
if dims.pages <= 1 {
return Ok(());
}
let output_pixels = static_output_pixels(options, dims).saturating_mul(dims.pages as usize);
if output_pixels <= constants::MAX_ANIMATED_OUTPUT_PIXELS {
return Ok(());
}
tracing::warn!(
input_bytes = input.len(),
width = dims.width,
height = dims.height,
pages = dims.pages,
out_width = options.width.unwrap_or(0),
out_height = options.height.unwrap_or(0),
"animated transform refused by the output work budget"
);
Err(MediaError::TooLargeToProcess)
}
pub fn transform_image(
input: &[u8],
options: &ImageOptions,
@@ -184,6 +209,9 @@ pub fn transform_image(
content_type: "image/gif",
});
}
if animated {
ensure_animated_output_admissible(input, options, media_limits)?;
}
if should_transform_animated_webp_direct(sniffed, options, format) {
validate_dimensions_u32(media_limits, sniffed.width, sniffed.height)?;
let resolved = resolve_auto_quality(