From 6724f8b7073a6b3a1819e94041a86377abfa96c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Mon, 21 Sep 2026 15:13:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B6=E7=B4=A7=E4=BA=A7=E7=89=A9=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D=E5=AF=B9=E8=BF=9C=E7=AB=AF=E6=89=A9=E5=B1=95?= =?UTF-8?q?=E5=90=8D=E7=9A=84=E5=8F=96=E5=80=BC=20-=20filename=20=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=8F=96=E8=BF=9C=E7=AB=AF=20URL=20=E6=9C=AB=E6=AE=B5?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=90=8D=EF=BC=8Cpath=5Fsegments=20=E5=B7=B2?= =?UTF-8?q?=E7=99=BE=E5=88=86=E5=8F=B7=E8=A7=A3=E7=A0=81=EF=BC=8C%2F=20/?= =?UTF-8?q?=20%5C=20=E4=BC=9A=E6=8B=BC=E5=87=BA=E8=B7=A8=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=20-=20=E6=89=A9=E5=B1=95=E5=90=8D=E5=8F=AA?= =?UTF-8?q?=E6=8E=A5=E5=8F=97=E9=95=BF=E5=BA=A6=E4=B8=8D=E8=B6=85=E8=BF=87?= =?UTF-8?q?=2010=20=E7=9A=84=20ASCII=20=E5=AD=97=E6=AF=8D=E6=95=B0?= =?UTF-8?q?=E5=AD=97=EF=BC=8C=E5=85=B6=E4=BD=99=E4=B8=80=E5=BE=8B=E9=80=80?= =?UTF-8?q?=E5=9B=9E=20glb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server-rs/crates/platform-tripo/src/common/types.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/server-rs/crates/platform-tripo/src/common/types.rs b/server-rs/crates/platform-tripo/src/common/types.rs index 516e95f39..1a8144879 100644 --- a/server-rs/crates/platform-tripo/src/common/types.rs +++ b/server-rs/crates/platform-tripo/src/common/types.rs @@ -13,6 +13,9 @@ use crate::{ text_to_model::result::TripoTextToModelResult, }; +/// 从远端地址推导出的扩展名上限;超出即按不可信处理,退回默认扩展名。 +const MAX_ARTIFACT_FILE_EXTENSION_LEN: usize = 10; + #[derive(Clone, Debug, Eq, PartialEq)] pub struct TripoTaskHandle { pub task_id: String, @@ -167,6 +170,9 @@ impl TripoDownloadedArtifact { } } + /// 产物落盘用的文件名。远端地址不可信,扩展名只接受短的字母数字 token: + /// `Url::path_segments` 已做百分号解码,`%2F` / `%5C` 这类编码分隔符会直接 + /// 进到扩展名里,不过滤就会拼出跨目录的路径。 pub fn filename(&self, name: &str) -> String { let extension = self .url @@ -175,7 +181,11 @@ impl TripoDownloadedArtifact { .and_then(|segments| segments.last()) .and_then(|segment| segment.rsplit_once('.')) .map(|(_, extension)| extension) - .filter(|extension| !extension.is_empty()) + .filter(|extension| { + !extension.is_empty() + && extension.len() <= MAX_ARTIFACT_FILE_EXTENSION_LEN + && extension.bytes().all(|byte| byte.is_ascii_alphanumeric()) + }) .unwrap_or("glb"); format!("{name}.{extension}") }