{"record":{"id":"4d983160d80d5d49","repo":"NousResearch/hermes-agent","slug":"unsafe-ssh-port-port-must-be-1-65535","errorCode":null,"errorMessage":"Unsafe SSH port: ${port} (must be 1-65535).","messagePattern":"Unsafe SSH port: (.+?) \\(must be 1-65535\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"apps/desktop/electron/ssh-connection.ts","lineNumber":71,"sourceCode":"    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\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","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/desktop/electron/ssh-connection.ts#L53-L89","documentation":"Thrown by validateSshTarget() when the port does not coerce to an integer in 1-65535: the value is normalized with Number(port) then checked with Number.isInteger plus range bounds, so NaN, floats, zero, negatives, and out-of-range values all fail. Note the SshConnection constructor defaults cfg.port to 22 when falsy before validating, so this fires mainly when port is truthy-but-invalid or when validateSshTarget is called directly.","triggerScenarios":"A truthy port like 'ssh' (NaN), 22.5, 0 is impossible via constructor (falsy → 22) but 70000, '0x10g', or '22.5' pass the truthy test and fail Number.isInteger/range. Calling validateSshTarget directly with undefined also fails (Number(undefined) = NaN).","commonSituations":"Config storing port as a non-numeric or empty-but-truthy string (e.g. ' ' or '22,'); a port field parsed from a URL string without conversion; user entering a value like '65536' or 'abc' in the port field.","solutions":["Set a valid integer port (1-65535), typically 22 for standard SSH.","If the port is optional, leave it unset/undefined so the constructor's default of 22 applies, rather than storing a junk string.","Coerce and validate at config save time: const p = Number(raw); if (!Number.isInteger(p) || p < 1 || p > 65535) reject."],"exampleFix":"// before\nnew SshConnection({ host, user, port: cfg.portString }) // 'abc' or '70000'\n\n// after\nconst port = cfg.portString ? Number(cfg.portString) : 22\nif (!Number.isInteger(port) || port < 1 || port > 65535) throw new RangeError(`invalid SSH port: ${cfg.portString}`)\nnew SshConnection({ host, user, port })","handlingStrategy":"validation","validationCode":"function normalizeSshPort(raw: unknown): number {\n  const p = raw == null || raw === '' ? 22 : Number(raw)\n  if (!Number.isInteger(p) || p < 1 || p > 65535) {\n    throw new RangeError(`invalid SSH port: ${String(raw)}`)\n  }\n  return p\n}\n\nconst port = normalizeSshPort(cfg.port)","typeGuard":"function isValidSshPort(p: unknown): boolean {\n  const n = Number(p)\n  return p == null || p === '' || (Number.isInteger(n) && n >= 1 && n <= 65535)\n}","tryCatchPattern":"try {\n  validateSshTarget(host, user, port)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Unsafe SSH port')) {\n    port = 22 // fall back to the default port after notifying the user\n    validateSshTarget(host, user, port)\n  } else throw e\n}","preventionTips":["Store the port as a number in config, not a string; default to 22 when unset.","Validate the port input (integer 1-65535) in the remote settings form.","Parse ports out of URLs with Number(new URL(u).port || 22)."],"tags":["ssh","validation","port"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}