NousResearch/hermes-agent · error · Error
Unsafe SSH target: host must not start with a dash ("${host}
Error message
Unsafe SSH target: host must not start with a dash ("${host}"). What it means
Thrown by validateSshTarget() when the SSH host begins with a dash. A leading dash makes argument parsers treat the value as an option flag once placed on the ssh command line, so it is rejected as an argument-injection hazard before any ssh invocation is built.
Source
Thrown at apps/desktop/electron/ssh-connection.ts:53
import net from 'node:net'
import os from 'node:os'
import path from 'node:path'
const DEFAULT_CONNECT_TIMEOUT_MS = 15_000
const DEFAULT_EXEC_TIMEOUT_MS = 20_000
const DEFAULT_FORWARD_TIMEOUT_MS = 15_000
const CONTROL_PERSIST_SECONDS = 300
// eslint-disable-next-line no-control-regex -- deliberately reject control chars in ssh targets
const _CONTROL_CHAR_RE = /[\x00-\x1f\x7f]/
function validateSshTarget(host, user, port) {
if (!host || typeof host !== 'string') {
throw new Error('Unsafe SSH target: host is required.')
}
if (host.startsWith('-')) {
throw new Error(`Unsafe SSH target: host must not start with a dash ("${host}").`)
}
if (_CONTROL_CHAR_RE.test(host)) {
throw new Error('Unsafe SSH target: host contains control characters.')
}
if (user && _CONTROL_CHAR_RE.test(user)) {
throw new Error('Unsafe SSH target: user contains control characters.')
}
if (user && user.startsWith('-')) {
throw new Error(`Unsafe SSH target: user must not start with a dash ("${user}").`)
}
const p = Number(port)
if (!Number.isInteger(p) || p < 1 || p > 65535) {
throw new Error(`Unsafe SSH port: ${port} (must be 1-65535).`)View on GitHub (pinned to c896c09c42)
Solutions
- Correct the host to a plain hostname or IP (no leading dash).
- Validate at the settings UI: reject host fields starting with '-' before save.
- If the user intended ssh options, use the supported dedicated fields (port, keyPath, user), never the host string.
Example fix
// before
new SshConnection({ host: '-oProxyCommand=evil', ... })
// after
new SshConnection({ host: 'example.com', port: 22, ... }) Defensive patterns
Strategy: validation
Validate before calling
if (typeof host !== 'string' || host.length === 0 || host.startsWith('-')) {
rejectConfig('SSH host must be a plain hostname and must not start with a dash')
} Type guard
function isDashSafe(value: unknown): value is string {
return typeof value === 'string' && value.length > 0 && !value.startsWith('-')
} Try / catch
try {
validateSshTarget(host, user, port)
} catch (e) {
if (e instanceof Error && e.message.includes('host must not start with a dash')) {
// not sanitizable: reject the config entry and ask for a corrected host
invalidateRemoteConfig('host looks like an ssh option, not a hostname')
return
}
throw e
} Prevention
- Reject leading-dash values in host/user/keyPath at the config UI layer.
- Expose supported ssh options as dedicated fields (port, keyPath) instead of letting users paste flags.
- Treat a leading-dash host in stored config as evidence of tampering and audit the config source.
When it happens
Trigger: host values like '-oProxyCommand=...', '-v', or any string starting with '-'. These arise from malformed user input, a mis-parse of a combined user@host string, or a deliberately crafted config value.
Common situations: User pastes an ssh option into the host field; a config editor stores a whole ssh command line in the host field; attacker-controlled config attempting option injection through the host slot.
Related errors
- Unsafe SSH target: user must not start with a dash ("${user}
- Unsafe SSH key path: must not start with a dash ("${keyPath}
- Unsafe SSH target: host contains control characters.
- Unsafe SSH target: user contains control characters.
- Unsafe SSH key path: contains control characters.
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/b18e6a1c1da9111d.
Report an issue: GitHub.