diff --git a/Cargo.toml b/Cargo.toml index 64920d58c..7626e4cca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,9 @@ bitwarden-crypto = { path = "crates/bitwarden-crypto", version = "=0.1.0" } bitwarden-exporters = { path = "crates/bitwarden-exporters", version = "=0.1.0" } bitwarden-generators = { path = "crates/bitwarden-generators", version = "=0.1.0" } +[workspace.lints.clippy] +unwrap_used = "deny" + # Compile all dependencies with some optimizations when building this crate on debug # This slows down clean builds by about 50%, but the resulting binaries can be orders of magnitude faster # As clean builds won't occur very often, this won't slow down the development process diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 000000000..a29e019ac --- /dev/null +++ b/clippy.toml @@ -0,0 +1,2 @@ +allow-unwrap-in-tests=true +allow-expect-in-tests=true diff --git a/crates/bitwarden-c/Cargo.toml b/crates/bitwarden-c/Cargo.toml index e3f966ee1..47d4ff5cd 100644 --- a/crates/bitwarden-c/Cargo.toml +++ b/crates/bitwarden-c/Cargo.toml @@ -21,3 +21,6 @@ bitwarden-json = { path = "../bitwarden-json", features = ["secrets"] } [dependencies] env_logger = ">=0.10.0, <0.12" + +[lints] +workspace = true diff --git a/crates/bitwarden-c/src/c.rs b/crates/bitwarden-c/src/c.rs index 934b20359..32abe3dc0 100644 --- a/crates/bitwarden-c/src/c.rs +++ b/crates/bitwarden-c/src/c.rs @@ -11,7 +11,8 @@ pub async extern "C" fn run_command( client_ptr: *const Client, ) -> *mut c_char { let client = unsafe { ffi_ref!(client_ptr) }; - let input_str = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr).to_bytes() }).unwrap(); + let input_str = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr) }.to_bytes()) + .expect("Input should be a valid string"); let result = client.run_command(input_str).await; match std::ffi::CString::new(result) { @@ -28,8 +29,8 @@ pub extern "C" fn init(c_str_ptr: *const c_char) -> *mut Client { if c_str_ptr.is_null() { box_ptr!(Client::new(None)) } else { - let input_string = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr).to_bytes() }) - .unwrap() + let input_string = str::from_utf8(unsafe { CStr::from_ptr(c_str_ptr) }.to_bytes()) + .expect("Input should be a valid string") .to_owned(); box_ptr!(Client::new(Some(input_string))) } diff --git a/crates/bitwarden-cli/Cargo.toml b/crates/bitwarden-cli/Cargo.toml index de5a4f9fc..b00479b2f 100644 --- a/crates/bitwarden-cli/Cargo.toml +++ b/crates/bitwarden-cli/Cargo.toml @@ -15,3 +15,6 @@ clap = { version = "4.5.1", features = ["derive"] } color-eyre = "0.6" inquire = "0.6.2" supports-color = "3.0.0" + +[lints] +workspace = true diff --git a/crates/bitwarden-crypto/Cargo.toml b/crates/bitwarden-crypto/Cargo.toml index ce0424c63..37359367b 100644 --- a/crates/bitwarden-crypto/Cargo.toml +++ b/crates/bitwarden-crypto/Cargo.toml @@ -21,7 +21,7 @@ mobile = ["dep:uniffi"] # Mobile-specific features [dependencies] aes = { version = ">=0.8.2, <0.9", features = ["zeroize"] } argon2 = { version = ">=0.5.0, <0.6", features = [ - "alloc", + "std", "zeroize", ], default-features = false } base64 = ">=0.21.2, <0.22" @@ -48,3 +48,6 @@ zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } [dev-dependencies] rand_chacha = "0.3.1" serde_json = ">=1.0.96, <2.0" + +[lints] +workspace = true diff --git a/crates/bitwarden-crypto/src/aes.rs b/crates/bitwarden-crypto/src/aes.rs index ee76f9936..dde588563 100644 --- a/crates/bitwarden-crypto/src/aes.rs +++ b/crates/bitwarden-crypto/src/aes.rs @@ -149,7 +149,8 @@ pub fn decrypt_aes128_hmac( /// Generate a MAC using HMAC-SHA256. fn generate_mac(mac_key: &[u8], iv: &[u8], data: &[u8]) -> Result<[u8; 32]> { - let mut hmac = PbkdfSha256Hmac::new_from_slice(mac_key).expect("HMAC can take key of any size"); + let mut hmac = + PbkdfSha256Hmac::new_from_slice(mac_key).expect("hmac new_from_slice should not fail"); hmac.update(iv); hmac.update(data); let mac: [u8; PBKDF_SHA256_HMAC_OUT_SIZE] = (*hmac.finalize().into_bytes()) diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 0217a4b93..d312882d7 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -119,15 +119,15 @@ impl EncString { match enc_type { 0 => { check_length(buf, 18)?; - let iv = buf[1..17].try_into().unwrap(); + let iv = buf[1..17].try_into().expect("Valid length"); let data = buf[17..].to_vec(); Ok(EncString::AesCbc256_B64 { iv, data }) } 1 | 2 => { check_length(buf, 50)?; - let iv = buf[1..17].try_into().unwrap(); - let mac = buf[17..49].try_into().unwrap(); + let iv = buf[1..17].try_into().expect("Valid length"); + let mac = buf[17..49].try_into().expect("Valid length"); let data = buf[49..].to_vec(); if enc_type == 1 { diff --git a/crates/bitwarden-crypto/src/error.rs b/crates/bitwarden-crypto/src/error.rs index cf9a9b048..7cfb354d7 100644 --- a/crates/bitwarden-crypto/src/error.rs +++ b/crates/bitwarden-crypto/src/error.rs @@ -28,6 +28,9 @@ pub enum CryptoError { #[error("Fingerprint error, {0}")] FingerprintError(#[from] FingerprintError), + #[error("Argon2 error, {0}")] + ArgonError(#[from] argon2::Error), + #[error("Number is zero")] ZeroNumber, } diff --git a/crates/bitwarden-crypto/src/fingerprint.rs b/crates/bitwarden-crypto/src/fingerprint.rs index d3e69d577..b88435dbc 100644 --- a/crates/bitwarden-crypto/src/fingerprint.rs +++ b/crates/bitwarden-crypto/src/fingerprint.rs @@ -51,7 +51,10 @@ fn hash_word(hash: [u8; 32]) -> Result { let remainder = hash_number.clone() % EFF_LONG_WORD_LIST.len(); hash_number /= EFF_LONG_WORD_LIST.len(); - phrase.push(EFF_LONG_WORD_LIST[remainder.to_usize().unwrap()].to_string()); + let index = remainder + .to_usize() + .expect("Remainder is less than EFF_LONG_WORD_LIST.len()"); + phrase.push(EFF_LONG_WORD_LIST[index].to_string()); } Ok(phrase.join("-")) diff --git a/crates/bitwarden-crypto/src/keys/shareable_key.rs b/crates/bitwarden-crypto/src/keys/shareable_key.rs index 2b9ec837f..e84e05ca3 100644 --- a/crates/bitwarden-crypto/src/keys/shareable_key.rs +++ b/crates/bitwarden-crypto/src/keys/shareable_key.rs @@ -2,9 +2,12 @@ use std::pin::Pin; use aes::cipher::typenum::U64; use generic_array::GenericArray; -use hmac::{Hmac, Mac}; +use hmac::Mac; -use crate::{keys::SymmetricCryptoKey, util::hkdf_expand}; +use crate::{ + keys::SymmetricCryptoKey, + util::{hkdf_expand, PbkdfSha256Hmac}, +}; /// Derive a shareable key using hkdf from secret and name. /// @@ -19,15 +22,16 @@ pub fn derive_shareable_key( // TODO: Are these the final `key` and `info` parameters or should we change them? I followed // the pattern used for sends - let res = Hmac::::new_from_slice(format!("bitwarden-{}", name).as_bytes()) - .unwrap() + let res = PbkdfSha256Hmac::new_from_slice(format!("bitwarden-{}", name).as_bytes()) + .expect("hmac new_from_slice should not fail") .chain_update(secret) .finalize() .into_bytes(); - let mut key: Pin>> = hkdf_expand(&res, info).unwrap(); + let mut key: Pin>> = + hkdf_expand(&res, info).expect("Input is a valid size"); - SymmetricCryptoKey::try_from(key.as_mut_slice()).unwrap() + SymmetricCryptoKey::try_from(key.as_mut_slice()).expect("Key is a valid size") } #[cfg(test)] diff --git a/crates/bitwarden-crypto/src/keys/utils.rs b/crates/bitwarden-crypto/src/keys/utils.rs index d83e212d0..a2df336e1 100644 --- a/crates/bitwarden-crypto/src/keys/utils.rs +++ b/crates/bitwarden-crypto/src/keys/utils.rs @@ -25,16 +25,13 @@ pub(super) fn derive_kdf_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result, ciphers: Vec) -> Result ResponseIntoString for Response { Ok(ser) => ser, Err(e) => { let error = Response::error(format!("Failed to serialize Response: {}", e)); - serde_json::to_string(&error).unwrap() + serde_json::to_string(&error).expect("Serialize should be infallible") } } } diff --git a/crates/bitwarden-napi/Cargo.toml b/crates/bitwarden-napi/Cargo.toml index 016401fe7..ea37593f2 100644 --- a/crates/bitwarden-napi/Cargo.toml +++ b/crates/bitwarden-napi/Cargo.toml @@ -32,3 +32,6 @@ napi-build = "2.1.0" [profile.release] lto = true + +[lints] +workspace = true diff --git a/crates/bitwarden-py/Cargo.toml b/crates/bitwarden-py/Cargo.toml index f66a0c11f..85af436bf 100644 --- a/crates/bitwarden-py/Cargo.toml +++ b/crates/bitwarden-py/Cargo.toml @@ -30,3 +30,6 @@ pyo3-asyncio = { version = "0.20.0", features = [ "attributes", "tokio-runtime", ] } + +[lints] +workspace = true diff --git a/crates/bitwarden-uniffi/Cargo.toml b/crates/bitwarden-uniffi/Cargo.toml index 94572b991..2f0a4e175 100644 --- a/crates/bitwarden-uniffi/Cargo.toml +++ b/crates/bitwarden-uniffi/Cargo.toml @@ -33,3 +33,6 @@ bitwarden-generators = { workspace = true, features = ["mobile"] } [build-dependencies] uniffi = { version = "=0.26.1", features = ["build"] } + +[lints] +workspace = true diff --git a/crates/bitwarden-wasm/Cargo.toml b/crates/bitwarden-wasm/Cargo.toml index a688f12b6..4cf65905f 100644 --- a/crates/bitwarden-wasm/Cargo.toml +++ b/crates/bitwarden-wasm/Cargo.toml @@ -30,3 +30,6 @@ bitwarden-json = { path = "../bitwarden-json", features = [ [dev-dependencies] wasm-bindgen-test = "0.3.41" + +[lints] +workspace = true diff --git a/crates/bitwarden/Cargo.toml b/crates/bitwarden/Cargo.toml index 4c9596b81..7d56b4c6e 100644 --- a/crates/bitwarden/Cargo.toml +++ b/crates/bitwarden/Cargo.toml @@ -84,3 +84,6 @@ rand_chacha = "0.3.1" tokio = { version = "1.36.0", features = ["rt", "macros"] } wiremock = "0.6.0" zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } + +[lints] +workspace = true diff --git a/crates/bitwarden/src/auth/api/request/mod.rs b/crates/bitwarden/src/auth/api/request/mod.rs index 2b5bde225..263d28357 100644 --- a/crates/bitwarden/src/auth/api/request/mod.rs +++ b/crates/bitwarden/src/auth/api/request/mod.rs @@ -54,7 +54,7 @@ async fn send_identity_connect_request( } let response = request - .body(serde_qs::to_string(&body).unwrap()) + .body(serde_qs::to_string(&body).expect("Serialize should be infallible")) .send() .await?; diff --git a/crates/bitwarden/src/auth/login/auth_request.rs b/crates/bitwarden/src/auth/login/auth_request.rs index 887809e4b..0e9cb6795 100644 --- a/crates/bitwarden/src/auth/login/auth_request.rs +++ b/crates/bitwarden/src/auth/login/auth_request.rs @@ -87,7 +87,7 @@ pub(crate) async fn complete_auth_request( if let IdentityTokenResponse::Authenticated(r) = response { let kdf = Kdf::PBKDF2 { - iterations: NonZeroU32::new(600_000).unwrap(), + iterations: NonZeroU32::new(600_000).expect("Non-zero number"), }; client.set_tokens( diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 68442d5d3..2374d6263 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -108,16 +108,17 @@ impl Client { client_builder } - let external_client = new_client_builder().build().unwrap(); + let external_client = new_client_builder().build().expect("Build should not fail"); let mut headers = header::HeaderMap::new(); headers.append( "Device-Type", - HeaderValue::from_str(&(settings.device_type as u8).to_string()).unwrap(), + HeaderValue::from_str(&(settings.device_type as u8).to_string()) + .expect("All numbers are valid ASCII"), ); let client_builder = new_client_builder().default_headers(headers); - let client = client_builder.build().unwrap(); + let client = client_builder.build().expect("Build should not fail"); let identity = bitwarden_api_identity::apis::configuration::Configuration { base_path: settings.identity_url, @@ -262,7 +263,10 @@ impl Client { user_key, private_key, )?); - Ok(self.encryption_settings.as_ref().unwrap()) + Ok(self + .encryption_settings + .as_ref() + .expect("Value is initialized previously")) } #[cfg(feature = "internal")] @@ -278,7 +282,7 @@ impl Client { Ok(self .encryption_settings .as_ref() - .expect("It was initialized on the previous line")) + .expect("Value is initialized previously")) } #[cfg(feature = "mobile")] @@ -307,7 +311,9 @@ impl Client { key: SymmetricCryptoKey, ) -> &EncryptionSettings { self.encryption_settings = Some(EncryptionSettings::new_single_key(key)); - self.encryption_settings.as_ref().unwrap() + self.encryption_settings + .as_ref() + .expect("Value is initialized previously") } #[cfg(feature = "internal")] @@ -321,7 +327,7 @@ impl Client { .ok_or(Error::VaultLocked)?; enc.set_org_keys(org_keys)?; - Ok(self.encryption_settings.as_ref().unwrap()) + Ok(&*enc) } } diff --git a/crates/bitwarden/src/util.rs b/crates/bitwarden/src/util.rs index f6568986d..5611b4077 100644 --- a/crates/bitwarden/src/util.rs +++ b/crates/bitwarden/src/util.rs @@ -6,19 +6,19 @@ use base64::{ }; pub fn default_pbkdf2_iterations() -> NonZeroU32 { - NonZeroU32::new(600_000).unwrap() + NonZeroU32::new(600_000).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_iterations() -> NonZeroU32 { - NonZeroU32::new(3).unwrap() + NonZeroU32::new(3).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_memory() -> NonZeroU32 { - NonZeroU32::new(64).unwrap() + NonZeroU32::new(64).expect("Non-zero number") } #[cfg(feature = "internal")] pub fn default_argon2_parallelism() -> NonZeroU32 { - NonZeroU32::new(4).unwrap() + NonZeroU32::new(4).expect("Non-zero number") } const INDIFFERENT: GeneralPurposeConfig = diff --git a/crates/bitwarden/src/vault/totp.rs b/crates/bitwarden/src/vault/totp.rs index 6ba754034..4586eda48 100644 --- a/crates/bitwarden/src/vault/totp.rs +++ b/crates/bitwarden/src/vault/totp.rs @@ -220,8 +220,8 @@ fn decode_b32(s: &str) -> Vec { let mut bytes = Vec::new(); for chunk in bits.as_bytes().chunks_exact(8) { - let byte_str = std::str::from_utf8(chunk).unwrap(); - let byte = u8::from_str_radix(byte_str, 2).unwrap(); + let byte_str = std::str::from_utf8(chunk).expect("The value is a valid string"); + let byte = u8::from_str_radix(byte_str, 2).expect("The value is a valid binary string"); bytes.push(byte); } diff --git a/crates/bw/Cargo.toml b/crates/bw/Cargo.toml index c54c35e28..ce69f87e9 100644 --- a/crates/bw/Cargo.toml +++ b/crates/bw/Cargo.toml @@ -26,3 +26,6 @@ bitwarden-cli = { path = "../bitwarden-cli", version = "0.1.0" } [dev-dependencies] tempfile = "3.10.0" + +[lints] +workspace = true diff --git a/crates/bws/Cargo.toml b/crates/bws/Cargo.toml index ccf143c47..491887f93 100644 --- a/crates/bws/Cargo.toml +++ b/crates/bws/Cargo.toml @@ -46,3 +46,6 @@ bitwarden = { workspace = true, features = ["secrets"] } [dev-dependencies] tempfile = "3.10.0" + +[lints] +workspace = true diff --git a/crates/bws/src/config.rs b/crates/bws/src/config.rs index 0ea1259f9..6cc471f97 100644 --- a/crates/bws/src/config.rs +++ b/crates/bws/src/config.rs @@ -46,23 +46,28 @@ impl ProfileKey { pub(crate) const FILENAME: &str = "config"; pub(crate) const DIRECTORY: &str = ".bws"; -pub(crate) fn get_config_path(config_file: Option<&Path>, ensure_folder_exists: bool) -> PathBuf { - let config_file = config_file.map(ToOwned::to_owned).unwrap_or_else(|| { - let base_dirs = BaseDirs::new().unwrap(); - base_dirs.home_dir().join(DIRECTORY).join(FILENAME) - }); +fn get_config_path(config_file: Option<&Path>, ensure_folder_exists: bool) -> Result { + let config_file = match config_file { + Some(path) => path.to_owned(), + None => { + let Some(base_dirs) = BaseDirs::new() else { + bail!("A valid home directory doesn't exist"); + }; + base_dirs.home_dir().join(DIRECTORY).join(FILENAME) + } + }; if ensure_folder_exists { if let Some(parent_folder) = config_file.parent() { - std::fs::create_dir_all(parent_folder).unwrap(); + std::fs::create_dir_all(parent_folder)?; } } - config_file + Ok(config_file) } pub(crate) fn load_config(config_file: Option<&Path>, must_exist: bool) -> Result { - let file = get_config_path(config_file, false); + let file = get_config_path(config_file, false)?; let content = match file.exists() { true => read_to_string(file), @@ -75,7 +80,7 @@ pub(crate) fn load_config(config_file: Option<&Path>, must_exist: bool) -> Resul } fn write_config(config: Config, config_file: Option<&Path>) -> Result<()> { - let file = get_config_path(config_file, true); + let file = get_config_path(config_file, true)?; let content = toml::to_string_pretty(&config)?; diff --git a/crates/bws/src/main.rs b/crates/bws/src/main.rs index 6ed78f7b7..182e20679 100644 --- a/crates/bws/src/main.rs +++ b/crates/bws/src/main.rs @@ -328,7 +328,7 @@ async fn process_commands() -> Result<()> { let state_file_path = state::get_state_file_path( profile.and_then(|p| p.state_file_dir).map(Into::into), access_token_obj.access_token_id.to_string(), - ); + )?; let mut client = bitwarden::Client::new(settings); diff --git a/crates/bws/src/render.rs b/crates/bws/src/render.rs index f9563c81b..c92c78662 100644 --- a/crates/bws/src/render.rs +++ b/crates/bws/src/render.rs @@ -41,18 +41,20 @@ pub(crate) fn serialize_response, const N: usiz ) { match output { Output::JSON => { - let mut text = serde_json::to_string_pretty(&data).unwrap(); + let mut text = + serde_json::to_string_pretty(&data).expect("Serialize should be infallible"); // Yaml/table/tsv serializations add a newline at the end, so we do the same here for // consistency text.push('\n'); pretty_print("json", &text, color); } Output::YAML => { - let text = serde_yaml::to_string(&data).unwrap(); + let text = serde_yaml::to_string(&data).expect("Serialize should be infallible"); pretty_print("yaml", &text, color); } Output::Env => { - let valid_key_regex = regex::Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").unwrap(); + let valid_key_regex = + regex::Regex::new("^[a-zA-Z_][a-zA-Z0-9_]*$").expect("regex is valid"); let mut commented_out = false; let mut text: Vec = data @@ -105,7 +107,7 @@ fn pretty_print(language: &str, data: &str, color: bool) { .input_from_bytes(data.as_bytes()) .language(language) .print() - .unwrap(); + .expect("Input is valid"); } else { print!("{}", data); } diff --git a/crates/bws/src/state.rs b/crates/bws/src/state.rs index ef0ef07e2..9c90da81f 100644 --- a/crates/bws/src/state.rs +++ b/crates/bws/src/state.rs @@ -1,18 +1,20 @@ use std::path::PathBuf; +use color_eyre::eyre::Result; + pub(crate) fn get_state_file_path( state_file_dir: Option, access_token_id: String, -) -> Option { +) -> Result> { if let Some(mut state_file_path) = state_file_dir { state_file_path.push(access_token_id); if let Some(parent_folder) = state_file_path.parent() { - std::fs::create_dir_all(parent_folder).unwrap(); + std::fs::create_dir_all(parent_folder)?; } - return Some(state_file_path); + return Ok(Some(state_file_path)); } - None + Ok(None) }