fix: audit fixes — password zeroize, blur downscale, symlink hardening, error filtering (v0.7.0)
Update PKGBUILD version / update-pkgver (push) Successful in 2s

- Add zeroize dependency, wrap password in Zeroizing<String> from entry extraction
  through to login_worker (prevents heap-resident plaintext)
- Add MAX_BLUR_DIMENSION (1920px) downscale before GPU blur to reduce 4K workload
- Wallpaper: use symlink_metadata + is_symlink rejection in greeter.rs and config.rs
- Avatar: add is_file() check, swap lookup order to ~/.face first (consistent with
  moonlock/moonset)
- greetd errors: show generic fallback in UI, log raw PAM details at debug level only
- fprintd: validate device path prefix before creating D-Bus proxy
- Locale: cache detected locale via OnceLock (avoid repeated env/file reads)
This commit is contained in:
2026-03-30 16:03:04 +02:00
parent a2dc89854d
commit 1d557ea135
7 changed files with 102 additions and 49 deletions
+16 -16
View File
@@ -94,7 +94,7 @@ pub fn get_users(passwd_path: Option<&Path>) -> Vec<User> {
users
}
/// Find avatar for a user: AccountsService icon > ~/.face > None.
/// Find avatar for a user: ~/.face > AccountsService icon > None.
/// Rejects symlinks to prevent path traversal.
pub fn get_avatar_path(username: &str, home: &Path) -> Option<PathBuf> {
get_avatar_path_with(username, home, Path::new(DEFAULT_ACCOUNTSSERVICE_DIR))
@@ -106,30 +106,30 @@ pub fn get_avatar_path_with(
home: &Path,
accountsservice_dir: &Path,
) -> Option<PathBuf> {
// AccountsService icon takes priority
// ~/.face takes priority (consistent with moonlock/moonset)
let face = home.join(".face");
if let Ok(meta) = face.symlink_metadata() {
if meta.file_type().is_symlink() {
log::warn!("Rejecting symlink avatar for {username}: {}", face.display());
} else if meta.is_file() {
log::debug!("Avatar for {username}: ~/.face {}", face.display());
return Some(face);
}
}
// AccountsService icon fallback
if accountsservice_dir.exists() {
let icon = accountsservice_dir.join(username);
if let Ok(meta) = icon.symlink_metadata() {
if meta.file_type().is_symlink() {
log::warn!("Rejecting symlink avatar for {username}: {}", icon.display());
} else {
} else if meta.is_file() {
log::debug!("Avatar for {username}: AccountsService {}", icon.display());
return Some(icon);
}
}
}
// ~/.face fallback
let face = home.join(".face");
if let Ok(meta) = face.symlink_metadata() {
if meta.file_type().is_symlink() {
log::warn!("Rejecting symlink avatar for {username}: {}", face.display());
} else {
log::debug!("Avatar for {username}: ~/.face {}", face.display());
return Some(face);
}
}
log::debug!("No avatar found for {username}");
None
}
@@ -248,7 +248,7 @@ mod tests {
}
#[test]
fn accountsservice_icon_takes_priority() {
fn face_file_takes_priority_over_accountsservice() {
let dir = tempfile::tempdir().unwrap();
let icons_dir = dir.path().join("icons");
fs::create_dir(&icons_dir).unwrap();
@@ -261,7 +261,7 @@ mod tests {
fs::write(&face, "fake face").unwrap();
let path = get_avatar_path_with("testuser", &home, &icons_dir);
assert_eq!(path, Some(icon));
assert_eq!(path, Some(face));
}
#[test]