From 49fc9e6db7744a194237900a858a542c900b866f Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Fri, 1 May 2026 13:54:23 -0400 Subject: [PATCH] fix(sandbox): skip Docker exec command after early stop Short-circuit the controlled shell wrapper when the stop file already exists so a cancelled Docker exec does not launch user code before the pid watcher can terminate it. --- lib/crates/fabro-sandbox/src/docker.rs | 41 ++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/crates/fabro-sandbox/src/docker.rs b/lib/crates/fabro-sandbox/src/docker.rs index 15276353e..4397844b6 100644 --- a/lib/crates/fabro-sandbox/src/docker.rs +++ b/lib/crates/fabro-sandbox/src/docker.rs @@ -723,6 +723,10 @@ stop_file={stop_file}; \ pid_file={pid_file}; \ user_command={command}; \ rm -f \"$pid_file\"; \ +if [ -e \"$stop_file\" ]; then \ + rm -f \"$stop_file\" \"$pid_file\"; \ + exit 143; \ +fi; \ ( \ while [ ! -e \"$stop_file\" ]; do sleep {stop_poll_sleep}; done; \ while [ ! -s \"$pid_file\" ]; do sleep {stop_poll_sleep}; done; \ @@ -1617,6 +1621,43 @@ mod tests { ); } + #[tokio::test] + async fn controlled_shell_command_skips_user_command_when_stop_already_requested() { + let tempdir = tempfile::tempdir().expect("tempdir should be created"); + let stop_file = tempdir.path().join("stop"); + let pid_file = tempdir.path().join("pid"); + let marker_file = tempdir.path().join("started"); + let stop_file = stop_file.to_string_lossy().into_owned(); + let pid_file = pid_file.to_string_lossy().into_owned(); + let marker_file = marker_file.to_string_lossy().into_owned(); + let command = docker_controlled_shell_command( + &format!("touch {}", shell_quote(&marker_file)), + &stop_file, + &pid_file, + ); + + fs::write(&stop_file, b"") + .await + .expect("early stop file should be written"); + let output = Command::new("/bin/bash") + .arg("-lc") + .arg(command) + .output() + .await + .expect("controlled shell command should run"); + + assert!( + !output.status.success(), + "controlled shell command should exit as stopped" + ); + assert!( + !fs::try_exists(&marker_file) + .await + .expect("marker file existence should be checked"), + "controlled shell command should not start user command after an early stop" + ); + } + async fn processes_with_marker(marker: &str) -> Vec { let output = Command::new("ps") .args(["-eo", "pid=,args="])