diff --git a/apps/ai-game-creator-shell/src-tauri/src/export/draft/taonier/build.rs b/apps/ai-game-creator-shell/src-tauri/src/export/draft/taonier/build.rs index c20b25dc6..078132dce 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/export/draft/taonier/build.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/export/draft/taonier/build.rs @@ -262,7 +262,23 @@ pub(crate) fn read_artifact_package(root: &Path) -> Result Result<(Vec, String), String> { - let mut file = std::fs::File::open(path) + // 不跟随符号链接打开:`read_artifact_package` 先做了 symlink_metadata 检查,这里用 + // no-follow 打开关掉「检查后、打开前被换成同长度链接」的 TOCTOU 窗口。 + let mut options = std::fs::OpenOptions::new(); + options.read(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + options.custom_flags(libc::O_CLOEXEC | libc::O_NOFOLLOW); + } + #[cfg(windows)] + { + use std::os::windows::fs::OpenOptionsExt; + const FILE_FLAG_OPEN_REPARSE_POINT: u32 = 0x0020_0000; + options.custom_flags(FILE_FLAG_OPEN_REPARSE_POINT); + } + let mut file = options + .open(path) .map_err(|error| format!("打开陶泥儿产物失败:{}:{error}", path.display()))?; let mut hasher = Sha256::new(); let mut bytes = Vec::with_capacity(expected_bytes as usize);