Dangerous os.spawn*() Call

HIGH

os.spawn*() spawns a new process and can execute arbitrary programs when the executable path or arguments are derived from untrusted input.

Rule Information

Language
Python
Category
Python Core
Author
Shivasurya
Shivasurya
Last Updated
2026-03-22
Tags
pythonos-spawnprocess-spawncommand-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-012 --project .
1
2
3
4
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

Python's os.spawnl(), os.spawnle(), os.spawnlp(), os.spawnlpe(), os.spawnv(), os.spawnve(), os.spawnvp(), and os.spawnvpe() functions spawn a new process to execute an external program. These functions are an older API for process creation and have been superseded by the subprocess module, which provides more control, better error handling, and a safer interface.

The spawn functions with the 'p' suffix (spawnlp, spawnvp) search PATH for the executable, which introduces additional risk if PATH can be manipulated. The 'e' suffix variants accept an explicit environment, which can be exploited if constructed from user input.

When the executable path or any argument is derived from untrusted input, an attacker can cause arbitrary program execution. Even the list-based variants (spawnv) that do not invoke a shell are dangerous when the executable path is attacker-controlled.

Security Implications

Potential attack scenarios if this vulnerability is exploited

1

Arbitrary Program Execution

An attacker controlling the executable path argument can run any program on the system. The spawned process runs with the same user credentials as the Python process, giving the attacker the same filesystem and network access.

2

PATH-Based Executable Hijacking

The spawnlp and spawnvp variants search PATH to find the executable. If PATH can be manipulated via environment injection or if the current working directory is included in PATH, an attacker can place a malicious binary named after the target executable to intercept the spawn call.

3

Environment Variable Injection

The spawnle and spawnve variants accept a custom environment dictionary. If this dictionary incorporates user-supplied values, an attacker can set LD_PRELOAD to inject shared library code, or manipulate PATH to control which binaries are found.

4

Resource Leak and Missing Error Handling

os.spawn*() provides less robust error handling than subprocess. Failures may leave zombie processes, uncollected exit codes, and unclosed file descriptors, which can be exploited for denial-of-service through resource exhaustion.

How to Fix

Recommended remediation steps

  • 1Replace all os.spawn*() calls with subprocess.run() using a list of arguments, which is more secure, more portable, and better maintained.
  • 2Use absolute paths for executables instead of relying on PATH search to prevent executable hijacking via PATH manipulation.
  • 3Validate all input that becomes an argument to any process-spawning call against a strict allowlist before use.
  • 4Set explicit timeouts on subprocess.run() to prevent resource exhaustion from long-running or hung child processes.
  • 5Capture stdout and stderr from spawned processes to prevent information leakage through process output.

Detection Scope

How Code Pathfinder analyzes your code for this vulnerability

This rule detects calls to os.spawnl(), os.spawnle(), os.spawnlp(), os.spawnlpe(), os.spawnv(), os.spawnve(), os.spawnvp(), and os.spawnvpe() from the Python os module. All variants are flagged since the spawn API is deprecated and all variants can execute arbitrary programs when given untrusted arguments.

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 Dangerous os.spawn*() Call

os.exec*() replaces the current process with a new program and never returns. os.spawn*() creates a new child process while the Python process continues running. Both can execute arbitrary programs when given untrusted arguments, but os.spawn*() is less destructive since the Python process survives and can handle errors.
The subprocess module provides a unified, cross-platform interface with better control over stdin/stdout/stderr, timeout handling, proper error propagation, and safer defaults. os.spawn*() was included for POSIX compatibility but lacks these features and is not recommended for use in new Python code.
Yes. The 'p' variants search PATH to find the executable, which introduces PATH manipulation as an additional attack vector. Using spawnv with an absolute path is slightly safer because it avoids PATH search, but is still dangerous when the absolute path is attacker-controlled.
No, os.spawn*() does not invoke a shell by default, unlike os.system(). The program and arguments are passed directly to the OS. This means shell metacharacter injection is not possible, but arbitrary program execution is still possible when the executable path or arguments are attacker-controlled.
os.spawn*() accepts a mode argument: os.P_NOWAIT returns the child PID immediately while the parent continues, and os.P_WAIT blocks until the child exits. For new code, always use subprocess.run() instead, which handles both cases more cleanly with proper timeout and error handling.
Replace os.spawnv(os.P_WAIT, path, args) with subprocess.run([path] + args[1:], check=True). Replace os.spawnve(os.P_WAIT, path, args, env) with subprocess.run([path] + args[1:], env=env, check=True). Ensure all paths are absolute, add timeouts, and capture output as needed.

New feature

Get these findings posted directly on your GitHub pull requests

The Dangerous os.spawn*() Call rule runs in CI and posts inline review comments on the exact lines — no dashboard, no SARIF viewer.

See how it works