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.
Summary
src/gaia/util.py:14-52builds shell commands with f-string interpolation of theportargument while running undershell=True. If a caller ever passes a non-integer (string) value — intentionally or by mistake — arbitrary shell commands execute.Location
Actual Risk
Today all callers pass
self.port(an int), so there is no live exploit. But:pidvariables come from parsed subprocess output — iflsof/netstatoutput were ever influenced by an attacker's chosen process name, the same pattern re-applies.shell=Truesites are listed below for the same hardening pass.Suggested Fix
port: intannotation and anint(port)cast at entry.shell=Falsewith list args (matches whatsrc/gaia/agents/code/tools/cli_tools.py:131andsrc/gaia/llm/lemonade_client.py:343already do usingpsutil):psutilis already a dep; this also removes the platform branching.Impact
kill_process_on_portimplementations in the repo — the other two (agents/code/tools/cli_tools.pyandllm/lemonade_client.py) already usepsutilsafely, so we should consolidate onto one.src/gaia/util.py:kill_process_on_portoutright and routing all callers to the safe version.