Skip to content

Commit 85fd896

Browse files
committed
fix: expand tilde in SSH agent socket path before setenv
1 parent d2f88c1 commit 85fd896

5 files changed

Lines changed: 57 additions & 28 deletions

File tree

TablePro/Core/SSH/Auth/AgentAuthenticator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ internal struct AgentAuthenticator: SSHAuthenticator {
2121
let originalSocketPath = ProcessInfo.processInfo.environment["SSH_AUTH_SOCK"]
2222
let needsSocketOverride = socketPath != nil
2323

24-
if let overridePath = socketPath, needsSocketOverride {
24+
if let overridePath = socketPath.map(SSHPathUtilities.expandTilde), needsSocketOverride {
2525
Self.agentSocketLock.lock()
2626
Self.logger.debug("Using custom SSH agent socket: \(overridePath, privacy: .private)")
2727
setenv("SSH_AUTH_SOCK", overridePath, 1)

TablePro/Core/SSH/Auth/PublicKeyAuthenticator.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal struct PublicKeyAuthenticator: SSHAuthenticator {
1212
let passphrase: String?
1313

1414
func authenticate(session: OpaquePointer, username: String) throws {
15-
let expandedPath = expandPath(privateKeyPath)
15+
let expandedPath = SSHPathUtilities.expandTilde(privateKeyPath)
1616

1717
guard FileManager.default.fileExists(atPath: expandedPath) else {
1818
throw SSHTunnelError.tunnelCreationFailed(
@@ -54,16 +54,4 @@ internal struct PublicKeyAuthenticator: SSHAuthenticator {
5454
}
5555
}
5656

57-
private func expandPath(_ path: String) -> String {
58-
if path.hasPrefix("~/") {
59-
return FileManager.default.homeDirectoryForCurrentUser
60-
.appendingPathComponent(String(path.dropFirst(2)))
61-
.path(percentEncoded: false)
62-
}
63-
if path == "~" {
64-
return FileManager.default.homeDirectoryForCurrentUser
65-
.path(percentEncoded: false)
66-
}
67-
return path
68-
}
6957
}

TablePro/Core/SSH/SSHConfigParser.swift

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ final class SSHConfigParser {
8686
hostname: currentHostname,
8787
port: currentPort,
8888
user: currentUser,
89-
identityFile: expandPath(currentIdentityFile),
90-
identityAgent: expandPath(currentIdentityAgent),
89+
identityFile: currentIdentityFile.map(SSHPathUtilities.expandTilde),
90+
identityAgent: currentIdentityAgent.map(SSHPathUtilities.expandTilde),
9191
proxyJump: currentProxyJump
9292
))
9393
}
@@ -133,8 +133,8 @@ final class SSHConfigParser {
133133
hostname: currentHostname,
134134
port: currentPort,
135135
user: currentUser,
136-
identityFile: expandPath(currentIdentityFile),
137-
identityAgent: expandPath(currentIdentityAgent),
136+
identityFile: currentIdentityFile.map(SSHPathUtilities.expandTilde),
137+
identityAgent: currentIdentityAgent.map(SSHPathUtilities.expandTilde),
138138
proxyJump: currentProxyJump
139139
))
140140
}
@@ -192,14 +192,4 @@ final class SSHConfigParser {
192192
return jumpHosts
193193
}
194194

195-
/// Expand ~ to home directory in path
196-
private static func expandPath(_ path: String?) -> String? {
197-
guard let path = path else { return nil }
198-
199-
if path.hasPrefix("~") {
200-
return FileManager.default.homeDirectoryForCurrentUser
201-
.appendingPathComponent(String(path.dropFirst(2))).path(percentEncoded: false)
202-
}
203-
return path
204-
}
205195
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//
2+
// SSHPathUtilities.swift
3+
// TablePro
4+
//
5+
6+
import Foundation
7+
8+
enum SSHPathUtilities {
9+
/// Expand ~ to the current user's home directory in a path.
10+
/// Unlike shell commands, `setenv()` and file APIs do not expand `~` automatically.
11+
static func expandTilde(_ path: String) -> String {
12+
if path.hasPrefix("~/") {
13+
return FileManager.default.homeDirectoryForCurrentUser
14+
.appendingPathComponent(String(path.dropFirst(2)))
15+
.path(percentEncoded: false)
16+
}
17+
if path == "~" {
18+
return FileManager.default.homeDirectoryForCurrentUser
19+
.path(percentEncoded: false)
20+
}
21+
return path
22+
}
23+
}

TableProTests/Core/SSH/SSHConfigurationTests.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,34 @@ struct SSHConfigurationTests {
150150
#expect(config.isValid == false)
151151
}
152152

153+
// MARK: - SSHPathUtilities
154+
155+
@Test("Tilde expansion resolves ~/path to home directory")
156+
func testTildeExpansionWithSubpath() {
157+
let home = FileManager.default.homeDirectoryForCurrentUser.path(percentEncoded: false)
158+
let result = SSHPathUtilities.expandTilde("~/Library/agent.sock")
159+
#expect(result == "\(home)/Library/agent.sock")
160+
}
161+
162+
@Test("Tilde expansion resolves bare ~ to home directory")
163+
func testTildeExpansionBare() {
164+
let home = FileManager.default.homeDirectoryForCurrentUser.path(percentEncoded: false)
165+
let result = SSHPathUtilities.expandTilde("~")
166+
#expect(result == home)
167+
}
168+
169+
@Test("Tilde expansion leaves absolute paths unchanged")
170+
func testTildeExpansionAbsolutePath() {
171+
let result = SSHPathUtilities.expandTilde("/absolute/path")
172+
#expect(result == "/absolute/path")
173+
}
174+
175+
@Test("Tilde expansion leaves empty string unchanged")
176+
func testTildeExpansionEmptyString() {
177+
let result = SSHPathUtilities.expandTilde("")
178+
#expect(result == "")
179+
}
180+
153181
@Test("Backward-compatible decoding without jumpHosts key")
154182
func testBackwardCompatibleDecoding() throws {
155183
let jsonString = """

0 commit comments

Comments
 (0)