1 日ほどあれこれ試行錯誤した結果、この回答に行きつきました。
どうやらこのアプローチは無駄だったようで、別の方法を探る必要があります。
elem.innerHTML に <script>alert(1)</script> を設定すると、エスケープが解除されて <script>alert(1)</script> になってしまいます(作曲画面で入力している間は、エスケープされていない状態で入力するとプレビューで削除されるのが確認できます)。これは問題でしょうか、それとも CSP が阻止してくれるのでしょうか?
https://meta.discourse.org/t/mitigate-xss-attacks-with-content-security-policy/104243!csp error|690x191
説明の一部としてスクリプトタグを含む投稿も、同様にこれらの CSP エラーを発生させているようです。今、本当に混乱しています。保存型 XSS を気にする必要はあるのでしょうか、それとも CSP が自動的にブロックしてくれるのでしょうか?作曲画面では self-XSS を防ぐために CKEditor を使用しています。もし気にする必要があるなら、安全でないタグを除去する必要があるようです。現時点では以下のようにしています。
value = Loofah.fragment(value).scrub!(:escape).to_s
しかし、elem.innerHTML にこの値を設定すると HTML エンティティがエスケープ解除されてしまうため、全く効果がありません。
編集:ついに混乱の原因を見つけました。要素の検査機能では実際の HTML は表示されません。HTML エンティティはすでに変換されて表示されてしまいます。
インスペクターで「HTML として編集」をクリックすると、実際にはすべて正常であることが明確になります。目に見えないはずのタグがレンダリングされていることを目撃していた時点で、この方向に気づくべきでした。
