Le paramètre de balises scalaires accepté dans la file d'attente de révision provoque une erreur HTTP 500 lors de l'approbation

L’approbation d’un sujet en file d’attente sur notre site auto-hébergé a échoué avec une erreur HTTP 500, car l’enregistrement en file d’attente contenait payload.tags = "macos" (une chaîne de caractères). La valeur malformée avait déjà été acceptée par POST /posts.

Je comprends que les clients doivent envoyer un tableau de tags. Le problème que je signale est qu’une valeur scalaire invalide peut être acceptée dans la file d’attente de révision et ne générer une erreur que plus tard, lorsqu’un modérateur approuve le post.

Version : 2026.10.0-latest, commit 3f2bf92368c78a7fa9a755f3d046adf35dd4459b.

Preuves issues de l’incident

Le post nécessitait une approbation en raison de approve_post_count.

À 13:37:52 UTC le 22 octobre 2026, les paramètres de la requête Rails originale contenaient déjà :

"tags" => "macos"

La requête était POST /posts et le journal d’accès correspondant a enregistré un code HTTP 200. Les noms des paramètres soumis étaient title, raw, category et tags.

La lecture du révisable en attente a confirmé :

{
  "type": "ReviewableQueuedPost",
  "status": 0,
  "version": 0,
  "topic_id": null,
  "payload": {
    "tags": "macos"
  }
}

Il s’agit d’un extrait des champs pertinents, avec le titre, le corps et les informations utilisateur omis. L’interface de révision affichait le tag sous forme de cinq lettres séparées : m, a, c, o, s.

Le clic sur Approuver a produit cette exception (cinq occurrences groupées dans les journaux) :

NoMethodError (undefined method '-' for an instance of String)
lib/discourse_tagging.rb:932:in 'DiscourseTagging.tags_for_saving'
lib/discourse_tagging.rb:43:in 'DiscourseTagging.tag_topic_by_names'
lib/discourse_tagging.rb:30:in 'DiscourseTagging.tag_topic'
lib/topic_creator.rb:228:in 'TopicCreator#setup_tags'
lib/topic_creator.rb:60:in 'TopicCreator#create'
lib/post_creator.rb:519:in 'PostCreator#create_topic'
lib/post_creator.rb:209:in 'block in PostCreator#create'
lib/post_creator.rb:419:in 'block in PostCreator#transaction'
lib/post_creator.rb:419:in 'PostCreator#transaction'
lib/post_creator.rb:207:in 'PostCreator#create'
app/models/reviewable_queued_post.rb:110:in 'ReviewableQueuedPost#perform_approve_post'
app/models/reviewable.rb:433:in 'Kernel#public_send'
app/models/reviewable.rb:433:in 'block in Reviewable#perform'
app/models/reviewable.rb:431:in 'Reviewable#perform'
app/controllers/reviewables_controller.rb:287:in 'ReviewablesController#perform'

Code pertinent

Voici les chemins de code vérifiés dans le conteneur en cours d’exécution au commit ci-dessus :

  • PostsController#create_params appelle params.permit(tags: %i[id name]), mais n’utilise pas les paramètres filtrés retournés. Il assigne ensuite la valeur originale avec result[f] = params[f].
  • NewPostManager#enqueue copie directement @args[:tags] dans le payload du révisable.
  • ReviewableQueuedPost#create_options convertit les tags uniquement lorsque la valeur est déjà un Array, laissant une chaîne scalaire inchangée.
  • DiscourseTagging.tags_for_saving évalue ensuite tags_arg - tag_names lorsque l’utilisateur qui approuve peut créer des tags, ce qui provoque une erreur pour une chaîne de caractères.

Comportement attendu

Rejeter un paramètre tags scalaire avec une erreur de validation claire avant de créer le révisable, ou le normaliser si c’est le comportement de compatibilité prévu. Un révisable malformé existant devrait également échouer gracieusement au lieu de provoquer une erreur 500 non gérée lors de la modération.

Contournement et limites de ce rapport

La modification du révisable, la suppression de l’ancien tag, la sélection du tag macos existant et l’enregistrement ont modifié payload.tags en un tableau d’objets de tags. La relecture a confirmé la version 1, toujours en attente, et l’interface affichait à nouveau un seul tag. Je n’ai pas réessayé l’approbation après ce changement.

Le client du soumissionnaire original est inconnu. Ce rapport n’établit pas que le compositeur natif génère des tags scalaires. Il est basé sur la requête réelle, le révisable stocké, l’exception et le code inspecté ; je ne l’ai pas reproduit séparément sur une installation propre.

Pour une reproduction en développement, le chemin à tester est : exiger l’approbation pour un post d’un utilisateur non-staff, soumettre POST /posts avec un scalaire tags: "macos" et d’autres champs valides, puis approuver le post en file d’attente résultant en tant qu’administrateur.

Il existe une discussion API antérieure présentant la même exception de soustraction de String. Ce rapport se concentre sur la valeur invalide étant acceptée dans la file d’attente de révision et cassant l’approbation plus tard.