From 8a96190db816f056151755d0e1cc8e103fb89db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 12 Sep 2026 21:32:20 +0800 Subject: [PATCH] =?UTF-8?q?=E9=99=90=E5=88=B6=E7=B4=A0=E6=9D=90=E5=88=86?= =?UTF-8?q?=E7=A6=BB=E5=9B=BE=E7=89=87=E7=9A=84=20Base64=20=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对源图和服务端处理图在解码前后统一执行 64 MiB 上限检查,避免无界内存分配。 --- .../commands/separation/workflow/extract.rs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs index f55db59e3..9477e2a37 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/separation/workflow/extract.rs @@ -5,6 +5,27 @@ use std::path::PathBuf; use std::time::Instant; use std::{fs, io::Cursor}; +const MAX_IMAGE_PAYLOAD_BYTES: usize = 64 * 1024 * 1024; + +fn decode_bounded_base64(value: &str, label: &str) -> Result, String> { + if value.len() > (MAX_IMAGE_PAYLOAD_BYTES / 3) * 4 + 4 { + return Err(format!( + "{label}超过 {} MiB 字节上限", + MAX_IMAGE_PAYLOAD_BYTES / 1024 / 1024 + )); + } + let decoded = base64::engine::general_purpose::STANDARD + .decode(value.trim()) + .map_err(|error| format!("解码{label}失败:{error}"))?; + if decoded.len() > MAX_IMAGE_PAYLOAD_BYTES { + return Err(format!( + "{label}超过 {} MiB 字节上限", + MAX_IMAGE_PAYLOAD_BYTES / 1024 / 1024 + )); + } + Ok(decoded) +} + #[derive(Deserialize)] struct RawEditResponse { data: Vec, @@ -54,9 +75,7 @@ async fn raw_extract_inner( .strip_prefix("data:") .and_then(|value| value.strip_suffix(";base64")); let is_png = mime.is_some_and(|value| value.eq_ignore_ascii_case("image/png")); - let image_bytes = base64::engine::general_purpose::STANDARD - .decode(data.trim()) - .map_err(|error| format!("解码源图失败:{error}"))?; + let image_bytes = decode_bounded_base64(data, "源图")?; if image_bytes.is_empty() { return Err("源图不能为空".to_string()); } @@ -199,9 +218,7 @@ async fn write_processed_image_inner( .split_once(',') .map(|(_, data)| data) .ok_or_else(|| "处理图 data URL 无效".to_string())?; - let processed_bytes = base64::engine::general_purpose::STANDARD - .decode(encoded) - .map_err(|error| format!("解析处理图失败:{error}"))?; + let processed_bytes = decode_bounded_base64(encoded, "处理图")?; let dimensions = image::ImageReader::new(Cursor::new(processed_bytes.as_slice())) .with_guessed_format() .map_err(|error| format!("识别处理图格式失败:{error}"))?