Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5dee57eeed | |||
| 6dba07f98e |
@@ -0,0 +1,168 @@
|
||||
# PR #312 多轨道混音+字幕渲染+视频拼接 审计报告
|
||||
|
||||
## 总览
|
||||
|
||||
- **结论**:⚠️ 有条件通过(需修复 P1 问题后方可合并)
|
||||
- **问题统计**:P0 0 项,P1 4 项,P2 9 项,P3 5 项
|
||||
- **改动范围**:6 个文件,+2614 行,-2 行
|
||||
- **PR标题**:feat: 多轨道混音 + 字幕渲染引擎 + 视频拼接(后端)
|
||||
- **改动文件**:
|
||||
- `apps/worker/video_processing/multi_track_mixer.py`(新增,+392 行)
|
||||
- `apps/worker/video_processing/subtitle_render_engine.py`(新增,+636 行)
|
||||
- `apps/worker/video_processing/concat_engine.py`(新增,+642 行)
|
||||
- `apps/worker/video_processing/render_audio.py`(修改,+19/-2)
|
||||
- `apps/worker/video_processing/unified_render_service.py`(修改,+2/-0)
|
||||
- `tests/unit/test_multi_track_subtitle_concat.py`(新增,+923 行)
|
||||
|
||||
## 三大重点审计结论
|
||||
|
||||
### 1. FFmpeg 滤镜注入风险
|
||||
|
||||
**总体评价**:✅ 低风险
|
||||
|
||||
- ✅ 所有 FFmpeg 调用均通过 `run_ffmpeg` 使用列表参数传递(`subprocess.run` with list args),无 `shell=True`,不存在命令注入风险
|
||||
- ✅ 数字参数(音量、时长、坐标等)均经过 `float()` / `int()` 转换,并设有范围钳制(如 `volume` 限制在 0.0~2.0)
|
||||
- ✅ 多轨道混音的 `filter_complex` 由受控的数值和内部标签拼接,标签名(`v{i}_in`、`a{i}_in` 等)由代码生成,不可控
|
||||
- ⚠️ `build_subtitle_filter` 的路径转义不完整(未转义 `[` `]`),但当前仅用于服务器生成的路径,风险较低
|
||||
- ⚠️ concat demuxer 的 list 文件中路径仅转义单引号,虽不直接导致命令注入(走 list 文件而非 shell),但若路径中含换行符可能拼接新的 file 行
|
||||
|
||||
### 2. 字幕文件路径安全
|
||||
|
||||
**总体评价**:⚠️ 中等风险(路径来源需关注)
|
||||
|
||||
- ✅ ASS 文件生成位置在 `work_dir` 内(服务器控制),文件名由 `plan_id` 构成,无路径遍历
|
||||
- ✅ ASS 文本内容经过 `_escape_ass_text` 转义(大括号→圆括号、换行→\N),防止 ASS 覆盖标签注入
|
||||
- ✅ 颜色值经过 `_hex_to_ass_color` 校验(非 6 位 hex 降级为白色)
|
||||
- ⚠️ `build_subtitle_filter` 中的字幕文件路径若来自用户输入,可能存在 filter 语法注入风险
|
||||
- ⚠️ 字幕文件无大小上限(极端场景下大量字幕片段可能生成超大 ASS 文件)
|
||||
- ❌ 新增模块未接入任何 `path_security` 校验(注:当前代码库中尚未发现 path_security.py 模块,仅在 __pycache__ 中有缓存文件)
|
||||
|
||||
### 3. 拼接资源消耗控制
|
||||
|
||||
**总体评价**:❌ 高风险(缺少关键限制)
|
||||
|
||||
- ❌ **拼接段数无上限**:`ConcatConfig.from_config_dict` 不限制 segments 数量,大量片段拼接会消耗大量内存和 CPU
|
||||
- ❌ **轨道数量无上限**:`MultiTrackMixConfig.from_config_dict` 不限制 tracks 数量
|
||||
- ❌ **无总时长上限**:拼接后总时长没有任何限制
|
||||
- ❌ **无单段文件大小限制**:输入文件大小没有校验
|
||||
- ⚠️ 使用 concat filter 模式时,所有输入文件同时解码,内存占用与段数成正比
|
||||
- ✅ 有 30 分钟的 FFmpeg 超时机制(`DEFAULT_FFMPEG_TIMEOUT = 1800`)
|
||||
- ✅ worker 级别有 4 并发限制(`worker_concurrency: int = 4`)
|
||||
- ⚠️ 临时文件(track 预处理文件、concat_list.txt)使用后未清理
|
||||
|
||||
## 问题清单(按等级)
|
||||
|
||||
### P0
|
||||
|
||||
无。所有 FFmpeg 调用均使用列表参数,不存在直接的命令注入漏洞。
|
||||
|
||||
### P1
|
||||
|
||||
1. **【multi_track_mixer.py】audio_path 无路径安全校验**
|
||||
- 位置:`AudioTrack.from_dict`(L98)及 `_prepare_single_track`(L173/L240)
|
||||
- 描述:`audio_path` 直接从用户配置中读取字符串,未经过任何路径校验(如路径白名单、路径遍历检测)。用户可通过 `plan.config.audio_tracks.tracks[].audio_path` 传入任意文件路径,ffprobe/ffmpeg 会读取该文件。虽不直接执行命令,但可探测服务器上任意文件的存在性,且音频/视频文件会被转码后进入输出流。
|
||||
- 风险:任意文件读取/探测(信息泄露)
|
||||
- 建议:参照项目既有的 `asset_id → local_path` 解析模式,或引入 `path_security` 模块校验路径必须在指定目录内
|
||||
|
||||
2. **【concat_engine.py】video_path 无路径安全校验**
|
||||
- 位置:`ConcatSegment.from_dict`(L72)及 `_concat_filter`(L319)、`_concat_demuxer`(L281)
|
||||
- 描述:`video_path` 直接从配置读取,无任何路径安全校验。同理,用户可通过拼接配置读取服务器任意文件。
|
||||
- 风险:任意文件读取/探测(信息泄露)
|
||||
- 建议:同上,增加路径安全校验
|
||||
|
||||
3. **【multi_track_mixer.py】无轨道数量上限**
|
||||
- 位置:`MultiTrackMixConfig.from_config_dict`(L119-L137)
|
||||
- 描述:`tracks` 列表无数量上限校验,用户可传入成百上千个轨道,导致 FFmpeg 同时打开大量文件、构建复杂 filter_complex,消耗大量 CPU/内存/文件描述符。
|
||||
- 风险:资源耗尽(DoS)
|
||||
- 建议:设置合理上限(如 `MAX_TRACKS = 16`),超过则截断并记录告警
|
||||
|
||||
4. **【concat_engine.py】无拼接段数上限**
|
||||
- 位置:`ConcatConfig.from_config_dict`(L99-L113)
|
||||
- 描述:`segments` 列表无数量上限校验。concat filter 模式下所有输入文件同时解码,内存占用线性增长。
|
||||
- 风险:资源耗尽(DoS)
|
||||
- 建议:设置合理上限(如 `MAX_SEGMENTS = 50`),超过则截断或报错
|
||||
|
||||
### P2
|
||||
|
||||
5. **【concat_engine.py】无总时长/单段时长上限**
|
||||
- 描述:没有限制拼接后总时长或单段时长,超长视频拼接会长时间占用 worker。
|
||||
- 建议:增加 `MAX_TOTAL_DURATION`(如 2 小时)和 `MAX_SEGMENT_DURATION` 限制
|
||||
|
||||
6. **【multi_track_mixer.py】预处理临时文件未清理**
|
||||
- 位置:`mix_multi_track` 方法(L303)
|
||||
- 描述:`track_{i}_{plan_id}.aac` 等临时文件在混音完成后未被清理,长期运行会占用磁盘空间。
|
||||
- 建议:混音完成后 `try/finally` 清理临时轨道文件
|
||||
|
||||
7. **【concat_engine.py】concat_list.txt 未清理**
|
||||
- 位置:`_concat_demuxer`(L275)
|
||||
- 描述:生成的 concat demuxer 列表文件使用后不删除。
|
||||
- 建议:使用完毕后删除列表文件
|
||||
|
||||
8. **【concat_engine.py】transition 字段无白名单校验**
|
||||
- 位置:`ConcatConfig.from_config_dict`(L132)
|
||||
- 描述:`transition = str(config.get("transition", "none"))` 直接转字符串,无白名单验证。虽然目前仅判断 `!= "none"` 时禁用 stream copy,但未来扩展转场效果(如 xfade)时,transition 名称会进入 filter_complex,存在注入风险。
|
||||
- 建议:使用枚举或白名单列表校验 transition 值
|
||||
|
||||
9. **【subtitle_render_engine.py】字幕滤镜路径转义不完整**
|
||||
- 位置:`build_subtitle_filter`(L633-L636)
|
||||
- 描述:仅转义了 `\`、`:`、`'`,未转义 `[`、`]`。若字幕文件路径中包含方括号,可能破坏 filter_complex 语法导致 FFmpeg 报错。
|
||||
- 当前风险:低(路径由服务器生成,不含方括号)
|
||||
- 建议:补充 `[` → `\\[`、`]` → `\\]` 的转义
|
||||
|
||||
10. **【subtitle_render_engine.py】字幕片段数量/文件大小无上限**
|
||||
- 描述:ASS 文件可包含无限多字幕片段,极端场景下可能生成超大文件。
|
||||
- 建议:增加 `MAX_SEGMENTS` 限制(如 10000 条)
|
||||
|
||||
11. **【concat_engine.py】_concat_filter 大量死代码**
|
||||
- 位置:`_concat_filter` 方法 L330-L418
|
||||
- 描述:方法前半部分(约 100 行)构建的 `filter_parts` 和 `concat_inputs` 在 L418 被 `filter_parts.clear()` 全部清除,然后从 L420 重新构建。前半部分是完全无用的死代码,增加维护成本且容易误读。
|
||||
- 建议:删除前半部分死代码,保留清晰的实现
|
||||
|
||||
12. **【multi_track_mixer.py】track_id/track_type 无字符校验(日志注入)**
|
||||
- 描述:`track_id` 和 `track_type` 是用户可控字符串,直接用于日志输出,存在日志注入风险(如插入伪造日志行)。
|
||||
- 建议:对日志输出的字符串做长度和换行符限制
|
||||
|
||||
13. **【API 层】新增配置字段无 schema 校验**
|
||||
- 位置:`packages/domain/config_schemas.py` 的 `normalize_plan_config`
|
||||
- 描述:`normalize_plan_config` 仅校验和清洗 `cover/title/subtitle/bgm/editing_mode/transition_enabled` 字段。新增加的 `audio_tracks`、`manual_subtitles`、`title_config`、`subtitle_config`(新格式)等字段未纳入校验,任意 JSON 都可以透传到 worker。
|
||||
- 建议:在 config_schemas 中新增对应的数据模型和校验逻辑
|
||||
|
||||
### P3
|
||||
|
||||
14. **【concat_engine.py】未使用的导入**
|
||||
- `import tempfile` 和 `from typing import Any` 未使用
|
||||
|
||||
15. **【subtitle_render_engine.py】未使用的导入**
|
||||
- `dataclasses.field`、`generate_ass_subtitles`、`generate_ass_from_timeline` 未使用(预留导入)
|
||||
|
||||
16. **【subtitle_render_engine.py】尚未集成到主渲染流程**
|
||||
- `SubtitleRenderEngine` 和 `build_subtitles_from_plan` 是独立模块,`unified_render_service.py` 仍使用旧的 `render_subtitles.py` 和 `subtitle_generator.py`,新引擎暂未接入
|
||||
|
||||
17. **【concat_engine.py】尚未集成到主渲染流程**
|
||||
- `ConcatEngine` 仅提供独立的拼接能力,未接入 `UnifiedRenderService` 的渲染管线
|
||||
|
||||
18. **【concat_engine.py】transition 文档标注"预留"但已有逻辑判断**
|
||||
- 字段注释写着"转场效果(none/crossfade)- 预留",但 `_can_use_stream_copy` 中已有 `if config.transition != "none": return False` 的判断,建议统一描述
|
||||
|
||||
## 亮点
|
||||
|
||||
1. **优秀的降级策略**:三个模块均有完善的降级/容错机制——单轨失败跳过、混音失败回退主音频、参数不一致自动降级到 filter 模式、配置无效返回 None 等,符合"能力不可用时不阻断主流程"的设计原则
|
||||
2. **完善的参数安全转换**:所有数字参数都经过 try/except 包裹的类型转换,失败时降级到默认值;音量、不透明度等有范围钳制
|
||||
3. **统一使用 run_ffmpeg**:所有 FFmpeg 调用均通过 `run_ffmpeg` 工具函数,使用列表参数传递,从根本上避免 shell 注入
|
||||
4. **单测覆盖充分**:52 个新增单测覆盖了配置解析、正常流程、异常场景、降级路径,完整度较高
|
||||
5. **代码结构清晰**:三个模块均遵循统一的风格(dataclass + from_dict + 引擎类 + 便捷函数),符合 UnifiedRenderService 架构风格
|
||||
6. **ASS 文本转义到位**:`_escape_ass_text` 正确转义了大括号(防止 ASS 覆盖标签注入)和换行符
|
||||
|
||||
## 修复建议(优先级排序)
|
||||
|
||||
1. **【P1】** 为 `audio_path` 和 `video_path` 增加路径安全校验(接入 path_security 模块或使用 asset_id 解析模式)
|
||||
2. **【P1】** 增加轨道数量上限(建议 `MAX_TRACKS = 16`)
|
||||
3. **【P1】** 增加拼接段数上限(建议 `MAX_SEGMENTS = 50`)
|
||||
4. **【P2】** 增加拼接总时长上限和单段时长上限
|
||||
5. **【P2】** 清理临时文件(预处理轨道文件、concat_list.txt)
|
||||
6. **【P2】** `transition` 字段增加白名单校验
|
||||
7. **【P2】** 删除 `_concat_filter` 中的死代码
|
||||
8. **【P2】** 字幕滤镜路径转义补充 `[` `]`
|
||||
9. **【P2】** API 层 config_schemas 增加新配置字段的校验模型
|
||||
10. **【P3】** 清理未使用的导入
|
||||
11. **【P3】** 字幕片段数量增加合理上限
|
||||
Executable
+324
@@ -0,0 +1,324 @@
|
||||
# 第二批渲染能力PR + P1问题复审 审计报告
|
||||
|
||||
**审计日期:** 2026-07-14
|
||||
**审计范围:** 9个PR(5个新渲染能力 + 4个P1问题复审)
|
||||
**审计重点:** 接口入参校验、安全风险、异常处理、单测覆盖、代码规范、降级机制
|
||||
|
||||
---
|
||||
|
||||
## 审计结果总览
|
||||
|
||||
| PR | 功能 | 结论 | P0 | P1 | P2 | P3 |
|
||||
|----|------|------|----|----|----|----|
|
||||
| #295 | TTS配音引擎 | ⚠️ 有条件通过 | 0 | 0 | 2 | 1 |
|
||||
| #296 | 视频裁剪/分割 | ✅ 通过 | 0 | 0 | 0 | 0 |
|
||||
| #298 | 水印+片头片尾 | ⚠️ 有条件通过 | 0 | 0 | 5 | 1 |
|
||||
| #299 | 画中画PiP | ⚠️ 有条件通过 | 0 | 0 | 2 | 1 |
|
||||
| #303 | 绿幕抠像+音频降噪 | ✅ 通过 | 0 | 0 | 0 | 0 |
|
||||
| #287 | 成片中心(复审) | ❌ 不推荐 | 0 | 1 | 0 | 1 |
|
||||
| #290 | 批量操作(复审) | ✅ 通过 | 0 | 0 | 0 | 0 |
|
||||
| #292 | ASR字幕(复审) | ❌ 不推荐 | 0 | 1 | 0 | 0 |
|
||||
| #291 | BGM混音(复审) | ❌ 不推荐 | 0 | 1 | 0 | 0 |
|
||||
|
||||
**汇总:** 通过2个,有条件通过3个,不推荐4个
|
||||
|
||||
---
|
||||
|
||||
## 第一批:5个新渲染能力PR审计
|
||||
|
||||
### PR #295 - TTS配音引擎 ⚠️ 有条件通过
|
||||
|
||||
**核心改动:**
|
||||
- 新增 TTS Port/Adapter 架构(`packages/ports/tts_service.py` + `packages/adapters/tts/mock_tts_service.py`)
|
||||
- 新增 TTS 配音引擎(`apps/worker/video_processing/tts_engine.py`)
|
||||
- 新增音色预设库(`packages/domain/voice_presets.py`,8种Mock音色)
|
||||
- 新增 TTS 服务工厂(`apps/worker/services/tts_service_factory.py`)
|
||||
- UnifiedRenderService 集成 TTS 配音(audio图层混音)
|
||||
- 新增 `/tts/presets` 接口获取音色列表
|
||||
- 413行单元测试
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P2 - MockTtsService 未使用 run_ffmpeg 工具函数
|
||||
- **位置:** `packages/adapters/tts/mock_tts_service.py:_synthesize_with_ffmpeg`
|
||||
- **问题描述:** 使用 `subprocess.run` 直接调用 ffmpeg,未使用项目统一的 `run_ffmpeg` 工具函数。虽然是 Mock 实现且参数均为内部生成,安全风险低,但不符合项目规范,也缺少统一的超时管理和错误处理。
|
||||
- **修复建议:** 改用 `from video_processing.ffmpeg_utils import run_ffmpeg` 调用。
|
||||
|
||||
#### P2 - 未使用变量和导入
|
||||
- **位置:**
|
||||
- `packages/adapters/tts/mock_tts_service.py:164` - `filters` 变量赋值但从未使用
|
||||
- `packages/domain/tts_config.py:5` - `dataclasses.field` 导入但未使用
|
||||
- **问题描述:** pyflakes 检查发现未使用的变量和导入
|
||||
- **修复建议:** 清理未使用代码。
|
||||
|
||||
#### P3 - TTS预设列表接口无分页
|
||||
- **位置:** `apps/api/app/api/routes/tts.py:list_preset_voices`
|
||||
- **问题描述:** 当前只有8个Mock音色问题不大,但后续接入真实TTS供应商后音色数量可能很多,建议预留分页。
|
||||
- **修复建议:** 暂无强制要求,后续扩展时考虑。
|
||||
|
||||
**亮点:**
|
||||
1. ✅ Port/Adapter 架构清晰,易于扩展多供应商
|
||||
2. ✅ 边界钳制完善(speed 0.5-2.0, pitch -12~12, volume 0-1)
|
||||
3. ✅ 失败降级机制完善(TTS失败不阻断渲染,单片段失败跳过)
|
||||
4. ✅ 单元测试覆盖全面(配置解析、Mock服务、引擎核心、失败降级等)
|
||||
5. ✅ 支持整段配音和字幕联动两种模式
|
||||
|
||||
---
|
||||
|
||||
### PR #296 - 视频裁剪/分割引擎 ✅ 通过
|
||||
|
||||
**核心改动:**
|
||||
- 新增 TrimEngine 裁剪引擎(`apps/worker/video_processing/trim_engine.py`,339行)
|
||||
- 支持单段裁剪(start/end/duration三选二)和多段裁剪
|
||||
- UnifiedRenderService 集成(ResolvedClip增加trim_config字段)
|
||||
- render_audio.py 同步支持音频裁剪
|
||||
- 268行单元测试
|
||||
|
||||
**问题清单:** 无P0/P1/P2问题
|
||||
|
||||
**亮点:**
|
||||
1. ✅ 三选二参数推导逻辑严谨,边界条件处理完善
|
||||
2. ✅ 多级降级(无效配置→使用完整素材,无效段→跳过)
|
||||
3. ✅ 与现有架构集成方式优雅(通过 ResolvedClip 扩展字段)
|
||||
4. ✅ 单元测试覆盖率高(边界值、异常场景、多段裁剪等)
|
||||
5. ✅ 向后兼容(支持 trim_start/trim_end 旧字段名)
|
||||
|
||||
---
|
||||
|
||||
### PR #298 - 水印+片头片尾引擎 ⚠️ 有条件通过
|
||||
|
||||
**核心改动:**
|
||||
- 新增 WatermarkEngine 水印引擎(图片水印 + 文字水印 + 滚动水印)
|
||||
- 新增 IntroOutroEngine 片头片尾引擎(视频片头 + 文字片头 + 拼接)
|
||||
- 新增 TrimEngine(与#296重叠,rebase后应统一)
|
||||
- UnifiedRenderService 集成水印(字幕前叠加)和片头片尾(后处理)
|
||||
- 单元测试覆盖
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P2 - WatermarkEngine 滚动水印表达式不完整
|
||||
- **位置:** `apps/worker/video_processing/watermark_engine.py:calc_scroll_x` 和 `build_image_watermark_filter`
|
||||
- **问题描述:**
|
||||
- `calc_scroll_x` 函数返回值缺少右括号:`f"mod({output_width}-mod({speed}*t\\,{output_width}+{wm_width})"`(应为两个右括号)
|
||||
- `build_image_watermark_filter` 中图片滚动水印的 `x_expr` 同样缺少右括号
|
||||
- 好消息:UnifiedRenderService 中的图片水印集成是内联实现的,使用了正确的表达式 `W-mod(...W+w)`,所以实际运行不受影响
|
||||
- 但 `calc_scroll_x` 函数定义后从未被调用,属死代码
|
||||
- **修复建议:** 修复表达式或移除未使用的函数,保持代码一致性。
|
||||
|
||||
#### P2 - 未使用变量
|
||||
- **位置:**
|
||||
- `watermark_engine.py` - `wm_input_idx` 变量赋值但未使用
|
||||
- `intro_outro_engine.py:163` - `bg` 变量赋值但未使用
|
||||
- `intro_outro_engine.py:360` - `segments` 变量赋值但未使用
|
||||
- **问题描述:** pyflakes 检查发现多处未使用变量
|
||||
- **修复建议:** 清理未使用代码。
|
||||
|
||||
#### P2 - 片头片尾 transition_effect 参数未实际生效
|
||||
- **位置:** `intro_outro_engine.py:concat_with_intro_outro`
|
||||
- **问题描述:** 函数签名有 `transition_effect` 和 `transition_duration` 参数,但实际使用 concat demuxer 做硬切,转场效果未实现。IntroOutroConfig 中也有这些配置项。
|
||||
- **修复建议:** 要么实现 xfade 转场,要么在配置中注明暂不支持转场、硬切拼接。
|
||||
|
||||
#### P3 - 视频片头路径直接使用用户输入
|
||||
- **位置:** `unified_render_service.py` 中 intro_video_path / outro_video_path 的使用
|
||||
- **问题描述:** 视频片头片尾的路径直接从 config 读取后用 `Path()` 打开,缺少路径遍历校验。当前是渲染内部调用,风险较低,但如果后续开放给用户自定义路径会有安全隐患。
|
||||
- **修复建议:** 增加安全校验,确保路径在允许的目录范围内。
|
||||
|
||||
**亮点:**
|
||||
1. ✅ 使用 `run_ffmpeg` 工具函数,符合项目规范
|
||||
2. ✅ 水印9宫格位置 + 边距配置,灵活实用
|
||||
3. ✅ 降级策略完善(图片不存在自动跳过,文字水印构建失败自动跳过)
|
||||
4. ✅ 片头片尾支持视频和文字两种模式
|
||||
5. ✅ 文字转义处理(`replace(":", "\\:")` 等)
|
||||
|
||||
---
|
||||
|
||||
### PR #299 - 画中画(PiP)引擎 ⚠️ 有条件通过
|
||||
|
||||
**核心改动:**
|
||||
- 新增 PiPEngine 画中画引擎(`apps/worker/video_processing/pip_engine.py`,483行)
|
||||
- 支持多图层叠加、9宫格+自由坐标定位、尺寸缩放、圆角裁剪、边框、透明度
|
||||
- 支持入场出场动画(淡入淡出 + 四方向滑入滑出)
|
||||
- 支持时间同步(独立start_time + duration + enable表达式)
|
||||
- UnifiedRenderService 集成(字幕前叠加)
|
||||
- 降级策略:素材不存在/无效时自动跳过
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P2 - 滑动动画实现分散在两处,设计不够清晰
|
||||
- **位置:** `pip_engine.py:_build_animation_filters` 和 `_build_overlay_expr`
|
||||
- **问题描述:**
|
||||
- `_build_animation_filters` 中 SLIDE_LEFT/RIGHT/TOP/BOTTOM 的实现都是 `pass`(注释说"在overlay表达式中处理")
|
||||
- 实际的滑动动画逻辑在 `_build_overlay_expr` 中实现
|
||||
- fade动画在预处理滤镜中实现,slide动画在overlay表达式中实现,设计不统一
|
||||
- 容易让维护者误以为slide动画未实现
|
||||
- **修复建议:** 统一动画实现位置,或在 `_build_animation_filters` 的 pass 处加详细注释说明 slide 动画的实现位置。
|
||||
|
||||
#### P2 - PiP素材URL类型未支持也未明确拒绝
|
||||
- **位置:** `pip_engine.py:validate_layer_source`
|
||||
- **问题描述:** `source_type="url"` 时直接返回 None(注释"暂时不支持直接URL")。如果用户配置了URL类型的PiP素材,会静默降级跳过,没有日志提示原因。
|
||||
- **修复建议:** 增加 warning 日志说明 URL 类型暂不支持,方便排错。
|
||||
|
||||
#### P3 - 圆角使用 geq 滤镜性能较差
|
||||
- **位置:** `pip_engine.py:_build_pip_pre_filter` 中圆角实现
|
||||
- **问题描述:** 使用 geq 逐像素计算实现圆角,性能较差。FFmpeg 5.0+ 有专门的 `rounded` 滤镜性能更好。
|
||||
- **修复建议:** 低优先级,后续可考虑根据 FFmpeg 版本选择最优实现。
|
||||
|
||||
**亮点:**
|
||||
1. ✅ 功能丰富:9宫格+自由坐标、圆角、边框、透明度、动画、时间同步
|
||||
2. ✅ validate() 方法校验配置合法性
|
||||
3. ✅ z_index 排序,支持多图层
|
||||
4. ✅ 降级策略:素材验证失败跳过,不阻断主流程
|
||||
5. ✅ 尺寸支持像素和百分比两种单位
|
||||
|
||||
---
|
||||
|
||||
### PR #303 - 绿幕抠像 + 音频降噪 ✅ 通过
|
||||
|
||||
**核心改动:**
|
||||
- 新增 ChromaKeyEngine 绿幕抠像引擎(基于 colorkey 滤镜)
|
||||
- 新增 NoiseReductionEngine 音频降噪引擎(基于 afftdn 滤镜)
|
||||
- 5种抠像预设 + 3种降噪等级预设
|
||||
- 溢色抑制、人声增强等高级功能
|
||||
- 便捷函数 + try/except 降级模式
|
||||
|
||||
**问题清单:** 无P0/P1/P2问题
|
||||
|
||||
**亮点:**
|
||||
1. ✅ 边界钳制完善(similarity 0.01-1.0, noise_floor -60~-5dB)
|
||||
2. ✅ 参数类型安全(_safe_float 容错处理)
|
||||
3. ✅ 降级模式正确(便捷函数 try/except,失败返回 None)
|
||||
4. ✅ 预设配置丰富(5种抠像预设、3种降噪等级)
|
||||
5. ✅ 代码结构清晰,每个引擎职责单一
|
||||
|
||||
---
|
||||
|
||||
## 第二批:4个P1问题PR复审
|
||||
|
||||
### PR #287 - 成片中心后端 ❌ 不推荐(P1问题未修复)
|
||||
|
||||
**复审问题:** 3个接口无项目权限校验,可越权
|
||||
|
||||
**修复状态:❌ 未修复**
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P1 - 4个接口均无项目权限校验(check_project_access导入但未调用)
|
||||
- **位置:** `apps/api/app/api/routes/videos.py` - 全部4个接口
|
||||
- **问题描述:**
|
||||
- `check_project_access` 已经从 `_helpers` 导入,但在所有接口中都没有被调用
|
||||
- **GET /videos** - 传入 project_id 时未校验用户是否有该项目访问权限;不传时返回所有项目的成片,数据泄露
|
||||
- **GET /videos/{video_id}** - 可通过任意 video_id 越权访问成片详情和下载链接
|
||||
- **PATCH /videos/{video_id}/review** - 可越权修改任意项目成片的复核状态
|
||||
- **POST /videos/batch-download** - 可越权批量下载任意项目的成片
|
||||
- **修复建议:** 每个涉及成片数据的接口都必须调用 `check_project_access` 校验:
|
||||
- 列表接口:校验 project_id 对应的项目权限
|
||||
- 单条详情/修改接口:先查视频的 project_id,再校验项目权限
|
||||
- 批量接口:逐条校验(或先查所有视频所属项目,统一校验)
|
||||
|
||||
#### P3 - 未使用的导入
|
||||
- **位置:** `apps/api/app/api/routes/videos.py:2` - `uuid` 导入但未使用
|
||||
- **修复建议:** 清理未使用的导入。
|
||||
|
||||
---
|
||||
|
||||
### PR #290 - 素材批量操作 ✅ 通过(P1问题已修复)
|
||||
|
||||
**复审问题:** 4个批量接口无数量上限
|
||||
|
||||
**修复状态:✅ 已修复**
|
||||
|
||||
**验证结果:**
|
||||
1. ✅ `MAX_BATCH_SIZE = 200` 常量定义
|
||||
2. ✅ BatchDeleteRequest - `max_length=MAX_BATCH_SIZE`
|
||||
3. ✅ BatchTagRequest - `max_length=MAX_BATCH_SIZE`
|
||||
4. ✅ BatchClassifyRequest - `max_length=MAX_BATCH_SIZE`
|
||||
5. ✅ BatchMarkRequest - `max_length=MAX_BATCH_SIZE`
|
||||
6. ✅ 4个接口都有逐项 `check_project_access` 权限校验
|
||||
7. ✅ 软删除替代硬删除,可恢复,设计更合理
|
||||
8. ✅ 返回成功/失败明细,前端可展示
|
||||
|
||||
---
|
||||
|
||||
### PR #292 - ASR自动字幕 ❌ 不推荐(P1问题未修复)
|
||||
|
||||
**复审问题:** `_extract_audio` 裸调用ffmpeg
|
||||
|
||||
**修复状态:❌ 未修复**
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P1 - _extract_audio 仍使用裸 subprocess.run 调用ffmpeg
|
||||
- **位置:** `apps/worker/video_processing/unified_render_service.py:_extract_audio`
|
||||
- **问题描述:**
|
||||
- `_extract_audio` 方法仍使用 `import subprocess` + `subprocess.run` 直接调用 `"ffmpeg"`
|
||||
- 未使用项目统一的 `run_ffmpeg` 工具函数(`video_processing.ffmpeg_utils.run_ffmpeg`)
|
||||
- 同一文件中其他FFmpeg调用都使用 `run_ffmpeg`,此处不一致
|
||||
- 问题:
|
||||
1. 超时 120s 硬编码,与全局 FFmpeg 超时策略(DEFAULT_FFMPEG_TIMEOUT=1800s)不一致
|
||||
2. 错误处理方式不同(抛 RuntimeError 而非 CalledProcessError)
|
||||
3. FFmpeg 二进制路径未统一管理(硬编码 `"ffmpeg"` vs `FFMPEG_BIN`)
|
||||
- **修复建议:** 改用 `run_ffmpeg` 工具函数:
|
||||
```python
|
||||
from video_processing.ffmpeg_utils import FFMPEG_BIN, run_ffmpeg
|
||||
|
||||
def _extract_audio(self, video_path: Path, output_path: Path) -> None:
|
||||
cmd = [
|
||||
FFMPEG_BIN, "-y", "-i", str(video_path),
|
||||
"-vn", "-acodec", "pcm_s16le",
|
||||
"-ar", "16000", "-ac", "1",
|
||||
str(output_path),
|
||||
]
|
||||
run_ffmpeg(cmd, timeout=120)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### PR #291 - BGM音轨混音 ❌ 不推荐(P1问题未修复)
|
||||
|
||||
**复审问题:** audio_url 外部直链无SSRF防护
|
||||
|
||||
**修复状态:❌ 未修复**
|
||||
|
||||
**问题清单:**
|
||||
|
||||
#### P1 - audio_url 下载无 SSRF 防护
|
||||
- **位置:** `apps/worker/worker_app/tasks/generation.py:_prepare_bgm_track`
|
||||
- **问题描述:**
|
||||
- `audio_url` 来源的 BGM 下载只校验了 scheme 为 http/https
|
||||
- **未校验域名白名单** - 可请求任意域名
|
||||
- **未校验内网IP** - 可访问 10.x.x.x、172.16-31.x.x、192.168.x.x、127.0.0.1 等内网地址
|
||||
- 代码中甚至有 `# nosec B310` 注释,说明开发者已知这是安全问题但未修复
|
||||
- 风险:攻击者可通过构造恶意 audio_url 探测内网服务、访问云元数据服务(169.254.169.254)等
|
||||
- **修复建议:** 增加 URL 安全校验:
|
||||
1. 解析 URL 获取 hostname
|
||||
2. 解析 hostname 对应的 IP 地址
|
||||
3. 检查 IP 是否为内网/回环/链路本地地址(10/8, 172.16/12, 192.168/16, 127/8, 169.254/16 等)
|
||||
4. 可选:增加域名白名单机制
|
||||
5. 移除 `# nosec B310` 注释
|
||||
|
||||
**附加说明:** 预设BGM库的下载也使用了 `urllib.request.urlretrieve` 且同样有 `# nosec B310`,但预设库的 URL 是代码中硬编码的,风险较低。主要风险来自用户可控的 `audio_url`。
|
||||
|
||||
---
|
||||
|
||||
## 总结与建议
|
||||
|
||||
### 可合并(2个)
|
||||
- **#296 视频裁剪** - 代码质量高,测试完善,可直接合并
|
||||
- **#290 批量操作** - P1问题已修复到位,可直接合并
|
||||
- **#303 绿幕+降噪** - 代码质量高,可直接合并
|
||||
|
||||
### 修复P2后可合并(3个)
|
||||
- **#295 TTS配音** - 修复未使用变量和改用 run_ffmpeg 后可合并
|
||||
- **#298 水印+片头片尾** - 修复滚动水印表达式和未使用变量后可合并
|
||||
- **#299 画中画** - 修复动画实现方式不统一的问题后可合并
|
||||
|
||||
### 修复P1后重新审计(3个)
|
||||
- **#287 成片中心** - 必须先加上所有接口的项目权限校验
|
||||
- **#292 ASR字幕** - _extract_audio 必须改用 run_ffmpeg
|
||||
- **#291 BGM混音** - 必须加上 audio_url 的 SSRF 防护
|
||||
|
||||
### 架构建议
|
||||
1. 所有 FFmpeg 调用统一走 `run_ffmpeg` 工具函数,禁止裸 `subprocess.run`
|
||||
2. 所有用户提供的 URL 下载必须经过 SSRF 防护校验
|
||||
3. 所有业务接口必须校验项目权限(`check_project_access`)
|
||||
4. 建议增加 CI 检查:pyflakes 静态检查 + 自定义规则扫描(禁止裸subprocess.run调用ffmpeg)
|
||||
Reference in New Issue
Block a user