mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
fix(desktop): parse user@host[:port] typed into the SSH host field
normalizeSshConfig only trimmed the host, so user@host worked only by accident (passed through as the literal target) and user@host:port did not split into host + port. Worse, typing user@host AND filling the User field could produce user@user@host. Now: split a leading user@ and a trailing :port off the host field; explicit user/port fields win (no doubling); IPv6 literals (multiple colons) and bare ~/.ssh/config aliases are left untouched. Tests cover user@host, user@host:port, explicit-fields-win, and the alias/IPv6 passthrough.
This commit is contained in:
parent
bb898b80f9
commit
9622497036
2 changed files with 57 additions and 3 deletions
|
|
@ -281,14 +281,37 @@ function normalizeSshConfig(entry) {
|
|||
if (!entry || typeof entry !== 'object' || entry.mode !== 'ssh') {
|
||||
return null
|
||||
}
|
||||
const host = String(entry.host || '').trim()
|
||||
let host = String(entry.host || '').trim()
|
||||
if (!host) {
|
||||
return null
|
||||
}
|
||||
// Parse a user@host[:port] target typed into the single host field. Explicit
|
||||
// user/port fields win, so filling the User field after typing user@host does
|
||||
// NOT double up into user@user@host. A bare ~/.ssh/config alias is preserved.
|
||||
let parsedUser
|
||||
let parsedPort
|
||||
const at = host.indexOf('@')
|
||||
if (at > 0) {
|
||||
parsedUser = host.slice(0, at)
|
||||
host = host.slice(at + 1)
|
||||
}
|
||||
// Only split a trailing :port when there's exactly one colon and a numeric
|
||||
// suffix — leaves IPv6 literals (multiple colons) and bare aliases alone.
|
||||
if ((host.match(/:/g) || []).length === 1) {
|
||||
const [h, p] = host.split(':')
|
||||
if (/^\d+$/.test(p)) {
|
||||
host = h
|
||||
parsedPort = Number.parseInt(p, 10)
|
||||
}
|
||||
}
|
||||
if (!host) {
|
||||
return null
|
||||
}
|
||||
const out = { mode: 'ssh', host }
|
||||
const user = String(entry.user || '').trim()
|
||||
const user = String(entry.user || '').trim() || parsedUser || ''
|
||||
if (user) out.user = user
|
||||
const port = Number.parseInt(String(entry.port ?? ''), 10)
|
||||
const explicitPort = Number.parseInt(String(entry.port ?? ''), 10)
|
||||
const port = Number.isInteger(explicitPort) && explicitPort > 0 ? explicitPort : parsedPort
|
||||
if (Number.isInteger(port) && port > 0 && port !== 22) {
|
||||
out.port = port
|
||||
}
|
||||
|
|
|
|||
|
|
@ -430,6 +430,37 @@ test('normalizeSshConfig preserves a non-default port', () => {
|
|||
})
|
||||
})
|
||||
|
||||
test('normalizeSshConfig parses user@host typed into the host field', () => {
|
||||
assert.deepEqual(normalizeSshConfig({ mode: 'ssh', host: 'jonny@mac-mini' }), {
|
||||
mode: 'ssh',
|
||||
host: 'mac-mini',
|
||||
user: 'jonny'
|
||||
})
|
||||
})
|
||||
|
||||
test('normalizeSshConfig parses user@host:port and drops a default :22', () => {
|
||||
assert.deepEqual(normalizeSshConfig({ mode: 'ssh', host: 'jonny@box:2222' }), {
|
||||
mode: 'ssh',
|
||||
host: 'box',
|
||||
user: 'jonny',
|
||||
port: 2222
|
||||
})
|
||||
assert.deepEqual(normalizeSshConfig({ mode: 'ssh', host: 'box:22' }), { mode: 'ssh', host: 'box' })
|
||||
})
|
||||
|
||||
test('normalizeSshConfig: explicit user/port win over user@host:port (no user@user@host)', () => {
|
||||
assert.deepEqual(
|
||||
normalizeSshConfig({ mode: 'ssh', host: 'jonny@box:2222', user: 'admin', port: 2200 }),
|
||||
{ mode: 'ssh', host: 'box', user: 'admin', port: 2200 }
|
||||
)
|
||||
})
|
||||
|
||||
test('normalizeSshConfig leaves a bare ~/.ssh/config alias and IPv6 literals alone', () => {
|
||||
assert.deepEqual(normalizeSshConfig({ mode: 'ssh', host: 'mac-mini' }), { mode: 'ssh', host: 'mac-mini' })
|
||||
// IPv6 (multiple colons) must NOT be split as host:port
|
||||
assert.deepEqual(normalizeSshConfig({ mode: 'ssh', host: 'fe80::1' }), { mode: 'ssh', host: 'fe80::1' })
|
||||
})
|
||||
|
||||
test('profileSshOverride returns a profile-scoped ssh descriptor or null', () => {
|
||||
const config = { profiles: { work: { mode: 'ssh', host: 'mac-mini', user: 'jonny' }, other: { mode: 'remote', url: 'http://x' } } }
|
||||
assert.deepEqual(profileSshOverride(config, 'work'), { mode: 'ssh', host: 'mac-mini', user: 'jonny' })
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue