diff --git a/Cargo.lock b/Cargo.lock index 4a481398..2a720da3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -910,7 +910,7 @@ dependencies = [ "web-time", "windows-sys 0.61.2", "xor_name", - "zip 2.4.2", + "zip", ] [[package]] @@ -973,7 +973,7 @@ dependencies = [ "tracing-appender", "tracing-subscriber", "xor_name", - "zip 9.0.5", + "zip", ] [[package]] @@ -1001,15 +1001,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" -dependencies = [ - "derive_arbitrary", -] - [[package]] name = "arc-swap" version = "1.9.2" @@ -1648,7 +1639,7 @@ dependencies = [ "arrayvec", "cc", "cfg-if", - "constant_time_eq 0.4.2", + "constant_time_eq", "cpufeatures 0.3.0", ] @@ -1787,25 +1778,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bzip2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47" -dependencies = [ - "bzip2-sys", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "c-kzg" version = "2.1.7" @@ -2098,12 +2070,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "constant_time_eq" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" - [[package]] name = "constant_time_eq" version = "0.4.2" @@ -2364,12 +2330,6 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" -[[package]] -name = "deflate64" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" - [[package]] name = "der" version = "0.7.10" @@ -2430,17 +2390,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive_arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "derive_more" version = "2.1.1" @@ -3110,11 +3059,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] @@ -4019,27 +3966,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lzma-rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" -dependencies = [ - "byteorder", - "crc", -] - -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "macro-string" version = "0.2.0" @@ -8096,15 +8022,6 @@ dependencies = [ "tiny-keccak", ] -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yasna" version = "0.5.2" @@ -8241,36 +8158,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "zip" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50" -dependencies = [ - "aes", - "arbitrary", - "bzip2", - "constant_time_eq 0.3.1", - "crc32fast", - "crossbeam-utils", - "deflate64", - "displaydoc", - "flate2", - "getrandom 0.3.4", - "hmac", - "indexmap 2.14.0", - "lzma-rs", - "memchr", - "pbkdf2", - "sha1", - "thiserror 2.0.18", - "time", - "xz2", - "zeroize", - "zopfli", - "zstd", -] - [[package]] name = "zip" version = "9.0.5" @@ -8290,43 +8177,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zopfli" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" -dependencies = [ - "bumpalo", - "crc32fast", - "log", - "simd-adler32", -] - -[[package]] -name = "zstd" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "7.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" -dependencies = [ - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "2.0.16+zstd.1.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" -dependencies = [ - "cc", - "pkg-config", -] diff --git a/ant-core/Cargo.toml b/ant-core/Cargo.toml index 8b6d3179..1a3b272f 100644 --- a/ant-core/Cargo.toml +++ b/ant-core/Cargo.toml @@ -52,7 +52,9 @@ toml = { version = "0.8" } tokio = { version = "1", features = ["sync", "macros"] } tokio-util = { version = "0.7", features = ["rt"], optional = true } utoipa = { version = "5", features = ["axum_extras"], optional = true } -zip = { version = "2", optional = true } +# Release .zip archives are Deflate-only. zip decodes them through flate2, on +# the backend the `flate2` dependency above selects. +zip = { version = "9", default-features = false, features = ["deflate-flate2"], optional = true } tower-http = { version = "0.6.8", features = ["cors"], optional = true } # Data operations diff --git a/ant-core/src/node/binary.rs b/ant-core/src/node/binary.rs index 87d7675f..0a32b7ff 100644 --- a/ant-core/src/node/binary.rs +++ b/ant-core/src/node/binary.rs @@ -720,6 +720,76 @@ mod tests { ); } + /// Build an in-memory zip archive from `(entry name, contents)` pairs. + fn zip_archive(entries: &[(&str, &[u8])]) -> Vec { + let mut writer = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); + for (name, data) in entries { + writer + .start_file(*name, zip::write::SimpleFileOptions::default()) + .unwrap(); + std::io::Write::write_all(&mut writer, data).unwrap(); + } + writer.finish().unwrap().into_inner() + } + + #[test] + fn extract_zip_finds_binary_in_subdirectory() { + // Windows release archives carry `.exe`, possibly under a folder. + let tmp = tempfile::tempdir().unwrap(); + let exe = format!("{BINARY_NAME}.exe"); + let data = b"fake binary"; + let zip_data = zip_archive(&[(&format!("release/{exe}"), data)]); + + let result = extract_zip(&zip_data, tmp.path(), BINARY_NAME).unwrap(); + assert_eq!(result.binary_path, tmp.path().join(&exe)); + assert_eq!(std::fs::read(&result.binary_path).unwrap(), data); + assert!(result.bootstrap_peers_path.is_none()); + } + + #[test] + fn extract_zip_finds_bootstrap_peers() { + let tmp = tempfile::tempdir().unwrap(); + let bp_data = b"[peers]\naddrs = [\"1.2.3.4:5000\"]\n"; + let zip_data = zip_archive(&[ + (BINARY_NAME, b"fake binary"), + (BOOTSTRAP_PEERS_FILE, bp_data), + ]); + + let result = extract_zip(&zip_data, tmp.path(), BINARY_NAME).unwrap(); + assert!(result.binary_path.exists()); + let bp_path = result.bootstrap_peers_path.unwrap(); + assert_eq!(bp_path, tmp.path().join(BOOTSTRAP_PEERS_FILE)); + assert_eq!(std::fs::read(&bp_path).unwrap(), bp_data); + } + + #[test] + fn extract_zip_missing_binary_errors() { + let tmp = tempfile::tempdir().unwrap(); + let zip_data = zip_archive(&[("README.md", b"no binary here")]); + + let result = extract_zip(&zip_data, tmp.path(), BINARY_NAME); + assert!(result.is_err()); + } + + #[test] + fn extract_zip_skips_path_traversal_entry() { + // An entry that escapes the archive root has no enclosed name, so it + // is skipped rather than matched by its final path component. + let tmp = tempfile::tempdir().unwrap(); + let install_dir = tmp.path().join("install"); + std::fs::create_dir(&install_dir).unwrap(); + let zip_data = zip_archive(&[(&format!("../{BINARY_NAME}"), b"malicious content")]); + + let result = extract_zip(&zip_data, &install_dir, BINARY_NAME); + let err = result.unwrap_err().to_string(); + assert!( + err.contains("not found"), + "expected not-found error, got: {err}" + ); + assert!(!tmp.path().join(BINARY_NAME).exists()); + assert!(!install_dir.join(BINARY_NAME).exists()); + } + #[tokio::test] async fn resolve_version_uses_cache() { let tmp = tempfile::tempdir().unwrap();