test(windows): guard PowerShell setup-runner encoded delivery (#7236) (#7520)

Issue #7236 reported that any non-empty worktree Setup Script failed on
Windows PowerShell with a "missing terminator" parser error, regardless
of content. Root cause: in pre-encoded builds the setup-runner command
(`cmd.exe /c "<runner>"`) was typed into PowerShell as raw stdin, where a
dropped/unbalanced double quote got re-parsed as an open string.

Encoded-command delivery (base64 UTF-16, shipped in v1.4.81) already
fixes this by passing the command as a shell argument with quotes intact.
This adds a regression test tying resolveSetupRunnerCommand to
resolveWindowsShellLaunchArgs: the real setup-runner command must reach
PowerShell via -EncodedCommand (startupCommandDeliveredInShellArgs),
never raw stdin, with its quotes preserved verbatim.

Co-authored-by: Neil <neil@stably.ai>
This commit is contained in:
Brennan Benson 2026-07-06 00:54:45 -07:00 committed by GitHub
parent 88640ef4c6
commit be4d963905
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 40 additions and 0 deletions

View File

@ -7,6 +7,7 @@ import {
buildWslInteractiveLoginShellCommand,
escapeWslShCommandForWindows
} from '../../shared/wsl-login-shell-command'
import { resolveSetupRunnerCommand } from '../../shared/setup-runner-command'
import { resolveWindowsShellLaunchArgs } from './windows-shell-args'
function expectedWslArgs(linuxCwd: string, distro?: string): string[] {
@ -300,3 +301,42 @@ describe('resolveWindowsShellLaunchArgs', () => {
])
})
})
// Regression guard for issue #7236: a worktree Setup Script runs through a
// generated `.cmd` runner invoked as `cmd.exe /c "<runner>"`. When PowerShell
// received that command as raw typed stdin, a dropped/unbalanced quote surfaced
// as a "missing terminator" parser error. Delivering it via -EncodedCommand
// (base64 UTF-16) keeps the quotes balanced and the text verbatim, so it can
// never be re-parsed as an open string.
describe('issue #7236: PowerShell setup-runner command delivery', () => {
// git rev-parse hands back a forward-slash Windows-absolute path for the runner.
const runnerPath = 'C:/Users/alice/repo/.git/orca/setup-runner.cmd'
it('wraps the setup runner in balanced double quotes', () => {
const { command } = resolveSetupRunnerCommand(runnerPath, 'windows')
expect(command).toBe(`cmd.exe /c "${runnerPath}"`)
expect((command.match(/"/g) ?? []).length % 2).toBe(0)
})
it('delivers the setup-runner command through -EncodedCommand, never raw stdin', () => {
const { command } = resolveSetupRunnerCommand(runnerPath, 'windows')
const result = resolveWindowsShellLaunchArgs(
'powershell.exe',
'C:\\Users\\alice\\repo',
'C:\\Users\\alice',
undefined,
command
)
// The flag tells the daemon/provider NOT to also type the command over
// stdin — raw stdin delivery is the pre-encoded path that broke in #7236.
expect(result.startupCommandDeliveredInShellArgs).toBe(true)
expect(result.shellArgs.slice(0, 3)).toEqual(['-NoLogo', '-NoExit', '-EncodedCommand'])
const decoded = Buffer.from(result.shellArgs[3] ?? '', 'base64').toString('utf16le')
expect(decoded).toContain(`\n${command}`)
expect(decoded.trimEnd().endsWith(command)).toBe(true)
// Quotes survive encoding intact, so PowerShell parses one balanced string.
expect((decoded.slice(decoded.lastIndexOf(command)).match(/"/g) ?? []).length % 2).toBe(0)
})
})