From d7716fb179a27bffe8d882f32f8f8efbd5047634 Mon Sep 17 00:00:00 2001 From: Luke L Date: Mon, 25 May 2026 21:08:41 -0500 Subject: [PATCH 1/2] fix: close stdin for non-interactive process --- rust/crates/runtime/src/bash.rs | 48 ++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/rust/crates/runtime/src/bash.rs b/rust/crates/runtime/src/bash.rs index dddf3ccfbb..3bb17ba092 100644 --- a/rust/crates/runtime/src/bash.rs +++ b/rust/crates/runtime/src/bash.rs @@ -330,20 +330,24 @@ fn prepare_tokio_command( prepare_sandbox_dirs(cwd); } - if let Some(launcher) = build_linux_sandbox_command(command, cwd, sandbox_status) { - let mut prepared = TokioCommand::new(launcher.program); - prepared.args(launcher.args); - prepared.current_dir(cwd); - prepared.envs(launcher.env); - return prepared; - } + let mut prepared = if let Some(launcher) = build_linux_sandbox_command(command, cwd, sandbox_status) { + let mut cmd = TokioCommand::new(launcher.program); + cmd.args(launcher.args); + cmd.envs(launcher.env); + cmd + } else { + let mut cmd = TokioCommand::new("sh"); + cmd.arg("-lc").arg(command); + if sandbox_status.filesystem_active { + cmd.env("HOME", cwd.join(".sandbox-home")); + cmd.env("TMPDIR", cwd.join(".sandbox-tmp")); + } + cmd + }; - let mut prepared = TokioCommand::new("sh"); - prepared.arg("-lc").arg(command).current_dir(cwd); - if sandbox_status.filesystem_active { - prepared.env("HOME", cwd.join(".sandbox-home")); - prepared.env("TMPDIR", cwd.join(".sandbox-tmp")); - } + prepared.current_dir(cwd); + // Guard against hanging on stdin + prepared.stdin(std::process::Stdio::null()); prepared } @@ -419,6 +423,24 @@ mod tests { assert_eq!(structured[0]["event"], "test.hung"); assert_eq!(structured[0]["data"]["provenance"], "bash.timeout"); } + + #[test] + fn prevents_stdin_hangs_by_redirecting_to_null() { + let output = execute_bash(BashCommandInput { + command: String::from("cat"), + timeout: Some(2_000), + description: None, + run_in_background: Some(false), + dangerously_disable_sandbox: Some(true), + namespace_restrictions: None, + isolate_network: None, + filesystem_mode: None, + allowed_mounts: None, + }) + .expect("bash command should execute cleanly"); + + assert!(!output.interrupted, "Command hung and was cut off by the timeout!"); + } } /// Maximum output bytes before truncation (16 KiB, matching upstream). From 485754bc7dc42349c6de1e14500a6dbe446bc3c3 Mon Sep 17 00:00:00 2001 From: Luke L Date: Mon, 25 May 2026 21:17:57 -0500 Subject: [PATCH 2/2] fix: close stdin for non-interactive process amended --- rust/crates/runtime/src/bash.rs | 34 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/rust/crates/runtime/src/bash.rs b/rust/crates/runtime/src/bash.rs index 3bb17ba092..32d325e4f2 100644 --- a/rust/crates/runtime/src/bash.rs +++ b/rust/crates/runtime/src/bash.rs @@ -330,20 +330,21 @@ fn prepare_tokio_command( prepare_sandbox_dirs(cwd); } - let mut prepared = if let Some(launcher) = build_linux_sandbox_command(command, cwd, sandbox_status) { - let mut cmd = TokioCommand::new(launcher.program); - cmd.args(launcher.args); - cmd.envs(launcher.env); - cmd - } else { - let mut cmd = TokioCommand::new("sh"); - cmd.arg("-lc").arg(command); - if sandbox_status.filesystem_active { - cmd.env("HOME", cwd.join(".sandbox-home")); - cmd.env("TMPDIR", cwd.join(".sandbox-tmp")); - } - cmd - }; + let mut prepared = + if let Some(launcher) = build_linux_sandbox_command(command, cwd, sandbox_status) { + let mut cmd = TokioCommand::new(launcher.program); + cmd.args(launcher.args); + cmd.envs(launcher.env); + cmd + } else { + let mut cmd = TokioCommand::new("sh"); + cmd.arg("-lc").arg(command); + if sandbox_status.filesystem_active { + cmd.env("HOME", cwd.join(".sandbox-home")); + cmd.env("TMPDIR", cwd.join(".sandbox-tmp")); + } + cmd + }; prepared.current_dir(cwd); // Guard against hanging on stdin @@ -439,7 +440,10 @@ mod tests { }) .expect("bash command should execute cleanly"); - assert!(!output.interrupted, "Command hung and was cut off by the timeout!"); + assert!( + !output.interrupted, + "Command hung and was cut off by the timeout!" + ); } }