From 1ae11215a4a22b6f49746158f9a50ec13094d8f4 Mon Sep 17 00:00:00 2001 From: Dane Wilson Date: Tue, 27 Oct 2026 19:16:26 -0400 Subject: [PATCH] fix(browser): isolate daemon from shell process group --- src/browser/daemon/process.rs | 4 ++++ tests/browser_device_auth.rs | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/browser/daemon/process.rs b/src/browser/daemon/process.rs index 919d7e8..75f0b7c 100644 --- a/src/browser/daemon/process.rs +++ b/src/browser/daemon/process.rs @@ -1,3 +1,4 @@ +use std::os::unix::process::CommandExt; use std::path::PathBuf; use std::time::Duration; @@ -107,7 +108,10 @@ pub fn spawn_daemon( let params_json = serde_json::to_string(params)?; + // Shell tools may clean up their process group when a command returns. + // Keep the daemon reachable by subsequent CLI invocations in other tools. let child = std::process::Command::new(exe) + .process_group(0) .args(["__daemon", "--session-name", session_name]) .env("STEEL_DAEMON_PARAMS", params_json) .stdin(std::process::Stdio::null()) diff --git a/tests/browser_device_auth.rs b/tests/browser_device_auth.rs index 33df804..0ede7c3 100644 --- a/tests/browser_device_auth.rs +++ b/tests/browser_device_auth.rs @@ -46,6 +46,7 @@ impl Drop for Fixture { } #[tokio::test(flavor = "multi_thread", worker_threads = 2)] +#[allow(unsafe_code)] // read-only process-group query for our own daemon PID async fn device_auth_uses_bearer_and_normalizes_root_query_before_native_cdp() { use std::os::unix::fs::PermissionsExt; let api = MockServer::start().await; @@ -133,6 +134,18 @@ async fn device_auth_uses_bearer_and_normalizes_root_query_before_native_cdp() { "{}", String::from_utf8_lossy(&start.stdout) ); + let daemon_pid: libc::pid_t = + std::fs::read_to_string(fixture.home.path().join("daemon-test.pid")) + .unwrap() + .trim() + .parse() + .unwrap(); + // SAFETY: getpgid only queries the known child process; it sends no signal. + let daemon_group = unsafe { libc::getpgid(daemon_pid) }; + assert_eq!( + daemon_group, daemon_pid, + "daemon must have its own group to survive shell-tool cleanup" + ); assert_eq!( *seen.lock().unwrap(), ["/?sessionId=test-session&token=synthetic-connect-token"]