mirror of
https://github.com/stablyai/orca.git
synced 2026-10-07 16:02:29 +00:00
* fix(ssh): launch the Windows relay outside sshd's job without WMI Win32-OpenSSH kills a session's job on close but allows breakaway. relay.js gains a one-shot launcher mode that starts the detached relay with CREATE_BREAKAWAY_FROM_JOB through the staged process-tree addon, so a standard user no longer needs a WMI Remote Enable grant. WMI stays as the fallback for a relay without the addon, and a refusal there is named. The Windows SSH-host lanes drop their WMI grant and assert the breakaway route and adoption. * fix(ssh): find runtime holds without WMI on a standard-user Windows host The store GC read held runtimes through Get-CimInstance Win32_Process, which WMI refuses to a standard user's SSH logon, so the pass kept every runtime. On a refusal it now reads this account's own process image paths through Get-Process. * build(relay): ship the Windows relay launcher addon in every desktop package macOS and Linux packages carried Windows relays without windows-process-tree.node, so a legacy-runtime relay they uploaded to a Windows SSH host could not launch outside sshd's job and fell back to WMI, which a standard user is refused. A reusable Windows job now compiles the x64 and arm64 addons once and uploads them; release-cut, release-mac-build, and the hourly/daily/adhoc mac builds download them before build:release and require both arches. Staging now rejects a binary with the wrong PE machine, the ReadProcessMemory import, or no spawnOutsideJob export, so a stale pre-launcher build cannot ship. * ci(ssh): run the Windows SSH-host lanes when the relay process-tree build scripts change The staging and gyp-rebuild scripts decide which windows-process-tree addon the relay ships, so a change to either must re-prove the Windows host cells. * test(ci): find the mac orcad-template download by artifact name The release mac job now also downloads the relay Windows process-tree addons, so the first download-artifact step is no longer the template's. --------- Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
114 lines
5.0 KiB
TypeScript
114 lines
5.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
formatRelayWindowsLaunchReport,
|
|
parseRelayWindowsLaunchReport,
|
|
RELAY_WINDOWS_BREAKAWAY_LAUNCH_FLAG
|
|
} from '../../shared/relay-windows-breakaway-launch'
|
|
import {
|
|
classifyWindowsRelayLaunchError,
|
|
WINDOWS_RELAY_LAUNCH_REFUSED_MARKER,
|
|
windowsRelayLaunchCommand
|
|
} from './ssh-relay-windows-launch-command'
|
|
import { getRemoteHostPlatform } from './ssh-remote-platform'
|
|
import { decodeRemotePowerShellScript } from './ssh-remote-powershell'
|
|
|
|
const host = getRemoteHostPlatform('win32-x64')
|
|
const opts = {
|
|
nodePath: 'C:/Users/me user/.orca-remote/runtimes/node-abc/node.exe',
|
|
remoteDir: 'C:/Users/me user/.orca-remote/relay-1',
|
|
sockPath: '\\\\.\\pipe\\orca-relay-1',
|
|
endpointDir: 'C:/Users/me user/.orca-remote/relay-1/agent-hooks/orca-relay-1',
|
|
graceTime: 300,
|
|
logFile: 'C:/Users/me user/.orca-remote/relay-1/relay.log',
|
|
errFile: 'C:/Users/me user/.orca-remote/relay-1/relay.err.log',
|
|
credentialFile: 'C:/Users/me user/.orca-remote/relay-1/orca-relay-1.credential'
|
|
}
|
|
|
|
function launchScript(): string {
|
|
return decodeRemotePowerShellScript(windowsRelayLaunchCommand(host, opts))
|
|
}
|
|
|
|
describe('windowsRelayLaunchCommand', () => {
|
|
it('starts the relay through the breakaway launcher on the same node.exe', () => {
|
|
const script = launchScript()
|
|
const launcher = `& '${opts.nodePath}' relay.js '${RELAY_WINDOWS_BREAKAWAY_LAUNCH_FLAG}' '--stdout-file' '${opts.logFile}' '--stderr-file' '${opts.errFile}' '--relay-args' '--detached' '--grace-time' '300' '--sock-path' '${opts.sockPath}'`
|
|
expect(script).toContain(launcher)
|
|
expect(script.indexOf(launcher)).toBeLessThan(script.indexOf('Invoke-CimMethod'))
|
|
})
|
|
|
|
it('reaches WMI only when the launcher reports itself unavailable', () => {
|
|
const script = launchScript()
|
|
const wmiBranch = script.slice(script.indexOf('if ($orcaLaunchCode -eq 3)'))
|
|
expect(wmiBranch.indexOf('Invoke-CimMethod')).toBeGreaterThan(0)
|
|
expect(wmiBranch.indexOf('Invoke-CimMethod')).toBeLessThan(wmiBranch.indexOf('elseif'))
|
|
expect(script).toContain(
|
|
`"C:/Users/me user/.orca-remote/relay-1/relay.js" --detached --grace-time 300`
|
|
)
|
|
expect(script).toContain(`1>"${opts.logFile}" 2>"${opts.errFile}"`)
|
|
})
|
|
|
|
it('names a WMI refusal so a standard-user host fails with a reason', () => {
|
|
const script = launchScript()
|
|
expect(script).toContain(`throw "${WINDOWS_RELAY_LAUNCH_REFUSED_MARKER}:`)
|
|
expect(script).toContain('Invoke-CimMethod -ErrorAction Stop')
|
|
})
|
|
|
|
it('emits nothing an EDR scores as a launch technique', () => {
|
|
const script = launchScript()
|
|
expect(script).not.toMatch(/Add-Type|ExecutionPolicy|Register-ScheduledTask|schtasks/iu)
|
|
})
|
|
|
|
it('passes the uploaded ripgrep to both routes', () => {
|
|
const script = decodeRemotePowerShellScript(
|
|
windowsRelayLaunchCommand(host, { ...opts, ripgrepPath: 'C:/rg/rg.exe' })
|
|
)
|
|
expect(script).toContain(`'--ripgrep-path' 'C:/rg/rg.exe'`)
|
|
expect(script).toContain('--ripgrep-path "C:/rg/rg.exe"')
|
|
})
|
|
})
|
|
|
|
describe('classifyWindowsRelayLaunchError', () => {
|
|
it('turns a refusal into a named error', () => {
|
|
const exec = new Error(
|
|
`Command "powershell.exe -EncodedCommand AAAA" failed (exit 1): ${WINDOWS_RELAY_LAUNCH_REFUSED_MARKER}: this account cannot start a process that outlives the SSH session: breakaway launcher unavailable (ORCA_RELAY_LAUNCH {"method":"unavailable","reason":"addon-missing"}) and WMI Win32_Process.Create denied (Access denied)\nAt line:1 char:1`
|
|
)
|
|
const classified = classifyWindowsRelayLaunchError(exec)
|
|
if (!(classified instanceof Error)) {
|
|
throw new Error('expected an Error')
|
|
}
|
|
expect(classified.message).toMatch(
|
|
/^The Windows host refused to start Orca's relay outside the SSH session\. ORCA_RELAY_LAUNCH_REFUSED: .*addon-missing.*Access denied\)$/u
|
|
)
|
|
expect(classified.message).not.toContain('EncodedCommand')
|
|
})
|
|
|
|
it('leaves every other launch failure as it was', () => {
|
|
const exec = new Error('Command "x" failed (exit 1): Relay launcher exited 1')
|
|
expect(classifyWindowsRelayLaunchError(exec)).toBe(exec)
|
|
})
|
|
})
|
|
|
|
describe('parseRelayWindowsLaunchReport', () => {
|
|
it('reads the launcher and WMI reports', () => {
|
|
const breakaway = formatRelayWindowsLaunchReport({ method: 'breakaway', pid: 7, inJob: false })
|
|
expect(parseRelayWindowsLaunchReport(`noise\r\n${breakaway}\r\n`)).toEqual({
|
|
method: 'breakaway',
|
|
pid: 7,
|
|
inJob: false
|
|
})
|
|
const unavailable = formatRelayWindowsLaunchReport({
|
|
method: 'unavailable',
|
|
reason: 'addon-missing'
|
|
})
|
|
expect(
|
|
parseRelayWindowsLaunchReport(`${unavailable}\nORCA_RELAY_LAUNCH {"method":"wmi"}`)
|
|
).toEqual({ method: 'wmi' })
|
|
})
|
|
|
|
it('reads nothing from output without a report', () => {
|
|
expect(parseRelayWindowsLaunchReport('')).toBeNull()
|
|
expect(parseRelayWindowsLaunchReport('ORCA_RELAY_LAUNCH not-json')).toBeNull()
|
|
expect(parseRelayWindowsLaunchReport('ORCA_RELAY_LAUNCH {"method":"breakaway"}')).toBeNull()
|
|
})
|
|
})
|