fix: re-audit findings — avatar path safety, persistence logging, tests
- Reject non-UTF-8 avatar paths early instead of passing empty string to GDK - Log persistence write failures with warn! instead of silently discarding - Reduce API surface: create_background_picture pub→fn - Add boundary test for MAX_USERNAME_LENGTH and socket connect failure test
This commit is contained in:
parent
09371b5fd2
commit
7c10516473
@ -174,7 +174,7 @@ pub fn create_wallpaper_window(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Picture widget for the wallpaper background, optionally with GPU blur.
|
/// Create a Picture widget for the wallpaper background, optionally with GPU blur.
|
||||||
pub fn create_background_picture(texture: &gdk::Texture, blur_radius: Option<f32>) -> gtk::Picture {
|
fn create_background_picture(texture: &gdk::Texture, blur_radius: Option<f32>) -> gtk::Picture {
|
||||||
let background = gtk::Picture::for_paintable(texture);
|
let background = gtk::Picture::for_paintable(texture);
|
||||||
background.set_content_fit(gtk::ContentFit::Cover);
|
background.set_content_fit(gtk::ContentFit::Cover);
|
||||||
background.set_hexpand(true);
|
background.set_hexpand(true);
|
||||||
@ -654,7 +654,13 @@ fn set_avatar_from_file(
|
|||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match Pixbuf::from_file_at_scale(path.to_str().unwrap_or(""), AVATAR_SIZE, AVATAR_SIZE, true) {
|
let Some(path_str) = path.to_str() else {
|
||||||
|
log::debug!("Non-UTF-8 avatar path, skipping: {}", path.display());
|
||||||
|
image.set_icon_name(Some("avatar-default-symbolic"));
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
match Pixbuf::from_file_at_scale(path_str, AVATAR_SIZE, AVATAR_SIZE, true) {
|
||||||
Ok(pixbuf) => {
|
Ok(pixbuf) => {
|
||||||
let texture = gdk::Texture::for_pixbuf(&pixbuf);
|
let texture = gdk::Texture::for_pixbuf(&pixbuf);
|
||||||
if let Some(name) = username {
|
if let Some(name) = username {
|
||||||
@ -1156,18 +1162,24 @@ fn save_last_user(username: &str) {
|
|||||||
|
|
||||||
fn save_last_user_to(path: &Path, username: &str) {
|
fn save_last_user_to(path: &Path, username: &str) {
|
||||||
log::debug!("Saving last user: {username}");
|
log::debug!("Saving last user: {username}");
|
||||||
if let Some(parent) = path.parent() {
|
if let Some(parent) = path.parent()
|
||||||
let _ = std::fs::create_dir_all(parent);
|
&& let Err(e) = std::fs::create_dir_all(parent)
|
||||||
|
{
|
||||||
|
log::warn!("Failed to create cache dir {}: {e}", parent.display());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let _ = std::fs::OpenOptions::new()
|
if let Err(e) = std::fs::OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.mode(0o600)
|
.mode(0o600)
|
||||||
.open(path)
|
.open(path)
|
||||||
.and_then(|mut f| f.write_all(username.as_bytes()));
|
.and_then(|mut f| f.write_all(username.as_bytes()))
|
||||||
|
{
|
||||||
|
log::warn!("Failed to save last user to {}: {e}", path.display());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_last_session(username: &str) -> Option<String> {
|
fn load_last_session(username: &str) -> Option<String> {
|
||||||
@ -1212,13 +1224,16 @@ fn save_last_session_to(path: &Path, session_name: &str) {
|
|||||||
log::debug!("Saving last session: {session_name}");
|
log::debug!("Saving last session: {session_name}");
|
||||||
use std::os::unix::fs::OpenOptionsExt;
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
let _ = std::fs::OpenOptions::new()
|
if let Err(e) = std::fs::OpenOptions::new()
|
||||||
.create(true)
|
.create(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(true)
|
.truncate(true)
|
||||||
.mode(0o600)
|
.mode(0o600)
|
||||||
.open(path)
|
.open(path)
|
||||||
.and_then(|mut f| f.write_all(session_name.as_bytes()));
|
.and_then(|mut f| f.write_all(session_name.as_bytes()))
|
||||||
|
{
|
||||||
|
log::warn!("Failed to save last session to {}: {e}", path.display());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -1233,6 +1248,7 @@ mod tests {
|
|||||||
assert!(is_valid_username("test.user"));
|
assert!(is_valid_username("test.user"));
|
||||||
assert!(is_valid_username("_admin"));
|
assert!(is_valid_username("_admin"));
|
||||||
assert!(is_valid_username("user@domain"));
|
assert!(is_valid_username("user@domain"));
|
||||||
|
assert!(is_valid_username(&"a".repeat(MAX_USERNAME_LENGTH)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1601,6 +1617,18 @@ mod tests {
|
|||||||
assert!(matches!(result, LoginResult::Cancelled));
|
assert!(matches!(result, LoginResult::Cancelled));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn login_worker_connect_failure() {
|
||||||
|
let cancelled = Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||||
|
let result = login_worker(
|
||||||
|
"alice", "pass", "/usr/bin/niri",
|
||||||
|
"/nonexistent/sock", &default_greetd_sock(), &cancelled,
|
||||||
|
load_strings(Some("en")),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn login_worker_invalid_exec_cmd() {
|
fn login_worker_invalid_exec_cmd() {
|
||||||
let (sock_path, handle) = fake_greetd(|stream| {
|
let (sock_path, handle) = fake_greetd(|stream| {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user