Skip to content

Security: kill_process_on_port uses shell=True with unvalidated port interpolation #789

@kovtcharov

Description

@kovtcharov

Summary

src/gaia/util.py:14-52 builds shell commands with f-string interpolation of the port argument while running under shell=True. If a caller ever passes a non-integer (string) value — intentionally or by mistake — arbitrary shell commands execute.

Location

# src/gaia/util.py
def kill_process_on_port(port):
    if sys.platform.startswith(\"win\"):
        result = subprocess.run(
            f\"netstat -ano | findstr :{port}\",   # ← f-string in shell=True
            shell=True, ...
        )
        ...
        subprocess.run(f\"taskkill /F /PID {pid} /F\", shell=True, ...)
    else:
        result = subprocess.run(
            f\"lsof -ti :{port}\",                 # ← f-string in shell=True
            shell=True, ...
        )
        ...
        subprocess.run(f\"kill -9 {pid}\", shell=True, ...)

Actual Risk

Today all callers pass self.port (an int), so there is no live exploit. But:

  • The signature type is untyped — nothing prevents a future refactor from passing a string.
  • The pid variables come from parsed subprocess output — if lsof/netstat output were ever influenced by an attacker's chosen process name, the same pattern re-applies.
  • Other shell=True sites are listed below for the same hardening pass.

Suggested Fix

  • port: int annotation and an int(port) cast at entry.
  • Prefer shell=False with list args (matches what src/gaia/agents/code/tools/cli_tools.py:131 and src/gaia/llm/lemonade_client.py:343 already do using psutil):
import psutil
def kill_process_on_port(port: int) -> None:
    for proc in psutil.process_iter([\"pid\", \"name\"]):
        try:
            for conn in proc.net_connections():
                if conn.laddr.port == int(port):
                    proc.kill()
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            continue

psutil is already a dep; this also removes the platform branching.

Impact

  • Hardens one of three kill_process_on_port implementations in the repo — the other two (agents/code/tools/cli_tools.py and llm/lemonade_client.py) already use psutil safely, so we should consolidate onto one.
  • Consider deleting src/gaia/util.py:kill_process_on_port outright and routing all callers to the safe version.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingsecuritySecurity-sensitive changestech debt

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions