subprocess Called with shell=True

HIGH

subprocess called with shell=True passes the command through the system shell, enabling command injection when any part of the command contains untrusted input.

Rule Information

Language
Python
Category
Python Core
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonsubprocessshell-truecommand-injectionshell-injectionCWE-78OWASP-A03
CWE References

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-021 --project .
1
2
3
4
5
6
rule.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

About This Rule

Understanding the vulnerability and how it is detected

Calling subprocess functions with shell=True passes the command to the system shell (/bin/sh on Unix, cmd.exe on Windows) for interpretation before execution. This means all shell metacharacters — semicolons, pipes, backticks, dollar signs, redirections, and command substitutions — are interpreted by the shell.

When any component of the command string is derived from untrusted input, an attacker can inject shell metacharacters to execute additional commands, redirect output, or access the shell's full feature set. This is equivalent in risk to calling os.system() with user input.

The fix is to remove shell=True and pass the command as a list of arguments. When a shell pipeline is genuinely required, ensure every element is either a hardcoded literal or validated against a strict allowlist.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Shell Metacharacter Injection

With shell=True, characters like ; | && || ` $() >> < and newlines are interpreted by the shell. An attacker injecting these characters into any part of the command string can chain additional commands, redirect output to arbitrary files, or access subshell features.

2

Environment Variable Expansion

The shell expands $VAR and ${VAR} expressions in the command string. If user input contains these patterns, the shell may expand them to sensitive environment variable values, leak credentials, or change command behavior based on environment state.

3

Glob and Brace Expansion

Shell glob expansion (*, ?, []) and brace expansion ({a,b}) in user input can cause the command to process unintended files or generate unexpected argument lists, leading to information disclosure or unintended file operations.

4

Bypass of Input Validation

Input validation for shell=True is extremely difficult to implement correctly because shell quoting rules vary by shell, context, and locale. Validation based on blocklists of shell metacharacters routinely misses edge cases. The only reliable mitigation is to remove shell=True.

How to Fix

Recommended remediation steps

  • 1Remove shell=True from all subprocess calls that process any user-controlled input; use a list of arguments with shell=False instead.
  • 2For shell pipelines, implement the pipeline in Python using subprocess.PIPE to connect processes without invoking a shell.
  • 3If shell=True is unavoidable, ensure every component of the command string is a hardcoded literal with no user-controlled content.
  • 4Use shlex.split() to tokenize a trusted shell command string into a list for use with shell=False.
  • 5Set the input, stdout, stderr, and timeout parameters explicitly on all subprocess calls to prevent resource leaks and information exposure.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule detects calls to subprocess.run(), subprocess.call(), subprocess.check_call(), subprocess.check_output(), and subprocess.Popen() that include the keyword argument shell=True or a positional True value in the shell parameter position. The rule flags all such calls since shell=True always routes the command through the system shell.

Compliance & Standards

Industry frameworks and regulations that require detection of this vulnerability

CWE Top 25
CWE-78 ranked #5 in 2023 Most Dangerous Software Weaknesses
OWASP Top 10
A03:2021 - Injection
NIST SP 800-53
SI-10: Information Input Validation
PCI DSS v4.0
Requirement 6.2.4 - Protect web-facing applications against injection attacks

References

External resources and documentation

Similar Rules

Explore related security rules for Python

Frequently Asked Questions

Common questions about subprocess Called with shell=True

Yes, if the entire command string is a hardcoded literal with no user-controlled components, variables, or format strings, shell=True is safe. However, it is still better practice to use a list with shell=False to prevent future changes from accidentally introducing user input into the command string.
Use subprocess.Popen() with stdout=subprocess.PIPE and pass the output of the first process to the stdin of the second: proc1 = Popen([cmd1], stdout=PIPE); proc2 = Popen([cmd2], stdin=proc1.stdout, stdout=PIPE). Alternatively, process the data in Python between subprocess calls, which is often more readable and maintainable.
Yes. On Linux/macOS, shell=True uses /bin/sh. On Windows, it uses cmd.exe. The injection techniques differ: Windows cmd.exe uses different metacharacters (& | ^). Code that uses shell=True should be audited on all target platforms, but the safe fix (removing shell=True) is the same on all platforms.
For running shell scripts, use subprocess.run(["/bin/bash", "script.sh", arg1, arg2]) with shell=False. Pass script arguments as additional list elements. Only use hardcoded, version-controlled script paths — never user-supplied script names.
shlex.quote() provides reasonable protection for simple cases on POSIX systems but is not a complete guarantee. It handles most metacharacters but has edge cases in non- standard shells, non-ASCII input, and complex quoting contexts. The reliable solution is to remove shell=True and use a list of arguments.
Code Pathfinder's PYTHON-LANG-SEC-021 rule uses .where("shell", True) to detect all subprocess calls with shell=True. Additionally, search for the string shell=True in the codebase and review each occurrence. Pay special attention to calls where the command is not a hardcoded string literal.

New feature

Get these findings posted directly on your GitHub pull requests

The subprocess Called with shell=True rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works