can1357/oh-my-pi · error
Invalid SSH username "${username}": an SSH username must not
Error message
Invalid SSH username "${username}": an SSH username must not begin with "-" (argument-injection guard) What it means
Companion to the host guard in buildSshTarget: SSH also parses `user@host` destinations where a username starting with "-" would let an option like `-oProxyCommand=...` slip through before the @ separator in some parse paths. The library rejects any username beginning with "-" before the target string reaches an ssh argv.
Source
Thrown at packages/coding-agent/src/ssh/utils.ts:17
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 theView on GitHub (pinned to 9690622007)
Solutions
- Correct the username in the ssh:// URL or config so it contains no leading dash.
- Pass ssh options via ~/.ssh/config or the tool's option fields instead of the username.
- Validate the username before calling buildSshTarget.
Example fix
// before
buildSshTarget("-oProxyCommand=evil", "host.example.com")
// after
buildSshTarget("deploy", "host.example.com") Defensive patterns
Strategy: validation
Validate before calling
function safeSshUser(user: string): boolean { return !user.startsWith("-"); }
if (username && !safeSshUser(username)) throw new Error("username must not begin with '-'"); Try / catch
try { const target = buildSshTarget(username, host); } catch (err) { log.warn("ssh username rejected", { username, err }); return; } Prevention
- Validate both components of ssh:// URLs (userinfo and host) before constructing destinations.
- Reject any credential/identity string beginning with '-' at input boundaries.
- Prefer explicit config fields for ssh options over embedding them in user@host strings.
When it happens
Trigger: Calling buildSshTarget with a username string starting with "-", e.g. from a URL like ssh://-oProxyCommand=evil@host or a config where the user field contains ssh options.
Common situations: Malformed ssh:// URLs where the userinfo component holds options, attacker-crafted remote URLs attempting option injection, or copy-paste errors putting flags into the username field.
Related errors
- Invalid SSH host "${host}": an SSH destination must not begi
- SSH control directory ${dir} ${isSymlink ? "is a symlink" :
- SSH control directory ${dir} ${reason}
- SSH key permissions must be 600 or stricter: ${keyPath}
- The managed-skills root is a symlink; refusing to operate ou
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/42f1624aa41300d8.
Report an issue: GitHub.