Резюме
Когда сообщение проходит через очередь на одобрение, ReviewableQueuedPost.payload["raw"] содержит полную копию отправленного текста. После того как сообщение будет одобрено, а затем удалено его автором, публичное сообщение заменяется заглушкой «(сообщение удалено автором)», но объект для ревью (reviewable) сохраняет исходный текст на протяжении всего времени существования записи сообщения — что, после удаления автором, означает бесконечный срок — и продолжает предоставлять его персоналу на странице /review.
Для этого не предусмотрен путь очистки (scrub): Reviewable.scrubbable_types содержит только [ReviewableUser], поэтому действие администратора «Очистить» (Scrub), добавленное в #36556 («модераторы теперь могут использовать действие ‘Scrub’ для удаления личных данных пользователя»), не покрывает сообщения в очереди. Данные в payload удаляются только в том случае, если кто-то окончательно уничтожит запись сообщения, чего не делает удаление автором и что интерфейс не позволяет сделать для первого сообщения темы.
Воспроизведено на v2026.7.1. Соответствующий код не изменился в ветке main по состоянию на 25.09.2026.
Версия
Discourse v2026.7.1 (self-hosted, локальный авторизованный тестовый экземпляр). Только ядро — плагины не задействованы.
Шаги для воспроизведения
- В качестве администратора настройте, чтобы сообщения обычного пользователя требовали одобрения. Например, установите
approve post countв 5 и удалитеtrust_level_0изapprove unless allowed groups. - В качестве этого обычного пользователя создайте тему с уникальным маркером в тексте. Ответ будет иметь вид
{"action": "enqueued"}. - В качестве персонала одобрите его:
PUT /review/<reviewable_id>/perform/approve_post?version=0. Сообщение будет создано. - В качестве автора удалите его через стандартное пользовательское действие —
DELETE /t/<topic_id>.jsonдля темы, первым сообщением в которой является данное сообщение. - В качестве персонала отправьте запрос
GET /review.json?status=all.
Ожидаемый результат
После того как автор удалит контент, копия этого текста в системе модерации должна следовать жизненному циклу контента: она должна быть удалена, очищена или, по крайней мере, быть доступной для очистки администратором так же, как отклоненный 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 (не является типом, доступным для очистки) |
| После окончательного уничтожения записи сообщения | строка reviewable исчезает (dependent: :destroy) |
Последняя строка — единственный путь, который очищает данные, и он недоступен через удаление автором: удаление автором помечает сообщение как user_deleted и, по истечении delete_removed_posts_after часов, перемещает его в корзину. Запись сообщения в корзине продолжает существовать, поэтому payload также продолжает существовать.
Источник проблемы
app/models/reviewable_queued_post.rbсоздает и читаетpayload['raw']; нигде не указано назначение или срок хранения этих данных.lib/post_destroyer.rb:562resolve_reviewables_for_author_deletionзатрагивает толькоReviewable.where(target: @post, status: pending). Одобреное reviewable для сообщения в очереди никогда не переходит в другое состояние и не очищается.app/models/post.rb:71has_many :reviewables, as: :target, dependent: :destroy— срабатывает при уничтожении записи, а не при «мягком» удалении, которое происходит при удалении автором.app/models/reviewable.rb:81scrubbable_typesвозвращает[ReviewableUser];app/controllers/reviewables_controller.rb:191дополнительно требуетstatus: rejected.- Не запланировано никакой очистки reviewables. В
app/jobs/scheduled/содержатся задачиclean_up_*/purge_*для черновиков, email-токенов, экспортов, загрузок, API-ключей и других объектов, но ничего для reviewables.
Влияние
Это не публичное раскрытие информации — очередь на ревью доступна только персоналу, и я не утверждаю обратного. Это пробел в хранении данных: текст, который пользователь удалил с форума, бесконечно остается в хранилище модерации, отображается в интерфейсе очереди на ревью, и ни одно действие администратора не позволяет удалить его, кроме окончательного уничтожения записи сообщения.
Это важно по той же причине, по которой в #36556 была добавлена функция очистки для отклоненных пользователей: payload reviewables может содержать личные данные, и у сайта, получившего запрос на удаление данных, нет поддерживаемого способа очистить payload сообщения в очереди.
Предлагаемое исправление
Любое из следующих решений закрывает проблему; лучше сделать оба:
- Расширьте
resolve_reviewables_for_author_deletion(и путь перемещения в корзину), чтобы очищатьpayload['raw']в reviewables, целевое сообщение которых было удалено автором или перемещено в корзину. - Добавьте
ReviewableQueuedPostвReviewable.scrubbable_typesи разрешите существующему действию администратора «Очистить» применяться, когда целевое сообщение больше не содержит контента, чтобы операторы имели поддерживаемое средство для очистки payload, созданных до исправления.
Тест регрессии должен утверждать, что после удаления автором одобренного сообщения в очереди payload reviewable больше не содержит отправленного текста.