Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 11 additions & 57 deletions crates/bashkit/tests/integration/threat_model_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,20 @@ mod resource_exhaustion {
})
}

/// Runs a script in the built CLI under `run_command_bounded`.
/// Runs a script in the built CLI under `run_command_bounded`, with
/// trailing `$0`/ARG0 words after the `-c` script.
/// The launcher applies the fail-closed CPU/stack caps, reports the
/// effective values as the first stderr line, then `exec`s the CLI so
/// exit codes and diagnostics stay the real producer's own.
fn run_cli_bounded(script: &str, limit: Duration) -> BoundedRun {
fn run_cli_argv_bounded(script: &str, extra_args: &[&str], limit: Duration) -> BoundedRun {
let launcher = format!(
"{BOUNDED_CLI_LIMITS}; echo \"harness-limits: stack=$(ulimit -s) cpu=$(ulimit -t)\" >&2; exec \"$@\""
);
let mut cmd = Command::new("bash");
cmd.args(["-c", &launcher, "_"])
.arg(bashkit_cli_path())
.args(["-c", script])
.args(extra_args)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
Expand All @@ -116,6 +118,12 @@ mod resource_exhaustion {
run
}

/// Runs a script in the built CLI under `run_command_bounded`, with
/// no trailing words after the `-c` script.
fn run_cli_bounded(script: &str, limit: Duration) -> BoundedRun {
run_cli_argv_bounded(script, &[], limit)
}

/// Runs an already-built command with one shared wall deadline for
/// child exit, pipe drain, and reaping:
/// - wall clock: past `limit` the child is killed; if it had already
Expand Down Expand Up @@ -218,60 +226,6 @@ mod resource_exhaustion {
}
}

/// Runs argv (a `-c` script plus trailing `$0`/ARG0 words) in a child
/// CLI process under the same bounds contract as [`run_cli_bounded`]:
/// wall-clock watchdog, 8 KiB output cap, real exit code preserved.
fn run_cli_argv_bounded(argv: &[&str], limit: Duration) -> BoundedRun {
use std::io::Read;
let mut child = Command::new(bashkit_cli_path());
child
.args(argv)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = child.spawn().expect("spawn bounded bashkit CLI");
let deadline = Instant::now() + limit;
loop {
match child.try_wait().expect("poll bounded CLI") {
Some(status) => {
let mut stdout = String::new();
let mut stderr = String::new();
child
.stdout
.take()
.unwrap()
.read_to_string(&mut stdout)
.unwrap();
child
.stderr
.take()
.unwrap()
.read_to_string(&mut stderr)
.unwrap();
return BoundedRun {
exit_code: status.code(),
stdout: stdout.chars().take(8192).collect(),
stderr: stderr.chars().take(8192).collect(),
timed_out: false,
};
}
None => {
if Instant::now() >= deadline {
child.kill().ok();
child.wait().ok();
return BoundedRun {
exit_code: None,
stdout: String::new(),
stderr: String::new(),
timed_out: true,
};
}
std::thread::sleep(Duration::from_millis(5));
}
}
}
}

/// TM-DOS-136: scaled multiline token x long ARG0 stays bounded in a
/// child. The exact finding shape: `for x "` plus N `a`+newline lines
/// plus the closing quote (the parser expects `do`), invoked with a long
Expand All @@ -289,7 +243,7 @@ mod resource_exhaustion {
}
script.push('"');
let arg0 = "B".repeat(8192);
let run = run_cli_argv_bounded(&["-c", &script, &arg0], Duration::from_secs(20));
let run = run_cli_argv_bounded(&script, &[&arg0], Duration::from_secs(20));
assert!(
!run.timed_out,
"token_lines={token_lines}: child hit the 20s watchdog"
Expand Down
Loading