Browse Source

fix(api): reject SSH Key ciphers with missing or empty key members

A type-5 cipher whose sshKey object carries null (or non-string) values
in privateKey, publicKey or keyFingerprint was accepted and stored, but
clients drop the malformed payload on read: the save looks successful
and the key material silently disappears. Bitwarden's own server rejects
the same request with a validation error, so a client that is correct
against cloud gets silent data loss against Vaultwarden.

Require all three members to be present as non-empty strings before
persisting a type-5 cipher, matching the cloud behavior described in
#7514. This is a presence check only — key material itself is not
parsed or validated.

Fixes #7514

Signed-off-by: Yunare Maia <yunare@gmail.com>
pull/7636/head
Yunare Maia 1 month ago
parent
commit
8e965902c6
  1. 24
      src/api/core/ciphers.rs

24
src/api/core/ciphers.rs

@ -513,14 +513,28 @@ pub async fn update_cipher_from_data(
_ => err!("Invalid type"), _ => err!("Invalid type"),
}; };
let type_data = if let Some(mut data) = type_data_opt { let type_data = if let Some(mut type_value) = type_data_opt {
// Remove the 'Response' key from the base object. // Remove the 'Response' key from the base object.
data.as_object_mut().unwrap().remove("response"); type_value.as_object_mut().unwrap().remove("response");
// Remove the 'Response' key from every Uri. // Remove the 'Response' key from every Uri.
if data["uris"].is_array() { if type_value["uris"].is_array() {
data["uris"] = clean_cipher_data(data["uris"].clone()); type_value["uris"] = clean_cipher_data(type_value["uris"].clone());
} }
data // An SSH Key cipher (type 5) requires all three key members to be
// non-empty strings. Bitwarden's server rejects the request otherwise;
// accepting it here would store a cipher whose sshKey payload is
// dropped by clients on read, which looks like a successful save but
// silently loses the key material.
if data.r#type == 5 {
let obj = type_value.as_object().unwrap();
for member in ["privateKey", "publicKey", "keyFingerprint"] {
let valid = matches!(obj.get(member), Some(Value::String(s)) if !s.is_empty());
if !valid {
err!(format!("SshKey.{member} is required and must be a non-empty string"));
}
}
}
type_value
} else { } else {
err!("Data missing") err!("Data missing")
}; };

Loading…
Cancel
Save