From 89ce111542be1b3e775dd75272279946070648fc Mon Sep 17 00:00:00 2001 From: Tarasov Aleksandr <55220741+arabianq@users.noreply.github.com> Date: Fri, 6 Mar 2026 23:02:07 +0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=92=20[security=20fix]=20Handle=20seri?= =?UTF-8?q?alization=20failures=20in=20daemon=20commands=20and=20socket=20?= =?UTF-8?q?communication.=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced `.unwrap()` with proper error handling during JSON serialization in `GetStateCommand`, `GetTracksCommand`, and `GetFullStateCommand`. - Added error handling for malformed client requests in the daemon's main loop. - Ensured the daemon stays running even if serialization or deserialization fails. - Handled potential errors from `get_all_devices()`. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- src/bin/daemon.rs | 24 ++++++++++++++++++++++-- src/types/commands.rs | 27 +++++++++++++++++++++------ 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/bin/daemon.rs b/src/bin/daemon.rs index f35e431..0653289 100644 --- a/src/bin/daemon.rs +++ b/src/bin/daemon.rs @@ -95,7 +95,21 @@ async fn commands_loop(listener: UnixListener) -> Result<(), Box> { return; } - let request: Request = serde_json::from_slice(&buffer).unwrap(); + let request: Request = match serde_json::from_slice(&buffer) { + Ok(req) => req, + Err(err) => { + let response = + Response::new(false, format!("Failed to parse request: {}", err)); + let response_data = match serde_json::to_vec(&response) { + Ok(data) => data, + Err(_) => return, // Should not happen with this simple Response + }; + let response_len = response_data.len() as u32; + let _ = stream.write_all(&response_len.to_le_bytes()).await; + let _ = stream.write_all(&response_data).await; + return; + } + }; // ---------- Read request (end) ---------- // ---------- Generate response (start) ---------- @@ -109,7 +123,13 @@ async fn commands_loop(listener: UnixListener) -> Result<(), Box> { // ---------- Generate response (end) ---------- // ---------- Send response (start) ---------- - let response_data = serde_json::to_vec(&response).unwrap(); + let response_data = match serde_json::to_vec(&response) { + Ok(data) => data, + Err(err) => { + eprintln!("Failed to serialize response: {}", err); + return; + } + }; let response_len = response_data.len() as u32; if stream.write_all(&response_len.to_le_bytes()).await.is_err() { diff --git a/src/types/commands.rs b/src/types/commands.rs index 69c99d7..8e149eb 100644 --- a/src/types/commands.rs +++ b/src/types/commands.rs @@ -183,7 +183,10 @@ impl Executable for GetStateCommand { async fn execute(&self) -> Response { let audio_player = get_audio_player().await.lock().await; let state = audio_player.get_state(); - Response::new(true, serde_json::to_string(&state).unwrap()) + match serde_json::to_string(&state) { + Ok(json) => Response::new(true, json), + Err(err) => Response::new(false, format!("Failed to serialize state: {}", err)), + } } } @@ -272,7 +275,10 @@ impl Executable for GetTracksCommand { async fn execute(&self) -> Response { let audio_player = get_audio_player().await.lock().await; let tracks = audio_player.get_tracks(); - Response::new(true, serde_json::to_string(&tracks).unwrap()) + match serde_json::to_string(&tracks) { + Ok(json) => Response::new(true, json), + Err(err) => Response::new(false, format!("Failed to serialize tracks: {}", err)), + } } } @@ -298,7 +304,10 @@ impl Executable for GetCurrentInputCommand { #[async_trait] impl Executable for GetAllInputsCommand { async fn execute(&self) -> Response { - let (input_devices, _output_devices) = get_all_devices().await.unwrap(); + let (input_devices, _output_devices) = match get_all_devices().await { + Ok(devices) => devices, + Err(err) => return Response::new(false, format!("Failed to get devices: {}", err)), + }; let mut input_devices_strings = vec![]; for device in input_devices { if device.name == "pwsp-virtual-mic" { @@ -375,7 +384,10 @@ impl Executable for GetDaemonVersionCommand { #[async_trait] impl Executable for GetFullStateCommand { async fn execute(&self) -> Response { - let (input_devices, _output_devices) = get_all_devices().await.unwrap(); + let (input_devices, _output_devices) = match get_all_devices().await { + Ok(devices) => devices, + Err(err) => return Response::new(false, format!("Failed to get devices: {}", err)), + }; let mut all_inputs = HashMap::new(); let mut current_input_nick = String::new(); @@ -400,9 +412,12 @@ impl Executable for GetFullStateCommand { tracks: audio_player.get_tracks(), volume: audio_player.volume, current_input: current_input_nick, - all_inputs: all_inputs, + all_inputs, }; - Response::new(true, serde_json::to_string(&full_state).unwrap()) + match serde_json::to_string(&full_state) { + Ok(json) => Response::new(true, json), + Err(err) => Response::new(false, format!("Failed to serialize full state: {}", err)), + } } }