fix: address pr-bug-scan validated finding from #6763

Added isBashReadlineShellOverride guard in isLocalWindowsConptyPaneForCtrlArrow so local Git Bash panes return false, blocking the ConPTY passthrough gate at terminal-shortcut-policy.ts:222.
This commit is contained in:
orca-bug-scan-bot
2026-07-04 12:56:31 -07:00
parent c6bfd86d4e
commit 44e00e66b8
2 changed files with 48 additions and 1 deletions
@@ -117,4 +117,35 @@ describe('isLocalWindowsConptyPaneForCtrlArrow', () => {
})
).toBe(false)
})
// Why: a local Git Bash pane on Windows is a native ConPTY, but its shell is
// readline, not PSReadLine — it does NOT bind \e[1;5D / \e[1;5C, so it still
// needs the \eb / \ef translation. Treating it as a passthrough ConPTY pane
// would break Ctrl+←/→ word-nav in Git Bash.
it('does NOT gate out translation for a local Git Bash pane (readline, not PSReadLine)', () => {
const worktreeId = 'repo-1::C:\\repo'
for (const shellOverride of [
'git-bash',
'C:\\Program Files\\Git\\bin\\bash.exe',
'bash.exe'
]) {
expect(
isLocalWindowsConptyPaneForCtrlArrow({
isWindows: true,
userAgent: WINDOWS_UA,
state: state({
tabsByWorktree: {
[worktreeId]: [{ id: 'tab-1', shellOverride }]
}
}),
worktreeId,
tabId: 'tab-1',
paneId: 1,
paneCwd: new Map([[1, { cwd: 'C:\\repo', confirmed: true }]]),
fallbackCwd: 'C:\\repo',
transport: localTransport({ cwd: 'C:\\repo', shellOverride })
})
).toBe(false)
}
})
})
@@ -43,6 +43,15 @@ function isRemoteRuntimePtyId(ptyId: string): boolean {
return ptyId.startsWith(REMOTE_RUNTIME_PTY_ID_PREFIX)
}
// Why: the Ctrl+Arrow ConPTY gate exists to defer to PSReadLine (PowerShell/
// cmd), which binds Ctrl+←/→ natively. A local Git Bash / bash shell on Windows
// ConPTY runs readline instead, which does NOT bind \e[1;5D / \e[1;5C — so it
// still needs the \eb / \ef translation, just like Linux/WSL. Matches the
// 'git-bash' shortcut literal and any bash(.exe) shell path.
function isBashReadlineShellOverride(shellOverride: string | null | undefined): boolean {
return /(?:^|[/\\])(?:git-)?bash(?:\.exe)?$/i.test(shellOverride ?? '')
}
export function isLocalWindowsConptyPaneForCtrlArrow({
isWindows,
userAgent,
@@ -75,6 +84,13 @@ export function isLocalWindowsConptyPaneForCtrlArrow({
const tabShellOverride = state.tabsByWorktree[worktreeId]?.find(
(candidate) => candidate.id === tabId
)?.shellOverride
const shellOverride = sessionMetadata?.shellOverride ?? tabShellOverride
// Why: a local Git Bash / bash pane is a native Windows ConPTY but runs
// readline, not PSReadLine — it needs the \eb / \ef translation, so it must
// NOT be gated out of it. Only PowerShell/cmd (PSReadLine) should pass through.
if (isBashReadlineShellOverride(shellOverride)) {
return false
}
const executionHostId: ExecutionHostId = hasLiveLocalSession
? LOCAL_EXECUTION_HOST_ID
: getExecutionHostIdForWorktree(state, worktreeId)
@@ -83,7 +99,7 @@ export function isLocalWindowsConptyPaneForCtrlArrow({
userAgent,
connectionId,
cwd: sessionMetadata?.cwd ?? paneCwd.get(paneId)?.cwd ?? fallbackCwd,
shellOverride: sessionMetadata?.shellOverride ?? tabShellOverride,
shellOverride,
executionHostId
})
}