Summary
Two small input-handling defects. Both are currently backstopped by other controls, so this is defence-in-depth rather than a live hole.
6a. Origin check fails open when the header is absent
fleet/bin/terminal.py:174-177 guards the terminal WebSocket with if origin and not .... A non-browser client that simply omits Origin passes. Given what is on the other side of that socket — os.execvpe of claude at repo root (terminal.py:110-118) — it should fail closed. Currently backstopped by CONTROL_PATHS and KILL_TOKEN.
6b. Hermes prompt still passed as argv (follow-up to #9)
fleet/bin/chat.py:228 — ["hermes", "-z", prompt]. A message beginning with - is parsed as a flag.
This is not shell injection; there is no shell=True anywhere in the repo. And ask_claude at chat.py:222-224 already does the right thing by sending the prompt on stdin.
Worth flagging: #9's acceptance criteria included "Prefer passing prompts on stdin wherever supported." The disclosure half was fixed (fleet.py:400-410 now redacts /api/processes for remote callers), but the stdin half was not carried through to ask_hermes. Reachable only from /chat/send, which is remote-blocked since #4.
Acceptance criteria
terminal.py rejects a WebSocket upgrade with a missing Origin, with a test.
ask_hermes passes the prompt on stdin, matching ask_claude; or uses -- to terminate flag parsing if stdin is unsupported.
Summary
Two small input-handling defects. Both are currently backstopped by other controls, so this is defence-in-depth rather than a live hole.
6a. Origin check fails open when the header is absent
fleet/bin/terminal.py:174-177guards the terminal WebSocket withif origin and not .... A non-browser client that simply omitsOriginpasses. Given what is on the other side of that socket —os.execvpeofclaudeat repo root (terminal.py:110-118) — it should fail closed. Currently backstopped byCONTROL_PATHSandKILL_TOKEN.6b. Hermes prompt still passed as argv (follow-up to #9)
fleet/bin/chat.py:228—["hermes", "-z", prompt]. A message beginning with-is parsed as a flag.This is not shell injection; there is no
shell=Trueanywhere in the repo. Andask_claudeatchat.py:222-224already does the right thing by sending the prompt on stdin.Worth flagging: #9's acceptance criteria included "Prefer passing prompts on stdin wherever supported." The disclosure half was fixed (
fleet.py:400-410now redacts/api/processesfor remote callers), but the stdin half was not carried through toask_hermes. Reachable only from/chat/send, which is remote-blocked since #4.Acceptance criteria
terminal.pyrejects a WebSocket upgrade with a missingOrigin, with a test.ask_hermespasses the prompt on stdin, matchingask_claude; or uses--to terminate flag parsing if stdin is unsupported.