207734e915
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.
52 lines
2.1 KiB
TypeScript
52 lines
2.1 KiB
TypeScript
export function sanitizeHostName(name: string): string {
|
|
const sanitized = name.replace(/[^a-zA-Z0-9._-]+/g, "_");
|
|
return sanitized.length > 0 ? sanitized : "remote";
|
|
}
|
|
|
|
export function buildSshTarget(username: string | undefined, host: string): string {
|
|
// SSH treats a destination starting with "-" as an option, so a host/user of
|
|
// `-oProxyCommand=...` becomes local command execution. Reject before this
|
|
// string reaches any `ssh` argv (this is the single render chokepoint for
|
|
// every connection, transfer, and sshfs mount).
|
|
if (host.startsWith("-")) {
|
|
throw new Error(
|
|
`Invalid SSH host "${host}": an SSH destination must not begin with "-" (argument-injection guard)`,
|
|
);
|
|
}
|
|
if (username?.startsWith("-")) {
|
|
throw new Error(
|
|
`Invalid SSH username "${username}": an SSH username must not begin with "-" (argument-injection guard)`,
|
|
);
|
|
}
|
|
return username ? `${username}@${host}` : host;
|
|
}
|
|
|
|
/**
|
|
* Single-quote a path for a POSIX remote shell, escaping embedded single quotes.
|
|
* Mirrors the private `quoteRemotePath` in `tools/ssh.ts`; shared here for the
|
|
* `ssh://` file-transfer helpers.
|
|
*/
|
|
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)}`;
|
|
}
|