Summary
When a post goes through the approval queue, ReviewableQueuedPost.payload["raw"] holds a full copy of the submitted body. After the post is approved and then deleted by its author, the public post is replaced by the “(post deleted by author)” stub, but the reviewable keeps the original body for as long as the post row exists — which, after an author deletion, is indefinitely — and keeps serving it to staff at /review.
There is no scrub path for it: Reviewable.scrubbable_types is [ReviewableUser], so the admin scrub action added in #36556 (“moderators can now use a ‘Scrub’ action to remove the user’s personal data”) does not cover queued posts. The payload is only removed if someone permanently destroys the post record, which is not what the author’s deletion does and which the UI does not allow for a topic’s first post.
Reproduced on v2026.7.1. The relevant code is unchanged on main as of 2026-09-25.
Version
Discourse v2026.7.1 (self-hosted, local authorized test instance). Core only — no plugin involved.
Steps to reproduce
-
As an admin, make a normal user’s posts require approval, for example set
approve post countto 5 and removetrust_level_0fromapprove unless allowed groups. -
As that normal user, create a topic with a unique marker in the body. The response comes back as
{"action": "enqueued"}. -
As staff, approve it:
PUT /review/<reviewable_id>/perform/approve_post?version=0. The post is created. -
As the author, delete it through the normal user-facing action —
DELETE /t/<topic_id>.jsonfor the topic whose first post this is. -
As staff, request
GET /review.json?status=all.
Expected
Once the author deletes the content, the moderation copy of the same text should follow the content’s lifecycle: it should be dropped, scrubbed, or at least be scrubbable by an admin the way a rejected ReviewableUser is.
Actual
Step 5 still returns the full original body. Measured on the reproduced run:
| Observation | Result |
|---|---|
posts row after the author’s deletion |
user_deleted = t, raw = '(topic deleted by author)' |
reviewables.payload after the author’s deletion |
{"raw":"Queued body DRLGQUEUEDfb08ab5b648f. padding …"} — unchanged |
Staff GET /review.json?status=all |
contains the original body |
Reviewable.scrubbable_types |
["ReviewableUser"] |
Admin PUT /review/<id>/scrub.json |
HTTP 404 (not a scrubbable type) |
| After the post record is permanently destroyed | reviewable row is gone (dependent: :destroy) |
The last row is the only path that clears it, and it is not reachable from the author’s deletion: the author’s delete marks the post user_deleted and, after delete_removed_posts_after hours, trashes it. A trashed post row keeps existing, so the payload keeps existing.
Where this comes from
-
app/models/reviewable_queued_post.rbbuilds and readspayload['raw']; nothing states a retention purpose or lifetime for it. -
lib/post_destroyer.rb:562resolve_reviewables_for_author_deletiononly touchesReviewable.where(target: @post, status: pending). An approved queued-post reviewable is never transitioned or scrubbed. -
app/models/post.rb:71has_many :reviewables, as: :target, dependent: :destroy— fires on record destruction, not on the soft delete that an author deletion produces. -
app/models/reviewable.rb:81scrubbable_typesreturns[ReviewableUser];app/controllers/reviewables_controller.rb:191additionally requiresstatus: rejected. -
There is no scheduled cleanup for reviewables.
app/jobs/scheduled/containsclean_up_*/purge_*jobs for drafts, email tokens, exports, uploads, api keys and more, but nothing for reviewables.
Impact
This is not a public disclosure — the review queue is staff-only, and I am not claiming otherwise. It is a data-retention gap: text a user removed from the forum stays in the moderation store indefinitely, is shown in the review queue UI, and no admin action can remove it short of permanently destroying the post record.
That matters for the same reason #36556 added scrubbing for rejected users: reviewable payloads can carry personal data, and a site that receives an erasure request has no supported way to clear a queued-post payload.
Suggested fix
Either of these closes it; doing both is better:
-
Extend
resolve_reviewables_for_author_deletion(and the trash path) to scrubpayload['raw']on reviewables whose target post has been author-deleted or trashed. -
Add
ReviewableQueuedPosttoReviewable.scrubbable_typesand let the existing admin scrub action apply when the target post no longer carries the content, so operators have a supported remedy for payloads created before the fix.
A regression spec would assert that after an author deletes an approved queued post, the reviewable payload no longer contains the submitted body.