diff --git a/src/main/ssh/ssh-g-host-block-match.test.ts b/src/main/ssh/ssh-g-host-block-match.test.ts index b4907b3e45e..ad2e0e13300 100644 --- a/src/main/ssh/ssh-g-host-block-match.test.ts +++ b/src/main/ssh/ssh-g-host-block-match.test.ts @@ -135,6 +135,17 @@ describe('ssh -G host-block matching', () => { expect(mockReadFileSync).not.toHaveBeenCalledWith('/keys/prod') }) + // Every other field is a bare default, so only the rewritten HostName can carry the verdict. + it('treats a rewritten HostName as a match when nothing else differs from the defaults', () => { + const config = buildConnectConfig( + storedTarget({ jumpHost: undefined }), + unmatchedDefaults({ hostname: '10.9.9.9' }), + { includeAgent: false, includePrivateKey: true } + ) + + expect(config.host).toBe('10.9.9.9') + }) + it('treats a User directive as a match even when HostName echoes the alias', () => { const config = buildConnectConfig( storedTarget({ configHost: 'github.com', label: 'github.com', host: 'github.com' }), @@ -146,9 +157,10 @@ describe('ssh -G host-block matching', () => { expect(config.username).toBe('git') }) + // Stored port stays non-22 so the legacy `target.port === 22` fallback can't supply the answer. it('treats a non-default Port as a match even when HostName echoes the alias', () => { const config = buildConnectConfig( - storedTarget({ port: 22 }), + storedTarget({ port: 2222 }), unmatchedDefaults({ port: 2022 }), { includeAgent: false, includePrivateKey: true } ) @@ -156,6 +168,26 @@ describe('ssh -G host-block matching', () => { expect(config.port).toBe(2022) }) + it('treats a ProxyJump directive as a match even when HostName echoes the alias', () => { + const target = storedTarget({ jumpHost: 'stale-bastion' }) + + expect(resolveEffectiveProxy(target, unmatchedDefaults({ proxyJump: 'edge' }))).toEqual({ + kind: 'jump-host', + jumpHost: 'edge' + }) + }) + + it('treats a ProxyCommand directive as a match even when HostName echoes the alias', () => { + const target = storedTarget({ jumpHost: undefined, proxyCommand: 'stale-proxy %h' }) + + expect( + resolveEffectiveProxy(target, unmatchedDefaults({ proxyCommand: 'cf access ssh %h' })) + ).toEqual({ + kind: 'proxy-command', + command: 'cf access ssh %h' + }) + }) + it('keeps manual targets on their stored fields regardless of ssh -G output', () => { const config = buildConnectConfig( storedTarget({ source: 'manual', configHost: undefined }), diff --git a/src/main/ssh/ssh-g-host-block-match.ts b/src/main/ssh/ssh-g-host-block-match.ts index 3f8de0f57c9..bfe85c57b41 100644 --- a/src/main/ssh/ssh-g-host-block-match.ts +++ b/src/main/ssh/ssh-g-host-block-match.ts @@ -34,6 +34,10 @@ function localAccountName(): string { * would silently replace a stored target's host/port/user/proxy/identity with * those defaults — dialing the bare alias as the local user — whenever the block * is renamed, removed, or lives on another machine. + * + * Known limitation: the signals below read the *effective* config, so a wildcard + * `Host *` supplying User/Port/ProxyCommand — or `CanonicalizeHostname yes` — + * still reads as a match for an alias whose own block is gone. */ export function hasOpenSshHostBlockMatch( target: HostBlockMatchTarget,