flawopen.com/Command Injection/Rust
Imagine a vending machine. Safe execution is pressing button B4. Command injection is when someone glues a small explosive to the coin that activates all dispenser motors at once.
Rust's memory safety guarantees do not prevent logic bugs. std::process::Command::new() is safe by default, but if developers explicitly call Command::new("sh").arg("-c").arg(format!("...", user_input)), command injection occurs.
Command line utilities and microservices in Rust have suffered remote code execution when executing git subcommands or wrapping ffmpeg via shell invocations.
RustSec Advisory Database.// Spawning shell explicitly with format! string
use std::process::Command;
fn run_backup(folder: &str) {
// Attacker input: "data; rm -rf /"
let cmd = format!("tar -czf backup.tar.gz {}", folder);
Command::new("sh")
.arg("-c")
.arg(&cmd)
.status()
.expect("failed");
}
// Calling binary directly with discrete .arg() calls
use std::process::Command;
fn run_backup(folder: &str) {
// folder is passed as a literal argument to tar, not shell
Command::new("tar")
.arg("-czf")
.arg("backup.tar.gz")
.arg(folder)
.status()
.expect("failed");
}
Calling Command::new("tar").arg(...) uses the kernel's execvp system call. The arguments are passed as separate memory buffers without shell parsing.
Rust prevents memory corruption (buffer overflows, use-after-free), but command injection is a semantic logic bug that Rust's type system cannot prevent if you explicitly invoke a shell.
The borrow checker ensures lifetimes and memory safety; it has no concept of shell syntax or command boundaries.
grep -rn 'Command::new("sh")' --include="*.rs" .
grep -rn 'Command::new("bash")' --include="*.rs" .
cargo clippy and audit all process execution call sites.Command::new("binary").arg() or .args() slicessh -c with format!() stringsIt is safe from shell injection, but if the binary supports dangerous flags (like -e in curl), you must prevent user inputs from starting with a hyphen (-).