refactor: 移除输入路径的 validate_safe_path 验证,放宽文件访问限制
This commit is contained in:
@@ -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![
|
||||||
|
|||||||
Reference in New Issue
Block a user