Omit sensitive process inputs from spawn trace logs (#60034)

## Why

Command arguments and environment variables can contain credentials. Logging them when spawning a child process can expose those credentials in trace output.

## What changed

Replace argument, `arg0`, and environment contents in `spawn_child_async` trace logs with argument and environment counts and a flag indicating whether `arg0` is overridden. Keep the program, working directory, network sandbox policy, and stdio policy in the trace.

## Testing

Add a regression test that captures trace output and verifies that argument, `arg0`, and environment canaries are omitted while the program path remains visible.

GitOrigin-RevId: c317e7dcc6fd8b4eafb6fcfe98dc8634807c813d
This commit is contained in:
Eric Traut
2026-11-07 02:01:23 +00:00
committed by copyberry
parent cfc11eb483
commit 738ef9f3bb
2 changed files with 46 additions and 1 deletions
+13 -1
View File
@@ -63,8 +63,16 @@ pub(crate) async fn spawn_child_async(request: SpawnChildRequest<'_>) -> std::io
env.retain(|name, _| !is_non_inheritable_env_var(name));
// Both command arguments and environment variables can contain credentials.
trace!(
"spawn_child_async: {program:?} {args:?} {arg0:?} {cwd:?} {network_sandbox_policy:?} {stdio_policy:?} {env:?}"
?program,
?cwd,
?network_sandbox_policy,
?stdio_policy,
arg_count = args.len(),
has_arg0_override = arg0.is_some(),
env_count = env.len(),
"spawn_child_async"
);
let mut cmd = match stdio_policy {
@@ -135,3 +143,7 @@ pub(crate) async fn spawn_child_async(request: SpawnChildRequest<'_>) -> std::io
cmd.kill_on_drop(true).spawn()
}
#[cfg(test)]
#[path = "spawn_tests.rs"]
mod tests;
+33
View File
@@ -0,0 +1,33 @@
#![cfg(test)]
use super::*;
use std::sync::Arc;
#[tokio::test]
async fn spawn_trace_omits_sensitive_inputs() -> std::io::Result<()> {
let dir = tempfile::tempdir()?;
let capture = tempfile::NamedTempFile::new()?;
let subscriber = tracing_subscriber::fmt()
.with_max_level(tracing::Level::TRACE)
.with_writer(Arc::new(capture.reopen()?))
.finish();
let _guard = tracing::subscriber::set_default(subscriber);
let result = spawn_child_async(SpawnChildRequest {
program: dir.path().join("missing-test-program"),
args: vec!["canary-argument".into()],
arg0: Some("canary-arg0"),
cwd: AbsolutePathBuf::from_absolute_path(dir.path())?,
network_sandbox_policy: NetworkSandboxPolicy::Enabled,
network: None,
stdio_policy: StdioPolicy::RedirectForShellTool,
env: HashMap::from([("SETTING".into(), "canary-env-value".into())]),
})
.await;
assert!(result.is_err());
let logs = std::fs::read_to_string(capture.path())?;
assert!(logs.contains("missing-test-program"));
assert!(!logs.contains("canary-"));
Ok(())
}