审计报告自审计
审计日期: 2026-04-05
对象: docs/audit_report.md(v1)
方法: 逐条与源代码交叉验证,检查事实准确性、严重性评级合理性、遗漏项
总评
原审计报告覆盖面较广,5个维度36条发现总体方向正确。但自审计发现 3处事实错误、2处严重性误判、4处遗漏。以下逐条分析。
一、事实错误(需修正)
❌ 错误 1: B-2 duration 属性"逻辑反转" — 描述不准确
原报告描述:
durationproperty 的逻辑为:当audio_path存在时尝试读取文件元数据(mutagen),如果读取失败则返回 None 并 fall through 到下面基于 segment 的计算
实际代码 (episode.py:54-59):
def duration(self) -> Optional[float]:
"""总时长"""
if self.audio_path:
# TODO: 从音频文件获取实际时长
return sum(s.duration or 0 for s in self.script)
return None
事实:
1. 代码中根本没有导入或使用 mutagen。该属性从未尝试读取音频文件元数据
2. 逻辑是:有 audio_path → 返回 segment 时长之和;无 audio_path → 返回 None
3. 问题不是"逻辑反转",而是实现与意图不符(TODO 注释说要从文件读取,但实际只做了 segment 累加),且当 audio_path 为 None 时直接返回 None 即使有 segment 数据
应修正为: "duration 属性与 TODO 注释意图不符——注释说要从音频文件读取实际时长,但代码只是累加 segment.duration。更严重的是,当 audio_path 为 None 时直接返回 None,丢弃了可能存在的 segment 时长数据。"
❌ 错误 2: B-6 _generate_id 格式字符串 — 引用不准确
原报告描述:
使用
datetime.now().strftime('%Y%m%d_%H%M%S')生成 ID
实际代码 (script.py:368-370):
事实: 格式为 %Y%m%d%H%M(精确到分钟,无下划线,无秒),比报告描述更粗糙——同一分钟内的两次调用必然冲突。
❌ 错误 3: Q-1 Image F821 错误 — 混用了两种不同性质的问题
原报告描述: 将 4 个 F821 错误统一列为"运行时崩溃"
事实: 这 4 个错误分两类:
| 类型 | 文件 | 严重性 |
|---|---|---|
| 运行时 NameError | pipeline.py:128 (edge_tts_make_mp3) |
必定崩溃 |
| 运行时 NameError | multimodal_pipeline.py:468 (create_smart_video_composer) |
必定崩溃 |
| 类型注解问题 | video.py:214 ('Image' 字符串注解) |
不崩溃* |
| 类型注解问题 | enhanced_video.py:504 ('Image' 字符串注解) |
不崩溃* |
video.py 和 enhanced_video.py 中 -> 'Image' 是字符串形式的返回类型注解。Image 在函数体内部通过 from PIL import Image 导入,运行时不会触发 NameError。这仅影响静态类型检查(mypy/ruff),不影响运行。
应区分: 2 个运行时崩溃 + 2 个静态分析问题。
二、严重性误判
⚠️ 误判 1: S-6 Path Traversal — 严重性被低估,影响面被低估
原报告: 列为 S-6(高风险但只举了2处)
实际验证: CLI 中 episode_id 直接拼接文件路径的实例有 16+ 处,全部无校验:
cli/main.py:269 json_file = Path(f"./episodes/drafts/{episode_id}_data.json")
cli/main.py:280 video_path = Path(f"./episodes/drafts/{episode_id}.mp4")
cli/main.py:282 video_path = Path(f"./episodes/drafts/{episode_id}.mp3")
cli/main.py:408 result_file = Path("./episodes/drafts") / f"{episode_id}_publish_result.json"
cli/main.py:513 json_file = Path(f"./episodes/drafts/{episode_id}_data.json")
...(共 16 处)
此外,cli/main.py:275 中 episode_id 还会被 JSON 数据覆盖:
应提升为 P0,影响面远超报告所述。
⚠️ 误判 2: A-4 双重平台实现 — 遗漏了关键细节
原报告: 说"CLI 使用 stub 版本"
补充发现:
1. create_multi_publisher 函数在 cli/main.py:25 被导入但从未调用(dead import)
2. CLI 在 line 372 直接实例化 MultiPlatformPublisher(configs),完全绕过了 factory 函数
3. bilibili.py 和 wechat_mp.py 中的真实实现从未被 CLI 导入
这意味着"双重实现"更准确地说是"实现断裂"——真实实现是孤立代码,永远不会被触发。
三、遗漏发现
🔴 遗漏 1: BilibiliAuth.__new__ 绕过初始化 ✅ 已修复
文件: src/publisher/bilibili.py:451-453
self.auth = BilibiliAuth.__new__(BilibiliAuth)
self.auth.access_token = access_token
self.auth.config = None
问题: __new__ 绕过 __init__,导致 refresh_token、expires_at、user_info 等字段未初始化。后续任何访问这些属性的代码(如 is_authenticated() 中的 self.expires_at)将抛出 AttributeError。
修复: 已替换为 BilibiliAuth(BilibiliAuthConfig(client_id="", client_secret="")),确保 __init__ 正常执行。
严重性: 高 — 使用 access_token 参数创建 publisher 后尝试刷新 token 会崩溃。
🔴 遗漏 2: Token 缓存文件无权限控制 ✅ 已修复
文件: src/publisher/bilibili.py:191-200, src/publisher/wechat_mp.py:115-124
问题: open(token_file, "w") 创建的文件使用系统默认 umask(通常 0o644),即所有用户可读。包含 access_token 和 refresh_token 的文件应设置为 0o600。
修复: 已改用 os.fdopen(os.open(token_file, os.O_WRONLY | os.O_CREAT, 0o600), "w") 创建文件。
严重性: 中 — 多用户共享服务器上有泄露风险。
🟡 遗漏 3: script.py 填充循环无上限保护 ✅ 已修复
文件: src/generator/script.py:353-354
问题: 如果 target_words 设置为极大值(如 999999999),此循环将消耗大量内存和 CPU。无最大迭代次数保护。
修复: 已添加 max_fill = 100 循环上限保护。
严重性: 低 — 需要故意设置异常参数,但属于防御性编程缺失。
🟡 遗漏 4: RSS published_at 可能为 None 导致崩溃 ✅ 已修复
文件: src/publisher/rss.py:70
问题: published_at 在 Episode dataclass 中默认为 None。如果 episode 尚未发布(status 不是 PUBLISHED),published_at 就是 None,调用 .strftime() 会抛出 AttributeError。
修复: 已在 rss.py 中添加 None 防护。
严重性: 低 — 被上游过滤保护,但属于脆弱假设。
四、报告质量评估
准确性统计
| 类别 | 总条目 | 准确 | 部分准确 | 错误 |
|---|---|---|---|---|
| 安全漏洞 (S) | 6 | 5 | 1 (S-6 范围低估) | 0 |
| 业务逻辑 (B) | 8 | 6 | 0 | 2 (B-2, B-6) |
| 代码质量 (Q) | 12 | 11 | 1 (Q-1 混用) | 0 |
| 合规性 (C) | 3 | 3 | 0 | 0 |
| 架构风险 (A) | 7 | 6 | 1 (A-4 细节不足) | 0 |
| 合计 | 36 | 31 (86%) | 3 (8%) | 2 (6%) |
优点
- 覆盖面广,5个维度均有发现
- 大部分行号引用准确
- 风险矩阵和优先级排序实用
- 快速修复清单可操作
不足
- B-2 duration 属性的描述未对照源码验证
- B-6 格式字符串直接凭记忆引用未核实
- Q-1 未区分类型注解 F821 和运行时 NameError
- S-6 的影响面分析不充分(实际 16+ 处 vs 报告 2 处)
- 遗漏了
__new__绕过初始化的严重 bug
五、修正后的优先级调整
| 变更 | 原评级 | 修正评级 | 原因 |
|---|---|---|---|
| S-6 Path Traversal | P1 | P0 | 16+ 处注入点 + JSON 二次注入 |
新增: __new__ 绕过 |
- | P1 | 运行时 AttributeError |
| 新增: Token 文件权限 | - | P2 | 多用户环境泄露 |
| B-2 duration 逻辑 | P2 | P3 | 非反转,仅实现不完整 |
| Q-1 Image F821 | P0(全部) | P2(2项) | 类型注解不导致运行时崩溃 |
六、修正后的 P0 清单
| # | 编号 | 问题 | 修复状态 |
|---|---|---|---|
| 1 | S-2 | RSS XML 注入(rss.py:44-74) |
✅ 已修复 |
| 2 | S-6 | Path Traversal(cli/main.py 16+ 处 + optimizer.py:213) |
✅ 已修复 |
| 3 | Q-1a | edge_tts_make_mp3 未定义(pipeline.py:128) |
✅ 已修复 |
| 4 | Q-1b | create_smart_video_composer 未定义(multimodal_pipeline.py:468) |
✅ 已修复 |
| 5 | B-1 | Episode 反序列化崩溃(cli/main.py:967) |
✅ 已修复 |
P0 全部修复完成。P1 修复状态:S-1 ✅, S-3 ⚠️(文件权限已加固,内容仍明文)。自审计中发现的遗漏 1-4 均已修复。
自审计完成。原报告准确性 86%,3 处需修正,4 处遗漏已补充。建议将修正内容合并入主报告。