APS-4Required
Never pass input to a shell
Run other programs without a shell, with input as separate arguments. Better still, use a library instead of a program.
How to do it
Call the program directly with an argument list, such as execFile in Node or subprocess.run([...]) in Python. Check input against an allow list, such as a known file name pattern. Put -- before user values, so they cannot pass as options. It is the same idea as passing values as parameters in database queries.
Examples
JavaScript example
// Bad: the shell reads ; and $( )
exec(`gzip --keep ${fileName}`);
// Good: no shell, input is one argument, and -- ends the options
execFile('gzip', ['--keep', '--', fileName]);
Python example
# Bad: shell=True reads ; and $( )
subprocess.run(f"gzip --keep {file_name}", shell=True)
# Good: an argument list, no shell
subprocess.run(["gzip", "--keep", "--", file_name], check=True)
Go example
// Go: exec.Command never starts a shell
err := exec.Command("gzip", "--keep", "--", fileName).Run()
How to check
Send PENTEST; sleep 5 and $(sleep 5) wherever input might reach a program, such as file names and image tools. Replies come back at normal speed. A search of the code for shell=True, exec(, system( and backticks finds no call that holds user input.
Sources
Related rules
- DSK-11 Check the sender and content of every message to the main process in Desktop apps and extensions
- DSK-28 Check who is calling before a privileged helper acts in Desktop apps and extensions
- DAT-8 Pass values as parameters in Data and databases
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/input-and-injection/command-injection">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.