From 05ff67b2cda0950812473c44b1c4361423d1e173 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Tue, 7 Apr 2026 01:01:17 +0000 Subject: [PATCH 1/2] fix(tar): pass -C directory to create_tar for VFS file resolution create_tar() never received the change_dir parameter from -C flag, so file paths were resolved relative to cwd instead of the specified directory. Now resolves files relative to -C path when provided. Closes #1118 --- crates/bashkit/src/builtins/archive.rs | 19 ++++++++++-- .../tests/spec_cases/bash/tar_create.test.sh | 29 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 crates/bashkit/tests/spec_cases/bash/tar_create.test.sh diff --git a/crates/bashkit/src/builtins/archive.rs b/crates/bashkit/src/builtins/archive.rs index 1606a98d..bc0b4ff3 100644 --- a/crates/bashkit/src/builtins/archive.rs +++ b/crates/bashkit/src/builtins/archive.rs @@ -165,7 +165,15 @@ impl Builtin for Tar { 2, )); } - create_tar(&ctx, &archive_name, &files, verbose, gzip).await + create_tar( + &ctx, + &archive_name, + &files, + verbose, + gzip, + change_dir.as_deref(), + ) + .await } else if extract { extract_tar( &ctx, @@ -192,12 +200,19 @@ async fn create_tar( files: &[String], verbose: bool, gzip: bool, + change_dir: Option<&str>, ) -> Result { let mut output_data: Vec = Vec::new(); let mut verbose_output = String::new(); + let base_dir = if let Some(dir) = change_dir { + resolve_path(ctx.cwd, dir) + } else { + ctx.cwd.clone() + }; + for file in files { - let path = resolve_path(ctx.cwd, file); + let path = resolve_path(&base_dir, file); if !ctx.fs.exists(&path).await.unwrap_or(false) { return Ok(ExecResult::err( diff --git a/crates/bashkit/tests/spec_cases/bash/tar_create.test.sh b/crates/bashkit/tests/spec_cases/bash/tar_create.test.sh new file mode 100644 index 00000000..6793d067 --- /dev/null +++ b/crates/bashkit/tests/spec_cases/bash/tar_create.test.sh @@ -0,0 +1,29 @@ +# tar create tests +# Tests tar -c with VFS files and -C flag (issue #1118) + +### tar_create_basic +# tar -c creates an archive from VFS files +echo "content" > /tmp/test.txt +tar -c -f /tmp/test.tar /tmp/test.txt +test -f /tmp/test.tar && echo "archive created" +### expect +archive created +### end + +### tar_create_with_C_flag +# tar -c -C resolves files relative to the given directory +echo "hello" > /tmp/file.txt +tar -c -f /tmp/out.tar -C /tmp file.txt +test -f /tmp/out.tar && echo "archive created" +### expect +archive created +### end + +### tar_create_gzip +# tar -czf creates a gzip archive +echo "data" > /tmp/gz.txt +tar -c -z -f /tmp/gz.tar.gz -C /tmp gz.txt +test -f /tmp/gz.tar.gz && echo "archive created" +### expect +archive created +### end From be371604ae2f84187df4ab9667bdf807b80ea232 Mon Sep 17 00:00:00 2001 From: Mykhailo Chalyi Date: Tue, 7 Apr 2026 01:10:39 +0000 Subject: [PATCH 2/2] fix(test): increase file count limit for tar bomb test Now that tar -c correctly uses -C directory, the creation step needs more headroom since it actually reads source files via VFS. --- crates/bashkit/tests/threat_model_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bashkit/tests/threat_model_tests.rs b/crates/bashkit/tests/threat_model_tests.rs index 3f89c557..55ce8227 100644 --- a/crates/bashkit/tests/threat_model_tests.rs +++ b/crates/bashkit/tests/threat_model_tests.rs @@ -2404,7 +2404,7 @@ mod archive_security { /// TM-DOS-008: Tar with many files — FS file count limit blocks extraction #[tokio::test] async fn threat_tar_bomb_many_files_blocked() { - let limits = FsLimits::new().max_file_count(20); + let limits = FsLimits::new().max_file_count(30); let fs = Arc::new(InMemoryFs::with_limits(limits)); let mut bash = Bash::builder().fs(fs).build();