Subprocess Statement#
Codeaudit checks the use of Subprocesses in a Python file.
Using the default Python library subprocess for Subprocess management is very powerfull.
Caution
When using the subprocess library the Python code can invoke an external executable.
Python codeaudit does not check on Popen.communicate This method cannot be directly used on a “foreign” process. It can only work on a launched by subprocess that is created by Popen in the same Python script. Popen.communicate() is a method of the Popen object.
It is good to known that Python’s subprocess module doesn’t provide a mechanism to “attach” to standard I/O streams of an already running process.
Note that when old subprocess functions are used, the severity is high. So avoid using the functions and migratie to .run for:
subprocess.callsubprocess.Popensubprocess.callsubprocess.check_callsubprocess.check_outputsubprocess.getoutputsubprocess.getstatusoutputsubprocess.run
Rationale#
The subprocess module allows Python code to execute external system commands. This is powerful — and dangerous — because it creates a bridge between untrusted input and the operating system shell.
Security vulnerabilities typically arise when:
User-controlled input is interpolated into command strings
The shell is invoked (shell=True)
Arguments are passed as strings instead of lists
Output or exit codes are trusted without validation
Errors are ignored or mishandled
If an attacker can influence any part of the command, they may achieve command injection, privilege escalation, data exfiltration, or remote code execution (RCE).
All of the listed APIs are safe by default only if used correctly — but very easy to misuse!
Preventive measures#
Applies to all subprocess APIs listed:
Avoid shell=True
This is the single biggest risk factor.
Shell metacharacters (;, &&, |, $(), `) become exploitable.
Pass arguments as lists, never strings
["ls", "-l"]instead ofls -l
Never concatenate user input into commands
Especially paths, filenames, flags, or filters.
Validate and sanitize all inputs
Use allowlists, not blocklists.
Check return codes explicitly
Silent failures can mask partial command execution.
Use the least-privileged user possible
Never run subprocesses as root unless unavoidable.
Prefer high-level Python APIs
Many shell commands have safe Python equivalents (os, pathlib, shutil).
Example (by API)#
subprocess.call#
Why it’s risky
Executes commands without raising exceptions
Return codes are often ignored
Vulnerable when
shell=Trueor when strings are used
Bad example
subprocess.call("rm -rf " + user_input, shell=True)
Risk:
user_input = "/tmp/data; rm -rf /"