fix(ssh): dispatch transfer snippets through verified transferShell

The previous fix gated on a verified `transferShell` but still let
OpenSSH hand the snippet to whatever `$SHELL` happens to be on the
remote. On a fish/csh/tcsh host the new gate would accept the host,
then fail anyway because the login shell can't parse `if [ ... ]; then
...` (P1 from PR #3722 review).

Each transfer command (read, write, stat, list) is now wrapped in
`<transferShell> -c '...'` via a shared `wrapInPosixShell` helper, so
the snippet is parsed by the same shell OMP's capability probe verified
can run it. `ensurePosixRemote` returns the verified shell so each
call site can do the wrap. `ssh-executor.ts`'s identical Windows-compat
helper is consolidated onto the same primitive (`buildCompatCommand` /
`quoteForCompatShell` removed).

Stays POSIX-clean across all four call sites; `-c` (not `-lc`) since
the snippets only call absolute builtins and don't need login-profile
setup. Capability *probing* still uses `-lc` to mirror the user env.

Test additions: one case asserts every dispatch starts with
`bash -c '...'` and embeds the original POSIX snippet (read/write/stat/list)
when transferShell is bash and login shell is unknown; another covers
the `sh -c` happy path.
This commit is contained in:
roboomp
2026-06-28 11:52:44 +00:00
parent c4ed1614eb
commit 207734e915
5 changed files with 65 additions and 45 deletions
+19
View File
@@ -30,3 +30,22 @@ export function quotePosixPath(value: string): string {
if (value.length === 0) return "''";
return `'${value.replace(/'/g, "'\\''")}'`;
}
/**
* Wrap a POSIX command in `<shell> -c '<command>'` so it runs under the
* named shell rather than whatever `$SHELL` happens to be on the remote.
*
* Used by the `ssh://` transfer helpers and the Windows compat dispatch:
* OpenSSH passes our snippets to `<login-shell> -c`, so a remote whose
* login shell is fish/csh/tcsh (or cmd/powershell on Windows compat)
* can't parse `if [ … ]; then …`. Wrapping forces parsing under the
* shell OMP actually verified can run the snippet.
*
* `-c` (not `-lc`): the transfer snippets only call absolute POSIX
* builtins (`head`/`cat`/`mv`/`test`/`ls`/`mkdir`/`rm`/`dirname`) and
* don't need login-profile setup. Capability *probing* still uses
* `-lc` to mirror the user's real environment.
*/
export function wrapInPosixShell(shell: "sh" | "bash" | "zsh", command: string): string {
return `${shell} -c ${quotePosixPath(command)}`;
}