HTML コードブロック内のエンティティがエディタ読み込み時に破損する問題を修正

basecamp/lexxy

Rails の content_tag における HTML エンティティの扱いが原因で、<pre> ブロック内の &lt; などのエンティティがエディタ読み込み時に実際の HTML タグとして解釈されてしまう問題が修正されました。html_safe 属性を除去することで、属性値のエスケープ処理を正常化しています。

背景

Lexxy エディタでは、HTML コードサンプルを <pre> ブロック内に表示する際、&lt;div&gt; のような HTML エンティティを使用します。この値が lexxy_rich_textarea_tag に渡される際、Rails の render() が返す html_safe 文字列として処理されていました。

content_taghtml_safe な値を属性値として扱う場合、HTML エスケープを行いません。そのため、&lt;&lt; のまま HTML 属性に書き込まれ、ブラウザが属性値を読み取る際に < として解釈されてしまいます。これにより、コードサンプルとして表示すべき文字列が実際の HTML タグとして認識され、エディタの表示が破損していました。

技術的な変更

lib/lexxy/rich_text_area_tag.rblexxy_rich_textarea_tag メソッドに、html_safe 属性を除去する処理が追加されました。

変更後:

value = render_custom_attachments_in(value)
# remove the html_safe attribute to preserve attribute escape
value = value.to_str if value.respond_to? :to_str

options[:name] ||= name
options[:value] ||= value

to_str メソッドを呼び出すことで、ActiveSupport::SafeBuffer から通常の String に変換されます。これにより、後続の content_tag 呼び出し時に &lt;&amp;lt; として二重エスケープされ、ブラウザが属性値を読み取る際に &lt; として正しく保持されるようになります。

テストでは以下のケースが追加され、HTML エンティティが保持されることを検証しています:

test "#lexxy_rich_textarea_tag preserves HTML entities in code blocks" do
  code_html = '<pre data-language="html">&lt;div&gt;test&lt;/div&gt;</pre>'.html_safe

  render inline: <<~ERB, locals: { code_html: code_html }
    <%= lexxy_rich_textarea_tag :body, code_html %>
  ERB

  assert_dom "lexxy-editor", count: 1 do |lexxy_editor, *|
    value = lexxy_editor["value"]
    assert_includes value, "&lt;div&gt;"
    assert_not_includes value, "<div>"
  end
end

設計判断

html_safe 属性の除去 という最小限の変更で問題を解決しています。

PR の調査によると、以前のコードでは value = "<div>#{value}</div>" のような文字列補間が行われており、これが html_safe を解除する副作用を持っていました。この処理が削除された際、html_safe な値がそのまま content_tag に渡されるようになり、エスケープ動作が変化したことが問題の原因です。

to_str による変換は、Rails の content_tag のエスケープロジックを意図的に活用する設計です。通常の文字列として扱うことで、Rails の標準的な HTML エスケープ処理が適用され、属性値内のエンティティが正しく二重エスケープされます。respond_to? チェックにより、html_safe でない値に対しても安全に動作します。

まとめ

本 PR は、Rails の content_tag における html_safe 文字列の扱いに起因する HTML エンティティの破損問題を修正しています。to_str による html_safe 属性の除去という 2 行の追加により、エディタ内のコードブロックが正しく表示されるようになりました。Rails のエスケープメカニズムを活用した、副作用の少ない解決策といえます。

記事メタデータ

Generated by:
Claude Sonnet 4.5 for DiffDaily

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文(総論)→背景・技術的な変更・設計判断(各論)→まとめ(結論)という3部構成が明確に適用されており、模範的な記事構成です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(```言語:ファイルパス)とPR番号のリンク記法([#749](URL))が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Railsの`content_tag`や`html_safe`の挙動に関する知識を前提としており、専門知識を持つエンジニアという対象読者に適切です。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論で構成され、各段落がトピックセンテンスで始まるなど、パラグラフ・ライティングの原則が守られており、非常に読みやすいです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事内で引用されているコードブロックは、提供されたDiffの内容と完全に一致しており、ファイル名も正確です。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`html_safe`, `ActiveSupport::SafeBuffer`, `to_str`, HTMLエンティティといった技術用語が、文脈に沿って正確に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

「`content_tag`が`html_safe`な値をエスケープしない」という問題の核心や、「`to_str`で`html_safe`属性を解除する」という解決策の説明は、技術的に正確で論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(問題の原因、解決策、過去のコードの挙動など)は、PRのDescriptionやDiffによって裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#749)やファイルパスが正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトル「HTML コードブロック内のエンティティがエディタ読み込み時に破損する問題を修正」は、PRのTitle「Fix HTML escaping in code blocks」の内容を的確に要約しており、整合性が取れています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事の内容は提供されたPR情報に限定されており、バージョンサポート状況やリリース日程といったPR外の知識の追記はありません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「以前のコードでは」といった時間表現は、PR Description内の記述と一致しており、時間的な前後関係は正確に記述されています。