mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 16:03:53 +00:00
fix(ssh): detach Windows no-input probe
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
@@ -1697,9 +1697,40 @@ jobs:
|
||||
if (Test-Path -LiteralPath $log) { Get-Content -LiteralPath $log -Tail 200 }
|
||||
try {
|
||||
if ($ownsAccount -and $userSid) {
|
||||
function Dismount-FixtureRegistryHive([string]$hiveName) {
|
||||
$registryPath = "Registry::HKEY_USERS\$hiveName"
|
||||
if (-not (Test-Path -LiteralPath $registryPath)) { return }
|
||||
$regPath = Join-Path $env:SystemRoot 'System32/reg.exe'
|
||||
$process = Start-Process -FilePath $regPath `
|
||||
-ArgumentList @('unload', "HKU\$hiveName") -PassThru -WindowStyle Hidden
|
||||
try {
|
||||
if (-not $process.WaitForExit(2000)) {
|
||||
$process.Kill()
|
||||
if (-not $process.WaitForExit(2000)) {
|
||||
throw "Fixture registry hive unload did not stop: $hiveName"
|
||||
}
|
||||
throw "Fixture registry hive unload timed out: $hiveName"
|
||||
}
|
||||
if ($process.ExitCode -ne 0) {
|
||||
throw "Fixture registry hive unload failed ($($process.ExitCode)): $hiveName"
|
||||
}
|
||||
} finally {
|
||||
$process.Dispose()
|
||||
}
|
||||
}
|
||||
$escapedSid = $userSid.Replace("'", "''")
|
||||
$profileFailure = $null
|
||||
for ($attempt = 1; $attempt -le 20; $attempt += 1) {
|
||||
try {
|
||||
# Why: the fixture's UsrClass hive can outlive sshd; unload only
|
||||
# this owned SID before asking Windows to delete its profile.
|
||||
Dismount-FixtureRegistryHive "${userSid}_Classes"
|
||||
Dismount-FixtureRegistryHive $userSid
|
||||
} catch {
|
||||
$profileFailure = $_.Exception.Message
|
||||
Start-Sleep -Milliseconds 500
|
||||
continue
|
||||
}
|
||||
$profile = Get-CimInstance Win32_UserProfile -Filter "SID='$escapedSid'" `
|
||||
-ErrorAction SilentlyContinue
|
||||
if (-not $profile) { break }
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
export function assertWindowsOpenSshWorkflow(workflow, expect) {
|
||||
const steps = workflow.jobs['build-windows-runtime'].steps
|
||||
const start = steps.find(
|
||||
(step) => step.name === 'Start loopback Windows OpenSSH system-SSH fixture'
|
||||
)
|
||||
const measure = steps.find(
|
||||
(step) => step.name === 'Measure exact full-size runtime over Windows system SSH'
|
||||
)
|
||||
const stop = steps.find(
|
||||
(step) => step.name === 'Stop loopback Windows OpenSSH system-SSH fixture'
|
||||
)
|
||||
|
||||
expect(start).toBeDefined()
|
||||
expect(measure).toBeDefined()
|
||||
expect(stop).toBeDefined()
|
||||
expect(start.shell).toBe('pwsh')
|
||||
expect(start.run).toContain('10.0.0.0p2-Preview')
|
||||
expect(start.run).toContain('23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5')
|
||||
expect(start.run).toContain('698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42')
|
||||
expect(start.run).toContain('4652e861c0335ee80a51306ceab75aa35c8865b235f97ce7dd5a0fd9dab44b5d')
|
||||
expect(start.run).toContain('7ad2b7721893c54ad6e4fec1a3477701fb48975323c2c4ac6cd0b8c972ab242a')
|
||||
expect(start.run).toContain('--connect-timeout 20 --max-time 300 --retry 2')
|
||||
expect(start.run).toContain('Get-AuthenticodeSignature')
|
||||
expect(start.run).toContain('$actualNativeNames -join "`n"')
|
||||
expect(start.run).toContain('$expectedNativeNames -join "`n"')
|
||||
expect(start.run).toContain("'ssh-sk-helper.exe'")
|
||||
expect(start.run).toContain("'sshd-session.exe'")
|
||||
expect(start.run).toContain('$libcryptoFiles.Count -ne 1')
|
||||
expect(start.run).toContain("$libcryptoSignature.Status -ne 'Valid'")
|
||||
expect(start.run).toContain('587116075365AA15BCD8E4FA9CB31BE372B5DE51')
|
||||
expect(start.run).toContain('CN=Microsoft 3rd Party Application Component')
|
||||
expect(start.run).toContain('F5877012FBD62FABCBDC8D8CEE9C9585BA30DF79')
|
||||
expect(start.run).toContain('3F56A45111684D454E231CFDC4DA5C8D370F9816')
|
||||
expect(start.run).toContain('$expectedExecutableSignerThumbprints -notcontains $thumbprint')
|
||||
expect(start.run).not.toContain("'NotSigned'")
|
||||
expect(start.run).toContain("if ($nativeFile.Name -eq 'libcrypto.dll') { continue }")
|
||||
expect(start.run).toContain("$signature.Status -ne 'Valid'")
|
||||
expect(start.run).toContain('CN=Microsoft Corporation')
|
||||
expect(start.run).toContain('[Security.AccessControl.DirectorySecurity]::new()')
|
||||
expect(start.run).toContain('[Security.AccessControl.FileSecurity]::new()')
|
||||
expect(start.run).toContain('$security.SetAccessRuleProtection($true, $false)')
|
||||
expect(start.run).toContain('[void]$security.AddAccessRule($accessRule)')
|
||||
expect(start.run).toContain('$acl.AreAccessRulesProtected')
|
||||
expect(start.run).toContain('Fixture ACL trustee closure changed')
|
||||
expect(start.run).toContain('@($systemSid, $administratorsSid)')
|
||||
expect(start.run).not.toContain('Add-WindowsCapability')
|
||||
expect(start.run).toContain('New-LocalUser')
|
||||
expect(start.run.indexOf("Join-Path $fixture 'account-owned'")).toBeLessThan(
|
||||
start.run.indexOf('New-LocalUser')
|
||||
)
|
||||
expect(start.run).toContain('ListenAddress 127.0.0.1')
|
||||
expect(start.run).toContain('PasswordAuthentication no')
|
||||
expect(start.run).toContain('StrictModes yes')
|
||||
expect(start.run).toContain('StrictHostKeyChecking=yes')
|
||||
expect(start.run).toContain('powershell.exe')
|
||||
expect(start.run).toContain('([Version]$remotePowerShellVersion).Major -ne 5')
|
||||
expect(start.run).toContain('/setowner')
|
||||
expect(start.run).toContain("Set-FixtureOwner $hostKey 'S-1-5-18'")
|
||||
expect(start.run).toContain('Set-FixtureOwner $authorizedKeys $userSid')
|
||||
expect(start.run).not.toContain('administrators_authorized_keys')
|
||||
expect(start.run).toContain('New-Service')
|
||||
expect(start.run.indexOf("Join-Path $fixture 'service-owned'")).toBeLessThan(
|
||||
start.run.indexOf('New-Service')
|
||||
)
|
||||
expect(measure.shell).toBe('pwsh')
|
||||
expect(measure.run).toContain("$env:ORCA_SSH_FORCE_SYSTEM_TRANSPORT = '1'")
|
||||
expect(measure.run).toContain('ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts')
|
||||
expect(measure.run).toContain("Join-Path $firstOutput 'runtime'")
|
||||
expect(stop.if).toBe("always() && runner.os == 'Windows'")
|
||||
expect(stop.shell).toBe('pwsh')
|
||||
expect(stop.run).toContain('fixture-owned')
|
||||
expect(stop.run).toContain('service-owned')
|
||||
expect(stop.run).toContain('account-owned')
|
||||
expect(stop.run).not.toContain('service-backup.json')
|
||||
expect(stop.run).not.toContain('sc.exe config')
|
||||
expect(stop.run).toContain('sc.exe delete')
|
||||
expect(stop.run).toContain('Dismount-FixtureRegistryHive "${userSid}_Classes"')
|
||||
expect(stop.run).toContain('Dismount-FixtureRegistryHive $userSid')
|
||||
expect(stop.run).toContain("Join-Path $env:SystemRoot 'System32/reg.exe'")
|
||||
expect(stop.run).toContain('@(\'unload\', "HKU\\$hiveName")')
|
||||
expect(stop.run).toContain('$process.WaitForExit(2000)')
|
||||
expect(stop.run).toContain('$process.Kill()')
|
||||
expect(stop.run.indexOf('Dismount-FixtureRegistryHive "${userSid}_Classes"')).toBeLessThan(
|
||||
stop.run.indexOf('Dismount-FixtureRegistryHive $userSid')
|
||||
)
|
||||
expect(stop.run.indexOf('Dismount-FixtureRegistryHive $userSid')).toBeLessThan(
|
||||
stop.run.indexOf('Remove-CimInstance -InputObject $profile')
|
||||
)
|
||||
expect(stop.run).toContain('Remove-CimInstance -InputObject $profile')
|
||||
expect(stop.run).toContain('Fixture profile did not settle within 10 seconds')
|
||||
expect(stop.run).toContain('fixture profile directory remains')
|
||||
expect(stop.run).toContain('Remove-LocalUser')
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { readFile } from 'node:fs/promises'
|
||||
|
||||
import { parse } from 'yaml'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { assertWindowsOpenSshWorkflow } from './ssh-relay-runtime-windows-openssh-workflow-contract.mjs'
|
||||
|
||||
const workflowUrl = new URL(
|
||||
'../../.github/workflows/ssh-relay-runtime-artifacts.yml',
|
||||
@@ -142,85 +143,7 @@ describe('SSH relay runtime artifact workflow', () => {
|
||||
|
||||
it('runs full-size Windows system SSH against loopback native OpenSSH', async () => {
|
||||
const workflow = parse(await readFile(workflowUrl, 'utf8'))
|
||||
const steps = workflow.jobs['build-windows-runtime'].steps
|
||||
const start = steps.find(
|
||||
(step) => step.name === 'Start loopback Windows OpenSSH system-SSH fixture'
|
||||
)
|
||||
const measure = steps.find(
|
||||
(step) => step.name === 'Measure exact full-size runtime over Windows system SSH'
|
||||
)
|
||||
const stop = steps.find(
|
||||
(step) => step.name === 'Stop loopback Windows OpenSSH system-SSH fixture'
|
||||
)
|
||||
|
||||
expect(start).toBeDefined()
|
||||
expect(measure).toBeDefined()
|
||||
expect(stop).toBeDefined()
|
||||
expect(start.shell).toBe('pwsh')
|
||||
expect(start.run).toContain('10.0.0.0p2-Preview')
|
||||
expect(start.run).toContain('23f50f3458c4c5d0b12217c6a5ddfde0137210a30fa870e98b29827f7b43aba5')
|
||||
expect(start.run).toContain('698c6aec31c1dd0fb996206e8741f4531a97355686b5431ef347d531b07fcd42')
|
||||
expect(start.run).toContain('4652e861c0335ee80a51306ceab75aa35c8865b235f97ce7dd5a0fd9dab44b5d')
|
||||
expect(start.run).toContain('7ad2b7721893c54ad6e4fec1a3477701fb48975323c2c4ac6cd0b8c972ab242a')
|
||||
expect(start.run).toContain('--connect-timeout 20 --max-time 300 --retry 2')
|
||||
expect(start.run).toContain('Get-AuthenticodeSignature')
|
||||
expect(start.run).toContain('$actualNativeNames -join "`n"')
|
||||
expect(start.run).toContain('$expectedNativeNames -join "`n"')
|
||||
expect(start.run).toContain("'ssh-sk-helper.exe'")
|
||||
expect(start.run).toContain("'sshd-session.exe'")
|
||||
expect(start.run).toContain('$libcryptoFiles.Count -ne 1')
|
||||
expect(start.run).toContain("$libcryptoSignature.Status -ne 'Valid'")
|
||||
expect(start.run).toContain('587116075365AA15BCD8E4FA9CB31BE372B5DE51')
|
||||
expect(start.run).toContain('CN=Microsoft 3rd Party Application Component')
|
||||
expect(start.run).toContain('F5877012FBD62FABCBDC8D8CEE9C9585BA30DF79')
|
||||
expect(start.run).toContain('3F56A45111684D454E231CFDC4DA5C8D370F9816')
|
||||
expect(start.run).toContain('$expectedExecutableSignerThumbprints -notcontains $thumbprint')
|
||||
expect(start.run).not.toContain("'NotSigned'")
|
||||
expect(start.run).toContain("if ($nativeFile.Name -eq 'libcrypto.dll') { continue }")
|
||||
expect(start.run).toContain("$signature.Status -ne 'Valid'")
|
||||
expect(start.run).toContain('CN=Microsoft Corporation')
|
||||
expect(start.run).toContain('[Security.AccessControl.DirectorySecurity]::new()')
|
||||
expect(start.run).toContain('[Security.AccessControl.FileSecurity]::new()')
|
||||
expect(start.run).toContain('$security.SetAccessRuleProtection($true, $false)')
|
||||
expect(start.run).toContain('[void]$security.AddAccessRule($accessRule)')
|
||||
expect(start.run).toContain('$acl.AreAccessRulesProtected')
|
||||
expect(start.run).toContain('Fixture ACL trustee closure changed')
|
||||
expect(start.run).toContain('@($systemSid, $administratorsSid)')
|
||||
expect(start.run).not.toContain('Add-WindowsCapability')
|
||||
expect(start.run).toContain('New-LocalUser')
|
||||
expect(start.run.indexOf("Join-Path $fixture 'account-owned'")).toBeLessThan(
|
||||
start.run.indexOf('New-LocalUser')
|
||||
)
|
||||
expect(start.run).toContain('ListenAddress 127.0.0.1')
|
||||
expect(start.run).toContain('PasswordAuthentication no')
|
||||
expect(start.run).toContain('StrictModes yes')
|
||||
expect(start.run).toContain('StrictHostKeyChecking=yes')
|
||||
expect(start.run).toContain('powershell.exe')
|
||||
expect(start.run).toContain('([Version]$remotePowerShellVersion).Major -ne 5')
|
||||
expect(start.run).toContain('/setowner')
|
||||
expect(start.run).toContain("Set-FixtureOwner $hostKey 'S-1-5-18'")
|
||||
expect(start.run).toContain('Set-FixtureOwner $authorizedKeys $userSid')
|
||||
expect(start.run).not.toContain('administrators_authorized_keys')
|
||||
expect(start.run).toContain('New-Service')
|
||||
expect(start.run.indexOf("Join-Path $fixture 'service-owned'")).toBeLessThan(
|
||||
start.run.indexOf('New-Service')
|
||||
)
|
||||
expect(measure.shell).toBe('pwsh')
|
||||
expect(measure.run).toContain("$env:ORCA_SSH_FORCE_SYSTEM_TRANSPORT = '1'")
|
||||
expect(measure.run).toContain('ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts')
|
||||
expect(measure.run).toContain("Join-Path $firstOutput 'runtime'")
|
||||
expect(stop.if).toBe("always() && runner.os == 'Windows'")
|
||||
expect(stop.shell).toBe('pwsh')
|
||||
expect(stop.run).toContain('fixture-owned')
|
||||
expect(stop.run).toContain('service-owned')
|
||||
expect(stop.run).toContain('account-owned')
|
||||
expect(stop.run).not.toContain('service-backup.json')
|
||||
expect(stop.run).not.toContain('sc.exe config')
|
||||
expect(stop.run).toContain('sc.exe delete')
|
||||
expect(stop.run).toContain('Remove-CimInstance -InputObject $profile')
|
||||
expect(stop.run).toContain('Fixture profile did not settle within 10 seconds')
|
||||
expect(stop.run).toContain('fixture profile directory remains')
|
||||
expect(stop.run).toContain('Remove-LocalUser')
|
||||
assertWindowsOpenSshWorkflow(workflow, expect)
|
||||
})
|
||||
|
||||
it('uploads only the first output after two clean builds verify and compare', async () => {
|
||||
|
||||
+22
-2
@@ -66,8 +66,28 @@ are atomically replaced with exact protected allow-only rules independent of dri
|
||||
owned `Win32_UserProfile` is deleted through a bounded native lifecycle before account/filesystem
|
||||
teardown. Focused tests pass 71 cases with one declared live skip, all three PowerShell blocks parse,
|
||||
all 283 artifact contracts pass, typecheck and full lint/reliability/max-lines pass, formatting/diff
|
||||
are clean, and the protected resolver files remain unchanged. Commit/push this isolated package and
|
||||
require fresh x64/arm64 live proof before advancing.
|
||||
are clean, and the protected resolver files remain unchanged. Exact head
|
||||
`b652c1ebcce3803443d84c89811146366cdbdd5b` is pushed. Artifact run `29542292712` preserves all
|
||||
non-Windows gates and independently reaches the Windows fixture on x64, but job `87766845800` is
|
||||
RED: exact ACL replacement, official OpenSSH setup, authentication, and PowerShell 5.1 pass before
|
||||
the production probe times out after 32.064 seconds. Closing the Node stdin pipe is insufficient;
|
||||
the no-input probe needs native OpenSSH `-n` without disabling connection reuse. Teardown also
|
||||
finds the owned fixture SID's `UsrClass.dat` hive still loaded after the bounded profile-removal
|
||||
loop. Add probe-only `-n`, boundedly unload only that SID's `_Classes` and primary hives before
|
||||
retrying `Win32_UserProfile` removal, and require fresh x64/arm64 live proof before advancing.
|
||||
ARM64 job `87766845741` independently reaches the same boundary: official OpenSSH setup passes,
|
||||
the production probe times out after 34.01 seconds, and teardown catches the same owned
|
||||
`UsrClass.dat` lock after the bounded profile loop. This is a shared native OpenSSH/profile
|
||||
lifecycle correction, not an x64-only exception.
|
||||
`E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-NOINPUT-PROFILE-CORRECTION-LOCAL-001` records the narrow
|
||||
correction locally green: only the no-input connection probe emits OpenSSH `-n` before `--`, reuse
|
||||
flags remain enabled, ordinary commands exclude `-n`, and teardown boundedly unloads only the
|
||||
fixture SID's `_Classes` and primary hives before retrying native profile deletion. The Windows
|
||||
workflow oracle is split by concrete domain responsibility without a max-lines bypass. Focused
|
||||
tests pass 115 cases with one declared native skip, all three PowerShell blocks parse, 283/283
|
||||
artifact contracts pass, typecheck and full lint/reliability/max-lines pass, formatting/diff are
|
||||
clean, and the protected resolver files remain unchanged. Commit/push this isolated package and
|
||||
require fresh native x64/arm64 proof before advancing.
|
||||
`E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-AUDIT-001` fixes the loopback-only official Microsoft
|
||||
server, fixture-owned non-admin account/ACL, exact host-key trust, Windows PowerShell 5.1,
|
||||
serial/default and four-channel metrics, cancellation/collision/cleanup, and deterministic teardown
|
||||
|
||||
+80
-1
@@ -9,7 +9,7 @@ work; keep exact commands, runner identities, hashes, metrics, and residual gaps
|
||||
Date created: 2026-07-14<br>
|
||||
Last updated: 2026-07-15<br>
|
||||
Current phase: Milestone 6 / Work Package 5 bounded runtime transfer — **In progress — 2026-07-15, Codex implementation owner: exact-head Windows OpenSSH live-proof diagnosis and fixture correction**. Keep every capability disconnected from every production/default SSH path. Real Apple/SignPath rehearsals remain an explicit late gate and are not on this package's critical path.<br>
|
||||
Session checkpoint: **WINDOWS ACL/PROBE/PROFILE CORRECTION LOCALLY GREEN / EXACT-HEAD PROOF NEXT — 2026-07-15, Codex implementation owner** — exact pushed head `d28552f0d953e40d96a4c24cdcdb5ba7d85b7c9f`, artifact run `29541119819`, and x64/arm64 jobs `87763404467`/`87763404492` independently pass checkout/toolchain, 283 contracts, clean-build equality/smoke, full-size cache, both pinned archive/library hashes, exact 15-file closure, and every audited signer check before exposing three narrower gaps. ARM64's C: fixture root retains explicit Authenticated Users and Users ACEs; x64's no-input production `ssh.exe` probe leaves piped stdin open and waits 30 seconds after the remote command closes; x64 teardown finds the owned profile's loaded `UsrClass.dat`. `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-EXACT-HEAD-CORRECTION-LOCAL-001` atomically replaces each fixture DACL with exact protected allow-only rules, closes stdin for the no-input production probe without disabling connection reuse, and deletes the owned `Win32_UserProfile` through a bounded native lifecycle before account/filesystem cleanup. Focused, PowerShell syntax, 283-contract, typecheck, full lint/reliability/max-lines, formatting, diff, and protected-resolver isolation gates pass locally. Commit/push and require fresh x64/arm64 live proof. Production runtime bytes still cross authenticated SSH and no product remote downloads anything. Do not connect a product caller, change legacy upload/fallback/default behavior, enable a tuple, publish an artifact, or perform SignPath work. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Session checkpoint: **WINDOWS NATIVE NO-INPUT/PROFILE-HIVE CORRECTION LOCALLY GREEN / EXACT-HEAD PROOF NEXT — 2026-07-15, Codex implementation owner** — exact head `b652c1ebcce3803443d84c89811146366cdbdd5b`, artifact run `29542292712`, x64 job `87766845800`, and ARM64 job `87766845741` preserve all prior artifact/build/cache/hash/closure/signer gates and independently expose the same native probe/profile gaps. `E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-NOINPUT-PROFILE-CORRECTION-LOCAL-001` adds probe-only OpenSSH `-n` before `--` while retaining connection reuse and excluding `-n` from ordinary commands; boundedly unloads only the fixture-owned SID's `_Classes` and primary hives before retrying native profile deletion; retains exact teardown assertions; and splits the Windows workflow oracle by concrete domain responsibility without a max-lines bypass. Focused tests, all three PowerShell parses, 283 artifact contracts, typecheck, full lint/reliability/max-lines, formatting, diff, and protected-resolver isolation pass locally. Commit/push this isolated package and require fresh x64/arm64 proof. Production runtime bytes still cross authenticated SSH and no product remote downloads anything. Do not connect a product caller, change legacy upload/fallback/default behavior, enable a tuple, publish an artifact, or perform SignPath work. Merging to `main` remains prohibited, SignPath is deferred, and legacy remains the production default.<br>
|
||||
Primary design: [SSH relay GitHub Release plan](./2026-07-14-ssh-relay-github-release-plan.html)<br>
|
||||
Motivating issues: [#8450](https://github.com/stablyai/orca/issues/8450), [#1693](https://github.com/stablyai/orca/issues/1693)
|
||||
|
||||
@@ -19092,6 +19092,85 @@ config/scripts/ssh-relay-runtime-*.test.mjs` passes 50 files and 283/283 cases i
|
||||
exact-head x64/arm64 proof before closing the live Windows gate. Legacy remains the sole
|
||||
production/default path; product/Beta/settings/fallback/tuple/publication/signing remain absent.
|
||||
|
||||
### E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-NOINPUT-PROFILE-RED-001 — exact head proves ACL and exposes native no-input/profile-hive gaps
|
||||
|
||||
- Date/owner/head/run: 2026-07-15, Codex implementation owner; exact pushed head
|
||||
`b652c1ebcce3803443d84c89811146366cdbdd5b`, artifact run `29542292712`, native x64 job
|
||||
`87766845800` (`windows-2022`, Windows Server 2022) and native ARM64 job `87766845741`
|
||||
(`windows-11-arm`).
|
||||
- Preserved evidence: all four non-Windows primary jobs and both Linux supplement jobs pass. The
|
||||
x64 job passes exact checkout/toolchain, 283 runtime artifact contracts, clean-build identity and
|
||||
smoke, full-size cache, both pinned archive/library hashes, exact 15-file closure, all audited
|
||||
signer checks, exact fixture DACL replacement, official OpenSSH service/authentication, and
|
||||
PowerShell 5.1 before the live transfer suite.
|
||||
- X64 RED: the production no-input connection probe times out after 32.064 seconds with
|
||||
`System SSH connection timed out`. Closing the spawned process's Node stdin pipe does not make
|
||||
native Windows OpenSSH detach its stdin worker. Preserve `systemSshConnectionReuse`; add
|
||||
OpenSSH `-n` only to the no-input probe argv before `--`, and prove argv order plus connection
|
||||
behavior.
|
||||
- Teardown RED: the bounded ten-second `Remove-CimInstance Win32_UserProfile` loop cannot settle
|
||||
while the owned fixture profile's `UsrClass.dat` remains loaded; profile and fixture directory
|
||||
deletion then fail and exact absence assertions catch both. For the fixture-owned SID only,
|
||||
boundedly unload `HKU\\<SID>_Classes` and then `HKU\\<SID>` with `reg.exe unload`, retry
|
||||
native profile deletion, and retain exact account/profile/directory absence assertions.
|
||||
- ARM64 independent RED: official OpenSSH fixture startup passes, then the same production probe
|
||||
fails with `System SSH connection timed out` after 34.01 seconds. The teardown reports
|
||||
`Fixture profile did not settle within 10 seconds`, then exact filesystem deletion identifies
|
||||
`C:\Users\orca_ssh_fixture\AppData\Local\Microsoft\Windows\UsrClass.dat` as used by another
|
||||
process and reports that the fixture profile directory remains. The same correction is therefore
|
||||
required on both native Windows architectures.
|
||||
- Promotion condition: update the workflow oracle, parse every PowerShell block, pass the focused,
|
||||
283-contract, typecheck, full-lint/reliability/max-lines, formatting, diff, and protected-resolver
|
||||
isolation gates, then require fresh exact-head x64/arm64 live proof. Legacy remains the sole
|
||||
production/default path and SignPath remains deferred.
|
||||
|
||||
### E-M6-WINDOWS-SYSTEM-SSH-TREE-LIVE-NOINPUT-PROFILE-CORRECTION-LOCAL-001 — probe-only native no-input and owned-hive teardown are locally green
|
||||
|
||||
- Date/owner/base/runner: 2026-07-15, Codex implementation owner; uncommitted correction atop exact
|
||||
RED head `b652c1ebcce3803443d84c89811146366cdbdd5b` on Apple arm64, macOS 26.2 build 25C56,
|
||||
Node 26.5.0, pnpm 10.24.0. Native Windows execution remains required.
|
||||
- Probe correction: `SystemSshBuildArgsOptions.noInput` emits OpenSSH `-n` before the `--`
|
||||
destination terminator. Only the raw cross-shell connection probe requests it; ordinary
|
||||
system-SSH commands are asserted to omit it. The probe still requests Orca ControlMaster,
|
||||
`ControlPersist=300`, and keepalives, so this does not evade the required connection-reuse path.
|
||||
The existing explicit stdin close remains as deterministic local pipe ownership.
|
||||
- Teardown correction: for the fixture-created account SID only, the workflow checks and unloads
|
||||
`HKU\\<SID>_Classes` before `HKU\\<SID>` through `%SystemRoot%\\System32\\reg.exe`.
|
||||
Every process wait is bounded to two seconds, a stuck process is killed and boundedly awaited,
|
||||
and failure is retried inside the existing ten-second profile lifecycle. Native
|
||||
`Remove-CimInstance Win32_UserProfile`, account deletion, directory deletion, and all exact
|
||||
absence assertions remain mandatory.
|
||||
- Structure/oracle correction: Windows OpenSSH workflow assertions moved into the concrete
|
||||
`config/scripts/ssh-relay-runtime-windows-openssh-workflow-contract.mjs` responsibility while
|
||||
`ssh-relay-runtime-workflow.test.mjs` remains the caller used by every historical command. The
|
||||
files are 93 and 551 lines respectively; no max-lines disable or budget bump was added.
|
||||
- Focused command: `/usr/bin/time -l pnpm exec vitest run --config config/vitest.config.ts
|
||||
--maxWorkers=1 src/main/ssh/ssh-connection.test.ts src/main/ssh/ssh-system-fallback.test.ts
|
||||
src/main/ssh/ssh-relay-runtime-windows-system-ssh-openssh-full-size.test.ts
|
||||
config/scripts/ssh-relay-runtime-workflow.test.mjs` passes 3 files plus one declared native live
|
||||
skip, 115 passed tests plus one skip, in 4.24 seconds real; maximum RSS is 158,269,440 bytes and
|
||||
peak footprint is 97,392,376 bytes. Independent YAML extraction plus PowerShell
|
||||
`[scriptblock]::Create()` parses all three start/measure/stop blocks.
|
||||
- Release-contract command: `/usr/bin/time -l pnpm exec vitest run --config
|
||||
config/vitest.config.ts --maxWorkers=1 --reporter=dot
|
||||
config/scripts/ssh-relay-runtime-*.test.mjs` passes 50 files and 283/283 cases in 21.65 seconds
|
||||
real; maximum RSS is 195,526,656 bytes and peak footprint is 98,375,440 bytes.
|
||||
- Static commands: `/usr/bin/time -l pnpm typecheck` passes in 6.75 seconds real with maximum RSS
|
||||
1,826,357,248 bytes and peak footprint 98,015,040 bytes. `/usr/bin/time -l pnpm lint` passes in
|
||||
22.61 seconds real with only the 26 pre-existing warnings, all 41 reliability gates, the
|
||||
355-entry max-lines ratchet, bundled guides, and localization checks; maximum RSS is
|
||||
2,036,318,208 bytes and peak footprint is 97,425,240 bytes.
|
||||
- Isolation evidence: targeted `oxfmt --check` passes all ten changed code/workflow/plan files;
|
||||
`git diff --check`, an independent no-index whitespace check for the new contract module, and
|
||||
exact working-tree equality to `HEAD` for `ssh-remote-node-resolution.ts` and its test pass. Only
|
||||
the artifact workflow, concrete workflow oracle split, no-input argv/probe implementation and
|
||||
tests, and three planning artifacts are touched.
|
||||
- Residual boundary: local macOS proof cannot execute native Windows `ssh.exe -n`, registry hive
|
||||
unload, profile removal, service/auth, full-size transfer, cancellation, collision, or teardown.
|
||||
Commit/push this isolated correction and require fresh exact-head x64/arm64 proof before closing
|
||||
the live Windows gate. Legacy remains the sole production/default path; SignPath and product
|
||||
caller/Beta/settings/fallback/tuple/publication work remain deferred.
|
||||
|
||||
## Accepted Gaps
|
||||
|
||||
No product gap is accepted merely because it appears in this list. Each entry requires explicit
|
||||
|
||||
@@ -1642,7 +1642,10 @@
|
||||
<td>
|
||||
Full-size runtime over SFTP and POSIX/Windows system SSH; tar/no-tar; no remote
|
||||
hash tools; corrupted upload; bundled-Node corruption/failure; bounded memory
|
||||
and concurrency; no-input system-SSH probe stdin/child settlement;
|
||||
and concurrency; no-input system-SSH probe uses native OpenSSH <code>-n</code>
|
||||
before the destination terminator and proves child settlement without disabling
|
||||
connection reuse; fixture-owned Windows profile hives are boundedly unloaded
|
||||
before native profile deletion;
|
||||
<code>MaxSessions=1</code>; interrupted upload; stale lock
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@@ -949,6 +949,7 @@ describe('SshConnection', () => {
|
||||
'echo ORCA-SYSTEM-SSH-OK',
|
||||
{
|
||||
wrapCommand: false,
|
||||
noInput: true,
|
||||
resolvedConfig: expect.objectContaining({ proxyUseFdpass: true })
|
||||
}
|
||||
)
|
||||
@@ -1002,6 +1003,7 @@ describe('SshConnection', () => {
|
||||
'echo ORCA-SYSTEM-SSH-OK',
|
||||
expect.objectContaining({
|
||||
wrapCommand: false,
|
||||
noInput: true,
|
||||
resolvedConfig: expect.objectContaining({ proxyUseFdpass: true })
|
||||
})
|
||||
)
|
||||
@@ -1012,6 +1014,7 @@ describe('SshConnection', () => {
|
||||
expect.objectContaining({
|
||||
disableControlMaster: true,
|
||||
wrapCommand: false,
|
||||
noInput: true,
|
||||
resolvedConfig: expect.objectContaining({ proxyUseFdpass: true })
|
||||
})
|
||||
)
|
||||
@@ -1050,7 +1053,7 @@ describe('SshConnection', () => {
|
||||
expect(spawnSystemSshCommandMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ proxyCommand: 'ssh -W %h:%p bastion.example.com' }),
|
||||
'echo ORCA-SYSTEM-SSH-OK',
|
||||
{ wrapCommand: false }
|
||||
{ wrapCommand: false, noInput: true }
|
||||
)
|
||||
})
|
||||
|
||||
@@ -1071,7 +1074,7 @@ describe('SshConnection', () => {
|
||||
expect(spawnSystemSshCommandMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ host: '192.168.0.210' }),
|
||||
'echo ORCA-SYSTEM-SSH-OK',
|
||||
{ wrapCommand: false }
|
||||
{ wrapCommand: false, noInput: true }
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -694,7 +694,8 @@ export class SshConnection {
|
||||
// Why: this probe runs before remote platform detection. A raw echo works
|
||||
// under POSIX shells, cmd.exe, and PowerShell; `/bin/sh` wrapping does not.
|
||||
const channel = this.spawnTrackedSystemSshCommand('echo ORCA-SYSTEM-SSH-OK', {
|
||||
wrapCommand: false
|
||||
wrapCommand: false,
|
||||
noInput: true
|
||||
})
|
||||
try {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
@@ -908,7 +909,10 @@ export class SshConnection {
|
||||
return new Error('SSH connection attempt was cancelled')
|
||||
}
|
||||
|
||||
private spawnTrackedSystemSshCommand(command: string, options?: SshExecOptions): ClientChannel {
|
||||
private spawnTrackedSystemSshCommand(
|
||||
command: string,
|
||||
options?: SshExecOptions & Pick<SystemSshBuildArgsOptions, 'noInput'>
|
||||
): ClientChannel {
|
||||
if (options?.signal?.aborted) {
|
||||
throw createSshOperationAbortError()
|
||||
}
|
||||
|
||||
@@ -391,9 +391,26 @@ describe('spawnSystemSsh', () => {
|
||||
expect(args).toContain('ServerAliveCountMax=3')
|
||||
})
|
||||
|
||||
it('places no-input mode before the destination without disabling connection reuse', () => {
|
||||
const args = buildSshArgs(createTarget(), {
|
||||
noInput: true,
|
||||
resolvedConfig: createResolvedConfig()
|
||||
})
|
||||
const noInputIdx = args.indexOf('-n')
|
||||
const terminatorIdx = args.indexOf('--')
|
||||
|
||||
expect(noInputIdx).toBeGreaterThan(-1)
|
||||
expect(noInputIdx).toBeLessThan(terminatorIdx)
|
||||
expect(args).toContain('ControlMaster=auto')
|
||||
expect(args).toContain('ControlPersist=300')
|
||||
expect(args).not.toContain('-S')
|
||||
})
|
||||
|
||||
it('spawns a remote command through the system ssh target', () => {
|
||||
spawnSystemSshCommand(createTarget({ configHost: 'fdpass-host' }), 'echo hello')
|
||||
|
||||
const args = spawnMock.mock.calls[0][1] as string[]
|
||||
expect(args).not.toContain('-n')
|
||||
expect(spawnMock).toHaveBeenCalledWith(
|
||||
SYSTEM_SSH_PATH,
|
||||
expect.arrayContaining(['--', 'deploy@fdpass-host', "exec /bin/sh -c 'echo hello'"]),
|
||||
|
||||
@@ -5,6 +5,7 @@ export type SystemSshBuildArgsOptions = {
|
||||
resolvedConfig?: SystemSshResolvedConfig | null
|
||||
disableControlMaster?: boolean
|
||||
suppressOrcaControlMaster?: boolean
|
||||
noInput?: boolean
|
||||
}
|
||||
|
||||
export function buildSshArgs(target: SshTarget, options?: SystemSshBuildArgsOptions): string[] {
|
||||
@@ -13,6 +14,11 @@ export function buildSshArgs(target: SshTarget, options?: SystemSshBuildArgsOpti
|
||||
args.push('-o', 'BatchMode=no')
|
||||
// Forward stdin/stdout for relay communication
|
||||
args.push('-T')
|
||||
if (options?.noInput === true) {
|
||||
// Why: closing a parent pipe does not reliably detach native Windows
|
||||
// OpenSSH's input worker for commands that can never consume stdin.
|
||||
args.push('-n')
|
||||
}
|
||||
|
||||
// Why: ControlMaster multiplexes all SSH exec commands over a single connection,
|
||||
// eliminating the ~9s handshake overhead per command. Without this, each
|
||||
@@ -91,6 +97,9 @@ export function getSystemSshBuildArgsFromOperationOptions(
|
||||
if (options?.suppressOrcaControlMaster === true) {
|
||||
buildArgsOptions.suppressOrcaControlMaster = true
|
||||
}
|
||||
if (options?.noInput === true) {
|
||||
buildArgsOptions.noInput = true
|
||||
}
|
||||
return Object.keys(buildArgsOptions).length === 0 ? undefined : buildArgsOptions
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user