Après avoir bidouillé pendant une journée, je suis tombé sur cette réponse.
Il semble que ce fut un effort perdu et que je dois emprunter une autre voie.
Lorsque je définis elem.innerHTML sur <script>alert(1)</script>, cela devient déséchapé. (je remarque en tapant ceci dans le compositeur que cela sera supprimé dans l’aperçu si je le tape sans échappement). Est-ce un problème ou la CSP l’empêchera-t-elle ?
Mitigate XSS Attacks with Content Security Policy
Les publications incluant des balises script dans le cadre d’une explication semblent également générer ces erreurs CSP. Je suis vraiment confus maintenant. Dois-je m’inquiéter du XSS stocké ou la CSP le bloquera-t-elle simplement ? Dans le compositeur, j’utilise CKEditor qui prévient le XSS auto. Si je dois m’en soucier, il semble que je doive élaguer les balises non sécurisées. Pour l’instant, je fais simplement :
value = Loofah.fragment(value).scrub!(:escape).to_s
mais cela semble n’avoir aucun effet car définir elem.innerHTML sur cette valeur déséchappe simplement les entités HTML.
EDIT : J’ai enfin trouvé la source de ma confusion : l’inspection de l’élément ne vous montre pas le vrai HTML. Il convertit déjà les entités HTML.
Si vous cliquez sur « éditer en HTML » dans l’inspecteur, il devient clair que tout est en fait correct. Le fait de voir des balises autrement invisibles être rendues aurait déjà dû m’orienter dans cette direction.

