diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9cc9526..13522ea 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -103,6 +103,20 @@ jobs: echo "release_id=$release_id" >> "$GITHUB_OUTPUT" shell: bash + build-and-test: + needs: release + if: needs.release.outputs.release-flag == 'false' + runs-on: + - thevickypedia-lite + - posix + steps: + - name: Build (no release) + run: cargo build + shell: bash + - name: Test (no release) + run: cargo test --no-run + shell: bash + upload_assets: needs: release strategy: diff --git a/src/resources/info.rs b/src/resources/info.rs index bc78d3c..d24ba7b 100644 --- a/src/resources/info.rs +++ b/src/resources/info.rs @@ -26,12 +26,10 @@ pub fn get_disk_usage(disks: &Disks) -> u64 { pub fn get_disks(disks: &Disks) -> Vec> { let mut disks_info = vec![]; for disk in disks.list() { - // todo: Check sysinfo source code for a way to get the `Model` information disks_info.push( HashMap::from([ ("Name".to_string(), disk.name().to_string_lossy().to_string()), ("Size".to_string(), squire::util::size_converter(disk.total_space()).to_string()), - // ("Model".to_string(), disk.name().to_string_lossy().to_string()), ("Kind".to_string(), disk.file_system().to_string_lossy().to_string()), ("Mount Point".to_string(), disk.mount_point().to_string_lossy().to_string()), ]) diff --git a/src/routes/monitor.rs b/src/routes/monitor.rs index c2b2a31..90b89b2 100644 --- a/src/routes/monitor.rs +++ b/src/routes/monitor.rs @@ -44,8 +44,8 @@ pub async fn monitor(request: HttpRequest, // legacy functions have a mechanism to check for physical devices, so it takes precedence let has_name_and_size = !legacy_disk_info.is_empty() && legacy_disk_info.iter().all(|disk| { - disk.contains_key("Name") && disk.contains_key("Size") - }); + disk.contains_key("Name") && disk.contains_key("Size") + }); let sys_info_disks = if has_name_and_size { log::debug!("Using legacy methods for disks!"); legacy_disk_info diff --git a/src/squire/startup.rs b/src/squire/startup.rs index 5bbaff4..09529d6 100644 --- a/src/squire/startup.rs +++ b/src/squire/startup.rs @@ -1,10 +1,10 @@ use std; use std::io::Write; -use chrono::{DateTime, Local}; - use crate::squire::settings; use crate::{constant, squire}; +use chrono::{DateTime, Local}; +use regex::Regex; /// Initializes the logger based on the provided debug flag and cargo information. /// @@ -238,6 +238,78 @@ fn load_env_vars() -> settings::Config { } } +/// Verifies the strength of a password string. +/// +/// A secret is considered strong if it meets the following conditions: +/// - At least 32 characters long +/// - Contains at least one digit +/// - Contains at least one uppercase letter +/// - Contains at least one lowercase letter +/// - Contains at least one special character +/// +/// # Arguments +/// +/// * `password` - A reference to a string slice (`&str`) that represents the password to check. +/// +/// # Returns +/// +/// This function returns a `Result<(), String>`. +/// - `Ok(())` is returned if all conditions are met. +/// - `Err(String)` is returned with an error message if any condition fails. +pub fn complexity_checker(password: &str) -> Result<(), String> { + let mock_password = "*".repeat(password.len()); + + // Check minimum length + if password.len() < 8 { + return Err( + format!( + "\npassword\n\t[{}] password must be at least 8 or more characters [value=invalid]\n", mock_password + ) + ); + } + + // Check for at least one digit + let has_digit = Regex::new(r"\d").unwrap(); + if !has_digit.is_match(password) { + return Err( + format!( + "\npassword\n\t[{}] password must include at least one digit [value=invalid]\n", mock_password + ) + ); + } + + // Check for at least one uppercase letter + let has_uppercase = Regex::new(r"[A-Z]").unwrap(); + if !has_uppercase.is_match(password) { + return Err( + format!( + "\npassword\n\t[{}] password must include at least one uppercase letter [value=invalid]\n", mock_password + ) + ); + } + + // Check for at least one lowercase letter + let has_lowercase = Regex::new(r"[a-z]").unwrap(); + if !has_lowercase.is_match(password) { + return Err( + format!( + "\npassword\n\t[{}] password must include at least one lowercase letter [value=invalid]\n", mock_password + ) + ); + } + + // Check for at least one special character + let has_special_char = Regex::new(r###"[ !#$%&'()*+,./:;<=>?@\\^_`{|}~"-]"###).unwrap(); + if !has_special_char.is_match(password) { + return Err( + format!( + "\npassword\n\t[{}] password must contain at least one special character [value=invalid]\n", mock_password + ) + ); + } + Ok(()) +} + /// Validates all the required environment variables with the required settings. /// /// # Returns @@ -253,12 +325,11 @@ fn validate_vars() -> settings::Config { ); errors.push_str(&err1); } - if config.password.len() < 8 { - let err2 = format!( - "\npassword\n\t[{}] password should be at least 8 or more characters [value=invalid]\n", - "*".repeat(config.password.len()) - ); - errors.push_str(&err2); + match complexity_checker(&config.password) { + Ok(_) => (), + Err(err) => { + errors.push_str(&err); + } } if !errors.is_empty() { panic!("{}", errors);