From 6c7c81e52ef24d39a51dec858f1d7328f0af6318 Mon Sep 17 00:00:00 2001 From: piyush295 Date: Mon, 28 Sep 2026 06:36:26 +0530 Subject: [PATCH] Reject SSH key ciphers with null required members MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An SSH key cipher (type 5) whose sshKey object has null in privateKey, publicKey or keyFingerprint was accepted (the API returned a cipher id), but the key material did not survive the round trip — reading the item back showed the sshKey object gone. Bitwarden's server rejects the same request with a validation error, so an API/CLI client that is correct against cloud got a silent-success + data loss against Vaultwarden. Add a presence check (not key-material validation, matching upstream) that requires the three SSH key members to be present and non-null, returning a validation error otherwise. First-party clients always populate all three, so only hand-built API/CLI payloads were affected. Closes #7514 --- src/api/core/ciphers.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/api/core/ciphers.rs b/src/api/core/ciphers.rs index a8c6aea0..c8c067df 100644 --- a/src/api/core/ciphers.rs +++ b/src/api/core/ciphers.rs @@ -562,6 +562,21 @@ pub async fn update_cipher_from_data( err!("Data missing") }; + // For SSH keys, ensure the required members are present and non-null. + // Bitwarden's server rejects an sshKey object with a `null` + // privateKey/publicKey/keyFingerprint, but without this check the null + // members are accepted and then silently dropped on read (data loss). + // A presence check (not key-material validation) matches the upstream + // behaviour. See https://github.com/dani-garcia/vaultwarden/issues/7514 + if data.r#type == 5 { + for field in ["privateKey", "publicKey", "keyFingerprint"] { + match type_data.get(field) { + Some(value) if value.is_string() => {} + _ => err!(format!("SSH key is missing the required '{field}' field")), + } + } + } + cipher.key = data.key; cipher.name = data.name; cipher.notes = data.notes;