fix(pwsp-gui): fix arbitrary file overwrite via path traversal (#159)

* Fix path traversal vulnerability in audio download logic

Co-authored-by: arabianq <55220741+arabianq@users.noreply.github.com>

* std::path::Path -> Path

* add random id to the filename if it's unknown

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
This commit is contained in:
Tarasov Aleksandr
2026-07-07 19:29:12 +03:00
committed by GitHub
co-authored by google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
parent 6ccc55a1d0
commit 4f8f663f12
+25 -9
View File
@@ -1,9 +1,13 @@
mod gui; mod gui;
use anyhow::{Context, Result}; use anyhow::{Context, Result, anyhow};
use pwsp_lib::utils::gui::ensure_pwsp_audio_dir; use pwsp_lib::utils::gui::ensure_pwsp_audio_dir;
use rust_i18n::i18n; use rust_i18n::i18n;
use std::{env, path::PathBuf}; use std::{
env,
path::{Path, PathBuf},
time::{SystemTime, UNIX_EPOCH},
};
i18n!("locales", fallback = "en"); i18n!("locales", fallback = "en");
@@ -31,19 +35,31 @@ async fn download_audio_from_url(uri: &str) -> Result<PathBuf> {
let target_url = uri let target_url = uri
.strip_prefix(prefix) .strip_prefix(prefix)
.ok_or_else(|| anyhow::anyhow!("URI does not containt an expected prefix: {}", prefix))?; .ok_or_else(|| anyhow!("URI does not containt an expected prefix: {}", prefix))?;
let file_name_encoded = target_url let file_name_encoded = match target_url.split('/').next_back() {
.split('/') Some(path) => path.to_string(),
.next_back() None => {
.unwrap_or("downloaded_audio.mp3"); let id = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went back")
.as_nanos();
format!("downloaded_audio_{}.mp3", id)
}
};
let file_name = percent_encoding::percent_decode_str(file_name_encoded) let file_name = percent_encoding::percent_decode_str(&file_name_encoded.clone())
.decode_utf8() .decode_utf8()
.unwrap_or_else(|_| file_name_encoded.into()) .unwrap_or_else(|_| file_name_encoded.into())
.into_owned(); .into_owned();
let save_path = ensure_pwsp_audio_dir().join(file_name); let normalized_file_name = file_name.replace('\\', "/");
let sanitized_file_name = Path::new(&normalized_file_name)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or("downloaded_audio.mp3");
let save_path = ensure_pwsp_audio_dir().join(sanitized_file_name);
let response = reqwest::get(target_url) let response = reqwest::get(target_url)
.await? .await?