FlawOpen

flawopen.com/Command Injection/Java Runtime.exec

Is Java's Runtime.exec() safe from command injection?

CWE-78: Improper Neutralization of Special Elements used in an OS CommandReference page
Short answer

Java’s Runtime.exec(String) does not implicitly run a shell, so shell operators in that string are not automatically interpreted by a shell. But the string is still tokenized into a program and arguments, and untrusted text can change which arguments the target program receives. ProcessBuilder makes the argument boundaries explicit; you must still validate each value and constrain the executable and allowed options.

RISKY
// User input is concatenated into a command string
Runtime.getRuntime().exec(
  "approved-tool --input " + filename
);
SAFER
// Fixed executable and separate arguments; validate filename first
String safeName = validateFilename(filename);
new ProcessBuilder(
  "/opt/app/bin/approved-tool",
  "--input", safeName
).start();

Shell injection and argument injection are different

Since Java 18, Oracle deprecates Runtime.exec(String) as error-prone and recommends exec(String[]) or ProcessBuilder. The string overload tokenizes on whitespace, which can split arguments such as paths containing spaces. Runtime.exec(String) does not implicitly launch sh or another shell; Oracle documents that the single-string overload converts the command string to an argument array. This blocks ordinary shell metacharacters from acting as shell syntax, but it does not make untrusted input safe: tokenization can still create extra arguments, and the target program may interpret an argument as an option. ProcessBuilder accepts a list of strings representing the executable and its arguments; each list element is an argument boundary on common platforms. Keep the executable fixed, validate values against the target program's expected grammar, reject or safely handle option-like values, and use a target-supported -- terminator only when its documentation guarantees that behavior. Avoid invoking a shell explicitly (for example, sh -c) with untrusted data.

FAQ

Does Runtime.exec(String) being shell-free make it safe?

No. It reduces shell-injection risk for that overload, but argument injection remains possible. The target program may interpret attacker-controlled arguments as options or filenames. Use ProcessBuilder with a fixed executable and separate arguments, then validate each value for the target program.

Does ProcessBuilder validate arguments for me?

No. It constructs the program-and-argument list and starts a process; it does not verify that the executable or arguments are safe or valid. Apply application-level allow-lists and validation, and follow the target program’s documented argument rules.

References

See also OS Command Injection (CWE-78): How It Works and How to Prevent It in Every Language — flawopen.com Node.js child_process.exec() Command Injection Risk exec() vs execFile() in Node.js: What's the Difference?