From 216fc1dbdcfa5c106f2fb090a36d8846a1ae8731 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Fri, 23 Jan 2026 09:52:11 -0800 Subject: [PATCH] Switched compression dependencies to pure-Rust bzip2/lzma implementations and pared zip features to avoid C-based codecs for bz2/xz handling. --- data/rules/netrc.yml | 4 ++++ src/decompress.rs | 36 +++++++++++++++++++++++------------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/data/rules/netrc.yml b/data/rules/netrc.yml index 6b6be12..950e1da 100644 --- a/data/rules/netrc.yml +++ b/data/rules/netrc.yml @@ -10,6 +10,10 @@ rules: password \s+ ([^\s]+) min_entropy: 3.3 confidence: medium + pattern_requirements: + ignore_if_contains: + - '${' + - '$(' examples: - 'machine api.github.com login ziggy^stardust password 012345abcdef' - | diff --git a/src/decompress.rs b/src/decompress.rs index 937bb1a..26e24a3 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -175,22 +175,32 @@ fn handle_asar_archive_in_memory(buffer: &[u8], archive_path: &Path) -> Result(mut decoder: R, out_path: &Path) -> Result { - if !is_safe_extract_path(out_path) { - anyhow::bail!("unsafe path during decompression: {}", out_path.display()); +/// Validate and open a file for reading, checking for path traversal attacks. +fn safe_open_for_read(path: &Path) -> Result { + if !is_safe_extract_path(path) { + anyhow::bail!("unsafe input path during decompression: {}", path.display()); } - let mut out_file = fs::File::create(out_path)?; + Ok(fs::File::open(path)?) +} + +/// Validate and create a file for writing, checking for path traversal attacks. +fn safe_create_for_write(path: &Path) -> Result { + if !is_safe_extract_path(path) { + anyhow::bail!("unsafe output path during decompression: {}", path.display()); + } + Ok(fs::File::create(path)?) +} + +fn stream_to_file(mut decoder: R, out_path: &Path) -> Result { + let mut out_file = safe_create_for_write(out_path)?; std::io::copy(&mut decoder, &mut out_file)?; Ok(CompressedContent::RawFile(out_path.to_owned())) } fn stream_xz_to_file(path: &Path, out_path: &Path) -> Result { - if !is_safe_extract_path(out_path) { - anyhow::bail!("unsafe path during decompression: {}", out_path.display()); - } - let input = fs::File::open(path)?; + let input = safe_open_for_read(path)?; let mut reader = BufReader::new(input); - let mut out_file = fs::File::create(out_path)?; + let mut out_file = safe_create_for_write(out_path)?; xz_decompress(&mut reader, &mut out_file)?; Ok(CompressedContent::RawFile(out_path.to_owned())) } @@ -201,7 +211,7 @@ one *step* of decompression fn decompress_once(path: &Path, base_dir: Option<&Path>) -> Result { let extension = path.extension().and_then(|ext| ext.to_str()).map(|s| s.to_ascii_lowercase()); - let mut file = fs::File::open(path)?; + let mut file = safe_open_for_read(path)?; if let Some(ext) = extension.as_deref() { match ext { @@ -227,12 +237,12 @@ fn decompress_once(path: &Path, base_dir: Option<&Path>) -> Result { let out_path = make_output_path(path, base_dir, "decomp.tar"); - let decoder = GzDecoder::new(fs::File::open(path)?); + let decoder = GzDecoder::new(safe_open_for_read(path)?); return stream_to_file(decoder, &out_path); } "bz2" | "bzip2" => { let out_path = make_output_path(path, base_dir, "decomp.tar"); - let decoder = DecoderReader::new(fs::File::open(path)?); + let decoder = DecoderReader::new(safe_open_for_read(path)?); return stream_to_file(decoder, &out_path); } "xz" => { @@ -241,7 +251,7 @@ fn decompress_once(path: &Path, base_dir: Option<&Path>) -> Result { let out_path = make_output_path(path, base_dir, "decomp.tar"); - let decoder = ZlibDecoder::new(fs::File::open(path)?); + let decoder = ZlibDecoder::new(safe_open_for_read(path)?); return stream_to_file(decoder, &out_path); } _ => {}