DOMPurifyの許可リスト漏れによりアンダーライン書式が消失していた問題を修正
エディタでアンダーライン書式を適用しても、値の出力時にHTMLサニタイザーが <u> タグを除去していたバグが修正されました。src/config/dom_purify.js の許可タグリストに "u" を追加する1行の修正で、書式の往復変換(round-trip)が正しく機能するようになります。
背景
Cmd+U(macOS)または Ctrl+U(Windows)でアンダーラインを適用すると、エディタのDOM上では <span class="lexxy-content__underline"> としてスタイルが反映され、エクスポート層が <u> タグに変換していました。しかし value の取得時に DOMPurify による sanitize() が走り、許可リストにない <u> タグが除去されて、アンダーライン情報が失われた状態でHTML文字列が返されていました。
この問題は #710 として報告されています。ユーザーがアンダーライン書式を適用して保存・再読み込みを行うと、エディタはアンダーラインを認識しない素のテキストとして表示していました。エクスポート層(text_node_export_helper.js)の実装は正しかったにもかかわらず、その下流のサニタイズ処理が結果を破壊するという、レイヤー間の設定不整合が根本原因です。
技術的な変更
変更は src/config/dom_purify.js の ALLOWED_HTML_TAGS 配列への "u" の追加1件と、リグレッションテストの追加2件のみです。
変更前:
const ALLOWED_HTML_TAGS = [ "a", "b", "blockquote", "br", "code", "div", "em",
"figcaption", "figure", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "mark", "ol", "p", "pre", "q", "s", "strong", "ul", "table", "tbody", "tr", "th", "td" ]
変更後:
const ALLOWED_HTML_TAGS = [ "a", "b", "blockquote", "br", "code", "div", "em",
"figcaption", "figure", "h1", "h2", "h3", "h4", "h5", "h6", "hr", "i", "img", "li", "mark", "ol", "p", "pre", "q", "s", "strong", "u", "ul", "table", "tbody", "tr", "th", "td" ]
テストは test/browser/tests/formatting/inline_formatting.test.js に2ケースが追加されています。1つ目はキーボードショートカットによるアンダーライン適用後に <u> タグが出力されることを検証し、2つ目は setValue("<p>Hello <u>everyone</u></p>") → value の往復変換でタグが保持されることを確認します。
test("underline via keyboard shortcut", async ({ page, editor }) => {
await editor.setValue(HELLO_EVERYONE)
await editor.select("everyone")
const modifier = process.platform === "darwin" ? "Meta" : "Control"
await editor.content.press(`${modifier}+u`)
await assertEditorHtml(editor, "<p>Hello <u>everyone</u></p>")
})
test("underline round-trips through setValue/value", async ({ editor }) => {
await editor.setValue("<p>Hello <u>everyone</u></p>")
await assertEditorHtml(editor, "<p>Hello <u>everyone</u></p>")
})
すでに許可されている "s"(打ち消し線)・"b" / "i" と同様のパターンで追加されており、許可リストの整合性が取られています。
設計判断
エクスポート層とサニタイズ設定を分離して管理する構造が採用されているため、今回のような「実装は正しいが設定が追いついていない」ミスが生じやすいトレードオフが存在します。
リッチテキストエディタにおいてHTMLサニタイズは必須ですが、エディタが生成するタグセットと許可リストは常に同期が必要です。今回のPRでは許可リストへの追加のみで修正が完結しており、エクスポート層の実装変更は不要でした。これは、エクスポート層の設計自体は正しかったことを示しています。
テストが setValue/value の往復変換を明示的に検証する形で追加されたことで、今後の許可リスト変更や新しい書式要素の追加時に同種のリグレッションを検出できる安全網が整備されました。
まとめ
エクスポート層とHTMLサニタイズ設定の間に生じていたタグ許可漏れという単純な設定ミスが、ユーザーには「書式が保存されない」という深刻な問題として現れていた例です。往復変換テストの追加により、今後は同種の設定不整合をCI上で早期に検出できる体制が整いました。