Fix 30 critical and high severity bugs from audit passes 6-8
Critical fixes: - Prevent path traversal via rename templates (sanitize_filename) - Prevent input == output data loss (paths_are_same check) - Undo now uses actual executor output paths instead of scanning directory - Filter empty paths from output_files (prevents trashing CWD on undo) - Sanitize URL download filenames to prevent path traversal writes High severity fixes: - Fix EXIF orientation 5/7 transforms per spec - Atomic file creation in find_unique_path (TOCTOU race) - Clean up 0-byte placeholder files on encoding failure - Cap canvas padding to 10000px, total dimensions to 65535 - Clamp crop dimensions to minimum 1px - Clamp DPI to 65535 before u16 cast in JPEG encoder - Force pixel path for non-JPEG/TIFF metadata stripping - Fast path now applies regex find/replace on rename stem - Add output_dpi to needs_pixel_processing check - Cap watermark image scale dimensions to 16384 - Cap template counter padding to 10 - Cap URL download size to 100MB - Fix progress bar NaN when total is zero - Fix calculate_eta underflow when current > total - Fix loaded.len()-1 underflow in preview callbacks - Replace ListItem downcast unwrap with if-let - Fix resize preview division by zero on degenerate images - Clamp rename cursor position to prevent overflow panic - Watch mode: skip output dirs to prevent infinite loop - Watch mode: drop tx sender so channel closes on exit - Watch mode: add delay for partially-written files - Watch mode: warn and skip unmatched files instead of wrong preset - Clean temp download directory on app close - Replace action downcast unwrap with checked if-let - Add BatchResult.output_files for accurate undo tracking
This commit is contained in:
@@ -68,12 +68,12 @@ fn crop_to_aspect_ratio(img: DynamicImage, w_ratio: f64, h_ratio: f64) -> Dynami
|
||||
|
||||
let (crop_w, crop_h) = if current_ratio > target_ratio {
|
||||
// Image is wider than target, crop width
|
||||
let new_w = (ih as f64 * target_ratio) as u32;
|
||||
(new_w.min(iw), ih)
|
||||
let new_w = (ih as f64 * target_ratio).round().max(1.0) as u32;
|
||||
(new_w.min(iw).max(1), ih)
|
||||
} else {
|
||||
// Image is taller than target, crop height
|
||||
let new_h = (iw as f64 / target_ratio) as u32;
|
||||
(iw, new_h.min(ih))
|
||||
let new_h = (iw as f64 / target_ratio).round().max(1.0) as u32;
|
||||
(iw, new_h.min(ih).max(1))
|
||||
};
|
||||
|
||||
let x = iw.saturating_sub(crop_w) / 2;
|
||||
@@ -193,9 +193,11 @@ fn apply_sepia(img: DynamicImage) -> DynamicImage {
|
||||
}
|
||||
|
||||
fn add_canvas_padding(img: DynamicImage, padding: u32) -> DynamicImage {
|
||||
// Cap padding to prevent unreasonable memory allocation
|
||||
let padding = padding.min(10_000);
|
||||
let (w, h) = (img.width(), img.height());
|
||||
let new_w = w.saturating_add(padding.saturating_mul(2));
|
||||
let new_h = h.saturating_add(padding.saturating_mul(2));
|
||||
let new_w = w.saturating_add(padding.saturating_mul(2)).min(65_535);
|
||||
let new_h = h.saturating_add(padding.saturating_mul(2)).min(65_535);
|
||||
|
||||
let mut canvas = RgbaImage::from_pixel(new_w, new_h, Rgba([255, 255, 255, 255]));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user