diff --git a/Cargo.lock b/Cargo.lock index 5c9d0fb58..71f98409a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,6 +444,7 @@ dependencies = [ "bitwarden-crypto", "chrono", "csv", + "itertools 0.12.1", "serde", "serde_json", "thiserror", diff --git a/crates/bitwarden-crypto/src/sensitive/sensitive.rs b/crates/bitwarden-crypto/src/sensitive/sensitive.rs index 86dd93ef5..7a3c202a5 100644 --- a/crates/bitwarden-crypto/src/sensitive/sensitive.rs +++ b/crates/bitwarden-crypto/src/sensitive/sensitive.rs @@ -101,6 +101,18 @@ impl JsonSchema for Sensitive { } } +impl Sensitive { + // We use a lot of `&str` in our tests, so we expose this helper + // to make it easier. + // IMPORTANT: This should not be used outside of test code + // Note that we can't just mark it with #[cfg(test)] because that only applies + // when testing this crate, not when testing other crates that depend on it. + // By at least limiting it to &'static str we should be able to avoid accidental usages + pub fn test(value: &'static str) -> Self { + Self::new(Box::new(value.to_string())) + } +} + #[cfg(test)] mod tests { use schemars::schema_for; @@ -109,7 +121,7 @@ mod tests { #[test] fn test_debug() { - let string = Sensitive::new(Box::new("test".to_string())); + let string = Sensitive::test("test"); assert_eq!( format!("{:?}", string), "Sensitive { type: \"alloc::string::String\", value: \"********\" }" diff --git a/crates/bitwarden-exporters/Cargo.toml b/crates/bitwarden-exporters/Cargo.toml index 40316437f..4f8364179 100644 --- a/crates/bitwarden-exporters/Cargo.toml +++ b/crates/bitwarden-exporters/Cargo.toml @@ -22,6 +22,7 @@ chrono = { version = ">=0.4.26, <0.5", features = [ "std", ], default-features = false } csv = "1.3.0" +itertools = "0.12.1" serde = { version = ">=1.0, <2.0", features = ["derive"] } serde_json = ">=1.0.96, <2.0" thiserror = ">=1.0.40, <2.0" diff --git a/crates/bitwarden-exporters/src/csv.rs b/crates/bitwarden-exporters/src/csv.rs index 644eeb030..ac6995588 100644 --- a/crates/bitwarden-exporters/src/csv.rs +++ b/crates/bitwarden-exporters/src/csv.rs @@ -1,9 +1,9 @@ use std::collections::HashMap; +use bitwarden_crypto::{DecryptedString, Sensitive}; use csv::Writer; use serde::Serializer; use thiserror::Error; -use uuid::Uuid; use crate::{Cipher, CipherType, Field, Folder}; @@ -14,7 +14,7 @@ pub enum CsvError { } pub(crate) fn export_csv(folders: Vec, ciphers: Vec) -> Result { - let folders: HashMap = folders.into_iter().map(|f| (f.id, f.name)).collect(); + let folders: HashMap<_, _> = folders.into_iter().map(|f| (f.id, f.name)).collect(); let rows = ciphers .into_iter() @@ -59,27 +59,30 @@ pub(crate) fn export_csv(folders: Vec, ciphers: Vec) -> Result, + folder: Option, #[serde(serialize_with = "bool_serialize")] favorite: bool, r#type: String, - name: String, - notes: Option, + name: DecryptedString, + notes: Option, #[serde(serialize_with = "fields_serialize")] fields: Vec, reprompt: u8, #[serde(serialize_with = "vec_serialize")] - login_uri: Vec, - login_username: Option, - login_password: Option, - login_totp: Option, + login_uri: Vec, + login_username: Option, + login_password: Option, + login_totp: Option, } -fn vec_serialize(x: &[String], s: S) -> Result +fn vec_serialize(x: &[DecryptedString], s: S) -> Result where S: Serializer, { - s.serialize_str(x.join(",").as_str()) + let iter = itertools::Itertools::intersperse(x.iter().map(|s| s.expose().as_str()), ","); + let result: Sensitive = Sensitive::new(Box::new(iter.collect())); + + s.serialize_str(result.expose()) } fn bool_serialize(x: &bool, s: S) -> Result @@ -98,8 +101,14 @@ where .map(|f| { format!( "{}: {}", - f.name.to_owned().unwrap_or_default(), - f.value.to_owned().unwrap_or_default() + f.name + .as_ref() + .map(|n| n.expose().to_owned()) + .unwrap_or_default(), + f.value + .as_ref() + .map(|n| n.expose().to_owned()) + .unwrap_or_default(), ) }) .collect::>() @@ -118,24 +127,24 @@ mod tests { let folders = vec![ Folder { id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), - name: "Test Folder A".to_string(), + name: DecryptedString::test("Test Folder A"), }, Folder { id: "583e7665-0126-4d37-9139-b0d20184dd86".parse().unwrap(), - name: "Test Folder B".to_string(), + name: DecryptedString::test("Test Folder B"), }, ]; let ciphers = vec![ Cipher { id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), folder_id: None, - name: "test@bitwarden.com".to_string(), + name: DecryptedString::test("test@bitwarden.com"), notes: None, r#type: CipherType::Login(Box::new(Login { - username: Some("test@bitwarden.com".to_string()), - password: Some("Abc123".to_string()), + username: Some(DecryptedString::test("test@bitwarden.com")), + password: Some(DecryptedString::test("Abc123")), login_uris: vec![LoginUri { - uri: Some("https://google.com".to_string()), + uri: Some(DecryptedString::test("https://google.com")), r#match: None, }], totp: None, @@ -150,29 +159,29 @@ mod tests { Cipher { id: "7dd81bd0-cc72-4f42-96e7-b0fc014e71a3".parse().unwrap(), folder_id: Some("583e7665-0126-4d37-9139-b0d20184dd86".parse().unwrap()), - name: "Steam Account".to_string(), + name: DecryptedString::test("Steam Account"), notes: None, r#type: CipherType::Login(Box::new(Login { - username: Some("steam".to_string()), - password: Some("3Pvb8u7EfbV*nJ".to_string()), + username: Some(DecryptedString::test("steam")), + password: Some(DecryptedString::test("3Pvb8u7EfbV*nJ")), login_uris: vec![LoginUri { - uri: Some("https://steampowered.com".to_string()), + uri: Some(DecryptedString::test("https://steampowered.com")), r#match: None, }], - totp: Some("steam://ABCD123".to_string()), + totp: Some(DecryptedString::test("steam://ABCD123")), })), favorite: true, reprompt: 0, fields: vec![ Field { - name: Some("Test".to_string()), - value: Some("v".to_string()), + name: Some(DecryptedString::test("Test")), + value: Some(DecryptedString::test("v")), r#type: 0, linked_id: None, }, Field { - name: Some("Hidden".to_string()), - value: Some("asdfer".to_string()), + name: Some(DecryptedString::test("Hidden")), + value: Some(DecryptedString::test("asdfer")), r#type: 1, linked_id: None, }, @@ -200,7 +209,7 @@ mod tests { let ciphers = vec![Cipher { id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), folder_id: None, - name: "My Card".to_string(), + name: DecryptedString::test("My Card"), notes: None, r#type: CipherType::Card(Box::new(Card { cardholder_name: None, @@ -229,7 +238,7 @@ mod tests { let ciphers = vec![Cipher { id: "d55d65d7-c161-40a4-94ca-b0d20184d91a".parse().unwrap(), folder_id: None, - name: "My Identity".to_string(), + name: DecryptedString::test("My Identity"), notes: None, r#type: CipherType::Identity(Box::new(Identity { title: None, diff --git a/crates/bitwarden-exporters/src/encrypted_json.rs b/crates/bitwarden-exporters/src/encrypted_json.rs index 1bbfd2660..c9a529091 100644 --- a/crates/bitwarden-exporters/src/encrypted_json.rs +++ b/crates/bitwarden-exporters/src/encrypted_json.rs @@ -83,6 +83,8 @@ pub(crate) struct EncryptedJsonExport { mod tests { use std::num::NonZeroU32; + use bitwarden_crypto::DecryptedString; + use super::*; use crate::{ Card, Cipher, CipherType, Field, Identity, Login, LoginUri, SecureNote, SecureNoteType, @@ -93,24 +95,24 @@ mod tests { let _export = export_encrypted_json( vec![Folder { id: "942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap(), - name: "Important".to_string(), + name: DecryptedString::test("Important"), }], vec![ Cipher { id: "25c8c414-b446-48e9-a1bd-b10700bbd740".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "Bitwarden".to_string(), - notes: Some("My note".to_string()), + name: DecryptedString::test("Bitwarden"), + notes: Some(DecryptedString::test("My note")), r#type: CipherType::Login(Box::new(Login { - username: Some("test@bitwarden.com".to_string()), - password: Some("asdfasdfasdf".to_string()), + username: Some(DecryptedString::test("test@bitwarden.com")), + password: Some(DecryptedString::test("asdfasdfasdf")), login_uris: vec![LoginUri { - uri: Some("https://vault.bitwarden.com".to_string()), + uri: Some(DecryptedString::test("https://vault.bitwarden.com")), r#match: None, }], - totp: Some("ABC".to_string()), + totp: Some(DecryptedString::test("ABC")), })), favorite: true, @@ -118,31 +120,31 @@ mod tests { fields: vec![ Field { - name: Some("Text".to_string()), - value: Some("A".to_string()), + name: Some(DecryptedString::test("Text")), + value: Some(DecryptedString::test("A")), r#type: 0, linked_id: None, }, Field { - name: Some("Hidden".to_string()), - value: Some("B".to_string()), + name: Some(DecryptedString::test("Hidden")), + value: Some(DecryptedString::test("B")), r#type: 1, linked_id: None, }, Field { - name: Some("Boolean (true)".to_string()), - value: Some("true".to_string()), + name: Some(DecryptedString::test("Boolean (true)")), + value: Some(DecryptedString::test("true")), r#type: 2, linked_id: None, }, Field { - name: Some("Boolean (false)".to_string()), - value: Some("false".to_string()), + name: Some(DecryptedString::test("Boolean (false)")), + value: Some(DecryptedString::test("false")), r#type: 2, linked_id: None, }, Field { - name: Some("Linked".to_string()), + name: Some(DecryptedString::test("Linked")), value: None, r#type: 3, linked_id: Some(101), @@ -157,8 +159,8 @@ mod tests { id: "23f0f877-42b1-4820-a850-b10700bc41eb".parse().unwrap(), folder_id: None, - name: "My secure note".to_string(), - notes: Some("Very secure!".to_string()), + name: DecryptedString::test("My secure note"), + notes: Some(DecryptedString::test("Very secure!")), r#type: CipherType::SecureNote(Box::new(SecureNote { r#type: SecureNoteType::Generic, @@ -177,16 +179,16 @@ mod tests { id: "3ed8de45-48ee-4e26-a2dc-b10701276c53".parse().unwrap(), folder_id: None, - name: "My card".to_string(), + name: DecryptedString::test("My card"), notes: None, r#type: CipherType::Card(Box::new(Card { - cardholder_name: Some("John Doe".to_string()), - exp_month: Some("1".to_string()), - exp_year: Some("2032".to_string()), - code: Some("123".to_string()), - brand: Some("Visa".to_string()), - number: Some("4111111111111111".to_string()), + cardholder_name: Some(DecryptedString::test("John Doe")), + exp_month: Some(DecryptedString::test("1")), + exp_year: Some(DecryptedString::test("2032")), + code: Some(DecryptedString::test("123")), + brand: Some(DecryptedString::test("Visa")), + number: Some(DecryptedString::test("4111111111111111")), })), favorite: false, @@ -202,14 +204,14 @@ mod tests { id: "41cc3bc1-c3d9-4637-876c-b10701273712".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "My identity".to_string(), + name: DecryptedString::test("My identity"), notes: None, r#type: CipherType::Identity(Box::new(Identity { - title: Some("Mr".to_string()), - first_name: Some("John".to_string()), + title: Some(DecryptedString::test("Mr")), + first_name: Some(DecryptedString::test("John")), middle_name: None, - last_name: Some("Doe".to_string()), + last_name: Some(DecryptedString::test("Doe")), address1: None, address2: None, address3: None, @@ -217,11 +219,11 @@ mod tests { state: None, postal_code: None, country: None, - company: Some("Bitwarden".to_string()), + company: Some(DecryptedString::test("Bitwarden")), email: None, phone: None, ssn: None, - username: Some("JDoe".to_string()), + username: Some(DecryptedString::test("JDoe")), passport_number: None, license_number: None, })), diff --git a/crates/bitwarden-exporters/src/json.rs b/crates/bitwarden-exporters/src/json.rs index 3f6c72c1f..f2c889f9f 100644 --- a/crates/bitwarden-exporters/src/json.rs +++ b/crates/bitwarden-exporters/src/json.rs @@ -1,3 +1,4 @@ +use bitwarden_crypto::DecryptedString; use chrono::{DateTime, Utc}; use thiserror::Error; use uuid::Uuid; @@ -36,7 +37,7 @@ struct JsonExport { #[serde(rename_all = "camelCase")] struct JsonFolder { id: Uuid, - name: String, + name: DecryptedString, } impl From for JsonFolder { @@ -57,8 +58,8 @@ struct JsonCipher { organization_id: Option, collection_ids: Option>, - name: String, - notes: Option, + name: DecryptedString, + notes: Option, r#type: u8, #[serde(skip_serializing_if = "Option::is_none")] @@ -75,7 +76,7 @@ struct JsonCipher { #[serde(skip_serializing_if = "Vec::is_empty")] fields: Vec, - password_history: Option>, + password_history: Option>, revision_date: DateTime, creation_date: DateTime, @@ -85,11 +86,11 @@ struct JsonCipher { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] struct JsonLogin { - username: Option, - password: Option, + username: Option, + password: Option, uris: Vec, - totp: Option, - fido2_credentials: Vec, + totp: Option, + fido2_credentials: Vec, } impl From for JsonLogin { @@ -107,7 +108,7 @@ impl From for JsonLogin { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] struct JsonLoginUri { - uri: Option, + uri: Option, r#match: Option, } @@ -137,12 +138,12 @@ impl From for JsonSecureNote { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] struct JsonCard { - cardholder_name: Option, - exp_month: Option, - exp_year: Option, - code: Option, - brand: Option, - number: Option, + cardholder_name: Option, + exp_month: Option, + exp_year: Option, + code: Option, + brand: Option, + number: Option, } impl From for JsonCard { @@ -161,24 +162,24 @@ impl From for JsonCard { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] struct JsonIdentity { - title: Option, - first_name: Option, - middle_name: Option, - last_name: Option, - address1: Option, - address2: Option, - address3: Option, - city: Option, - state: Option, - postal_code: Option, - country: Option, - company: Option, - email: Option, - phone: Option, - ssn: Option, - username: Option, - passport_number: Option, - license_number: Option, + title: Option, + first_name: Option, + middle_name: Option, + last_name: Option, + address1: Option, + address2: Option, + address3: Option, + city: Option, + state: Option, + postal_code: Option, + country: Option, + company: Option, + email: Option, + phone: Option, + ssn: Option, + username: Option, + passport_number: Option, + license_number: Option, } impl From for JsonIdentity { @@ -209,8 +210,8 @@ impl From for JsonIdentity { #[derive(serde::Serialize)] #[serde(rename_all = "camelCase")] struct JsonField { - name: Option, - value: Option, + name: Option, + value: Option, r#type: u8, linked_id: Option, } @@ -278,17 +279,17 @@ mod tests { id: "25c8c414-b446-48e9-a1bd-b10700bbd740".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "Bitwarden".to_string(), - notes: Some("My note".to_string()), + name: DecryptedString::test("Bitwarden"), + notes: Some(DecryptedString::test("My note")), r#type: CipherType::Login(Box::new(Login { - username: Some("test@bitwarden.com".to_string()), - password: Some("asdfasdfasdf".to_string()), + username: Some(DecryptedString::test("test@bitwarden.com")), + password: Some(DecryptedString::test("asdfasdfasdf")), login_uris: vec![LoginUri { - uri: Some("https://vault.bitwarden.com".to_string()), + uri: Some(DecryptedString::test("https://vault.bitwarden.com")), r#match: None, }], - totp: Some("ABC".to_string()), + totp: Some(DecryptedString::test("ABC")), })), favorite: true, @@ -296,31 +297,31 @@ mod tests { fields: vec![ Field { - name: Some("Text".to_string()), - value: Some("A".to_string()), + name: Some(DecryptedString::test("Text")), + value: Some(DecryptedString::test("A")), r#type: 0, linked_id: None, }, Field { - name: Some("Hidden".to_string()), - value: Some("B".to_string()), + name: Some(DecryptedString::test("Hidden")), + value: Some(DecryptedString::test("B")), r#type: 1, linked_id: None, }, Field { - name: Some("Boolean (true)".to_string()), - value: Some("true".to_string()), + name: Some(DecryptedString::test("Boolean (true)")), + value: Some(DecryptedString::test("true")), r#type: 2, linked_id: None, }, Field { - name: Some("Boolean (false)".to_string()), - value: Some("false".to_string()), + name: Some(DecryptedString::test("Boolean (false)")), + value: Some(DecryptedString::test("false")), r#type: 2, linked_id: None, }, Field { - name: Some("Linked".to_string()), + name: Some(DecryptedString::test("Linked")), value: None, r#type: 3, linked_id: Some(101), @@ -406,8 +407,8 @@ mod tests { id: "23f0f877-42b1-4820-a850-b10700bc41eb".parse().unwrap(), folder_id: None, - name: "My secure note".to_string(), - notes: Some("Very secure!".to_string()), + name: DecryptedString::test("My secure note"), + notes: Some(DecryptedString::test("Very secure!")), r#type: CipherType::SecureNote(Box::new(SecureNote { r#type: SecureNoteType::Generic, @@ -456,16 +457,16 @@ mod tests { id: "3ed8de45-48ee-4e26-a2dc-b10701276c53".parse().unwrap(), folder_id: None, - name: "My card".to_string(), + name: DecryptedString::test("My card"), notes: None, r#type: CipherType::Card(Box::new(Card { - cardholder_name: Some("John Doe".to_string()), - exp_month: Some("1".to_string()), - exp_year: Some("2032".to_string()), - code: Some("123".to_string()), - brand: Some("Visa".to_string()), - number: Some("4111111111111111".to_string()), + cardholder_name: Some(DecryptedString::test("John Doe")), + exp_month: Some(DecryptedString::test("1")), + exp_year: Some(DecryptedString::test("2032")), + code: Some(DecryptedString::test("123")), + brand: Some(DecryptedString::test("Visa")), + number: Some(DecryptedString::test("4111111111111111")), })), favorite: false, @@ -516,14 +517,14 @@ mod tests { id: "41cc3bc1-c3d9-4637-876c-b10701273712".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "My identity".to_string(), + name: DecryptedString::test("My identity"), notes: None, r#type: CipherType::Identity(Box::new(Identity { - title: Some("Mr".to_string()), - first_name: Some("John".to_string()), + title: Some(DecryptedString::test("Mr")), + first_name: Some(DecryptedString::test("John")), middle_name: None, - last_name: Some("Doe".to_string()), + last_name: Some(DecryptedString::test("Doe")), address1: None, address2: None, address3: None, @@ -531,11 +532,11 @@ mod tests { state: None, postal_code: None, country: None, - company: Some("Bitwarden".to_string()), + company: Some(DecryptedString::test("Bitwarden")), email: None, phone: None, ssn: None, - username: Some("JDoe".to_string()), + username: Some(DecryptedString::test("JDoe")), passport_number: None, license_number: None, })), @@ -608,24 +609,24 @@ mod tests { let export = export_json( vec![Folder { id: "942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap(), - name: "Important".to_string(), + name: DecryptedString::test("Important"), }], vec![ Cipher { id: "25c8c414-b446-48e9-a1bd-b10700bbd740".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "Bitwarden".to_string(), - notes: Some("My note".to_string()), + name: DecryptedString::test("Bitwarden"), + notes: Some(DecryptedString::test("My note")), r#type: CipherType::Login(Box::new(Login { - username: Some("test@bitwarden.com".to_string()), - password: Some("asdfasdfasdf".to_string()), + username: Some(DecryptedString::test("test@bitwarden.com")), + password: Some(DecryptedString::test("asdfasdfasdf")), login_uris: vec![LoginUri { - uri: Some("https://vault.bitwarden.com".to_string()), + uri: Some(DecryptedString::test("https://vault.bitwarden.com")), r#match: None, }], - totp: Some("ABC".to_string()), + totp: Some(DecryptedString::test("ABC")), })), favorite: true, @@ -633,31 +634,31 @@ mod tests { fields: vec![ Field { - name: Some("Text".to_string()), - value: Some("A".to_string()), + name: Some(DecryptedString::test("Text")), + value: Some(DecryptedString::test("A")), r#type: 0, linked_id: None, }, Field { - name: Some("Hidden".to_string()), - value: Some("B".to_string()), + name: Some(DecryptedString::test("Hidden")), + value: Some(DecryptedString::test("B")), r#type: 1, linked_id: None, }, Field { - name: Some("Boolean (true)".to_string()), - value: Some("true".to_string()), + name: Some(DecryptedString::test("Boolean (true)")), + value: Some(DecryptedString::test("true")), r#type: 2, linked_id: None, }, Field { - name: Some("Boolean (false)".to_string()), - value: Some("false".to_string()), + name: Some(DecryptedString::test("Boolean (false)")), + value: Some(DecryptedString::test("false")), r#type: 2, linked_id: None, }, Field { - name: Some("Linked".to_string()), + name: Some(DecryptedString::test("Linked")), value: None, r#type: 3, linked_id: Some(101), @@ -672,8 +673,8 @@ mod tests { id: "23f0f877-42b1-4820-a850-b10700bc41eb".parse().unwrap(), folder_id: None, - name: "My secure note".to_string(), - notes: Some("Very secure!".to_string()), + name: DecryptedString::test("My secure note"), + notes: Some(DecryptedString::test("Very secure!")), r#type: CipherType::SecureNote(Box::new(SecureNote { r#type: SecureNoteType::Generic, @@ -692,16 +693,16 @@ mod tests { id: "3ed8de45-48ee-4e26-a2dc-b10701276c53".parse().unwrap(), folder_id: None, - name: "My card".to_string(), + name: DecryptedString::test("My card"), notes: None, r#type: CipherType::Card(Box::new(Card { - cardholder_name: Some("John Doe".to_string()), - exp_month: Some("1".to_string()), - exp_year: Some("2032".to_string()), - code: Some("123".to_string()), - brand: Some("Visa".to_string()), - number: Some("4111111111111111".to_string()), + cardholder_name: Some(DecryptedString::test("John Doe")), + exp_month: Some(DecryptedString::test("1")), + exp_year: Some(DecryptedString::test("2032")), + code: Some(DecryptedString::test("123")), + brand: Some(DecryptedString::test("Visa")), + number: Some(DecryptedString::test("4111111111111111")), })), favorite: false, @@ -717,14 +718,14 @@ mod tests { id: "41cc3bc1-c3d9-4637-876c-b10701273712".parse().unwrap(), folder_id: Some("942e2984-1b9a-453b-b039-b107012713b9".parse().unwrap()), - name: "My identity".to_string(), + name: DecryptedString::test("My identity"), notes: None, r#type: CipherType::Identity(Box::new(Identity { - title: Some("Mr".to_string()), - first_name: Some("John".to_string()), + title: Some(DecryptedString::test("Mr")), + first_name: Some(DecryptedString::test("John")), middle_name: None, - last_name: Some("Doe".to_string()), + last_name: Some(DecryptedString::test("Doe")), address1: None, address2: None, address3: None, @@ -732,11 +733,11 @@ mod tests { state: None, postal_code: None, country: None, - company: Some("Bitwarden".to_string()), + company: Some(DecryptedString::test("Bitwarden")), email: None, phone: None, ssn: None, - username: Some("JDoe".to_string()), + username: Some(DecryptedString::test("JDoe")), passport_number: None, license_number: None, })), diff --git a/crates/bitwarden-exporters/src/lib.rs b/crates/bitwarden-exporters/src/lib.rs index f17d31a2d..22febad8f 100644 --- a/crates/bitwarden-exporters/src/lib.rs +++ b/crates/bitwarden-exporters/src/lib.rs @@ -1,4 +1,4 @@ -use bitwarden_crypto::Kdf; +use bitwarden_crypto::{DecryptedString, Kdf}; use chrono::{DateTime, Utc}; use thiserror::Error; use uuid::Uuid; @@ -22,7 +22,7 @@ pub enum Format { /// that is not tied to the internal vault models. We may revisit this in the future. pub struct Folder { pub id: Uuid, - pub name: String, + pub name: DecryptedString, } /// Export representation of a Bitwarden cipher. @@ -33,8 +33,8 @@ pub struct Cipher { pub id: Uuid, pub folder_id: Option, - pub name: String, - pub notes: Option, + pub name: DecryptedString, + pub notes: Option, pub r#type: CipherType, @@ -50,8 +50,8 @@ pub struct Cipher { #[derive(Clone)] pub struct Field { - pub name: Option, - pub value: Option, + pub name: Option, + pub value: Option, pub r#type: u8, pub linked_id: Option, } @@ -75,24 +75,24 @@ impl ToString for CipherType { } pub struct Login { - pub username: Option, - pub password: Option, + pub username: Option, + pub password: Option, pub login_uris: Vec, - pub totp: Option, + pub totp: Option, } pub struct LoginUri { - pub uri: Option, + pub uri: Option, pub r#match: Option, } pub struct Card { - pub cardholder_name: Option, - pub exp_month: Option, - pub exp_year: Option, - pub code: Option, - pub brand: Option, - pub number: Option, + pub cardholder_name: Option, + pub exp_month: Option, + pub exp_year: Option, + pub code: Option, + pub brand: Option, + pub number: Option, } pub struct SecureNote { @@ -104,24 +104,24 @@ pub enum SecureNoteType { } pub struct Identity { - pub title: Option, - pub first_name: Option, - pub middle_name: Option, - pub last_name: Option, - pub address1: Option, - pub address2: Option, - pub address3: Option, - pub city: Option, - pub state: Option, - pub postal_code: Option, - pub country: Option, - pub company: Option, - pub email: Option, - pub phone: Option, - pub ssn: Option, - pub username: Option, - pub passport_number: Option, - pub license_number: Option, + pub title: Option, + pub first_name: Option, + pub middle_name: Option, + pub last_name: Option, + pub address1: Option, + pub address2: Option, + pub address3: Option, + pub city: Option, + pub state: Option, + pub postal_code: Option, + pub country: Option, + pub company: Option, + pub email: Option, + pub phone: Option, + pub ssn: Option, + pub username: Option, + pub passport_number: Option, + pub license_number: Option, } #[derive(Error, Debug)] diff --git a/crates/bitwarden/src/tool/exporters/mod.rs b/crates/bitwarden/src/tool/exporters/mod.rs index 9e9e99ed5..5982dfe7e 100644 --- a/crates/bitwarden/src/tool/exporters/mod.rs +++ b/crates/bitwarden/src/tool/exporters/mod.rs @@ -206,7 +206,7 @@ impl From for bitwarden_exporters::SecureNoteType { mod tests { use std::num::NonZeroU32; - use bitwarden_crypto::Kdf; + use bitwarden_crypto::{DecryptedString, Kdf}; use chrono::{DateTime, Utc}; use super::*; @@ -216,7 +216,7 @@ mod tests { fn test_try_from_folder_view() { let view = FolderView { id: Some("fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap()), - name: "test_name".to_string(), + name: DecryptedString::test("test_name"), revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), }; @@ -226,7 +226,7 @@ mod tests { f.id, "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap() ); - assert_eq!(f.name, "test_name".to_string()); + assert_eq!(f.name.expose(), "test_name"); } #[test] @@ -234,8 +234,8 @@ mod tests { let cipher_view = CipherView { r#type: CipherType::Login, login: Some(LoginView { - username: Some("test_username".to_string()), - password: Some("test_password".to_string()), + username: Some(DecryptedString::test("test_username")), + password: Some(DecryptedString::test("test_password")), password_revision_date: None, uris: None, totp: None, @@ -246,7 +246,7 @@ mod tests { folder_id: None, collection_ids: vec![], key: None, - name: "My login".to_string(), + name: DecryptedString::test("My login"), notes: None, identity: None, card: None, @@ -272,7 +272,7 @@ mod tests { "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap() ); assert_eq!(cipher.folder_id, None); - assert_eq!(cipher.name, "My login".to_string()); + assert_eq!(cipher.name.expose(), "My login"); assert_eq!(cipher.notes, None); assert!(!cipher.favorite); assert_eq!(cipher.reprompt, 0); @@ -288,8 +288,8 @@ mod tests { assert_eq!(cipher.deleted_date, None); if let bitwarden_exporters::CipherType::Login(l) = cipher.r#type { - assert_eq!(l.username, Some("test_username".to_string())); - assert_eq!(l.password, Some("test_password".to_string())); + assert_eq!(l.username.unwrap().expose(), "test_username"); + assert_eq!(l.password.unwrap().expose(), "test_password"); assert!(l.login_uris.is_empty()); assert_eq!(l.totp, None); } else { diff --git a/crates/bitwarden/src/vault/cipher/cipher.rs b/crates/bitwarden/src/vault/cipher/cipher.rs index e981227c0..865ae778d 100644 --- a/crates/bitwarden/src/vault/cipher/cipher.rs +++ b/crates/bitwarden/src/vault/cipher/cipher.rs @@ -432,8 +432,8 @@ mod tests { CipherView { r#type: CipherType::Login, login: Some(login::LoginView { - username: Some("test_username".to_string()), - password: Some("test_password".to_string()), + username: Some(DecryptedString::test("test_username")), + password: Some(DecryptedString::test("test_password")), password_revision_date: None, uris: None, totp: None, @@ -444,7 +444,7 @@ mod tests { folder_id: None, collection_ids: vec![], key: None, - name: "My test login".to_string(), + name: DecryptedString::test("My test login"), notes: None, identity: None, card: None,