Skip to content

Commit d65b7e1

Browse files
committed
fix: preserve user-provided SSH username/port and resolve relative Include paths against ~/.ssh
- ensureEnvironment now overlays caller-supplied username and port onto the ssh -G resolved target instead of discarding them. - resolveSshConfigIncludePattern resolves relative Include patterns against ~/.ssh/ (per OpenSSH ssh_config(5)) instead of the parent file's directory. - Thread homeDir through collectSshConfigAliasesFromFile so discoverDesktopSshHosts passes it to the include resolver.
1 parent d2cd406 commit d65b7e1

File tree

1 file changed

+18
-7
lines changed

1 file changed

+18
-7
lines changed

apps/desktop/src/sshEnvironment.ts

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,13 @@ function expandHomePath(input: string, homeDir: string = OS.homedir()): string {
8686

8787
function resolveSshConfigIncludePattern(
8888
includePattern: string,
89-
directory: string,
89+
_directory: string,
9090
homeDir: string = OS.homedir(),
9191
): string {
9292
const expandedPattern = expandHomePath(includePattern, homeDir);
9393
return Path.isAbsolute(expandedPattern)
9494
? expandedPattern
95-
: Path.resolve(directory, expandedPattern);
95+
: Path.resolve(Path.join(homeDir, ".ssh"), expandedPattern);
9696
}
9797

9898
function hasSshPattern(value: string): boolean {
@@ -132,6 +132,7 @@ function expandGlob(pattern: string): ReadonlyArray<string> {
132132
function collectSshConfigAliasesFromFile(
133133
filePath: string,
134134
visited = new Set<string>(),
135+
homeDir: string = OS.homedir(),
135136
): ReadonlyArray<string> {
136137
const resolvedPath = Path.resolve(filePath);
137138
if (visited.has(resolvedPath) || !FS.existsSync(resolvedPath)) {
@@ -153,9 +154,9 @@ function collectSshConfigAliasesFromFile(
153154
const normalizedDirective = directive.toLowerCase();
154155
if (normalizedDirective === "include") {
155156
for (const includePattern of rawArgs) {
156-
const resolvedPattern = resolveSshConfigIncludePattern(includePattern, directory);
157+
const resolvedPattern = resolveSshConfigIncludePattern(includePattern, directory, homeDir);
157158
for (const includedPath of expandGlob(resolvedPattern)) {
158-
for (const alias of collectSshConfigAliasesFromFile(includedPath, visited)) {
159+
for (const alias of collectSshConfigAliasesFromFile(includedPath, visited, homeDir)) {
159160
aliases.add(alias);
160161
}
161162
}
@@ -884,8 +885,13 @@ async function stopTunnel(entry: SshTunnelEntry): Promise<void> {
884885
export async function discoverDesktopSshHosts(input?: {
885886
readonly homeDir?: string;
886887
}): Promise<readonly DesktopDiscoveredSshHost[]> {
887-
const sshDirectory = Path.join(input?.homeDir ?? OS.homedir(), ".ssh");
888-
const configAliases = collectSshConfigAliasesFromFile(Path.join(sshDirectory, "config"));
888+
const homeDir = input?.homeDir ?? OS.homedir();
889+
const sshDirectory = Path.join(homeDir, ".ssh");
890+
const configAliases = collectSshConfigAliasesFromFile(
891+
Path.join(sshDirectory, "config"),
892+
new Set<string>(),
893+
homeDir,
894+
);
889895
const knownHosts = readKnownHostsHostnames(Path.join(sshDirectory, "known_hosts"));
890896
const discovered = new Map<string, DesktopDiscoveredSshHost>();
891897

@@ -1002,7 +1008,12 @@ export class DesktopSshEnvironmentManager {
10021008
target: DesktopSshEnvironmentTarget,
10031009
options?: { readonly issuePairingToken?: boolean },
10041010
): Promise<DesktopSshEnvironmentBootstrap> {
1005-
const resolvedTarget = await resolveDesktopSshTarget(target.alias || target.hostname);
1011+
const baseResolved = await resolveDesktopSshTarget(target.alias || target.hostname);
1012+
const resolvedTarget: DesktopSshEnvironmentTarget = {
1013+
...baseResolved,
1014+
...(target.username !== null ? { username: target.username } : {}),
1015+
...(target.port !== null ? { port: target.port } : {}),
1016+
};
10061017
const key = targetConnectionKey(resolvedTarget);
10071018
const packageSpec = this.options.resolveCliPackageSpec?.();
10081019
const entry = await this.ensureTunnelEntry(key, resolvedTarget, packageSpec);

0 commit comments

Comments
 (0)