fix(security): fix percent_decode to handle multi-byte UTF-8 properly

The previous implementation decoded percent-encoded bytes one at a time
and cast each to char, which produces invalid characters for multi-byte
UTF-8 sequences (e.g. Chinese filenames). Fixed to accumulate raw bytes
first and convert to String at the end with proper UTF-8 validation.
This commit is contained in:
2026-03-31 23:29:00 +08:00
parent a8108cea9d
commit 566cfd0cbc

View File

@@ -1624,29 +1624,29 @@ fn file_mkdir_route(
}
fn percent_decode(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let mut bytes = Vec::with_capacity(s.len());
let mut chars = s.bytes();
while let Some(b) = chars.next() {
if b == b'%' {
let h = chars.next().unwrap_or(0);
let l = chars.next().unwrap_or(0);
let hex = [h, l];
if let Ok(s) = std::str::from_utf8(&hex) {
if let Ok(val) = u8::from_str_radix(s, 16) {
result.push(val as char);
if let Ok(hex_str) = std::str::from_utf8(&hex) {
if let Ok(val) = u8::from_str_radix(hex_str, 16) {
bytes.push(val);
continue;
}
}
result.push('%');
result.push(h as char);
result.push(l as char);
bytes.push(b'%');
bytes.push(h);
bytes.push(l);
} else if b == b'+' {
result.push(' ');
bytes.push(b' ');
} else {
result.push(b as char);
bytes.push(b);
}
}
result
String::from_utf8(bytes).unwrap_or_else(|e| String::from_utf8_lossy(e.as_bytes()).into_owned())
}
fn list_video_files(dir: &Path) -> Vec<VideoFileInfo> {