How to fix Command Injection
in Poem
Executive Summary
Command injection in Poem (Rust) typically occurs when user-supplied input is passed directly into a shell execution context via std::process::Command. In Rust, while Command::new doesn't invoke a shell by default, developers often introduce vulnerabilities by explicitly calling 'sh -c' or 'cmd /c' with unsanitized strings. To secure your endpoints, you must treat all external input as untrusted and avoid shell interpolation.
The Vulnerable Pattern
use poem::{handler, post, web::Json, Route}; use serde::Deserialize; use std::process::Command;#[derive(Deserialize)] struct PingRequest { address: String, }
#[handler] async fn vulnerable_ping(req: Json
) -> String { // VULNERABLE: Using sh -c with string interpolation allows an attacker // to inject commands via the ‘address’ field (e.g., ’; rm -rf /’) let output = Command::new(“sh”) .arg(“-c”) .arg(format!(“ping -c 4 {}”, req.address)) .output(); match output { Ok(o) => String::from_utf8_lossy(&o.stdout).to_string(), Err(_) => "Execution failed".into(), }
}
The Secure Implementation
The fix involves two primary layers of defense. First, remove the shell ('sh -c') entirely. By invoking the binary ('ping') directly and passing user input as a separate argument via .arg(), the operating system treats the input as data, not as executable code or shell instructions. This prevents command chaining (e.g., using ;, &&, or |). Second, implement strict input validation (allow-listing) to ensure the 'address' string conforms to expected patterns, such as an IP address or a hostname, further reducing the attack surface.
use poem::{handler, post, web::Json, Route}; use serde::Deserialize; use std::process::Command;#[derive(Deserialize)] struct PingRequest { address: String, }
#[handler] async fn secure_ping(req: Json
) -> String { // SECURE: Pass arguments individually. std::process::Command // handles escaping and does not invoke a shell unless explicitly told to. // Additionally, validate the input format (e.g., ensure it’s a valid IP/Hostname). if req.address.chars().any(|c| !c.is_alphanumeric() && c != ’.’) { return “Invalid address format”.into(); } let output = Command::new("ping") .arg("-c") .arg("4") .arg(&req.address) // Input passed as a discrete argument .output(); match output { Ok(o) => String::from_utf8_lossy(&o.stdout).to_string(), Err(_) => "Execution failed".into(), }
}
Your Poem API
might be exposed to Command Injection
74% of Poem apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.