From 1b7f018fd601bf2051b0de97c5aeaa5b3aadfe3f Mon Sep 17 00:00:00 2001 From: FabseGP Date: Sat, 7 Feb 2026 09:34:13 +0000 Subject: [PATCH] chore: ensure favicon is png and 64x64 (#1418) --- pumpkin-config/src/lib.rs | 5 +- pumpkin/src/server/connection_cache.rs | 63 ++++++++++++++++++-------- 2 files changed, 46 insertions(+), 22 deletions(-) diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 12b92a49a..e5bb0545a 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -104,7 +104,7 @@ pub struct BasicConfiguration { /// Whether to use a server favicon pub use_favicon: bool, /// Path to server favicon - pub favicon_path: String, + pub favicon_path: Option, /// The default level name pub default_level_name: String, /// Whether chat messages should be signed or not @@ -139,7 +139,7 @@ impl Default for BasicConfiguration { force_gamemode: false, scrub_ips: true, use_favicon: true, - favicon_path: "icon.png".to_string(), + favicon_path: None, default_level_name: "world".to_string(), allow_chat_reports: false, white_list: false, @@ -201,7 +201,6 @@ pub trait LoadConfiguration { merged_config } else { let content = Self::default(); - if let Err(err) = fs::write(&path, toml::to_string(&content).unwrap()) { log::warn!( "Couldn't write default config to {:?}. Reason: {}", diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 6080145d4..93b78278d 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -18,6 +18,14 @@ const DEFAULT_ICON: &[u8] = include_bytes!("../../../assets/default_icon.png"); fn load_icon_from_file>(path: P) -> Result> { let buf = fs::read(path)?; + if buf.len() >= 24 { + let width = u32::from_be_bytes([buf[16], buf[17], buf[18], buf[19]]); + let height = u32::from_be_bytes([buf[20], buf[21], buf[22], buf[23]]); + + if width != 64 || height != 64 { + return Err("Invalid favicon dimensions (must be 64x64)".into()); + } + } Ok(load_icon_from_bytes(&buf)) } @@ -117,28 +125,45 @@ impl CachedStatus { pub fn build_response(config: &BasicConfiguration) -> StatusResponse { let favicon = if config.use_favicon { - let icon_path = &config.favicon_path; - log::debug!("Attempting to load server favicon from '{icon_path}'"); - - match load_icon_from_file(icon_path) { - Ok(icon) => Some(icon), - Err(e) => { - let error_message = e.downcast_ref::().map_or_else( - || format!("other error: {e}; using default."), - |io_err| { - if io_err.kind() == std::io::ErrorKind::NotFound { - "not found; using default.".to_string() - } else { - format!("I/O error: {io_err}; using default.") - } - }, - ); - log::warn!("Failed to load favicon from '{icon_path}': {error_message}"); + config.favicon_path.as_ref().map_or_else( + || { + log::debug!("Loading default icon"); // Attempt to load default icon Some(load_icon_from_bytes(DEFAULT_ICON)) - } - } + }, + |icon_path| { + if !std::path::Path::new(icon_path) + .extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("png")) + { + log::warn!("Favicon is not a PNG-image, using default."); + return Some(load_icon_from_bytes(DEFAULT_ICON)); + } + log::debug!("Attempting to load server favicon from '{icon_path}'"); + + match load_icon_from_file(icon_path) { + Ok(icon) => Some(icon), + Err(e) => { + let error_message = e.downcast_ref::().map_or_else( + || format!("other error: {e}; using default."), + |io_err| { + if io_err.kind() == std::io::ErrorKind::NotFound { + "not found; using default.".to_string() + } else { + format!("I/O error: {io_err}; using default.") + } + }, + ); + log::warn!( + "Failed to load favicon from '{icon_path}': {error_message}" + ); + + Some(load_icon_from_bytes(DEFAULT_ICON)) + } + } + }, + ) } else { log::info!("Favicon usage is disabled."); None