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=True found
  • FAILED: One or more subprocess calls with shell=True detected

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=True is 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