Skip to content

Commit

Permalink
Sanitize filenames during zip extraction
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Oct 31, 2024
1 parent 8d3408f commit bb82076
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 2 deletions.
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ uv-virtualenv = { path = "crates/uv-virtualenv" }
uv-warnings = { path = "crates/uv-warnings" }
uv-workspace = { path = "crates/uv-workspace" }

sanitize-filename = { version = "0.5.0" }
anstream = { version = "0.6.15" }
anyhow = { version = "1.0.89" }
async-channel = { version = "2.3.1" }
Expand Down
1 change: 1 addition & 0 deletions crates/uv-extract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ md-5 = { workspace = true }
rayon = { workspace = true }
reqwest = { workspace = true }
rustc-hash = { workspace = true }
sanitize-filename = { workspace = true }
sha2 = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
Expand Down
16 changes: 14 additions & 2 deletions crates/uv-extract/src/stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
reader: R,
target: impl AsRef<Path>,
) -> Result<(), Error> {
/// Sanitize a filename for use on Windows.
fn sanitize(filename: impl AsRef<str>) -> String {
sanitize_filename::sanitize_with_options(
filename,
sanitize_filename::Options {
windows: cfg!(windows),
truncate: false,
replacement: "",
},
)
}

let target = target.as_ref();
let mut reader = futures::io::BufReader::with_capacity(DEFAULT_BUF_SIZE, reader.compat());
let mut zip = async_zip::base::read::stream::ZipFileReader::new(&mut reader);
Expand All @@ -28,7 +40,7 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
while let Some(mut entry) = zip.next_with_entry().await? {
// Construct the (expected) path to the file on-disk.
let path = entry.reader().entry().filename().as_str()?;
let path = target.join(path);
let path = target.join(sanitize(path));
let is_dir = entry.reader().entry().dir()?;

// Either create the directory or write the file to disk.
Expand Down Expand Up @@ -84,7 +96,7 @@ pub async fn unzip<R: tokio::io::AsyncRead + Unpin>(
if has_any_executable_bit != 0 {
// Construct the (expected) path to the file on-disk.
let path = entry.filename().as_str()?;
let path = target.join(path);
let path = target.join(sanitize(path));

let permissions = fs_err::tokio::metadata(&path).await?.permissions();
if permissions.mode() & 0o111 != 0o111 {
Expand Down

0 comments on commit bb82076

Please sign in to comment.