Command Execution

PySubprocess

The subprocess standard library module for spawning child processes. Most call APIs accept either a list[str] (safe) or a string with shell=True (command-injection sink when the string contains user input).

5 sinks
Taint flow0 sources 5 sinks
Sinks — dangerous call
.run()
.call()
.check_call()
.check_output()
.Popen()
Quick-start rule — copy and run
from codepathfinder.python_decorators import python_rule
from codepathfinder import calls, flows
from codepathfinder.presets import PropagationPresets


@python_rule(
    id="PYTHON-CMDI-001",
    name="Command injection via subprocess with shell=True",
    severity="CRITICAL",
    category="command-execution",
    cwe="CWE-78",
    owasp="A03:2021",
    message="User input flows into subprocess with shell=True. Pass args as a list and avoid shell=True.",
)
def detect_subprocess_shell_injection():
    return flows(
        from_sources=[
            calls("request.args.get", "request.form.get", "request.get_json"),
            calls("input"),
        ],
        to_sinks=[
            calls("subprocess.run", match_name={"shell": True}).tracks(0),
            calls("subprocess.Popen", match_name={"shell": True}).tracks(0),
            calls("subprocess.call", match_name={"shell": True}).tracks(0),
            calls("subprocess.check_output", match_name={"shell": True}).tracks(0),
        ],
        sanitized_by=[calls("shlex.quote"), calls("shlex.split")],
        propagates_through=PropagationPresets.standard(),
        scope="global",
    )
pathfinder scan --ruleset custom/security --project .

Sinks

.run()Sink
#
Signature
subprocess.run(args, *, shell=False, capture_output=False, ...) -> CompletedProcess

Runs a command and waits for completion. Sink when args is a string with shell=True.

tracks:0
.call()Sink
#
Signature
subprocess.call(args, *, shell=False, ...) -> int

Runs a command and returns its exit code. Sink under shell=True.

tracks:0
.check_call()Sink
#
Signature
subprocess.check_call(args, *, shell=False, ...) -> int

Like call() but raises on non-zero exit. Same injection risk.

tracks:0
.check_output()Sink
#
Signature
subprocess.check_output(args, *, shell=False, ...) -> bytes

Runs a command and returns stdout. Sink under shell=True.

tracks:0
.Popen()Sink
#
Signature
subprocess.Popen(args, *, shell=False, ...) -> Popen

Low-level process constructor. Sink when args is a string with shell=True.

tracks:0

Fully-Qualified Names

FQNField
subprocessfqns[0]

Wrong FQN → 0 findings. Verify with: change fqns to garbage → must produce 0 results.

Import

rule.py
from codepathfinder.go_rule import PySubprocess