Files
orca/src/main/ssh/ssh-session-limit-error.test.ts
f215a48064 fix: pr-bug-scan validated finding from #6952 (#7180)
* fix: address pr-bug-scan validated finding from #6952

throwNodeNotFound() now re-raises AbortError when the shared signal is aborted, so a signal-cancelled node probe no longer launders into 'Node.js not found'; sequential fallback runs.

* fix(ssh): make session-limited (MaxSessions=1) relay deploys actually succeed

Review of #7180 verified the parent fallback end-to-end against a real
MaxSessions=1 sshd and found the connect still failed. Four gaps, in order
of discovery:

- isSshSessionLimitError missed stock OpenSSH, which refuses session
  channels over MaxSessions with SSH2_OPEN_CONNECT_FAILED (2) and 'open
  failed' — reason 4 never matched, so the fallback never triggered.
- execCommand settled aborted commands before the channel finished
  closing, so the sequential fallback reissued execs while sshd still
  counted the old session.
- SshConnection.waitForSshCallback rejected aborts mid-channel-open
  immediately, leaking a confirmed-late channel that held the only
  session slot; it now settles after the late channel closes (bounded)
  and drains its streams so ssh2 emits 'close'.
- Session channel opens now retry transient session-limit refusals
  (sshd frees the slot only after processing our close-ack, which the
  next open can beat by microseconds), and the remote orca CLI shim
  install is non-fatal like the managed-hook install — after the relay
  bridge occupies the sole session slot, raw-connection extras must
  degrade instead of failing the connection.

Verified live against Docker sshd (OpenSSH 9.2, MaxSessions=1): fresh
deploy (upload + native deps + launch), reconnect cycles, and a PTY
round-trip all succeed; unrestricted-sshd regression run also passes.

Co-authored-by: Orca <help@stably.ai>

* Handle ssh execution aborts immediately during retry backoff or hangs

- Cancel the session-limit retry delay immediately if the operation is
  aborted during backoff.
- Limit the wait time to a 5-second grace period when aborted during a
  channel open that is hung and never invokes its callback, rather than
  waiting for the full connection timeout.

---------

Co-authored-by: orca-bug-scan-bot <orca-bug-scan-bot@stably.ai>
Co-authored-by: Jinjing <6427696+AmethystLiang@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
2026-07-04 01:26:55 -07:00

63 lines
2.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { isSshSessionLimitError } from './ssh-session-limit-error'
describe('isSshSessionLimitError', () => {
it('matches ssh2 open failures with the resource-shortage reason code', () => {
expect(
isSshSessionLimitError(
Object.assign(new Error('(SSH) Channel open failure: open failed'), { reason: 4 })
)
).toBe(true)
})
it('matches the OpenSSH MaxSessions rejection (SSH2_OPEN_CONNECT_FAILED + "open failed")', () => {
// Why: stock OpenSSH sshd refuses session channels over MaxSessions with
// reason 2, not resource-shortage — observed against OpenSSH 9.2.
expect(
isSshSessionLimitError(
Object.assign(new Error('(SSH) Channel open failure: open failed'), { reason: 2 })
)
).toBe(true)
})
it('matches OpenSSH mux and MaxSessions failures', () => {
expect(
isSshSessionLimitError(
new Error(
'mux_client_request_session: session request failed: Session open refused by peer'
)
)
).toBe(true)
expect(
isSshSessionLimitError(new Error('open failed: MaxSessions limit reached on remote host'))
).toBe(true)
})
it('does not match generic channel-open failures without a session-limit reason', () => {
expect(
isSshSessionLimitError(
Object.assign(new Error('(SSH) Channel open failure: open failed'), { reason: 1 })
)
).toBe(false)
expect(
isSshSessionLimitError(
Object.assign(new Error('(SSH) Channel open failure: open failed'), { reason: 3 })
)
).toBe(false)
})
it('does not match unrelated command failures', () => {
expect(
isSshSessionLimitError(new Error('Command "node" failed (exit 1): Node.js not found'))
).toBe(false)
expect(isSshSessionLimitError(new Error('channel open failure while parsing output'))).toBe(
false
)
expect(
isSshSessionLimitError(
new Error('open failed: administratively prohibited: forwarding disabled')
)
).toBe(false)
})
})