Skip to content

Commit af26036

Browse files
committed
fix: address code review — credential injection, Pro gate, decrypt race, passphrase handling
1 parent 0b1e6ed commit af26036

3 files changed

Lines changed: 27 additions & 15 deletions

File tree

TablePro/Core/Services/Export/ConnectionExportService.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,8 @@ enum ConnectionExportService {
350350
let decryptedData: Data
351351
do {
352352
decryptedData = try ConnectionExportCrypto.decrypt(data: data, passphrase: passphrase)
353-
} catch let error as ConnectionExportCryptoError {
354-
throw ConnectionExportError.decryptionFailed(error.localizedDescription ?? "Unknown error")
353+
} catch {
354+
throw ConnectionExportError.decryptionFailed(error.localizedDescription)
355355
}
356356
return try decodeData(decryptedData)
357357
}
@@ -528,7 +528,7 @@ enum ConnectionExportService {
528528

529529
for item in preview.items {
530530
let resolution = resolutions[item.id] ?? .skip
531-
let envelopeIndex = itemIndexMap[item.id] ?? 0
531+
guard let envelopeIndex = itemIndexMap[item.id] else { continue }
532532

533533
switch resolution {
534534
case .skip:

TablePro/Views/Connection/ConnectionExportOptionsSheet.swift

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct ConnectionExportOptionsSheet: View {
2222

2323
private var canExport: Bool {
2424
if includeCredentials {
25-
return passphrase.count >= 8 && passphrase == confirmPassphrase
25+
return (passphrase as NSString).length >= 8 && passphrase == confirmPassphrase
2626
}
2727
return true
2828
}
@@ -86,28 +86,34 @@ struct ConnectionExportOptionsSheet: View {
8686
}
8787

8888
private func performExport() {
89+
let shouldEncrypt = includeCredentials && isProAvailable
90+
let capturedPassphrase = passphrase
91+
let capturedConnections = connections
92+
93+
// Zero passphrase state before dismissing
94+
passphrase = ""
95+
confirmPassphrase = ""
8996
dismiss()
9097

91-
// Delay slightly to let sheet dismiss before showing NSSavePanel
9298
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
9399
let panel = NSSavePanel()
94100
panel.allowedContentTypes = [.tableproConnectionShare]
95-
let defaultName = connections.count == 1
96-
? "\(connections[0].name).tablepro"
101+
let defaultName = capturedConnections.count == 1
102+
? "\(capturedConnections[0].name).tablepro"
97103
: "Connections.tablepro"
98104
panel.nameFieldStringValue = defaultName
99105
panel.canCreateDirectories = true
100106
guard panel.runModal() == .OK, let url = panel.url else { return }
101107

102108
do {
103-
if includeCredentials {
109+
if shouldEncrypt {
104110
try ConnectionExportService.exportConnectionsEncrypted(
105-
connections,
111+
capturedConnections,
106112
to: url,
107-
passphrase: passphrase
113+
passphrase: capturedPassphrase
108114
)
109115
} else {
110-
try ConnectionExportService.exportConnections(connections, to: url)
116+
try ConnectionExportService.exportConnections(capturedConnections, to: url)
111117
}
112118
} catch {
113119
AlertHelper.showErrorSheet(

TablePro/Views/Connection/ConnectionImportSheet.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ struct ConnectionImportSheet: View {
2020
@State private var encryptedData: Data?
2121
@State private var passphrase = ""
2222
@State private var passphraseError: String?
23+
@State private var isDecrypting = false
24+
@State private var wasEncryptedImport = false
2325

2426
var body: some View {
2527
VStack(spacing: 0) {
@@ -246,7 +248,7 @@ struct ConnectionImportSheet: View {
246248
Button(String(localized: "Decrypt")) { decryptFile() }
247249
.buttonStyle(.borderedProminent)
248250
.keyboardShortcut(.defaultAction)
249-
.disabled(passphrase.isEmpty)
251+
.disabled(passphrase.isEmpty || isDecrypting)
250252
}
251253
.padding(12)
252254
}
@@ -311,8 +313,9 @@ struct ConnectionImportSheet: View {
311313
}
312314

313315
private func decryptFile() {
314-
guard let data = encryptedData else { return }
316+
guard let data = encryptedData, !isDecrypting else { return }
315317
let currentPassphrase = passphrase
318+
isDecrypting = true
316319

317320
Task.detached(priority: .userInitiated) {
318321
do {
@@ -321,13 +324,16 @@ struct ConnectionImportSheet: View {
321324
await MainActor.run {
322325
passphraseError = nil
323326
encryptedData = nil
327+
wasEncryptedImport = true
324328
preview = result
325329
selectReadyItems(result)
330+
isDecrypting = false
326331
}
327332
} catch {
328333
await MainActor.run {
329334
passphraseError = error.localizedDescription
330335
passphrase = ""
336+
isDecrypting = false
331337
}
332338
}
333339
}
@@ -361,8 +367,8 @@ struct ConnectionImportSheet: View {
361367

362368
let result = ConnectionExportService.performImport(preview, resolutions: resolutions)
363369

364-
// Restore credentials if this was an encrypted import
365-
if let envelope = preview.envelope.credentials, !envelope.isEmpty {
370+
// Only restore credentials from verified encrypted imports (not plaintext files)
371+
if wasEncryptedImport, preview.envelope.credentials != nil {
366372
ConnectionExportService.restoreCredentials(
367373
from: preview.envelope,
368374
connectionIdMap: result.connectionIdMap

0 commit comments

Comments
 (0)