El cuerpo completo de una publicación en cola permanece en la cola de revisión después de que su autor la elimine, sin forma de borrarla

Resumen

Cuando una publicación pasa por la cola de aprobación, ReviewableQueuedPost.payload["raw"] contiene una copia completa del cuerpo enviado. Después de que la publicación se aprueba y luego es eliminada por su autor, la publicación pública se reemplaza por el marcador “(publicación eliminada por el autor)”, pero el objeto revisable (reviewable) conserva el cuerpo original durante todo el tiempo que exista la fila de la publicación —lo cual, tras una eliminación por el autor, es indefinidamente— y sigue sirviéndolo al personal en /review.

No existe un proceso de depuración (scrub) para ello: Reviewable.scrubbable_types es [ReviewableUser], por lo que la acción de depuración de administrador añadida en #36556 (“los moderadores ahora pueden usar una acción de ‘Depurar’ para eliminar los datos personales del usuario”) no cubre las publicaciones en cola. El payload solo se elimina si alguien destruye permanentemente el registro de la publicación, lo cual no es lo que hace la eliminación por el autor y lo que la interfaz de usuario no permite para la primera publicación de un tema.

Reproducido en v2026.7.1. El código relevante no ha cambiado en main a fecha del 25 de septiembre de 2026.

Versión

Discourse v2026.7.1 (autoalojado, instancia de prueba autorizada local). Solo núcleo — no se involucra ningún plugin.

Pasos para reproducir

  1. Como administrador, haga que las publicaciones de un usuario normal requieran aprobación, por ejemplo, establezca approve post count en 5 y quite trust_level_0 de approve unless allowed groups.

  2. Como ese usuario normal, cree un tema con un marcador único en el cuerpo. La respuesta vuelve como {"action": "enqueued"}.

  3. Como personal, apruébelo: PUT /review/<reviewable_id>/perform/approve_post?version=0. Se crea la publicación.

  4. Como autor, elimínela a través de la acción normal orientada al usuario — DELETE /t/<topic_id>.json para el tema del cual esta es la primera publicación.

  5. Como personal, solicite GET /review.json?status=all.

Esperado

Una vez que el autor elimina el contenido, la copia de moderación del mismo texto debería seguir el ciclo de vida del contenido: debería ser descartada, depurada o, al menos, ser depurable por un administrador de la misma manera que un ReviewableUser rechazado.

Realidad

El paso 5 aún devuelve el cuerpo original completo. Medido en la ejecución reproducida:

Observación Resultado
Fila de posts tras la eliminación del autor user_deleted = t, raw = '(tema eliminado por el autor)'
reviewables.payload tras la eliminación del autor {"raw":"Cuerpo en cola DRLGQUEUEDfb08ab5b648f. relleno …"} — sin cambios
GET /review.json?status=all del personal contiene el cuerpo original
Reviewable.scrubbable_types ["ReviewableUser"]
PUT /review/<id>/scrub.json del administrador HTTP 404 (no es un tipo depurable)
Después de que el registro de la publicación se destruya permanentemente la fila del reviewable desaparece (dependent: :destroy)

La última fila es el único camino que lo limpia, y no es alcanzable desde la eliminación del autor: la eliminación del autor marca la publicación como user_deleted y, después de las horas de delete_removed_posts_after, la envía a la papelera. Una fila de publicación en la papelera sigue existiendo, por lo que el payload sigue existiendo.

De dónde proviene esto

  • app/models/reviewable_queued_post.rb construye y lee payload['raw']; nada establece un propósito o tiempo de retención para ello.

  • lib/post_destroyer.rb:562 resolve_reviewables_for_author_deletion solo toca Reviewable.where(target: @post, status: pending). Un reviewable de publicación en cola aprobado nunca se transiciona ni se depura.

  • app/models/post.rb:71 has_many :reviewables, as: :target, dependent: :destroy — se dispara en la destrucción del registro, no en la eliminación blanda que produce la eliminación por el autor.

  • app/models/reviewable.rb:81 scrubbable_types devuelve [ReviewableUser]; app/controllers/reviewables_controller.rb:191 adicionalmente requiere status: rejected.

  • No hay una limpieza programada para los reviewables. app/jobs/scheduled/ contiene trabajos clean_up_* / purge_* para borradores, tokens de correo, exportaciones, cargas, claves de API y más, pero nada para reviewables.

Impacto

Esto no es una divulgación pública — la cola de revisión es solo para el personal, y no afirmo lo contrario. Es una brecha de retención de datos: el texto que un usuario eliminó del foro permanece en el almacén de moderación indefinidamente, se muestra en la interfaz de usuario de la cola de revisión y ninguna acción de administrador puede eliminarlo sin destruir permanentemente el registro de la publicación.

Eso importa por la misma razón por la que #36556 añadió la depuración para usuarios rechazados: los payloads de reviewables pueden contener datos personales, y un sitio que recibe una solicitud de borrado no tiene una forma soportada de limpiar un payload de publicación en cola.

Solución sugerida

Cualquiera de estas lo cierra; hacer ambas es mejor:

  1. Extender resolve_reviewables_for_author_deletion (y el camino de la papelera) para depurar payload['raw'] en reviewables cuya publicación objetivo haya sido eliminada por el autor o enviada a la papelera.

  2. Añadir ReviewableQueuedPost a Reviewable.scrubbable_types y permitir que la acción de depuración de administrador existente se aplique cuando la publicación objetivo ya no contenga el contenido, para que los operadores tengan un remedio soportado para payloads creados antes de la corrección.

Una especificación de regresión debería afirmar que después de que un autor elimine una publicación en cola aprobada, el payload del reviewable ya no contiene el cuerpo enviado.

2 Me gusta