Repository navigation
Conversation
doublewhy
force-pushed
the
1290-run-command-timeout
branch
from
October 11, 2026 16:20
f6a1128 to
aa226d6
Compare
doublewhy
marked this pull request as ready for review
October 11, 2026 17:42
This was referenced Oct 12, 2026
…and times out When the 30 s timer of the one-shot exec behind run_command fired, executeCommand rejected but kept no reference to the channel. The remote command kept running, and every chunk it wrote was still appended to the stdout and stderr buffers inside the MCP server. executeCommand now keeps the channel. When the timer fires it sends the command TERM and closes the channel, and a channel the server opens only after that is stopped as soon as it opens. Output that arrives after the timeout still reaches the capture tee but is no longer buffered. The rejection message and the default timeout are unchanged. A server may refuse the signal (OpenSSH does for a ForceCommand session), which the close covers, and an error from ssh2 on a torn-down connection is logged instead of escaping the timer. Closes #1290
doublewhy
force-pushed
the
1290-run-command-timeout
branch
from
October 12, 2026 08:51
aa226d6 to
083df5a
Compare
This was referenced Oct 12, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
run_commandruns throughSSHConnectionManager.executeCommandwith the fixed 30 s default timeout. When the timer fired,executeCommandrejected but kept no reference to the exec channel: the channel stayed open, the remote command kept running, and every chunk it wrote was still appended to the result buffers inside the MCP server (evidence and a hermetic reproduction in #1290). This PR stops the command and closes its channel when the timer fires.Changes
mcp/aptl-mcp-common/src/ssh-manager.ts:executeCommandkeeps the channel. On timeout the newstopTimedOutCommandsendsTERMwith ssh2'sClientChannel.signal(), thenclose(), before the promise rejects. A channel the server opens only after the timer fired is stopped the same way as soon as it opens.ptyTee) but is no longer buffered; onecollecthelper holds that guard for both data handlers.signal()/close()once its connection is torn down (Instance unusable after …);stopTimedOutCommandcatches and logs it, so it cannot crash the server from the timer.tests/ssh.test.tsadds a#1290block that drives the realexecuteCommandthrough a stub connection and anEventEmitterchannel withstderr,signal()andclose(). No file is added or removed and no lockfile changes, so the inventory and MCP lockfile digests are unchanged.Done-when matrix
Tests are in the
#1290block, run by CI'sMCP TypeScript tests + coveragejob (npx vitest run --coverageinmcp/aptl-mcp-common; passed in Checks run 38226224239). "Stash" = dev'sssh-manager.tsrestored withgit stash push.ssh-manager.ts:stopTimedOutCommand,executeCommandsignals the command, closes the channel and stops buffering when the timer firessignalnever called withTERMssh-manager.ts:executeCommand(collect)out,out,err)collectguard alone removed: fails (late chunk'stoStringcalled 2 times)ssh-manager.ts:executeCommand(exec callback)stops a channel the server opens only after the timer firedsignalnever called withTERMleaves a command that finishes in time alonestill rejects when ssh2 throws because the connection is already gonefails under stash (nothing logged) and with thetry/catchremoved (Instance unusable after fatal errorescapes the timer).Scope
TERMis coreutilstimeout(1)'s default and lets a wrapper finish its records (the capture broker does:containers/kali-capture/broker.py:466-470); the close follows, so there is no second signal.session_signal_reqinsession.c). The Kali path runsForceCommand /usr/local/bin/broker.py(sshd_config:19), so there the close ends the session: the broker's next write fails and its cleanup terminates the inner ssh process group (broker.py:507-516). This path is established by reading the code; the real-server verification is tracked in Verify the run_command timeout against a real sshd running the capture broker #1384.run_commandtimeout (The run_command tool has no per-call timeout #1320), and the persistent-session command timeout inssh-session.ts:343-352(A persistent-session command timeout leaves the command running #1318).Overlap with #1279
#1279 (#967) edits other functions in the same two files. On dev 1abe440,
git merge-tree --write-tree --name-only HEAD 22afced2for this head and #1279's head 22afced is clean (exit 0, tree 76eb78b6) and keeps both PRs' changes.Verification
Head 083df5a, based on dev 1abe440, Node 22.23.3, npm 10.9.9.
executeCommandwith a 200 ms timeout: it rejected withCommand timeout after 200ms: nmap -sV 10.0.0.5, then took 1,000 late 1 KiB chunks with 0signal()and 0close()calls and buffered all 1,000.mcp/aptl-mcp-common:npm ci,npm run build,npx tsc --noEmitpassed;npx vitest runon the 9 test files touchingexecuteCommand,run_command,ssh-managerand the areas fix: repair Wazuh rule authoring through MCP #1251/fix(mcp): harvest Kali captures from the workspace-scoped capture container #1257 changed: 201 passed.mcp/mcp-redrebuilt against this common build:npx vitest run tests/capture.test.ts tests/logger.test.ts67 passed. The eight dependent MCPs built withnpx tsc -p tsconfig.build.jsonon the previous rebase.pre-commit run --fileson both files and the ESLint complexity gate from.pre-commit-ci.yamlpassed.ForceCommandcapture broker is not in run_command leaves the remote command running and buffering after its timeout #1290's done-when and is tracked in Verify the run_command timeout against a real sshd running the capture broker #1384.Requirement UIDs: none (bug/maintenance).
Closes #1290