{"record":{"id":"c18a71c2676ecbc5","repo":"NousResearch/hermes-agent","slug":"unsafe-ssh-key-path-must-not-start-with-a-dash","errorCode":null,"errorMessage":"Unsafe SSH key path: must not start with a dash (\"${keyPath}\").","messagePattern":"Unsafe SSH key path: must not start with a dash \\(\"(.+?)\"\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/ssh-connection.ts","lineNumber":85,"sourceCode":"\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\n  if (keyPath.startsWith('-')) {\n    throw new Error(`Unsafe SSH key path: must not start with a dash (\"${keyPath}\").`)\n  }\n}\n\n// Token / secret redaction\n\nconst _REDACTIONS: Array<[RegExp, string]> = [\n  [/(HERMES_DASHBOARD_SESSION_TOKEN=)(\\S+)/g, '$1<redacted>'],\n  [/(X-Hermes-Session-Token[\"']?\\s*[:=]\\s*[\"']?)([^\\s\"'&]+)/gi, '$1<redacted>'],\n  [/(Authorization[\"']?\\s*:\\s*Bearer\\s+)(\\S+)/gi, '$1<redacted>'],\n  [/([?&](?:token|ticket)=)([^\\s&\"']+)/gi, '$1<redacted>']\n]\n\nfunction redactSecrets(text) {\n  let out = String(text == null ? '' : text)\n\n  for (const [re, repl] of _REDACTIONS) {\n    out = out.replace(re, repl)\n  }","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/ssh-connection.ts#L67-L103","documentation":"Thrown by validateKeyPath() when the SSH key path starts with a dash. Since the path is passed as an -i argument to ssh, a leading dash would let it be parsed as an option flag, so it is rejected as an argument-injection hazard.","triggerScenarios":"cfg.keyPath beginning with '-', e.g. '-oProxyCommand=...' or a malformed path like '-keys/id_rsa'.","commonSituations":"User pastes an ssh option into the key path field; a path-joining bug producing '-' + segment; crafted config input.","solutions":["Use a proper absolute path to the key file, e.g. /home/user/.ssh/id_ed25519.","Fix the upstream bug that produced a leading-dash path string.","Validate at config save: reject keyPath.startsWith('-')."],"exampleFix":"// before\nnew SshConnection({ host, keyPath: '-i /home/me/.ssh/id' })\n\n// after\nnew SshConnection({ host, keyPath: '/home/me/.ssh/id_ed25519' })","handlingStrategy":"validation","validationCode":"if (keyPath && typeof keyPath === 'string' && keyPath.startsWith('-')) {\n  rejectConfig('SSH key path must be an absolute path, not an option-like string')\n}","typeGuard":"function isDashSafeKeyPath(k: unknown): k is string {\n  return typeof k === 'string' && !k.startsWith('-')\n}","tryCatchPattern":"try {\n  validateKeyPath(keyPath)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('key path: must not start with a dash')) {\n    invalidateRemoteConfig('key path field contains an ssh-flag-like value')\n    return\n  }\n  throw e\n}","preventionTips":["Use a file picker or path.normalize on user input for key paths.","Accept only absolute paths for keyPath in the settings UI.","Do not let users paste full ssh -i command fragments into config fields."],"tags":["ssh","security","argument-injection","validation"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}