# 前端系统兼容性审查报告 > 审查范围:`tauri-app/src` 全部源码 + `tauri-app/src-tauri/src` Rust 层命令 > 审查维度:跨平台(macOS/Windows)、Tauri API、媒体/音频、CSS、网络、文件系统 > 审查日期:2026-05-21 --- ## 一、综述 本次审查共发现 **28 项兼容性问题**,其中: | 级别 | 数量 | 说明 | |------|------|------| | 🔴 严重 | 6 | 可能导致功能失效、安全漏洞或数据损坏 | | 🟡 中等 | 11 | 潜在风险,特定场景下会触发问题 | | 🟢 低风险 | 11 | 建议优化,影响较小或仅存在于边缘场景 | **关键结论**: 1. **Windows 路径处理是最大隐患**:多处 Rust 代码对 Windows 路径的反斜杠、大小写、UNC 前缀处理不完善,可能导致 FFmpeg 调用失败或安全检查被绕过。 2. **前端内存泄漏已确认 1 处**:`CoverDesign.tsx` 的 `URL.createObjectURL` 未释放。 3. **Asset Protocol 过度授权**:`tauri.conf.json` 中 `"scope": "/**"` 允许 WebView 读取整个文件系统。 4. **CSS/Web API 兼容性良好**:项目运行在 Tauri 封装的 WebView(Edge/WebKit)中,现代 CSS 特性和 Web API 支持度较高,未发现严重兼容性问题。 --- ## 二、🔴 严重问题(6 项) ### 1. `URL.createObjectURL` 内存泄漏 — 背景图上传 **位置**:`tauri-app/src/pages/VideoCreation/CoverDesign.tsx:181` ```typescript const url = URL.createObjectURL(file); setConfig(prev => ({ ...prev, backgroundImage: url })); ``` **问题**:本地上传背景图时创建 Blob URL,但**从未调用 `URL.revokeObjectURL(url)`**。用户多次上传不同背景图时,旧的 Blob URL 会一直占用内存,直到页面刷新。 **影响**:内存泄漏,长时间使用后可能导致应用卡顿或崩溃。 **修复建议**: ```typescript const prevUrl = config.backgroundImage; const url = URL.createObjectURL(file); setConfig(prev => ({ ...prev, backgroundImage: url })); // 释放旧的 Blob URL if (prevUrl?.startsWith('blob:')) { URL.revokeObjectURL(prevUrl); } // 组件卸载时也要清理 useEffect(() => { return () => { if (config.backgroundImage?.startsWith('blob:')) { URL.revokeObjectURL(config.backgroundImage); } }; }, []); ``` --- ### 2. Windows 敏感路径检查大小写不敏感问题 **位置**:`tauri-app/src-tauri/src/commands/file.rs:47` ```rust let windows_denied = vec![ r"c:\windows\", r"c:\program files\", r"c:\program files (x86)\", r"c:\users\all users\", ]; ``` **问题**:Windows 文件系统(NTFS)是**大小写保留但大小写不敏感**的。用户传入 `C:\Windows\` 或 `C:\WINDOWS\` 会**完全绕过**上述安全检查。 **影响**:攻击者可通过大小写变体访问系统敏感目录。 **修复建议**: ```rust let path_lower = path.to_lowercase(); let windows_denied = vec![ r"c:\windows\", r"c:\program files\", r"c:\program files (x86)\", r"c:\users\all users\", ]; for denied in &windows_denied { if path_lower.starts_with(denied) { return Err(...); } } ``` --- ### 3. Asset Protocol 范围过度授权 **位置**:`tauri-app/src-tauri/tauri.conf.json` ```json "assetProtocol": { "enable": true, "scope": ["$APPLOCALDATA/**", "$APPDATA/**", "$APPCONFIG/**", "/**"] } ``` **问题**:`/**` 允许 WebView 通过 `asset://` 协议读取**整个文件系统的任何文件**。这意味着前端 JavaScript 可以构造 URL 访问用户的任何本地文件(如 `asset:///etc/passwd` 或 `asset://C:/Users/xxx/Documents/`)。 **影响**:严重安全漏洞。即使需要配合路径遍历,也极大扩大了攻击面。 **修复建议**:移除 `/**`,仅保留应用数据目录: ```json "assetProtocol": { "enable": true, "scope": ["$APPLOCALDATA/**", "$APPDATA/**", "$APPCONFIG/**"] } ``` > 注:如果确有需要访问用户选择的文件,应通过 Tauri Dialog API 让用户主动选择,而非开放全局文件系统。 --- ### 4. `escape_ffmpeg_path` 不支持 Windows 路径格式 **位置**:`tauri-app/src-tauri/src/ffmpeg_cmd.rs:23` ```rust fn escape_ffmpeg_path(path: &str) -> String { path.replace("'", "'\\''") } ``` **问题**:该函数仅转义单引号,但**不处理 Windows 反斜杠 `\` 和盘符冒号 `:`**。在 FFmpeg 的 `ass='{}':fontsdir='{}'` filter 语法或 concat demuxer 的 `file 'path'` 格式中,Windows 路径如 `C:\Users\name\video.mp4` 中的反斜杠可能被 FFmpeg 解析为转义序列。 **影响**:Windows 用户在字幕压制、字体加载、视频合成时,FFmpeg 可能因路径解析错误而失败。 **修复建议**: ```rust fn escape_ffmpeg_path(path: &str) -> String { // 1. 统一使用正斜杠(FFmpeg 支持跨平台路径分隔符) let normalized = path.replace('\\', "/"); // 2. 转义单引号(用于 FFmpeg filter 语法中的引号包裹) normalized.replace("'", "'\\''") } ``` > 注意:Windows 上 `C:/Users/...` 这种正斜杠路径 FFmpeg 完全支持,这是最简单的跨平台方案。 --- ### 5. `canonicalize()` 在 Windows 上返回 UNC 路径导致下游问题 **位置**:多处使用 `std::fs::canonicalize` - `tauri-app/src-tauri/src/commands/product.rs:198,258,345` - `tauri-app/src-tauri/src/commands/project.rs:124,140` - `tauri-app/src-tauri/src/ffmpeg_cmd.rs:46,792` **问题**:在 Windows 上,`std::fs::canonicalize()` 返回 UNC 路径格式 `\\?\C:\Users\...`。这种路径格式: 1. **FFmpeg 某些版本不支持**,可能导致命令执行失败 2. **与 `starts_with` 比较时行为异常**,如果比较路径不是 UNC 格式 3. **序列化到 JSON 传给前端时**,前端可能无法正确理解这种路径 **影响**:Windows 上的文件校验、路径比较、FFmpeg 调用可能全部受影响。 **修复建议**:封装一个跨平台的 `normalize_path` 函数,替代 `canonicalize`: ```rust use std::path::{Path, PathBuf}; fn normalize_path(path: &Path) -> PathBuf { // 使用 dunce::simplified() 消除 UNC 前缀,同时保持路径有效性 dunce::simplified(path).to_path_buf() } ``` > 需要添加 `dune` crate 依赖,这是 Rust 社区处理 UNC 路径的标准方案。 --- ### 6. `atob()` 解析 JWT 存在 base64url 兼容性问题 **位置**:`tauri-app/src/api/client.ts:116` ```typescript const payload = JSON.parse(atob(token.split('.')[1])); ``` **问题**:JWT 使用 **base64url** 编码(将 `+` → `-`,`/` → `_`,去掉 padding `=`),而 `atob()` 是标准 **base64** 解码器。如果 JWT payload 中包含 `-`、`_` 或需要 padding 的字符,`atob()` 会抛出 `DOMException`。 **当前影响有限**:因为 `exp` 字段通常是纯数字时间戳,但理论上如果用户 ID 或其他 claim 包含这些字符就会失败。 **修复建议**: ```typescript function base64UrlDecode(str: string): string { // base64url → base64 let padding = ''; const padLen = 4 - (str.length % 4); if (padLen !== 4) { padding = '='.repeat(padLen); } const base64 = str.replace(/-/g, '+').replace(/_/g, '/') + padding; return atob(base64); } // 使用 const payload = JSON.parse(base64UrlDecode(token.split('.')[1])); ``` --- ## 三、🟡 中等问题(11 项) ### 7. `crossOrigin = 'anonymous'` 跨域图片污染 Canvas **位置**: - `tauri-app/src/hooks/useCoverFabric.ts:192,235` ```typescript image.crossOrigin = 'anonymous'; image.src = imagePath; ``` **问题**:当加载远程 HTTP(S) 图片时,如果服务器未配置 `Access-Control-Allow-Origin` 响应头,Canvas 会被**污染(tainted)**。被污染的 Canvas 调用 `toDataURL()` 会抛出 `SecurityError: The canvas has been tainted by cross-origin data`。 当前代码用 try-catch 静默吞掉了错误,用户会看到空白封面,但不知道原因。 **修复建议**:捕获错误并向用户提示: ```typescript try { // ...加载图片... } catch (err) { console.error('封面图片加载失败:', err); toast.error('封面图片加载失败,可能是跨域限制或图片链接失效'); } ``` --- ### 8. `requestAnimationFrame` + Video 字幕同步在后台标签页节流 **位置**:`tauri-app/src/hooks/useCanvasSubtitleRenderer.ts:158-168` ```typescript const onFrame = () => { drawFrame(); if (!video.paused) { rafRef.current = requestAnimationFrame(onFrame); } }; ``` **问题**:当应用窗口不在前台或标签页在后台时,浏览器会**节流 `requestAnimationFrame`**(通常降到 1fps 或完全暂停)。这会导致 Canvas 字幕与视频画面不同步。 **影响**:用户切出应用再切回时,字幕可能短暂错位。 **修复建议**:使用 `video.requestVideoFrameCallback()`(如果支持)作为更精确的同步机制,或在 `visibilitychange` 事件触发时强制重绘: ```typescript useEffect(() => { const onVisibilityChange = () => { if (!document.hidden) drawFrame(); }; document.addEventListener('visibilitychange', onVisibilityChange); return () => document.removeEventListener('visibilitychange', onVisibilityChange); }, [drawFrame]); ``` --- ### 9. `video` 控制条高度硬编码导致字幕定位偏移 **位置**:`tauri-app/src/hooks/useCanvasSubtitleRenderer.ts:86` ```typescript const VIDEO_CONTROLS_HEIGHT = 40; ``` **问题**:`