摘要
当帖子进入审批队列时,ReviewableQueuedPost.payload["raw"] 保存了提交内容的完整副本。帖子被批准并由作者删除后,公开帖子会被替换为“(帖子已被作者删除)”的占位符,但只要帖子记录仍然存在,审核对象(reviewable)就会一直保留原始内容——而在作者删除后,该记录会无限期保留——并持续在 /review 页面向工作人员展示该内容。
目前没有清理(scrub)路径:Reviewable.scrubbable_types 仅包含 [ReviewableUser],因此 #36556 中新增的管理员清理操作(“版主现在可以使用‘清理’操作来移除用户的个人数据”)并不涵盖排队中的帖子。只有当有人永久销毁帖子记录时,该 payload 才会被移除,而作者删除操作并不会执行此动作,且 UI 也不允许对主题的首帖执行此操作。
已在 v2026.7.1 上复现。截至 2026-09-25,main 分支上的相关代码未发生变化。
版本
Discourse v2026.7.1(自托管,本地授权测试实例)。仅核心代码——未涉及任何插件。
复现步骤
- 以管理员身份,设置普通用户的帖子需要审批,例如将
approve post count设置为 5,并从approve unless allowed groups中移除trust_level_0。 - 以该普通用户身份,创建一个主题,并在正文中包含一个唯一标记。响应返回为
{"action": "enqueued"}。 - 以工作人员身份批准该帖子:
PUT /review/<reviewable_id>/perform/approve_post?version=0。帖子随即创建。 - 以作者身份,通过正常的用户端操作删除该帖子——对于该主题的首帖,执行
DELETE /t/<topic_id>.json。 - 以工作人员身份请求
GET /review.json?status=all。
预期行为
一旦作者删除了内容,该文本的审核副本应遵循内容的生命周期:它应当被丢弃、清理(scrub),或者至少像被拒绝的 ReviewableUser 一样,允许管理员进行清理。
实际行为
步骤 5 仍然返回完整的原始正文。在复现运行中测量结果如下:
| 观察项 | 结果 |
|---|---|
作者删除后的 posts 记录 |
user_deleted = t, raw = '(topic deleted by author)' |
作者删除后的 reviewables.payload |
{"raw":"Queued body DRLGQUEUEDfb08ab5b648f. padding …"} — 未改变 |
工作人员 GET /review.json?status=all |
包含原始正文 |
Reviewable.scrubbable_types |
["ReviewableUser"] |
管理员 PUT /review/<id>/scrub.json |
HTTP 404(非可清理类型) |
| 帖子记录被永久销毁后 | 审核对象记录消失(dependent: :destroy) |
最后一行是唯一能清除该数据的路径,且无法通过作者删除操作到达:作者删除会将帖子标记为 user_deleted,并在 delete_removed_posts_after 小时后将其移入回收站。回收站中的帖子记录仍然存在,因此 payload 也仍然存在。
原因分析
app/models/reviewable_queued_post.rb构建并读取payload['raw'];没有任何地方说明其保留目的或生命周期。lib/post_destroyer.rb:562中的resolve_reviewables_for_author_deletion仅处理Reviewable.where(target: @post, status: pending)。已批准的排队帖子审核对象永远不会被转换或清理。app/models/post.rb:71中的has_many :reviewables, as: :target, dependent: :destroy— 这在记录销毁时触发,而作者删除产生的是软删除。app/models/reviewable.rb:81中的scrubbable_types返回[ReviewableUser];app/controllers/reviewables_controller.rb:191还额外要求status: rejected。- 没有针对审核对象的定期清理任务。
app/jobs/scheduled/包含用于草稿、邮件令牌、导出、上传、API 密钥等的clean_up_*/purge_*任务,但没有针对审核对象的任务。
影响
这不是公开泄露——审核队列仅对工作人员可见,我也并未声称它是公开的。这是一个数据保留缺口:用户从论坛移除的文本会无限期地保留在审核存储中,在审核队列 UI 中显示,且除非永久销毁帖子记录,否则没有任何管理员操作可以移除它。
这与 #36556 为被拒绝用户添加清理功能的原因相同:审核对象 payload 可能包含个人数据,而收到删除请求的网站没有受支持的方法来清除排队帖子的 payload。
建议修复方案
以下任一方案均可解决此问题;同时实施两者更佳:
- 扩展
resolve_reviewables_for_author_deletion(以及回收站路径),以清理目标帖子已被作者删除或移入回收站的审核对象的payload['raw']。 - 将
ReviewableQueuedPost添加到Reviewable.scrubbable_types中,并让现有的管理员清理操作在目标帖子不再包含内容时生效,以便运营人员拥有受支持的补救措施来处理修复前创建的 payload。
回归测试规格应断言:在作者删除已批准的排队帖子后,审核对象 payload 中不再包含提交的正文。