fix(ssh): allow blank user in VS Code authority (#10072)
This commit is contained in:
parent
8448557be9
commit
fc05769edf
|
|
@ -542,6 +542,29 @@ describe('registerShellHandlers', () => {
|
|||
])
|
||||
})
|
||||
|
||||
it('opens a manual port-22 target with a host-only authority when username is blank', async () => {
|
||||
sshTargets.set(
|
||||
'ssh-1',
|
||||
createSshTarget({
|
||||
source: 'manual',
|
||||
configHost: 'builder.example.com',
|
||||
host: 'builder.example.com',
|
||||
username: ''
|
||||
})
|
||||
)
|
||||
resolveCliCommandMock.mockReturnValueOnce('/usr/local/bin/code')
|
||||
const handler = getHandler('shell:openInExternalEditor')
|
||||
|
||||
await expect(
|
||||
handler({}, { path: '/srv/project', command: 'code', connectionId: 'ssh-1' })
|
||||
).resolves.toEqual({ ok: true })
|
||||
expect(getSpawnArgsForWindowsMock).toHaveBeenCalledWith('/usr/local/bin/code', [
|
||||
'--remote',
|
||||
'ssh-remote+builder.example.com',
|
||||
'/srv/project'
|
||||
])
|
||||
})
|
||||
|
||||
it('rejects relative SSH paths before resolving or spawning a launcher', async () => {
|
||||
sshTargets.set('ssh-1', createSshTarget())
|
||||
const handler = getHandler('shell:openInExternalEditor')
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ describe('resolveVsCodeSshAuthority', () => {
|
|||
).toEqual({ ok: true, authority: 'ada@builder.example.com' })
|
||||
})
|
||||
|
||||
it.each(['', ' '])(
|
||||
'uses a host-only authority for a manual port-22 target without a username',
|
||||
(username) => {
|
||||
expect(resolveVsCodeSshAuthority(createTarget({ username }))).toEqual({
|
||||
ok: true,
|
||||
authority: 'builder.example.com'
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
it('requires an alias for manual targets on non-default ports', () => {
|
||||
expect(
|
||||
resolveVsCodeSshAuthority(
|
||||
|
|
@ -49,11 +59,12 @@ describe('resolveVsCodeSshAuthority', () => {
|
|||
|
||||
it.each([
|
||||
createTarget({ host: ' ' }),
|
||||
createTarget({ username: '' }),
|
||||
createTarget({ host: 'builder\nmalicious' }),
|
||||
createTarget({ username: '\n' }),
|
||||
createTarget({ username: 'ada\nmalicious' }),
|
||||
createTarget({ source: 'ssh-config', configHost: '\u0000builder' }),
|
||||
createTarget({ port: 0 })
|
||||
])('rejects incomplete or unsafe target fields', (target) => {
|
||||
])('rejects invalid or unsafe target fields', (target) => {
|
||||
expect(resolveVsCodeSshAuthority(target)).toEqual({
|
||||
ok: false,
|
||||
reason: 'ssh-target-invalid'
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export type VsCodeSshAuthorityResult =
|
|||
| { ok: false; reason: 'ssh-target-invalid' }
|
||||
| { ok: false; reason: 'ssh-alias-required'; host: string; port: number }
|
||||
|
||||
function isValidAuthorityPart(value: string): boolean {
|
||||
function isValidAuthorityPart(value: string, allowEmpty = false): boolean {
|
||||
return (
|
||||
value.length > 0 &&
|
||||
(allowEmpty || value.trim().length > 0) &&
|
||||
!Array.from(value).some((character) => {
|
||||
const codePoint = character.codePointAt(0) ?? 0
|
||||
return codePoint <= 0x1f || codePoint === 0x7f
|
||||
|
|
@ -18,17 +18,17 @@ function isValidAuthorityPart(value: string): boolean {
|
|||
|
||||
export function resolveVsCodeSshAuthority(target: SshTarget): VsCodeSshAuthorityResult {
|
||||
if (isOpenSshConfigBackedTarget(target)) {
|
||||
const configHost = target.configHost?.trim() ?? ''
|
||||
const configHost = target.configHost ?? ''
|
||||
return isValidAuthorityPart(configHost)
|
||||
? { ok: true, authority: configHost }
|
||||
? { ok: true, authority: configHost.trim() }
|
||||
: { ok: false, reason: 'ssh-target-invalid' }
|
||||
}
|
||||
|
||||
const host = target.host.trim()
|
||||
const username = target.username.trim()
|
||||
if (
|
||||
!isValidAuthorityPart(host) ||
|
||||
!isValidAuthorityPart(username) ||
|
||||
!isValidAuthorityPart(target.host) ||
|
||||
!isValidAuthorityPart(target.username, true) ||
|
||||
!Number.isInteger(target.port) ||
|
||||
target.port < 1 ||
|
||||
target.port > 65_535
|
||||
|
|
@ -38,5 +38,5 @@ export function resolveVsCodeSshAuthority(target: SshTarget): VsCodeSshAuthority
|
|||
if (target.port !== 22) {
|
||||
return { ok: false, reason: 'ssh-alias-required', host, port: target.port }
|
||||
}
|
||||
return { ok: true, authority: `${username}@${host}` }
|
||||
return { ok: true, authority: username ? `${username}@${host}` : host }
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue