SC008: NoSubprocessShell
Overview
| Property | Value |
|---|---|
| ID | SC008 |
| Name | NoSubprocessShell |
| Group | security |
| Severity | WARNING |
Description
Scans Python source files for subprocess calls that use shell=True, which can lead to shell injection vulnerabilities when command arguments contain user-supplied data.
Using shell=True in subprocess calls is a security risk that can lead to:
- Shell injection attacks
- Arbitrary command execution
- Privilege escalation
- Data exfiltration through command manipulation
What it checks
The check scans all .py files (excluding test files, .venv/, and __pycache__/) for:
- subprocess.run(shell=True): Run commands with shell interpretation
- subprocess.call(shell=True): Call commands with shell interpretation
- subprocess.Popen(shell=True): Open process with shell interpretation
- subprocess.check_call(shell=True): Check call with shell interpretation
- subprocess.check_output(shell=True): Check output with shell interpretation
Result states
- PASSED: No subprocess calls with
shell=Truefound - FAILED: One or more subprocess calls with
shell=Truedetected
How to fix
Use a list of arguments instead of a shell string
import subprocess
# Bad: shell=True with string command
user_input = get_user_input()
subprocess.run(f"grep {user_input} file.txt", shell=True)
# Good: list of arguments (no shell interpretation)
subprocess.run(["grep", user_input, "file.txt"])Use shlex.split() for complex commands
import subprocess
import shlex
# Good: parse command string safely
cmd = "ls -la /path/to/dir"
subprocess.run(shlex.split(cmd))Use shlex.quote() if shell=True is absolutely necessary
import subprocess
import shlex
# If you must use shell=True, quote user input
user_input = get_user_input()
safe_input = shlex.quote(user_input)
subprocess.run(f"echo {safe_input}", shell=True)Avoid shell features when possible
import subprocess
from pathlib import Path
# Bad: using shell for globbing
subprocess.run("rm *.tmp", shell=True)
# Good: use Python's pathlib for file operations
for tmp_file in Path(".").glob("*.tmp"):
tmp_file.unlink()Why WARNING severity?
This check is a WARNING because:
shell=Trueis sometimes needed for shell features (pipes, redirects)- The risk depends on whether user input reaches the command
- Legitimate use cases exist when command strings are hardcoded
- However, it’s still a code smell that warrants review
Configuration
Skip this check
[tool.pycmdcheck]
skip = ["SC008"]CLI
pycmdcheck --skip SC008