From a7d9c65dfbea73293d837304fec0439356efb191 Mon Sep 17 00:00:00 2001 From: Christophe Vila Date: Fri, 22 May 2026 14:22:21 +0200 Subject: [PATCH] fix: add reserved keys filter and TOTP reverse mapping in KDBXMapper --- .../Sources/MyPassCore/KDBX/KDBXMapper.swift | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/MyPassCore/Sources/MyPassCore/KDBX/KDBXMapper.swift b/MyPassCore/Sources/MyPassCore/KDBX/KDBXMapper.swift index bb26d22..4b8c333 100644 --- a/MyPassCore/Sources/MyPassCore/KDBX/KDBXMapper.swift +++ b/MyPassCore/Sources/MyPassCore/KDBX/KDBXMapper.swift @@ -28,13 +28,15 @@ enum KDBXMapper { static func entry(from e: KPKEntry) -> Entry { // e.customAttributes: only non-default attributes - let customFields: [CustomField] = e.customAttributes.map { attr in - CustomField( - id: UUID(), - key: attr.key, - value: ProtectedString(attr.value, isProtected: attr.protect) - ) - } + let customFields: [CustomField] = e.customAttributes + .filter { !reservedKeys.contains($0.key ?? "") } + .map { attr in + CustomField( + id: UUID(), + key: attr.key, + value: ProtectedString(attr.value, isProtected: attr.protect) + ) + } // Parse TOTP from the otp URI attribute (KeePassOTP format) let totpURI = e.customAttributes.first { $0.key == "otp" }?.value @@ -105,6 +107,11 @@ enum KDBXMapper { ) kpk.addCustomAttribute(attr) } + if let totp = e.totp { + let uri = totpURI(from: totp) + let attr = KPKAttribute(key: "otp", value: uri, isProtected: false) + kpk.addCustomAttribute(attr) + } for att in e.attachments { let bin = KPKBinary(name: att.name, data: att.data) kpk.addBinary(bin) @@ -118,4 +125,20 @@ enum KDBXMapper { guard let u = nsUUID else { return UUID() } return UUID(uuidString: u.uuidString) ?? UUID() } + + private static func totpURI(from config: TOTPConfig) -> String { + var components = URLComponents() + components.scheme = "otpauth" + components.host = "totp" + components.path = "/MyPass" + components.queryItems = [ + URLQueryItem(name: "secret", value: config.secret), + URLQueryItem(name: "period", value: String(config.period)), + URLQueryItem(name: "digits", value: String(config.digits)), + URLQueryItem(name: "algorithm", value: config.algorithm.rawValue), + ] + return components.url?.absoluteString ?? "" + } + + private static let reservedKeys: Set = ["Title", "UserName", "Password", "URL", "Notes"] }