mirror of
https://github.com/stablyai/orca.git
synced 2026-10-05 08:02:33 +00:00
#16432 was fixed by chunking writes to 32KB, on the belief that a `DefaultShell=cmd.exe` host caps one stdin at roughly 50KB. Re-measured on Windows 11 26200.9168 / OpenSSH_for_Windows_10.0p2, that premise is wrong in both directions, and the chunking does not fix the hang. The real constraint: a read on Windows PowerShell 5.1's redirected-stdin handle over a non-pty ssh exec can die permanently when it finds the stream momentarily empty, taking both the remaining data and the EOF with it. It is probabilistic per such read — not a size threshold, and not certain on the first one. Measured by swapping the copy loop for a counting reader: a 1.5s gap before any byte -> 0 bytes received, 6 of 6 1 byte, 1.5s gap, then 32767 -> exactly 1 byte 32768, 1.5s gap, then 32768 -> exactly 32768 a continuous 2MB -> 167936 / 270336 / 372736 Those three 2MB figures are one payload run three times under the same conditions, which is what rules out a threshold. Independently reproduced by a second harness where one 1.9MB counted read completed through 39 reads and another died after 11. A payload that fits one burst usually presents only one read that can find the stream empty, which is why 32KB mostly works — and it still failed 15 times in 120 under load, and 1 in 40 on a quiet host. Neither rate survives the 62 execs a 1.9MB file needs: even 2.5% compounds to about four uploads in five failing. No chunk size helps, because the defect is per blocking read, not per byte. Three controls on the same host, same DefaultShell, rule out both a size limit and cmd.exe: `findstr` took 2,016,000 bytes through one exec's stdin, sftp moved 1.9MB 5/5, and PowerShell 7 took 2MB in one exec. Windows writes now go over the sftp subsystem, whose batch script is read by the *local* client, so no remote process reads a pipe at all. PowerShell 7 is the fallback where sftp is unavailable, and Windows PowerShell 5.1 is last, still bounded, and now reports the host limitation and its remedy instead of a bare timeout. Measured on the same host, through this code: 1.9MB x20 all succeeded, hash-verified, median 315ms, against 0/6 before. 32KB x120 zero hangs, against 15/120. Also: - Stage under a unique name per attempt. An abandoned write leaves a remote process that may still hold the staging file, and losing contact is not evidence it died (docs/reference/ssh-execution-boundary.md), so a retry must not reuse a name its predecessor may own. Sweep is best-effort and never treated as proof of anything. - Create upload directories over sftp too; the JSON mkdir batch rode the same defective read. - Cover makeWindowsWriteFileCommand and the publish command against the 8000-char budget, which F11 flagged as untested.
123 lines
5.2 KiB
TypeScript
123 lines
5.2 KiB
TypeScript
import { gunzipSync } from 'node:zlib'
|
|
import { describe, expect, it } from 'vitest'
|
|
import { CMD_EXE_COMMAND_LINE_MAX_CHARS } from '../providers/windows-shell-args'
|
|
import { getRemoteHostPlatform } from './ssh-remote-platform'
|
|
import { tryStealInstallLockCommand } from './ssh-relay-install-lock-commands'
|
|
import { decodeRemotePowerShellScript, powerShellCommand } from './ssh-remote-powershell'
|
|
import {
|
|
makeWindowsPublishStagedFileCommand,
|
|
makeWindowsWriteFileCommand
|
|
} from './system-ssh-windows-file-write'
|
|
import {
|
|
cleanupOwnedRelayUploadStageCommand,
|
|
promoteOwnedRelayUploadStageCommand,
|
|
recoverOneStaleRelayUploadStageCommand,
|
|
reserveRelayUploadStageCommand,
|
|
type RelayUploadStageSlot
|
|
} from './ssh-relay-upload-stage-commands'
|
|
|
|
const windows = getRemoteHostPlatform('win32-x64')
|
|
const owner = '.sftp-namespace-123e4567e89b12d3a456426614174000'
|
|
const pool = 'C:\\Users\\orca\\.orca-remote\\.upload-stages'
|
|
const stage: RelayUploadStageSlot = {
|
|
poolDir: pool,
|
|
slotName: 'slot-0',
|
|
slotDir: `${pool}\\slot-0`,
|
|
claimDir: `${pool}\\claim-0`,
|
|
deleteDir: `${pool}\\delete-0`
|
|
}
|
|
|
|
// Why: sshd runs an exec request through its DefaultShell, which is cmd.exe on a
|
|
// stock Windows OpenSSH install, and cmd.exe refuses a longer line with exit 1
|
|
// and a localized "The command line is too long" — the whole connect dies there.
|
|
describe('Windows remote command line limit', () => {
|
|
it.each([
|
|
['recover stale upload stage', recoverOneStaleRelayUploadStageCommand(windows, pool)],
|
|
['reserve upload stage', reserveRelayUploadStageCommand(windows, pool, owner)],
|
|
[
|
|
'promote upload stage',
|
|
promoteOwnedRelayUploadStageCommand(windows, stage, owner, 'C:\\Users\\orca\\.orca-remote')
|
|
],
|
|
['cleanup upload stage', cleanupOwnedRelayUploadStageCommand(windows, stage, owner)],
|
|
[
|
|
'steal stale install lock',
|
|
tryStealInstallLockCommand(windows, 'C:\\Users\\orca\\.orca-remote\\relay', 1_200)
|
|
],
|
|
// F11 flagged these two as uncovered. They carry one path literal each, so they are the file
|
|
// commands whose length a caller can actually move.
|
|
['write file', makeWindowsWriteFileCommand('C:\\Users\\orca\\.orca-remote\\relay.js')],
|
|
[
|
|
'publish staged file',
|
|
makeWindowsPublishStagedFileCommand(
|
|
'C:\\Users\\orca\\.orca-remote\\relay.js.orca-partial-0123456789ab',
|
|
'C:\\Users\\orca\\.orca-remote\\relay.js',
|
|
'create'
|
|
)
|
|
]
|
|
])('keeps the %s command inside what sshd\u2019s cmd.exe accepts', (_name, command) => {
|
|
expect(command.length).toBeLessThanOrEqual(CMD_EXE_COMMAND_LINE_MAX_CHARS)
|
|
})
|
|
|
|
it('leaves a command that already fits byte-identical', () => {
|
|
const script = "Write-Output ([Environment]::GetFolderPath('UserProfile'))"
|
|
expect(decodeRemotePowerShellScript(powerShellCommand(script))).toBe(script)
|
|
})
|
|
|
|
it('carries an oversized script through gzip without altering it', () => {
|
|
const script = Array.from(
|
|
{ length: 200 },
|
|
(_unused, index) => `Write-Output ${index}; $slot = 'C:\\Users\\orca\\stage-${index}'`
|
|
).join('\n')
|
|
const command = powerShellCommand(script)
|
|
expect(command.length).toBeLessThanOrEqual(CMD_EXE_COMMAND_LINE_MAX_CHARS)
|
|
expect(decodeRemotePowerShellScript(command)).toBe(script)
|
|
const bootstrap = Buffer.from(
|
|
command.match(/-EncodedCommand\s+([A-Za-z0-9+/=]+)$/u)?.[1] ?? '',
|
|
'base64'
|
|
).toString('utf16le')
|
|
const payload = bootstrap.match(/FromBase64String\('([A-Za-z0-9+/=]+)'\)/u)?.[1] ?? ''
|
|
expect(gunzipSync(Buffer.from(payload, 'base64')).toString('utf-8')).toBe(script)
|
|
expect(bootstrap).toContain('Invoke-Expression $OrcaScriptText')
|
|
})
|
|
|
|
it('refuses a script no encoding can fit instead of letting cmd.exe reject it', () => {
|
|
let seed = 12345
|
|
const incompressible = Array.from({ length: 60_000 }, () => {
|
|
seed = (seed * 1103515245 + 12345) % 2147483648
|
|
return String.fromCharCode(97 + (seed % 26))
|
|
}).join('')
|
|
expect(() => powerShellCommand(`Write-Output '${incompressible}'`)).toThrow(
|
|
/Orca budgets 8000 for a line sshd hands to cmd\.exe/u
|
|
)
|
|
})
|
|
})
|
|
|
|
/**
|
|
* F11 asked whether a pathological path could reach the budget, and what happens if it does.
|
|
* Measured: the inline encoding crosses 8000 at roughly 2500 high-entropy path characters — an
|
|
* order of magnitude past what Windows itself accepts — and the failure is a throw before any ssh
|
|
* is spawned, never a hang.
|
|
*/
|
|
describe('Windows file command budget headroom', () => {
|
|
it('absorbs a path far longer than Windows will accept', () => {
|
|
const deep = `C:\\Users\\orca\\${'segment\\'.repeat(30)}relay.js`
|
|
|
|
expect(deep.length).toBeGreaterThan(260)
|
|
expect(makeWindowsWriteFileCommand(deep).length).toBeLessThanOrEqual(
|
|
CMD_EXE_COMMAND_LINE_MAX_CHARS
|
|
)
|
|
})
|
|
|
|
it('throws rather than spawning a line cmd.exe would refuse', () => {
|
|
// Random segments so gzip cannot rescue it, which is the only way to reach the ceiling at all.
|
|
const incompressible = Array.from(
|
|
{ length: 400 },
|
|
(_unused, index) => `${index}-${Math.random().toString(36).slice(2)}`
|
|
).join('\\')
|
|
|
|
expect(() => makeWindowsWriteFileCommand(`C:\\${incompressible}\\f.bin`)).toThrow(
|
|
/Orca budgets 8000/
|
|
)
|
|
})
|
|
})
|