fix(tools): validate zip symlink entries on every platform (#350)

Zip symlink entries were only validated on Unix: on Windows they fell through to the regular-file path, so a link escaping the destination was written as a file instead of being rejected (and the escape test failed on Windows CI). The symlink branch now validates on every platform; Unix creates the in-archive link, while Windows skips it, since creating symlinks needs a privilege most users lack.

Co-authored-by: a-rookie-of-C-language <[email protected]>
Цей коміт міститься в:
arookieofcтаa-rookie-of-C-language authored and GitHub committed 2026-10-30 13:18:55 +08:00
1 джерело 3563f70810
коміт 27bef6956e
1 file changed
+10 -2
+10 -2
Переглянути файл
@@ -121,13 +121,16 @@ fn extract_zip(archive: &Path, dest: &Path, cancel: &CancellationToken) -> IdeRe
if let Some(parent) = out.parent() {
fs::create_dir_all(parent)?;
}
#[cfg(unix)]
if file.is_symlink() {
let mut target = String::new();
io::Read::read_to_string(&mut file, &mut target)?;
validate_link(dest, &rel, Path::new(&target))?;
let _ = fs::remove_file(&out);
std::os::unix::fs::symlink(target, &out)?;
#[cfg(unix)]
std::os::unix::fs::symlink(&target, &out)?;
// Windows can't create symlinks without SeCreateSymbolicLinkPrivilege
// (os error 1314), so we drop in-archive links there rather than fail
// the whole extraction. Validation above still rejects escapes.
continue;
}
let mut w = File::create(&out)?;
@@ -485,6 +488,11 @@ mod tests {
assert!(extract(&archive, ArchiveKind::Zip, &dest, "x", &CancellationToken::new()).is_err());
assert!(fs::symlink_metadata(dest.join("link")).is_err());
}
}
#[cfg(unix)]
#[test]
fn zip_internal_symlink_is_created() {
// A link inside the archive (e.g. a framework's Versions/Current) still extracts.
let tmp = tempfile::tempdir().unwrap();
let archive = tmp.path().join("ok.zip");