From 98646242e0fa6c4bacbb4edcc99e273e0120064b Mon Sep 17 00:00:00 2001 From: Lysann Tranvouez Date: Mon, 9 Mar 2026 12:11:37 +0100 Subject: [PATCH] fix deleting directory this used to corrupt the local state (password entities remained in DB but files/dirs were removed from git and disk) --- pass/en.lproj/Localizable.strings | 1 + passKit/Helpers/AppError.swift | 1 + passKit/Models/PasswordStore.swift | 4 ++++ passKitTests/Models/PasswordStoreTest.swift | 18 ++++++++++++++++++ 4 files changed, 24 insertions(+) diff --git a/pass/en.lproj/Localizable.strings b/pass/en.lproj/Localizable.strings index 5f26010..10d8b30 100644 --- a/pass/en.lproj/Localizable.strings +++ b/pass/en.lproj/Localizable.strings @@ -73,6 +73,7 @@ "KeyImportError." = "Cannot import the key."; "FileNotFoundError." = "File '%@' cannot be read."; "PasswordDuplicatedError." = "Cannot add the password; password is duplicated."; +"CannotDeleteDirectoryError." = "Cannot delete directories; delete passwords instead."; "GitResetError." = "Cannot identify the latest synced commit."; "GitCreateSignatureError." = "Cannot create a valid author/committer signature."; "GitPushNotSuccessfulError." = "Pushing local changes was not successful. Make sure there are no uncommitted changes on the remote repository."; diff --git a/passKit/Helpers/AppError.swift b/passKit/Helpers/AppError.swift index 8e0aa21..8bcc84b 100644 --- a/passKit/Helpers/AppError.swift +++ b/passKit/Helpers/AppError.swift @@ -15,6 +15,7 @@ public enum AppError: Error, Equatable { case keyImport case readingFile(fileName: String) case passwordDuplicated + case cannotDeleteDirectory case gitReset case gitCommit case gitCreateSignature diff --git a/passKit/Models/PasswordStore.swift b/passKit/Models/PasswordStore.swift index 22c90d9..78c92c4 100644 --- a/passKit/Models/PasswordStore.swift +++ b/passKit/Models/PasswordStore.swift @@ -273,6 +273,10 @@ public class PasswordStore { } public func delete(passwordEntity: PasswordEntity) throws { + if passwordEntity.isDir { + throw AppError.cannotDeleteDirectory + } + let deletedFileURL = passwordEntity.fileURL(in: storeURL) let deletedFilePath = passwordEntity.path try gitRm(path: passwordEntity.path) diff --git a/passKitTests/Models/PasswordStoreTest.swift b/passKitTests/Models/PasswordStoreTest.swift index 883471c..8b4d670 100644 --- a/passKitTests/Models/PasswordStoreTest.swift +++ b/passKitTests/Models/PasswordStoreTest.swift @@ -143,9 +143,27 @@ final class PasswordStoreTest: XCTestCase { try passwordStore.delete(passwordEntity: entity!) XCTAssertNil(passwordStore.fetchPasswordEntity(with: "personal/github.com.gpg")) + XCTAssertNil(passwordStore.fetchPasswordEntity(with: "personal")) + XCTAssertFalse(FileManager.default.fileExists(atPath: localRepoURL.appendingPathComponent("personal").path)) waitForExpectations(timeout: 1, handler: nil) } + func testDeleteDirectoryFails() throws { + try cloneRepository(.withGPGID) + + expectation(forNotification: .passwordStoreUpdated, object: nil).isInverted = true + + let entity = passwordStore.fetchPasswordEntity(with: "personal") + XCTAssertThrowsError(try passwordStore.delete(passwordEntity: entity!)) { error in + XCTAssertTrue(error is AppError, "Unexpected error type: \(type(of: error))") + XCTAssertEqual(error as? AppError, .cannotDeleteDirectory) + } + + XCTAssertNotNil(passwordStore.fetchPasswordEntity(with: "personal/github.com.gpg")) + XCTAssertTrue(FileManager.default.fileExists(atPath: localRepoURL.appendingPathComponent("personal/github.com.gpg").path)) + waitForExpectations(timeout: 0.1, handler: nil) + } + func testEditPasswordValue() throws { try cloneRepository(.withGPGID) try importSinglePGPKey()