Interactive Playground
Experiment with the vulnerable code and security rule below. Edit the code to see how the rule detects different vulnerability patterns.
pathfinder scan --ruleset python/PYTHON-LANG-SEC-020 --project .About This Rule
Understanding the vulnerability and how it is detected
Python's subprocess module provides the recommended API for spawning child processes. The module is significantly safer than os.system() when used correctly — passing a list of arguments with shell=False (the default) bypasses the shell entirely and passes arguments directly to the OS exec() syscall.
However, subprocess becomes dangerous when: (1) shell=True is used with a string command containing user input; (2) the first argument in a list is attacker-controlled, pointing to an arbitrary executable; or (3) arguments in the list are derived from untrusted input without proper validation, such as injecting paths that traverse to sensitive files.
This rule audits all subprocess calls to ensure the command and arguments are safe. subprocess.call(), subprocess.check_call(), subprocess.check_output(), subprocess.run(), and subprocess.Popen() all require the same care.
Security Implications
Potential attack scenarios if this vulnerability is exploited
Command Injection via shell=True
When shell=True is combined with a string that includes user input, shell metacharacters in the input can inject additional commands. This is equivalent to calling os.system() and is the most common subprocess-related vulnerability.
Arbitrary Executable via First Argument
If the first element of the command list (the executable path) is attacker-controlled, any program on the system can be run. This can be exploited via path traversal or by pointing to attacker-placed binaries.
Argument Injection
Even with shell=False, attacker-controlled values in the argument list can inject unintended flags. For example, if a filename from user input starts with "-", it may be interpreted as a command-line option by the target program (e.g., rsync, git, ffmpeg).
Sensitive Output Exposure
subprocess commands may output sensitive information. If stdout is not captured, the output appears in server logs or the terminal, potentially exposing credentials, system configuration, or private data.
How to Fix
Recommended remediation steps
- 1Always use subprocess with a list of arguments and shell=False to prevent shell metacharacter interpretation.
- 2Validate all user-controlled values that become subprocess arguments against a strict allowlist or regex before use.
- 3Use absolute paths for executables to prevent PATH hijacking attacks.
- 4Set a timeout on all subprocess calls to prevent resource exhaustion from hung child processes.
- 5Capture stdout and stderr explicitly to prevent information leakage and ensure proper error handling.
Detection Scope
How Code Pathfinder analyzes your code for this vulnerability
This rule detects calls to subprocess.call(), subprocess.check_call(), subprocess.check_output(), subprocess.run(), and subprocess.Popen() from the Python subprocess module. All call sites are flagged for review to ensure shell=False is used and that command arguments are validated.
Compliance & Standards
Industry frameworks and regulations that require detection of this vulnerability
References
External resources and documentation
Similar Rules
Explore related security rules for Python
Dangerous os.system() or os.popen() Call
os.system() and os.popen() execute shell commands via /bin/sh, enabling command injection when arguments contain untrusted input.
Dangerous os.exec*() Call
os.exec*() replaces the current process image with a new program, enabling arbitrary program execution when arguments are untrusted.
Dangerous os.spawn*() Call
os.spawn*() spawns a new process and can execute arbitrary programs when the executable path or arguments are derived from untrusted input.
Frequently Asked Questions
Common questions about Dangerous subprocess Usage
New feature
Get these findings posted directly on your GitHub pull requests
The Dangerous subprocess Usage rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.