From 662ebcbbd11527c10a49acab407bacebe622cfa4 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 30 May 2026 10:26:37 -0700 Subject: [PATCH] perf: handle large ssh config alias lists (#3792) --- src/main/ssh/ssh-config-parser.test.ts | 35 ++++++++++++++++++++++++++ src/main/ssh/ssh-config-parser.ts | 14 ++++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/main/ssh/ssh-config-parser.test.ts b/src/main/ssh/ssh-config-parser.test.ts index 2b5cf6af34a..541742df8c0 100644 --- a/src/main/ssh/ssh-config-parser.test.ts +++ b/src/main/ssh/ssh-config-parser.test.ts @@ -6,6 +6,16 @@ vi.mock('os', () => ({ homedir: () => '/home/testuser' })) +const LARGE_HOST_ALIAS_COUNT = 150_000 + +function buildHostAliases(count: number): string { + const aliases: string[] = [] + for (let index = 0; index < count; index += 1) { + aliases.push(`generated-${index}`) + } + return aliases.join(' ') +} + describe('parseSshConfig', () => { it('parses a basic host block', () => { const config = ` @@ -172,6 +182,31 @@ Host staging stage *.example.com ]) }) + it('parses large concrete alias lists on one Host line', () => { + const config = [ + `Host ${buildHostAliases(LARGE_HOST_ALIAS_COUNT)}`, + ' HostName generated.example.com', + 'Host after', + ' HostName after.example.com' + ].join('\n') + + const hosts = parseSshConfig(config) + + expect(hosts).toHaveLength(LARGE_HOST_ALIAS_COUNT + 1) + expect(hosts[0]).toEqual({ + host: 'generated-0', + hostname: 'generated.example.com' + }) + expect(hosts[LARGE_HOST_ALIAS_COUNT - 1]).toEqual({ + host: `generated-${LARGE_HOST_ALIAS_COUNT - 1}`, + hostname: 'generated.example.com' + }) + expect(hosts.at(-1)).toEqual({ + host: 'after', + hostname: 'after.example.com' + }) + }) + it('applies identity agent settings to every concrete alias on a multi-pattern Host line', () => { const config = ` Host staging stage diff --git a/src/main/ssh/ssh-config-parser.ts b/src/main/ssh/ssh-config-parser.ts index 70b0cacdbe0..391bf0cf20d 100644 --- a/src/main/ssh/ssh-config-parser.ts +++ b/src/main/ssh/ssh-config-parser.ts @@ -44,7 +44,7 @@ export function parseSshConfig(content: string): SshConfigHost[] { if (key === 'host') { if (current.length > 0) { - hosts.push(...current) + appendHosts(hosts, current) } const patterns = splitHostPatterns(value) @@ -62,7 +62,7 @@ export function parseSshConfig(content: string): SshConfigHost[] { if (key === 'match') { if (current.length > 0) { - hosts.push(...current) + appendHosts(hosts, current) } current = [] continue @@ -122,11 +122,19 @@ export function parseSshConfig(content: string): SshConfigHost[] { } if (current.length > 0) { - hosts.push(...current) + appendHosts(hosts, current) } return hosts } +function appendHosts(target: SshConfigHost[], entries: SshConfigHost[]): void { + // Why: generated SSH configs can put many concrete aliases on one Host line; + // spreading that block into push can exceed JavaScript's argument limit. + for (const entry of entries) { + target.push(entry) + } +} + function splitHostPatterns(input: string): string[] { const patterns: string[] = [] let current = ''