{"record":{"id":"274e70867f10ba08","repo":"NousResearch/hermes-agent","slug":"unsafe-ssh-target-user-must-not-start-with-a-dash","errorCode":null,"errorMessage":"Unsafe SSH target: user must not start with a dash (\"${user}\").","messagePattern":"Unsafe SSH target: user must not start with a dash \\(\"(.+?)\"\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/ssh-connection.ts","lineNumber":65,"sourceCode":"function validateSshTarget(host, user, port) {\n  if (!host || typeof host !== 'string') {\n    throw new Error('Unsafe SSH target: host is required.')\n  }\n\n  if (host.startsWith('-')) {\n    throw new Error(`Unsafe SSH target: host must not start with a dash (\"${host}\").`)\n  }\n\n  if (_CONTROL_CHAR_RE.test(host)) {\n    throw new Error('Unsafe SSH target: host contains control characters.')\n  }\n\n  if (user && _CONTROL_CHAR_RE.test(user)) {\n    throw new Error('Unsafe SSH target: user contains control characters.')\n  }\n\n  if (user && user.startsWith('-')) {\n    throw new Error(`Unsafe SSH target: user must not start with a dash (\"${user}\").`)\n  }\n\n  const p = Number(port)\n\n  if (!Number.isInteger(p) || p < 1 || p > 65535) {\n    throw new Error(`Unsafe SSH port: ${port} (must be 1-65535).`)\n  }\n}\n\nfunction validateKeyPath(keyPath) {\n  if (!keyPath) {\n    return\n  }\n\n  if (_CONTROL_CHAR_RE.test(keyPath)) {\n    throw new Error('Unsafe SSH key path: contains control characters.')\n  }\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/ssh-connection.ts#L47-L83","documentation":"Thrown by validateSshTarget() when the user field starts with a dash. Like the host check, this prevents the value being consumed as an ssh option flag once interpolated into the command line (e.g. via user@host argument construction).","triggerScenarios":"cfg.user values such as '-o...' or any truthy string beginning with '-'. Arises from a mis-parsed combined target string or malicious config input.","commonSituations":"Splitting 'user@host' where the split produced a leading-dash fragment; user pasting an ssh flag into the username field; crafted config attempting option injection through the user slot.","solutions":["Correct the username to a plain account name with no leading dash.","Fix the parsing code that produced the malformed user value (verify the user@host split).","Validate username shape at the config/UI layer with a leading-dash rejection like the host field."],"exampleFix":"// before\nconst [user, host] = rawTarget.split('@') // rawTarget = '-oX@host'\n\n// after\nconst [user, host] = rawTarget.split('@')\nif (user.startsWith('-')) throw new TypeError(`invalid username: ${user}`)","handlingStrategy":"validation","validationCode":"if (user && typeof user === 'string' && user.startsWith('-')) {\n  rejectConfig('SSH user must not start with a dash')\n}","typeGuard":"function isDashSafeUser(u: unknown): u is string {\n  return typeof u === 'string' && !u.startsWith('-')\n}","tryCatchPattern":"try {\n  validateSshTarget(host, user, port)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('user must not start with a dash')) {\n    // not sanitizable: the value is option-shaped; reject the config\n    invalidateRemoteConfig('username field contains an ssh-flag-like value')\n    return\n  }\n  throw e\n}","preventionTips":["Validate username shape (e.g. /^[a-z_][a-z0-9._-]*$/i) at entry time.","When splitting 'user@host', check both halves before use.","Never map free-form ssh arguments into the user field."],"tags":["ssh","security","argument-injection","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}