From 301aa6687a0735f1059b55079d0eb554975561f5 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:14:14 +0000 Subject: [PATCH] feat(security): validate username input in D-Bus calls Introduced `validate_username` helper function in `src/app/fprint.rs` to enforce strict validation (alphanumeric, -, _, ., max 255 chars) on usernames before passing them to privileged `fprintd` D-Bus methods. This mitigates potential injection risks or malformed data issues. Applied validation to: - `list_enrolled_fingers_dbus` - `delete_fingerprint_dbus` - `delete_fingers` - `clear_all_fingers_dbus` - `enroll_fingerprint_process` Added unit tests to verify validation logic. --- src/app/fprint.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/app/fprint.rs b/src/app/fprint.rs index 21777e1..05a41d6 100644 --- a/src/app/fprint.rs +++ b/src/app/fprint.rs @@ -22,6 +22,7 @@ pub async fn list_enrolled_fingers_dbus( device: &DeviceProxy<'static>, username: String, ) -> zbus::Result> { + validate_username(&username)?; device.list_enrolled_fingers(&username).await } @@ -31,6 +32,7 @@ pub async fn delete_fingerprint_dbus( finger: String, username: String, ) -> zbus::Result<()> { + validate_username(&username)?; let device = DeviceProxy::builder(connection).path(path)?.build().await?; device.claim(&username).await?; @@ -44,6 +46,7 @@ pub async fn delete_fingers( path: zbus::zvariant::OwnedObjectPath, username: String, ) -> zbus::Result<()> { + validate_username(&username)?; let device = DeviceProxy::builder(connection).path(path)?.build().await?; device.claim(&username).await?; @@ -60,6 +63,11 @@ pub async fn clear_all_fingers_dbus( let mut last_error = None; for username in usernames { + if let Err(e) = validate_username(&username) { + last_error = Some(e); + continue; + } + if let Err(e) = device.claim(&username).await { last_error = Some(e); continue; @@ -101,6 +109,7 @@ where S: Sink + Unpin + Send, S::Error: std::fmt::Debug + Send, { + validate_username(&username)?; let device = DeviceProxy::builder(&connection) .path(path)? .build() @@ -165,3 +174,51 @@ where Ok(()) } + +fn validate_username(username: &str) -> zbus::Result<()> { + if username.is_empty() { + return Err(zbus::Error::Failure("Username cannot be empty".to_string())); + } + if username.len() > 255 { + return Err(zbus::Error::Failure("Username is too long".to_string())); + } + if !username.chars().all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == '.') { + return Err(zbus::Error::Failure(format!( + "Invalid characters in username: {}", + username + ))); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_validate_username() { + // Valid usernames + assert!(validate_username("user").is_ok()); + assert!(validate_username("user1").is_ok()); + assert!(validate_username("user_name").is_ok()); + assert!(validate_username("user-name").is_ok()); + assert!(validate_username("user.name").is_ok()); + assert!(validate_username("u").is_ok()); + assert!(validate_username("123").is_ok()); + assert!(validate_username("User").is_ok()); // Uppercase is allowed by our validation + + // Invalid usernames + assert!(validate_username("").is_err()); + assert!(validate_username("user name").is_err()); // space + assert!(validate_username("user/name").is_err()); // slash + assert!(validate_username("user@name").is_err()); // @ + assert!(validate_username("user!name").is_err()); // ! + assert!(validate_username("user?name").is_err()); // ? + + let long_name = "a".repeat(256); + assert!(validate_username(&long_name).is_err()); + + let max_len_name = "a".repeat(255); + assert!(validate_username(&max_len_name).is_ok()); + } +}