Audit subprocess calls in ServerKit backend services for recurring bug patterns.
Scope: ${ARGUMENTS:-backend/app/services/}
What to Scan For
Pattern 1: Missing sudo on privileged commands
Search for subprocess.run() calls that invoke commands requiring root but lack sudo:
systemctl (start, stop, restart, enable, disable, daemon-reload) — ALL require sudo
firewall-cmd (--state, --get-default-zone, --list-services, --list-ports, --list-rich-rules, --get-zones, --zone=, --add-, --remove-*, --reload) — ALL require sudo
ufw (status, enable, disable, allow, deny, delete) — ALL require sudo
iptables / ip6tables — ALL require sudo
nginx (-s reload, -t) — ALL require sudo
certbot — ALL require sudo
freshclam — requires sudo
For each match, check if the command list starts with 'sudo'. If not, flag it.
Ignore: Commands that legitimately don't need sudo (git, python, pip, npm, node, which, cat, ls, grep, df, free, uptime, whoami, hostname, lsb_release).
Pattern 2: Missing exception handling on distro-specific commands
Search for subprocess.run() calls that use distro-specific package managers without try/except FileNotFoundError:
dpkg — Debian/Ubuntu only, doesn't exist on RHEL/Fedora/CentOS
apt / apt-get — Debian/Ubuntu only
rpm — RHEL family only, doesn't exist on Debian
dnf / yum — RHEL family only
For each match, check if it's wrapped in a try/except that catches FileNotFoundError (or a broad Exception on the immediate call). If not, flag it.
Acceptable: If the call is guarded by os.path.exists('/usr/bin/<cmd>') before the subprocess call, that's fine — don't flag it.
Pattern 3: Raw subprocess calls that should use system utilities
The project provides centralized utilities in app/utils/system.py. Flag direct subprocess calls that bypass them:
subprocess.run(['sudo', 'systemctl', ...]) → should use ServiceControl.start/stop/restart/reload/enable/disable/daemon_reload()
subprocess.run(['systemctl', 'is-active', ...]) → should use ServiceControl.is_active()
subprocess.run(['systemctl', 'is-enabled', ...]) → should use ServiceControl.is_enabled()
subprocess.run(['dpkg', ...]) or subprocess.run(['rpm', ...]) for package checks → should use PackageManager.is_installed()
os.path.exists('/usr/bin/apt') or similar distro detection → should use PackageManager.detect()
for path in ['/usr/bin/X', '/usr/sbin/X']: if os.path.exists(path) binary search → should use is_command_available()
subprocess.run(['sudo', 'apt-get', 'install', ...]) or ['sudo', 'dnf', 'install', ...] → should use PackageManager.install()
subprocess.run(['sudo', <privileged-cmd>, ...]) for other privileged commands → should use run_privileged()
Acceptable exceptions (do NOT flag these):
- Docker CLI calls in
docker_service.py (uses Docker socket)
- Git CLI calls (no privilege needed)
- Python/pip in venvs (per-app, no root)
- MySQL/PostgreSQL CLI (auth via socket/password)
- WP-CLI (
sudo -u www-data pattern)
- Read-only commands (
clamscan, fail2ban-client status, uname, which, ssh-keygen, etc.)
Pattern 4: Container/environment assumptions
Code that assumes bare-metal or KVM and breaks inside LXC containers, Docker, or restricted environments. Search for these patterns:
4a. Unguarded /proc and /sys reads
open('/proc/cpuinfo') or similar /proc/ reads without try/except (FileNotFoundError, PermissionError)
open('/sys/class/...') or /sys/ reads — often restricted in unprivileged containers
/proc/meminfo, /proc/net/, /proc/diskstats — may be empty or permission-denied
Flag if the read is NOT wrapped in a try/except that handles FileNotFoundError or PermissionError.
4b. psutil calls that fail in containers
psutil.disk_io_counters() — returns None when /proc/diskstats is unavailable
psutil.disk_partitions() — returns container-internal mounts, not host disks
psutil.sensors_temperatures() — fails without /sys/class/thermal/
psutil.sensors_fans() — same issue
psutil.net_io_counters(pernic=True) — may show veth interfaces only
Flag if the return value is used without a None / empty check. For example, psutil.disk_io_counters().read_bytes will crash if the call returns None.
4c. Docker socket assumptions
subprocess.run(['docker', ...]) without checking if Docker is installed/running first
- Direct access to
/var/run/docker.sock without verifying the socket exists
- Any Docker operation that doesn't handle
FileNotFoundError or ConnectionError
Flag if there's no pre-check (e.g., is_docker_installed()) or no try/except around the call.
Acceptable: Calls inside docker_service.py that are already gated behind is_docker_installed() at the API layer.
4d. Firewall/network commands without capability checks
firewall-cmd, ufw, iptables — require CAP_NET_ADMIN which is dropped in unprivileged LXC
- These commands fail silently or with cryptic errors when capabilities are missing
Flag if the subprocess call doesn't handle the failure case (no try/except, or no check of returncode).
4e. Hardcoded device paths
/dev/ references (e.g., /dev/sda, /dev/null is fine) — block devices don't exist in containers
/dev/fuse — FUSE device, unavailable in unprivileged LXC
Flag hardcoded /dev/ paths (except /dev/null, /dev/stdin, /dev/stdout, /dev/stderr, /dev/urandom, /dev/random).
Acceptable: Paths discovered dynamically from psutil or lsblk output.
How to Audit
- Use Grep to find all
subprocess.run( calls in the target scope
- For each match, read the surrounding context (5-10 lines) to check:
- Is
sudo present for privileged commands?
- Is there a
try/except FileNotFoundError for distro-specific commands?
- Is there an
os.path.exists() guard?
- Is there a system utility (
ServiceControl, PackageManager, run_privileged, is_command_available) that should be used instead?
- Also search for
os.path.exists('/usr/bin/apt') and similar distro-detection patterns
- Search for
open('/proc/ and open('/sys/ — check for missing error handling
- Search for
psutil.disk_io_counters, psutil.disk_partitions, psutil.sensors_ — check return values are guarded against None
- Search for
/dev/ string literals — flag hardcoded device paths (except standard ones)
- Search for
docker subprocess calls outside docker_service.py — check for availability guards
- For firewall commands, check that
returncode is inspected or the call is wrapped in try/except
- Collect all violations
Report Format
For each issue found, report:
[PATTERN] file_path:line_number
Command: ['systemctl', 'restart', ...]
Fix: Add 'sudo' as first element
or for Pattern 3:
[PATTERN 3] file_path:line_number
Command: subprocess.run(['sudo', 'systemctl', 'restart', service], ...)
Fix: Use ServiceControl.restart(service)
Then provide a summary:
## Summary
- Files scanned: X
- Total subprocess calls: X
- Missing sudo: X instances across Y files
- Missing exception handling: X instances across Y files
- Should use system utilities: X instances across Y files
- Container/environment assumptions: X instances across Y files
- Clean: X calls
If issues are found, ask the user if they want you to fix them automatically.
If no issues are found, confirm the codebase is clean for these patterns.
1---2name: audit-subprocess3description: Scan backend services for subprocess bugs — missing sudo, missing exception handling on distro-specific commands, raw subprocess calls bypassing system utilities, and container/environment assumptions that break in LXC or restricted environments. Reports issues with file, line, and fix.4---56Audit subprocess calls in ServerKit backend services for recurring bug patterns.7Scope: **${ARGUMENTS:-backend/app/services/}**89## What to Scan For1011### Pattern 1: Missing `sudo` on privileged commands1213Search for `subprocess.run()` calls that invoke commands requiring root but lack `sudo`:1415- **`systemctl`** (start, stop, restart, enable, disable, daemon-reload) — ALL require sudo16- **`firewall-cmd`** (--state, --get-default-zone, --list-services, --list-ports, --list-rich-rules, --get-zones, --zone=*, --add-*, --remove-*, --reload) — ALL require sudo17- **`ufw`** (status, enable, disable, allow, deny, delete) — ALL require sudo18- **`iptables`** / **`ip6tables`** — ALL require sudo19- **`nginx`** (-s reload, -t) — ALL require sudo20- **`certbot`** — ALL require sudo21- **`freshclam`** — requires sudo2223For each match, check if the command list starts with `'sudo'`. If not, flag it.2425**Ignore**: Commands that legitimately don't need sudo (git, python, pip, npm, node, which, cat, ls, grep, df, free, uptime, whoami, hostname, lsb_release).2627### Pattern 2: Missing exception handling on distro-specific commands2829Search for `subprocess.run()` calls that use distro-specific package managers without `try/except FileNotFoundError`:3031- **`dpkg`** — Debian/Ubuntu only, doesn't exist on RHEL/Fedora/CentOS32- **`apt`** / **`apt-get`** — Debian/Ubuntu only33- **`rpm`** — RHEL family only, doesn't exist on Debian34- **`dnf`** / **`yum`** — RHEL family only3536For each match, check if it's wrapped in a `try/except` that catches `FileNotFoundError` (or a broad `Exception` on the immediate call). If not, flag it.3738**Acceptable**: If the call is guarded by `os.path.exists('/usr/bin/<cmd>')` before the subprocess call, that's fine — don't flag it.3940### Pattern 3: Raw subprocess calls that should use system utilities4142The project provides centralized utilities in `app/utils/system.py`. Flag direct subprocess calls that bypass them:4344- **`subprocess.run(['sudo', 'systemctl', ...])`** → should use `ServiceControl.start/stop/restart/reload/enable/disable/daemon_reload()`45- **`subprocess.run(['systemctl', 'is-active', ...])`** → should use `ServiceControl.is_active()`46- **`subprocess.run(['systemctl', 'is-enabled', ...])`** → should use `ServiceControl.is_enabled()`47- **`subprocess.run(['dpkg', ...])`** or **`subprocess.run(['rpm', ...])`** for package checks → should use `PackageManager.is_installed()`48- **`os.path.exists('/usr/bin/apt')`** or similar distro detection → should use `PackageManager.detect()`49- **`for path in ['/usr/bin/X', '/usr/sbin/X']: if os.path.exists(path)`** binary search → should use `is_command_available()`50- **`subprocess.run(['sudo', 'apt-get', 'install', ...])`** or **`['sudo', 'dnf', 'install', ...]`** → should use `PackageManager.install()`51- **`subprocess.run(['sudo', <privileged-cmd>, ...])`** for other privileged commands → should use `run_privileged()`5253**Acceptable exceptions** (do NOT flag these):54- Docker CLI calls in `docker_service.py` (uses Docker socket)55- Git CLI calls (no privilege needed)56- Python/pip in venvs (per-app, no root)57- MySQL/PostgreSQL CLI (auth via socket/password)58- WP-CLI (`sudo -u www-data` pattern)59- Read-only commands (`clamscan`, `fail2ban-client status`, `uname`, `which`, `ssh-keygen`, etc.)6061### Pattern 4: Container/environment assumptions6263Code that assumes bare-metal or KVM and breaks inside LXC containers, Docker, or restricted environments. Search for these patterns:6465#### 4a. Unguarded `/proc` and `/sys` reads6667- **`open('/proc/cpuinfo')`** or similar `/proc/` reads without `try/except (FileNotFoundError, PermissionError)`68- **`open('/sys/class/...')`** or `/sys/` reads — often restricted in unprivileged containers69- **`/proc/meminfo`**, **`/proc/net/`**, **`/proc/diskstats`** — may be empty or permission-denied7071Flag if the read is NOT wrapped in a try/except that handles `FileNotFoundError` or `PermissionError`.7273#### 4b. `psutil` calls that fail in containers7475- **`psutil.disk_io_counters()`** — returns `None` when `/proc/diskstats` is unavailable76- **`psutil.disk_partitions()`** — returns container-internal mounts, not host disks77- **`psutil.sensors_temperatures()`** — fails without `/sys/class/thermal/`78- **`psutil.sensors_fans()`** — same issue79- **`psutil.net_io_counters(pernic=True)`** — may show veth interfaces only8081Flag if the return value is used without a `None` / empty check. For example, `psutil.disk_io_counters().read_bytes` will crash if the call returns `None`.8283#### 4c. Docker socket assumptions8485- **`subprocess.run(['docker', ...])`** without checking if Docker is installed/running first86- Direct access to **`/var/run/docker.sock`** without verifying the socket exists87- Any Docker operation that doesn't handle `FileNotFoundError` or `ConnectionError`8889Flag if there's no pre-check (e.g., `is_docker_installed()`) or no try/except around the call.9091**Acceptable**: Calls inside `docker_service.py` that are already gated behind `is_docker_installed()` at the API layer.9293#### 4d. Firewall/network commands without capability checks9495- **`firewall-cmd`**, **`ufw`**, **`iptables`** — require `CAP_NET_ADMIN` which is dropped in unprivileged LXC96- These commands fail silently or with cryptic errors when capabilities are missing9798Flag if the subprocess call doesn't handle the failure case (no try/except, or no check of `returncode`).99100#### 4e. Hardcoded device paths101102- **`/dev/`** references (e.g., `/dev/sda`, `/dev/null` is fine) — block devices don't exist in containers103- **`/dev/fuse`** — FUSE device, unavailable in unprivileged LXC104105Flag hardcoded `/dev/` paths (except `/dev/null`, `/dev/stdin`, `/dev/stdout`, `/dev/stderr`, `/dev/urandom`, `/dev/random`).106107**Acceptable**: Paths discovered dynamically from `psutil` or `lsblk` output.108109## How to Audit1101111. Use **Grep** to find all `subprocess.run(` calls in the target scope1122. For each match, read the surrounding context (5-10 lines) to check:113 - Is `sudo` present for privileged commands?114 - Is there a `try/except FileNotFoundError` for distro-specific commands?115 - Is there an `os.path.exists()` guard?116 - Is there a system utility (`ServiceControl`, `PackageManager`, `run_privileged`, `is_command_available`) that should be used instead?1173. Also search for `os.path.exists('/usr/bin/apt')` and similar distro-detection patterns1184. Search for `open('/proc/` and `open('/sys/` — check for missing error handling1195. Search for `psutil.disk_io_counters`, `psutil.disk_partitions`, `psutil.sensors_` — check return values are guarded against `None`1206. Search for `/dev/` string literals — flag hardcoded device paths (except standard ones)1217. Search for `docker` subprocess calls outside `docker_service.py` — check for availability guards1228. For firewall commands, check that `returncode` is inspected or the call is wrapped in try/except1239. Collect all violations124125## Report Format126127For each issue found, report:128129```130[PATTERN] file_path:line_number131 Command: ['systemctl', 'restart', ...]132 Fix: Add 'sudo' as first element133```134135or for Pattern 3:136137```138[PATTERN 3] file_path:line_number139 Command: subprocess.run(['sudo', 'systemctl', 'restart', service], ...)140 Fix: Use ServiceControl.restart(service)141```142143Then provide a summary:144145```146## Summary147- Files scanned: X148- Total subprocess calls: X149- Missing sudo: X instances across Y files150- Missing exception handling: X instances across Y files151- Should use system utilities: X instances across Y files152- Container/environment assumptions: X instances across Y files153- Clean: X calls154```155156If issues are found, ask the user if they want you to fix them automatically.157If no issues are found, confirm the codebase is clean for these patterns.