Repository navigation
Description
What happened
Security and stability hardening audit finding H04 / P2 (2026-09-28). Priority is an audit recommendation, not a claim of a production incident.
The MCP adapter starts the CLI in its own session and, on cancellation or timeout, calls stop_codex_wake(process) with the default 0.2 s grace: SIGTERM to the CLI process group, then SIGKILL. A --wake CLI, however, starts codex app-server with start_new_session=True, so the app-server is outside the group the adapter signals. Only the CLI's own SIGTERM handler reaches it, by raising KeyboardInterrupt and running the same TERM, 0.2 s, KILL sequence against the app-server group.
Both 0.2 s grace periods start at nearly the same moment. If the app-server does not exit within the grace period, the adapter can SIGKILL the CLI before the CLI escalates to SIGKILL on its child. The detached app-server then keeps running the resumed turn, which can continue to spend model usage and edit files after the MCP caller saw the wake as cancelled.
Isolated fixture: a fake app-server that ignores SIGTERM, answers initialize/thread/resume, and never completes the turn. It ran under run_codex_wake in a child started the same way the adapter starts the CLI, and then stop_codex_wake was applied to that child. The CLI returned -9 every time, and the app-server was still alive 1 s later in 3 of 5 runs. The audit's earlier run had 2 of 3. Only fixture processes were signalled and cleaned up.
What you expected
Cancelling or timing out an MCP wake leaves no app-server that session-peer started. One option: the adapter waits for the CLI's worst-case cleanup budget (grace, wait(timeout=3), Darwin ps check) before SIGKILL. Another: the CLI reports the app-server PGID so the parent can reap it with the existing PID-reuse safeguards.
Environment
- Baseline: published session-peer v1.0.2, commit
2046836822b88e53246de73851186598604cb5ec. - Reproduction: isolated source fixtures with a synthetic app-server; no live message/ACK tests or production mutation.
- Audit environment: macOS arm64, Python 3.14.7.
- Discovery output: not applicable to this fixture; no real session identifiers or credentials are included.
Source evidence
- session_peer_mcp.py#L43
- session_peer_core/codex.py#L854
- session_peer_core/codex.py#L891
- session_peer_core/codex.py#L901-L903
Acceptance criteria
- Add MCP cancellation and timeout fixtures with a TERM-resistant app-server started via
--wake, and require that no app-server started by session-peer survives the cleanup deadline. - Keep cleanup bounded and limited to process groups session-peer owns; never signal a reused or unrelated PGID.
- Cover prompt app-server exit, SIGTERM-ignoring app-server, repeated cancellation, and normal completion.
- Keep outcome semantics: a queued message is not reported as retracted, and the wake result stays
unknown/activation_interruptedwhere applicable.
Scope and limitations
The fixture does not measure how long a real codex app-server takes to shut down on SIGTERM or stdin EOF. The race needs an app-server that is still running after the grace period.
Target: v1.0.3. This issue does not authorize a release or operating-service changes.
Related, not duplicate: #205 (descendants in the same app-server group after the leader exits) and #206 (CLI descendants in the MCP-owned group). Neither covers a child placed in a separate session by the CLI.
Checked
- Searched existing issues for duplicates.
- Removed credentials, conversation contents, private paths and real device/session identifiers.
- Used isolated fixtures without weakening inbound permissions.
Activity