限制素材分离图片的 Base64 解码大小

对源图和服务端处理图在解码前后统一执行 64 MiB 上限检查,避免无界内存分配。
This commit is contained in:
2026-09-12 21:32:20 +08:00
parent 584da5b76b
commit 8a96190db8
@@ -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<Vec<u8>, 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<RawEditItem>,
@@ -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}"))?