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}") }