refactor: 移除输入路径的 validate_safe_path 验证,放宽文件访问限制

This commit is contained in:
小鱼开发
2026-06-09 11:22:57 +08:00
parent da03669a99
commit c2209dec85
+16 -67
View File
@@ -60,50 +60,10 @@ fn normalize_path(path: &std::path::Path) -> String {
} }
} }
/// 验证路径在允许的目录内,防止路径遍历攻击 /// 清理输出路径(转绝对路径)
/// 允许的目录:应用数据目录(app_local_data_dir
fn validate_safe_path(path: &str) -> Result<String, String> {
let path = std::path::Path::new(path);
// 获取绝对路径
let abs_path = if path.is_absolute() {
path.to_path_buf()
} else {
std::env::current_dir()
.map_err(|e| format!("无法获取当前目录: {}", e))?
.join(path)
};
// 检查是否在允许的目录内
let allowed_dir = crate::storage::paths::get_app_data_dir()
.map_err(|e| format!("无法获取应用数据目录: {}", e))?;
// 规范化路径
let canonical = abs_path.canonicalize()
.unwrap_or(abs_path.clone());
// 同时规范化允许目录(Windows 上 canonicalize 会添加 \\?\ 前缀,
// 必须与输入路径保持一致的规范化格式才能正确比较)
let allowed_canonical = allowed_dir.canonicalize()
.unwrap_or(allowed_dir.clone());
// 检查是否在允许目录下
if !canonical.starts_with(&allowed_canonical) {
return Err(format!("路径不在允许目录内: {}", path.display()));
}
Ok(canonical.to_string_lossy().to_string())
}
/// 清理并验证输出路径
pub fn sanitize_output_path(path: &str) -> Result<String, String> { pub fn sanitize_output_path(path: &str) -> Result<String, String> {
let path = std::path::Path::new(path); let path = std::path::Path::new(path);
// 获取父目录并验证
if let Some(parent) = path.parent() {
validate_safe_path(&parent.to_string_lossy())?;
}
// 确保是绝对路径 // 确保是绝对路径
if path.is_absolute() { if path.is_absolute() {
Ok(path.to_string_lossy().to_string()) Ok(path.to_string_lossy().to_string())
@@ -215,8 +175,7 @@ pub async fn standardize_video(
target_width: u32, target_width: u32,
target_height: u32, target_height: u32,
) -> Result<(), String> { ) -> Result<(), String> {
// 验证路径安全 let safe_input = input_path.to_string();
let safe_input = validate_safe_path(input_path)?;
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let vf = format!( let vf = format!(
@@ -244,8 +203,7 @@ pub async fn standardize_video(
* 拼接视频 - 快速模式 (要求编码/分辨率一致) * 拼接视频 - 快速模式 (要求编码/分辨率一致)
*/ */
pub async fn concat_videos_copy(app: &AppHandle, list_path: &str, output_path: &str) -> Result<(), String> { pub async fn concat_videos_copy(app: &AppHandle, list_path: &str, output_path: &str) -> Result<(), String> {
// 验证路径安全 let safe_list = list_path.to_string();
let safe_list = validate_safe_path(list_path)?;
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let args = vec![ let args = vec![
@@ -321,9 +279,8 @@ pub async fn concat_videos_robust(
* 音画合并 - 优化音质 * 音画合并 - 优化音质
*/ */
pub async fn add_audio_to_video(app: &AppHandle, video_path: &str, audio_path: &str, output_path: &str) -> Result<(), String> { pub async fn add_audio_to_video(app: &AppHandle, video_path: &str, audio_path: &str, output_path: &str) -> Result<(), String> {
// 验证路径安全 let safe_video = video_path.to_string();
let safe_video = validate_safe_path(video_path)?; let safe_audio = audio_path.to_string();
let safe_audio = validate_safe_path(audio_path)?;
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let args = vec![ let args = vec![
@@ -353,8 +310,7 @@ pub async fn create_cover_video(
target_width: u32, target_width: u32,
target_height: u32, target_height: u32,
) -> Result<(), String> { ) -> Result<(), String> {
// 验证路径安全 let safe_input = input_path.to_string();
let safe_input = validate_safe_path(input_path)?;
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let vf = format!( let vf = format!(
@@ -491,13 +447,13 @@ pub async fn burn_ass_subtitle(
output_path: &str, output_path: &str,
overlay_image: Option<&str>, overlay_image: Option<&str>,
) -> Result<(), String> { ) -> Result<(), String> {
// 输入路径验证HTTP URL 直接传递,本地文件需要安全检查 // 输入路径:HTTP URL 直接传递,本地文件直接传递
let safe_video = if video_path.starts_with("http://") || video_path.starts_with("https://") { let safe_video = if video_path.starts_with("http://") || video_path.starts_with("https://") {
video_path.to_string() video_path.to_string()
} else { } else {
validate_safe_path(video_path)? video_path.to_string()
}; };
let safe_ass = validate_safe_path(ass_path)?; let safe_ass = ass_path.to_string();
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
// 准备临时工作目录(ASS + 字体),使用相对路径避开 Windows C: 盘符问题 // 准备临时工作目录(ASS + 字体),使用相对路径避开 Windows C: 盘符问题
@@ -505,7 +461,7 @@ pub async fn burn_ass_subtitle(
// 如果有 overlay 图片,先 overlay 再 burn 字幕(两步避免 filter_complex 音频映射问题) // 如果有 overlay 图片,先 overlay 再 burn 字幕(两步避免 filter_complex 音频映射问题)
if let Some(img_path) = overlay_image { if let Some(img_path) = overlay_image {
let safe_img = validate_safe_path(img_path)?; let safe_img = img_path.to_string();
let temp_output = format!("{}.tmp_overlay.mp4", safe_output); let temp_output = format!("{}.tmp_overlay.mp4", safe_output);
// Step 1: overlay PNG 到视频 // Step 1: overlay PNG 到视频
@@ -588,14 +544,8 @@ pub async fn mix_bgm_to_video(
video_volume: f64, video_volume: f64,
bgm_volume: f64, bgm_volume: f64,
) -> Result<(), String> { ) -> Result<(), String> {
let safe_video = validate_safe_path(video_path)?; let safe_video = video_path.to_string();
// BGM 可以是用户通过系统文件选择器选取的任意本地文件, let safe_bgm = bgm_path.to_string();
// 验证文件存在且不含路径遍历字符
let safe_bgm = if !bgm_path.contains("..") && std::path::Path::new(bgm_path).exists() {
bgm_path.to_string()
} else {
return Err(format!("BGM 文件不存在或路径非法: {}", bgm_path));
};
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
// 构建 filter_complex: // 构建 filter_complex:
@@ -635,9 +585,8 @@ pub async fn replace_audio_track(
audio_path: &str, audio_path: &str,
output_path: &str, output_path: &str,
) -> Result<(), String> { ) -> Result<(), String> {
// 验证路径安全 let safe_video = video_path.to_string();
let safe_video = validate_safe_path(video_path)?; let safe_audio = audio_path.to_string();
let safe_audio = validate_safe_path(audio_path)?;
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let args = vec![ let args = vec![
@@ -839,7 +788,7 @@ pub async fn extract_audio_segment(
duration: f64, duration: f64,
output_path: &str, output_path: &str,
) -> Result<(), String> { ) -> Result<(), String> {
let safe_input = validate_safe_path(input_path)?; let safe_input = input_path.to_string();
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let start_str = format!("{:.3}", start); let start_str = format!("{:.3}", start);
@@ -873,7 +822,7 @@ pub async fn extract_audio_from_video(
input_path: &str, input_path: &str,
output_path: &str, output_path: &str,
) -> Result<(), String> { ) -> Result<(), String> {
let safe_input = validate_safe_path(input_path)?; let safe_input = input_path.to_string();
let safe_output = sanitize_output_path(output_path)?; let safe_output = sanitize_output_path(output_path)?;
let args = vec![ let args = vec![