util: Keep default permissions when extracting Zip with unset permissions (#45515)

This ensures that we do not extract files with no permissions (`0o000`),
because these would become unusable on the host

Release Notes:

- N/A
This commit is contained in:
Finn Evers
2025-12-22 10:28:11 +01:00
committed by GitHub
parent 397fcf6083
commit 746b76488c

View File

@@ -109,7 +109,9 @@ pub async fn extract_seekable_zip<R: AsyncRead + AsyncSeek + Unpin>(
.await
.with_context(|| format!("extracting into file {path:?}"))?;
if let Some(perms) = entry.unix_permissions() {
if let Some(perms) = entry.unix_permissions()
&& perms != 0o000
{
use std::os::unix::fs::PermissionsExt;
let permissions = std::fs::Permissions::from_mode(u32::from(perms));
file.set_permissions(permissions)
@@ -132,7 +134,8 @@ mod tests {
use super::*;
async fn compress_zip(src_dir: &Path, dst: &Path) -> Result<()> {
#[allow(unused_variables)]
async fn compress_zip(src_dir: &Path, dst: &Path, keep_file_permissions: bool) -> Result<()> {
let mut out = smol::fs::File::create(dst).await?;
let mut writer = ZipFileWriter::new(&mut out);
@@ -155,8 +158,8 @@ mod tests {
ZipEntryBuilder::new(filename.into(), async_zip::Compression::Deflate);
use std::os::unix::fs::PermissionsExt;
let metadata = std::fs::metadata(path)?;
let perms = metadata.permissions().mode() as u16;
builder = builder.unix_permissions(perms);
let perms = keep_file_permissions.then(|| metadata.permissions().mode() as u16);
builder = builder.unix_permissions(perms.unwrap_or_default());
writer.write_entry_whole(builder, &data).await?;
}
#[cfg(not(unix))]
@@ -206,7 +209,9 @@ mod tests {
let zip_file = test_dir.path().join("test.zip");
smol::block_on(async {
compress_zip(test_dir.path(), &zip_file).await.unwrap();
compress_zip(test_dir.path(), &zip_file, true)
.await
.unwrap();
let reader = read_archive(&zip_file).await;
let dir = tempfile::tempdir().unwrap();
@@ -237,7 +242,9 @@ mod tests {
// Create zip
let zip_file = test_dir.path().join("test.zip");
compress_zip(test_dir.path(), &zip_file).await.unwrap();
compress_zip(test_dir.path(), &zip_file, true)
.await
.unwrap();
// Extract to new location
let extract_dir = tempfile::tempdir().unwrap();
@@ -251,4 +258,39 @@ mod tests {
assert_eq!(extracted_perms.mode() & 0o777, 0o755);
});
}
#[cfg(unix)]
#[test]
fn test_extract_zip_sets_default_permissions() {
use std::os::unix::fs::PermissionsExt;
smol::block_on(async {
let test_dir = tempfile::tempdir().unwrap();
let executable_path = test_dir.path().join("my_script");
// Create an executable file
std::fs::write(&executable_path, "#!/bin/bash\necho 'Hello'").unwrap();
// Create zip
let zip_file = test_dir.path().join("test.zip");
compress_zip(test_dir.path(), &zip_file, false)
.await
.unwrap();
// Extract to new location
let extract_dir = tempfile::tempdir().unwrap();
let reader = read_archive(&zip_file).await;
extract_zip(extract_dir.path(), reader).await.unwrap();
// Check permissions are preserved
let extracted_path = extract_dir.path().join("my_script");
assert!(extracted_path.exists());
let extracted_perms = std::fs::metadata(&extracted_path).unwrap().permissions();
assert_eq!(
extracted_perms.mode() & 0o777,
0o644,
"Expected default set of permissions for unzipped file with no permissions set."
);
});
}
}