fix: audit fixes — symlink-safe avatars, blur downscale + padding, config validation (v0.8.0)
Update PKGBUILD version / update-pkgver (push) Successful in 2s
Update PKGBUILD version / update-pkgver (push) Successful in 2s
- Replace canonicalize() with symlink_metadata + is_file + !is_symlink for avatar lookup (prevents symlink traversal to arbitrary files) - Fix blur padding offset from (0,0) to (-pad,-pad) to prevent edge darkening - Add MAX_BLUR_DIMENSION (1920px) downscale before GPU blur - Validate blur per config source (invalid user value preserves system default) - Wallpaper: use symlink_metadata + is_file + !is_symlink in resolve_background_path
This commit is contained in:
+29
-19
@@ -47,32 +47,34 @@ pub fn get_avatar_path(home: &Path, username: Option<&str>) -> Option<PathBuf> {
|
||||
}
|
||||
|
||||
/// Find avatar with configurable AccountsService dir (for testing).
|
||||
/// Rejects symlinks to prevent path traversal.
|
||||
pub fn get_avatar_path_with(
|
||||
home: &Path,
|
||||
username: Option<&str>,
|
||||
accountsservice_dir: &Path,
|
||||
) -> Option<PathBuf> {
|
||||
// ~/.face takes priority — canonicalize to resolve symlinks
|
||||
// ~/.face takes priority
|
||||
let face = home.join(".face");
|
||||
if face.exists() {
|
||||
if let Ok(canonical) = std::fs::canonicalize(&face) {
|
||||
log::debug!("Avatar: using ~/.face ({})", canonical.display());
|
||||
return Some(canonical);
|
||||
if let Ok(meta) = face.symlink_metadata() {
|
||||
if meta.file_type().is_symlink() {
|
||||
log::warn!("Rejecting symlink avatar: {}", face.display());
|
||||
} else if meta.is_file() {
|
||||
log::debug!("Avatar: using ~/.face ({})", face.display());
|
||||
return Some(face);
|
||||
}
|
||||
// canonicalize failed (e.g. permissions) — skip rather than return unresolved symlink
|
||||
log::warn!("Avatar: ~/.face exists but canonicalize failed, skipping");
|
||||
}
|
||||
|
||||
// AccountsService icon — also canonicalize for consistency
|
||||
// AccountsService icon fallback
|
||||
if let Some(name) = username {
|
||||
if accountsservice_dir.exists() {
|
||||
let icon = accountsservice_dir.join(name);
|
||||
if icon.exists() {
|
||||
if let Ok(canonical) = std::fs::canonicalize(&icon) {
|
||||
log::debug!("Avatar: using AccountsService icon ({})", canonical.display());
|
||||
return Some(canonical);
|
||||
if let Ok(meta) = icon.symlink_metadata() {
|
||||
if meta.file_type().is_symlink() {
|
||||
log::warn!("Rejecting symlink avatar: {}", icon.display());
|
||||
} else if meta.is_file() {
|
||||
log::debug!("Avatar: using AccountsService icon ({})", icon.display());
|
||||
return Some(icon);
|
||||
}
|
||||
log::warn!("Avatar: AccountsService icon exists but canonicalize failed, skipping");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,8 +109,7 @@ mod tests {
|
||||
let face = dir.path().join(".face");
|
||||
fs::write(&face, "fake image").unwrap();
|
||||
let path = get_avatar_path_with(dir.path(), None, Path::new("/nonexistent"));
|
||||
let expected = fs::canonicalize(&face).unwrap();
|
||||
assert_eq!(path, Some(expected));
|
||||
assert_eq!(path, Some(face));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -119,8 +120,7 @@ mod tests {
|
||||
let icon = icons_dir.join("testuser");
|
||||
fs::write(&icon, "fake image").unwrap();
|
||||
let path = get_avatar_path_with(dir.path(), Some("testuser"), &icons_dir);
|
||||
let expected = fs::canonicalize(&icon).unwrap();
|
||||
assert_eq!(path, Some(expected));
|
||||
assert_eq!(path, Some(icon));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -133,8 +133,18 @@ mod tests {
|
||||
let icon = icons_dir.join("testuser");
|
||||
fs::write(&icon, "fake image").unwrap();
|
||||
let path = get_avatar_path_with(dir.path(), Some("testuser"), &icons_dir);
|
||||
let expected = fs::canonicalize(&face).unwrap();
|
||||
assert_eq!(path, Some(expected));
|
||||
assert_eq!(path, Some(face));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_symlink_avatar() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let target = dir.path().join("secret");
|
||||
fs::write(&target, "secret content").unwrap();
|
||||
let face = dir.path().join(".face");
|
||||
std::os::unix::fs::symlink(&target, &face).unwrap();
|
||||
let path = get_avatar_path_with(dir.path(), None, Path::new("/nonexistent"));
|
||||
assert!(path.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user