From e2664e33ed3bc4aa02606c8f8ac2e653968981e3 Mon Sep 17 00:00:00 2001 From: Mick Grove Date: Wed, 1 Apr 2026 17:25:19 -0700 Subject: [PATCH] updated dependencies --- Cargo.toml | 6 ++--- crates/kingfisher-scanner/Cargo.toml | 2 +- src/decompress.rs | 40 +++++++++++++++++++++++++++- tests/live_db_validation.rs | 33 ++++++++++++----------- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b6daf0..8da4df7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,7 @@ base32 = "0.5.1" crossbeam-skiplist = "0.1.3" tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"] } mongodb = { version = "3.4", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"] } -mysql_async = { version = "0.36.1", default-features = false, features = ["default-rustls"] } +mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"] } bson = { version = "3.1.0", features = ["serde"] } ring = "0.17.14" pem = "3.0.6" @@ -195,7 +195,7 @@ tree_magic_mini = "3.2" content_inspector = "0.2.4" rustc-hash = "2.1.1" bzip2-rs = "0.1.2" -zip = { version = "2.4.2", default-features = false, features = ["deflate", "deflate64", "time"] } +zip = { version = "8.5.0", default-features = false, features = ["deflate", "deflate64", "time"] } tar = "0.4.44" lzma-rs = "0.3.0" asar = "0.3.0" @@ -282,7 +282,7 @@ temp-env = "0.3.6" wiremock = "0.6.5" git2 = { version = "0.20.3", default-features = false } rand_chacha = "0.10.0" -testcontainers = "0.15.0" +testcontainers = "0.27.2" [profile.release] debug = false diff --git a/crates/kingfisher-scanner/Cargo.toml b/crates/kingfisher-scanner/Cargo.toml index 2a760b7..cf24224 100644 --- a/crates/kingfisher-scanner/Cargo.toml +++ b/crates/kingfisher-scanner/Cargo.toml @@ -173,7 +173,7 @@ hex = { workspace = true, optional = true } url = { version = "2.5.7", optional = true } bson = { version = "3.1.0", features = ["serde"], optional = true } mongodb = { version = "3.4", default-features = false, features = ["rustls-tls", "aws-auth", "compat-3-0-0", "dns-resolver"], optional = true } -mysql_async = { version = "0.36.1", default-features = false, features = ["default-rustls"], optional = true } +mysql_async = { version = "0.36.2", default-features = false, features = ["default-rustls"], optional = true } tokio-postgres = { version = "0.7", default-features = false, features = ["runtime"], optional = true } tokio-postgres-rustls = { version = "0.13.0", optional = true } rustls = { version = "0.23.35", optional = true } diff --git a/src/decompress.rs b/src/decompress.rs index c3bc804..94cbc1f 100644 --- a/src/decompress.rs +++ b/src/decompress.rs @@ -348,11 +348,12 @@ pub fn decompress_file_to_temp(path: &Path) -> Result<(CompressedContent, TempDi #[cfg(test)] mod tests { - use std::fs::File; + use std::{fs::File, io::Write}; use flate2::{write::GzEncoder, Compression}; use tar::Builder; use tempfile::tempdir; + use zip::{write::SimpleFileOptions, CompressionMethod, ZipWriter}; use super::{decompress_once, CompressedContent}; @@ -454,6 +455,43 @@ mod tests { Ok(()) } + #[test] + fn smoke_decompress_zip_archive() -> anyhow::Result<()> { + let dir = tempdir()?; + let zip_path = dir.path().join("payload.zip"); + let github_pat = "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"; // this is not a real secret + + { + let file = File::create(&zip_path)?; + let mut zip = ZipWriter::new(file); + let options = SimpleFileOptions::default() + .compression_method(CompressionMethod::Deflated) + .unix_permissions(0o644); + + zip.start_file("nested/secret.txt", options)?; + zip.write_all(format!("token={github_pat}\n").as_bytes())?; + zip.finish()?; + } + + let tmp = tempdir()?; + let content = decompress_once(&zip_path, Some(tmp.path()))?; + if let CompressedContent::ArchiveFiles(files) = content { + let mut found = false; + for (logical, path) in files { + if logical.ends_with("!nested/secret.txt") { + let txt = std::fs::read_to_string(&path)?; + assert!(txt.contains(github_pat)); + found = true; + } + } + assert!(found, "did not find nested/secret.txt in ArchiveFiles"); + } else { + panic!("expected ArchiveFiles for zip archive, got {:?}", content); + } + + Ok(()) + } + /// 3) Nested archive: outer.tar.gz ──▶ outer.tar (contains inner.tar.gz) └──▶ inner.tar.gz /// ──▶ inner.tar (contains secret.txt) #[test] diff --git a/tests/live_db_validation.rs b/tests/live_db_validation.rs index dd5c12d..78801cd 100644 --- a/tests/live_db_validation.rs +++ b/tests/live_db_validation.rs @@ -8,7 +8,11 @@ use std::time::{Duration, Instant}; use anyhow::{anyhow, Result}; use kingfisher::validation::{validate_mysql, validate_postgres}; -use testcontainers::{clients::Cli, core::WaitFor, GenericImage}; +use testcontainers::{ + core::{IntoContainerPort, WaitFor}, + runners::AsyncRunner, + GenericImage, ImageExt, +}; use tokio::{net::TcpStream, time::sleep}; const HOST_ALIAS: &str = "kingfisherlocal"; @@ -40,15 +44,15 @@ async fn wait_for_port(host: &str, port: u16) -> Result<()> { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[ignore] async fn validates_mysql_secret_against_testcontainer() -> Result<()> { - let docker = Cli::default(); - let image = GenericImage::new("mysql", "8.4") + let container = GenericImage::new("mysql", "8.4") + .with_exposed_port(3306.tcp()) + .with_wait_for(WaitFor::message_on_stdout("MySQL init process done. Ready for start up.")) .with_env_var("MYSQL_ROOT_PASSWORD", "secret") .with_env_var("MYSQL_DATABASE", "app") .with_env_var("MYSQL_ROOT_HOST", "%") - .with_wait_for(WaitFor::message_on_stdout("MySQL init process done. Ready for start up.")); - - let container = docker.run(image); - let port = container.get_host_port_ipv4(3306); + .start() + .await?; + let port = container.get_host_port_ipv4(3306.tcp()).await?; wait_for_port(HOST_ALIAS, port).await?; @@ -66,21 +70,19 @@ async fn validates_mysql_secret_against_testcontainer() -> Result<()> { ); drop(container); - drop(docker); Ok(()) } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] #[ignore] async fn validates_postgres_secret_against_testcontainer() -> Result<()> { - let docker = Cli::default(); - let image = GenericImage::new("postgres", "15") + let container = GenericImage::new("postgres", "15") + .with_exposed_port(5432.tcp()) + .with_wait_for(WaitFor::message_on_stdout("database system is ready to accept connections")) .with_env_var("POSTGRES_PASSWORD", "secret") - .with_wait_for(WaitFor::message_on_stdout( - "database system is ready to accept connections", - )); - let container = docker.run(image); - let port = container.get_host_port_ipv4(5432); + .start() + .await?; + let port = container.get_host_port_ipv4(5432.tcp()).await?; wait_for_port(HOST_ALIAS, port).await?; @@ -91,6 +93,5 @@ async fn validates_postgres_secret_against_testcontainer() -> Result<()> { assert!(metadata.is_empty(), "expected no metadata but found {metadata:?}"); drop(container); - drop(docker); Ok(()) }