feat: add built-in secure sudo password prompt#2101
Conversation
🦋 Changeset detectedLatest commit: 77a2baf The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 95973403c8
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
sudo 1.9+ only consults SUDO_ASKPASS when askpass mode is active, so env injection alone never triggered the password prompt — plain sudo invocations still failed with the no-TTY error (spotted in review on PR MoonshotAI#2101). Both engines now write a bin/sudo shim (exec real-sudo -A "$@") into the per-session askpass dir and prepend it to the spawned command's PATH. -S/--stdin callers pass through without -A (sudo rejects that combination); -n keeps its non-interactive semantics; an explicit caller-side -A is harmless. Real-sudo smoke tests (cancel-only, no credentials) plus -n/-A/-S flag tests cover the behavior, and the fake-sudo e2e tests now mimic sudo 1.9's -A requirement. Also folds the new agent-core-v2 inline comments into file headers per the package's header-only comment convention.
sudo 1.9+ only consults SUDO_ASKPASS when askpass mode is active, so env injection alone never triggered the password prompt — plain sudo invocations still failed with the no-TTY error (spotted in review on PR MoonshotAI#2101). Both engines now write a bin/sudo shim (exec real-sudo -A "$@") into the per-session askpass dir and prepend it to the spawned command's PATH. -S/--stdin callers pass through without -A (sudo rejects that combination); -n keeps its non-interactive semantics; an explicit caller-side -A is harmless. Real-sudo smoke tests (cancel-only, no credentials) plus -n/-A/-S flag tests cover the behavior, and the fake-sudo e2e tests now mimic sudo 1.9's -A requirement. Also folds the new agent-core-v2 inline comments into file headers per the package's header-only comment convention.
2eeb4d7 to
fd6d8b8
Compare
When a Bash tool command invokes sudo and a password is required, the
Bash tool injects a per-session askpass helper (SUDO_ASKPASS) into the
spawned environment. sudo invokes the helper, which authenticates over
a per-session unix socket (0700 dir, random 128-bit token) and triggers
a masked local password prompt in the connected client:
- TUI: masked input dialog via a new requestPassword reverse RPC
(agent-core SDKAPI -> node-sdk -> TUI reverse-rpc controller)
- Web: new 'password' interaction kind in agent-core-v2, surfaced by
kap-server as event.password.requested/resolved + REST
/sessions/{id}/passwords, rendered as a masked PasswordCard in
kimi-web
The password flows only client -> in-memory resolution -> socket ->
helper stdout -> sudo. It never enters the model context, wire.jsonl
(journaled responses are redacted at the interaction kernel), the
transcript, WS broadcasts, logs, or telemetry.
Enabled by default on macOS/Linux; disable with
[sudo_askpass] enabled = false in config.toml. Commands containing
sudo still go through the normal permission approval flow.
Resolve MoonshotAI#2090
sudo 1.9+ only consults SUDO_ASKPASS when askpass mode is active, so env injection alone never triggered the password prompt — plain sudo invocations still failed with the no-TTY error (spotted in review on PR MoonshotAI#2101). Both engines now write a bin/sudo shim (exec real-sudo -A "$@") into the per-session askpass dir and prepend it to the spawned command's PATH. -S/--stdin callers pass through without -A (sudo rejects that combination); -n keeps its non-interactive semantics; an explicit caller-side -A is harmless. Real-sudo smoke tests (cancel-only, no credentials) plus -n/-A/-S flag tests cover the behavior, and the fake-sudo e2e tests now mimic sudo 1.9's -A requirement. Also folds the new agent-core-v2 inline comments into file headers per the package's header-only comment convention.
fd6d8b8 to
9d793a2
Compare
|
Thanks for putting this together — it's a lot of work, and it covers both the TUI and Web UI across both engines. We're not going to merge this right now, for a few reasons. One of them is That said, we're keeping this PR open rather than closing it — once v2 settles down and we've written up the guidelines, we may come back to it for reference or pick it up directly. The feature itself is genuinely useful, and the sudo-stdin pain (#2090, #249) is on our radar. If we still haven't shipped something like this by then, please bug us about it. ❤️ |
…ayer The Bash tool (os/backends, L6) imported ISessionSudoAskpassService from the session domain (L7) — an upward dependency against the layering rules, surfaced during review on PR MoonshotAI#2101. The env-provider contract now lives in os/interface (L1) as ISudoAskpassEnvProvider; the Session-scope sudoAskpass channel implements and registers it there, so every import flows downward and the layer-check exception is removed.
|
Thanks for the detailed feedback — totally understand holding this until v2's guidelines are written down. The layering point was spot-on: No expectation on timing from my side — feel free to pick this up whenever it fits your schedule, or mine it for reference. Happy to keep it rebased in the meantime. |
Related Issue
Resolve #2090 (also addresses the pain point reported in #249)
cc @sailist @liruifengv — would appreciate a review, especially on the interaction-kernel redaction and the new REST/WS surface.
Problem
When the agent's
Bashtool runs a command that invokessudo, the command fails because stdin is closed and there is no TTY to read the password from. Users have to run privileged commands manually in another terminal, or build their ownSUDO_ASKPASShacks. See the linked issue for details.What changed
A built-in secure sudo password prompt, for both the TUI and the web UI:
Bashtool injectsSUDO_ASKPASS(+ a per-session 128-bit token and the triggering command) into spawned commands on macOS/Linux. The per-session helper dir (mode0700) holdshelper.sh/helper.mjsand a unix socket; sudo auto-invokes the helper when it needs a password (no TTY is present), the helper authenticates over the socket, and the engine issues one password request per connection. Deep session dirs that would exceed the 104-bytesun_pathcap fall back to a short session-keyed dir under the OS temp dir.agent-core→node-sdk→apps/kimi-code): newrequestPasswordreverse RPC mirroring the approval flow, rendered as a masked (•) input dialog showing the sudo prompt and the triggering command. The submitted value is never trimmed, so whitespace-containing passwords work.agent-core-v2→kap-server→apps/kimi-web): newpasswordinteraction kind surfaced asevent.password.requested/resolved+GET/POST /api/v1/sessions/{id}/passwords+pending_passwordsin the snapshot, rendered as a maskedPasswordCard(top priority over question/approval cards, since sudo is blocking a running process).wire.jsonl(the interaction kernel journals a redacted{cancelled}response and broadcasts only that shape), the transcript, WS events, REST envelopes/snapshots, request logs, or telemetry. Helper connections that disconnect mid-prompt auto-cancel. Cancelling the prompt makes the helper exit 1, so sudo fails with its normal error — identical to today's UX. Commands containingsudostill go through the normal permission approval flow.[sudo_askpass] enabled = falseinconfig.tomldisables the feature (default enabled; no-op on Windows). One config shape works for both engines.config-files.md(en/zh) new[sudo_askpass]section;reference/tools.md(en/zh) Bash section documents the behavior.Known consideration for reviewers: as with any askpass-based design (e.g. Cursor's), the token travels in the spawned command's environment, so a malicious command could present a phishing prompt via the socket — the dialog always shows the sudo prompt text and the triggering command to help users judge. The token only authenticates same-user local connections, and no password is ever exposed to the model either way.
Testing
sudoend-to-end tests (a tempsudoon PATH that invokes$SUDO_ASKPASS) proving submit and cancel paths through the real Bash tool layer, socket roundtrips with the real spawned helper, token rejection, journaling redaction, WS/REST shapes, reducer/mapper coverage, and dialog masking assertions. No real sudo is used in tests.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.