Model subprocesses can prompt on mecated's controlling terminal
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 152
- Forks
- 16
- Avg merge
- 14h 48m
- Merged PRs (30d)
- 536
Description
Summary
Model-executed subprocesses can access the foreground mecated process's controlling terminal. A Git subprocess opened /dev/tty and printed Username for 'https://github.com': directly in the daemon's terminal while its Shell tool call appeared to produce no captured output and eventually timed out or was canceled.
mecated serve is a headless harness even when an operator runs it in a foreground terminal. Agent subprocesses must not be able to interact with that terminal, solicit operator input outside the permission UI, or bypass captured tool I/O.
Observed behavior
- Session:
50028801afa1b8a77186921f8e171d66 - Client:
mecated v0.0.38-57-g7cf4e5dfd - Daemon was running in the foreground on macOS with stdin/stdout/stderr attached to
ttys000. - The terminal's last line became:
Username for 'https://github.com': - No prompt appeared in the Mecatl client or captured Shell output.
Two session records correlate with the terminal behavior:
- In run
run_wre4znaat4cfe44uwwae3hj46i, a parallel branch invokedgit push origin HEAD:renovate/hashicorp-agent-skills-digest. The recorded result was[command produced no output and timed out after 120000ms]. - In run
run_amsmkfsdalpgsodtfupy7gerdi, a parallel branch invoked the same plaingit pushform. It was later recorded as[command was canceled before producing output]; the branch duration was approximately 419 seconds.
After the runs completed, process inspection showed only mecated attached to the TTY and no live Git child, confirming that the visible username prompt was stale terminal output rather than the daemon still awaiting input.
Other Git operations in the same session that explicitly used GIT_TERMINAL_PROMPT=0 failed promptly with a captured fatal: could not read Username for 'https://github.com': terminal prompts disabled, demonstrating the expected noninteractive behavior.
Likely cause
The Shell runner captures stdout/stderr and leaves cmd.Stdin unset, but process-group isolation uses Setpgid rather than detaching the child from the daemon's controlling terminal. Programs such as Git can open /dev/tty directly, so null/captured standard streams do not prevent interaction with the foreground daemon terminal.
The Git-hardening environment does not close this gap:
gitenv.Scrubremoves every inheritedGIT_*variable, including an inheritedGIT_TERMINAL_PROMPT=0.- Its neutralizing variables do not add
GIT_TERMINAL_PROMPT=0back. - Git credential helpers may have their own interactive paths as well.
Impact
- A model-triggered process can display prompts outside Mecatl's event stream and permission surfaces.
- A foreground operator could mistakenly enter credentials into an agent-controlled subprocess.
- Prompt text and operator input bypass normal Shell output capture and audit records.
- Runs can hang until their command timeout or cancellation.
- The problem is broader than Git: any child able to open the controlling TTY can attempt interactive I/O.
This is related in spirit to #1608, but it is independently actionable: terminal detachment and noninteractive subprocess behavior should hold even before or without a complete macOS Seatbelt boundary.
Expected behavior
Every model-executed subprocess, fork-time Git operation, hook, and other agent-adjacent command must be unable to access the daemon's controlling terminal. An operation requiring interactive input should fail promptly and return a captured diagnostic through its normal result channel.
Possible remediation
- Start agent-adjacent subprocess groups in a detached session/no-controlling-TTY configuration while preserving whole-process-group cancellation semantics.
- Add
GIT_TERMINAL_PROMPT=0explicitly to the canonical Git environment instead of merely depending on inherited state. - Disable Git Credential Manager and askpass interaction where applicable, for example with an appropriate noninteractive GCM setting, as defense in depth.
- Apply the same no-TTY invariant consistently across main Shell, sandboxed/force-copy Shell, fork-time Git, hooks, and any other subprocess adapters.
Suggested acceptance coverage
- Under a real pseudo-terminal, a child attempting to open
/dev/ttycannot read from or write prompts to the daemon terminal. - An HTTPS Git operation without credentials fails promptly, produces a captured diagnostic, and never emits a terminal prompt.
- The canonical hardened Git environment includes an explicit terminal-prompt prohibition after scrubbing.
- Timeout/cancellation still kills and reaps the complete subprocess group after terminal detachment.
- Tests cover the main, sandboxed worktree, force-copy, and fork-time Git execution paths.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Trace the Shell runner, gitenv.Scrub, and the main, sandboxed/force-copy, fork-time Git, and hook execution paths. Start by reproducing the behavior under a real pseudo-terminal, then inspect how process groups and standard streams are configured. Done means children cannot access the daemon TTY, hardened Git operations fail promptly with captured diagnostics, and timeout/cancellation still reaps the full process group across the listed paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100