Volltext eines in der Warteschlange stehenden Beitrags bleibt in der Prüfungs-Warteschlange, wenn der Autor ihn löscht – ohne Möglichkeit zur Bereinigung

Zusammenfassung

Wenn ein Beitrag die Genehmigungs-Warteschlange durchläuft, enthält ReviewableQueuedPost.payload["raw"] eine vollständige Kopie des eingereichten Inhalts. Nach der Genehmigung und anschließenden Löschung durch den Autor wird der öffentliche Beitrag durch den Platzhalter „(Beitrag vom Autor gelöscht)“ ersetzt, aber der Reviewable behält den ursprünglichen Inhalt so lange bei, wie die Beitragszeile existiert – was nach einer Autor-Löschung unbegrenzt ist – und stellt ihn weiterhin dem Personal unter /review bereit.

Es gibt keinen Weg, ihn zu bereinigen (scrub): Reviewable.scrubbable_types ist [ReviewableUser], daher deckt die in #36556 hinzugefügte Admin-Bereinigungsaktion („Moderatoren können jetzt eine ‚Bereinigen‘-Aktion verwenden, um die persönlichen Daten des Benutzers zu entfernen“) eingestufte Beiträge nicht ab. Die Payload wird nur entfernt, wenn jemand die Beitragszeile dauerhaft zerstört, was nicht das ist, was eine Autor-Löschung tut, und was die UI für den ersten Beitrag eines Themas nicht zulässt.

Reproduziert auf v2026.7.1. Der relevante Code ist auf main Stand 2026-09-25 unverändert.

Version

Discourse v2026.7.1 (self-hosted, lokale autorisierte Testinstanz). Nur Core – keine Plugin beteiligt.

Schritte zur Reproduktion

  1. Als Admin die Beiträge eines normalen Benutzers zur Genehmigungspflicht machen, z. B. approve post count auf 5 setzen und trust_level_0 aus approve unless allowed groups entfernen.

  2. Als dieser normale Benutzer ein Thema mit einem eindeutigen Marker im Inhalt erstellen. Die Antwort lautet {"action": "enqueued"}.

  3. Als Personal genehmigen: PUT /review/<reviewable_id>/perform/approve_post?version=0. Der Beitrag wird erstellt.

  4. Als Autor über die normale benutzersichtbare Aktion löschen – DELETE /t/<topic_id>.json für das Thema, dessen erster Beitrag dies ist.

  5. Als Personal GET /review.json?status=all anfordern.

Erwartet

Sobald der Autor den Inhalt löscht, sollte die Moderationskopie desselben Textes dem Lebenszyklus des Inhalts folgen: Sie sollte verworfen, bereinigt oder zumindest von einem Admin bereinigt werden können, so wie ein abgelehnter ReviewableUser.

Tatsächlich

Schritt 5 liefert weiterhin den vollständigen ursprünglichen Inhalt. Gemessen bei der reproduzierten Ausführung:

Beobachtung Ergebnis
posts-Zeile nach der Autor-Löschung user_deleted = t, raw = '(topic deleted by author)'
reviewables.payload nach der Autor-Löschung {"raw":"Queued body DRLGQUEUEDfb08ab5b648f. padding …"} – unverändert
Personal GET /review.json?status=all enthält den ursprünglichen Inhalt
Reviewable.scrubbable_types ["ReviewableUser"]
Admin PUT /review/<id>/scrub.json HTTP 404 (kein bereinigungsfähiger Typ)
Nach der dauerhaften Zerstörung der Beitragszeile Reviewable-Zeile ist weg (dependent: :destroy)

Die letzte Zeile ist der einzige Weg, der sie aufräumt, und sie ist nicht über die Autor-Löschung erreichbar: Die Autor-Löschung markiert den Beitrag als user_deleted und verschiebt ihn nach delete_removed_posts_after Stunden in den Papierkorb. Eine im Papierkorb befindliche Beitragszeile existiert weiterhin, daher existiert die Payload weiterhin.

Ursprung

  • app/models/reviewable_queued_post.rb baut und liest payload['raw']; es wird kein Aufbewahrungszweck oder eine Lebensdauer dafür angegeben.

  • lib/post_destroyer.rb:562 resolve_reviewables_for_author_deletion berührt nur Reviewable.where(target: @post, status: pending). Ein genehmigter eingestufter Beitrags-Reviewable wird nie überführt oder bereinigt.

  • app/models/post.rb:71 has_many :reviewables, as: :target, dependent: :destroy – wird bei der Zerstörung des Datensatzes ausgelöst, nicht bei der Soft-Löschung, die eine Autor-Löschung erzeugt.

  • app/models/reviewable.rb:81 scrubbable_types gibt [ReviewableUser] zurück; app/controllers/reviewables_controller.rb:191 erfordert zusätzlich status: rejected.

  • Es gibt keine geplante Bereinigung für Reviewables. app/jobs/scheduled/ enthält clean_up_* / purge_*-Jobs für Entwürfe, E-Mail-Tokens, Exporte, Uploads, API-Schlüssel und mehr, aber nichts für Reviewables.

Auswirkung

Dies ist keine öffentliche Offenlegung – die Review-Warteschlange ist nur für Personal zugänglich, und ich behaupte das Gegenteil nicht. Es ist eine Lücke bei der Datenaufbewahrung: Text, den ein Benutzer aus dem Forum entfernt hat, verbleibt unbegrenzt im Moderationsspeicher, wird in der Review-Warteschlangen-UI angezeigt und kann durch keine Admin-Aktion entfernt werden, außer durch die dauerhafte Zerstörung der Beitragszeile.

Das ist aus demselben Grund wichtig, aus dem #36556 das Bereinigen für abgelehnte Benutzer hinzugefügt hat: Reviewable-Payloads können persönliche Daten enthalten, und eine Site, die einen Löschungsantrag erhält, hat keinen unterstützten Weg, eine eingestufte Beitrags-Payload zu bereinigen.

Vorschlag zur Behebung

Beides schließt die Lücke; beides zu tun ist besser:

  1. resolve_reviewables_for_author_deletion (und den Papierkorb-Pfad) erweitern, um payload['raw'] bei Reviewables zu bereinigen, deren Zielbeitrag vom Autor gelöscht oder in den Papierkorb verschoben wurde.

  2. ReviewableQueuedPost zu Reviewable.scrubbable_types hinzufügen und die bestehende Admin-Bereinigungsaktion anwenden lassen, wenn der Zielbeitrag den Inhalt nicht mehr trägt, damit Betreiber ein unterstütztes Mittel für Payloads haben, die vor der Behebung erstellt wurden.

Ein Regressionstest sollte behaupten, dass nach dem Löschen eines genehmigten eingestuferten Beitrags durch den Autor die Reviewable-Payload den eingereichten Inhalt nicht mehr enthält.

2 „Gefällt mir“