NousResearch/hermes-agent · error · Error
Unsafe SSH target: user contains control characters.
Error message
Unsafe SSH target: user contains control characters.
What it means
Thrown by validateSshTarget() when the optional user field contains control characters (same _CONTROL_CHAR_RE class as the host). The user is interpolated into ssh command lines (user@host), so control bytes there are a command-corruption and injection hazard.
Source
Thrown at apps/desktop/electron/ssh-connection.ts:61
// 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).`)
}
}
function validateKeyPath(keyPath) {
if (!keyPath) {
return
}
View on GitHub (pinned to c896c09c42)
Solutions
- Sanitize or re-type the username; strip the control-character class before constructing the connection.
- Inspect the saved remote config for embedded control bytes and fix the entry.
- Add UI-level validation mirroring /^[^\x00-\x1f\x7f]*$/ for the user field.
Example fix
// before
new SshConnection({ host, user: rawUser, port })
// after
const user = rawUser.replace(/[\x00-\x1f\x7f]/g, '').trim()
new SshConnection({ host, user, port }) Defensive patterns
Strategy: validation
Validate before calling
const CONTROL = /[\x00-\x1f\x7f]/
if (user && (typeof user !== 'string' || CONTROL.test(user))) {
rejectConfig('SSH user contains control characters')
} Type guard
function isControlFreeUser(u: unknown): u is string {
return typeof u === 'string' && !/[\x00-\x1f\x7f]/.test(u)
} Try / catch
try {
validateSshTarget(host, user, port)
} catch (e) {
if (e instanceof Error && e.message === 'Unsafe SSH target: user contains control characters.') {
user = String(user).replace(/[\x00-\x1f\x7f]/g, '').trim()
if (!user) { user = '' } // empty user is allowed by the validator
validateSshTarget(host, user, port)
} else throw e
} Prevention
- Trim and strip control bytes from usernames derived from pasted 'user@host' strings.
- Decode URL-encoded usernames carefully — some encodings decode to control bytes.
- Apply the shared control-character validation in the settings form, not only at connect time.
When it happens
Trigger: A truthy cfg.user matching /[\x00-\x1f\x7f]/ — pasted usernames with invisible characters, or values sourced from a corrupted or crafted config.
Common situations: Paste from a source with hidden formatting characters; a username auto-filled from an ssh URL whose percent-encoding decoded to a control byte.
Related errors
- Unsafe SSH target: host contains control characters.
- Unsafe SSH target: host must not start with a dash ("${host}
- Unsafe SSH target: user must not start with a dash ("${user}
- Unsafe SSH key path: contains control characters.
- Unsafe SSH key path: must not start with a dash ("${keyPath}
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/bf209940ce3ef815.
Report an issue: GitHub.