For AI agents: A markdown version of this page is available at https://docs.datadoghq.com/security/code_security/static_analysis/static_analysis_rules/rust-security/command-injection.md. A documentation index is available at /llms.txt.
This product is not supported for your selected Datadog site. ().

Metadata

ID: rust-security/command-injection

Language: Rust

Severity: Warning

Category: Security

CWE: 78

Description

Passing a non-literal argument to Command::new("sh").arg("-c").arg(...) (or another shell variant) lets the shell interpret the argument as a script. If any part of that argument comes from user-controlled input, an attacker can append shell metacharacters (;, &&, `, $(...)) to inject arbitrary commands. Pass arguments directly to the target binary without a shell wrapper, or rigorously validate and escape the input.

Learn More

Non-Compliant Code Examples

use std::process::Command;

async fn bad() {
    let user_input = std::env::args().nth(1).unwrap();

    // .arg().arg() chain
    let _ = Command::new("sh").arg("-c").arg(user_input.clone()).output();

    // Full path
    let _ = Command::new("/bin/sh").arg("-c").arg(user_input.clone()).output();

    // format! macro — non-literal
    let _ = Command::new("sh").arg("-c").arg(format!("ls {}", user_input)).output();

    // Other shell
    let _ = Command::new("zsh").arg("-c").arg(&user_input).output();

    // Fully qualified path — tokio
    let _ = tokio::process::Command::new("sh").arg("-c").arg(&user_input).output();

    // Fully qualified path — std::process
    let _ = std::process::Command::new("bash").arg("-c").arg(&user_input).output();

    // .args array form
    let _ = Command::new("sh").args(["-c", &user_input]).output();
}

Compliant Code Examples

use std::process::Command;

fn ok() {
    // Hardcoded shell script — not tainted
    let _ = Command::new("sh").arg("-c").arg("ls -la").output();

    // Not a shell binary
    let user_input = std::env::args().nth(1).unwrap();
    let _ = Command::new("ls").arg(&user_input).output();

    // Shell binary but not -c
    let _ = Command::new("sh").arg("--version").output();

    // Structural match but @flag predicate filters out "-v"
    let _ = Command::new("sh").arg("-v").arg(&user_input).output();

    // No shell wrapper — args passed directly to the target binary
    let _ = Command::new("grep").arg("pattern").arg(&user_input).output();

    // Raw string — exercises the raw_string_literal branch of the JS guard
    let _ = Command::new("sh").arg("-c").arg(r"echo hi").output();

    // .args form with both literals
    let _ = Command::new("sh").args(["-c", "ls -la"]).output();

    // .args form but not a shell binary
    let _ = Command::new("ls").args(["-l", &user_input]).output();
}
https://static.datadoghq.com/static/images/logos/github_avatar.svg https://static.datadoghq.com/static/images/logos/vscode_avatar.svg jetbrains

Seamless integrations. Try Datadog Code Security