跳转至

审计报告自审计

审计日期: 2026-04-05
对象: docs/audit_report.md(v1)
方法: 逐条与源代码交叉验证,检查事实准确性、严重性评级合理性、遗漏项


总评

原审计报告覆盖面较广,5个维度36条发现总体方向正确。但自审计发现 3处事实错误、2处严重性误判、4处遗漏。以下逐条分析。


一、事实错误(需修正)

❌ 错误 1: B-2 duration 属性"逻辑反转" — 描述不准确

原报告描述:

duration property 的逻辑为:当 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):

def _generate_id(self) -> str:
    return f"EP{datetime.now().strftime('%Y%m%d%H%M')}"

事实: 格式为 %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.pyenhanced_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 数据覆盖:

episode_id = episode_data.get('episode_id', 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.pywechat_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_tokenexpires_atuser_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

while len(content) < dialog.target_words * 0.8:
    content += " " + "这需要我们深入理解并实践运用。"

问题: 如果 target_words 设置为极大值(如 999999999),此循环将消耗大量内存和 CPU。无最大迭代次数保护。

修复: 已添加 max_fill = 100 循环上限保护。

严重性: 低 — 需要故意设置异常参数,但属于防御性编程缺失。


🟡 遗漏 4: RSS published_at 可能为 None 导致崩溃 ✅ 已修复

文件: src/publisher/rss.py:70

episode.published_at.strftime('%a, %d %b %Y %H:%M:%S %z')

问题: published_atEpisode 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%)

优点

  1. 覆盖面广,5个维度均有发现
  2. 大部分行号引用准确
  3. 风险矩阵和优先级排序实用
  4. 快速修复清单可操作

不足

  1. B-2 duration 属性的描述未对照源码验证
  2. B-6 格式字符串直接凭记忆引用未核实
  3. Q-1 未区分类型注解 F821 和运行时 NameError
  4. S-6 的影响面分析不充分(实际 16+ 处 vs 报告 2 处)
  5. 遗漏了 __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 处遗漏已补充。建议将修正内容合并入主报告。