技術記事ぎじゅつの きじ

Libraryの原稿を安全に表示するために、Markdown変換を自前で書いた話

外部サービスから取得したMarkdownをそのままHTMLにせず、対応する記法を限定した小さな変換器をReactで書きました。生HTMLの扱い、リンクの安全性、見出しレベルの規則を説明します。

公開こうかい

約2分で読めますやく 2ぷんで よめます

Libraryから取得した本文はMarkdownです。表示するだけならライブラリでHTMLに変換してdangerouslySetInnerHTMLに渡せば済みますが、このサイトでは対応する記法を限定した変換器を自前で書き、React要素として描画しています。

生HTMLを通さない

一番の理由は、本文に含まれるHTMLをそのまま出さないことです。Reactは文字列をテキストとして描画するので、<script><img onerror>が原稿に紛れ込んでもそのまま文字として表示されます。変換器はブロックごとに要素を組み立てるだけで、HTML文字列を生成しません。

リンク先を制限する

リンクはhttps:http:mailto:tel:、ページ内の#、サイト内の/だけを許しています。javascript:のようなスキームは、リンクにせず書かれたままの文字列として出します。

const safeHrefPattern = /^(https?:\/\/|mailto:|tel:|#|\/)/i;

画像も同じ考え方で、https:か同一サイトのパスだけを<img>にします。

対応している記法

必要になったものだけを足しています。

記法対応
見出し、段落、箇条書き、番号付きあり
引用、区切り線、表あり
コードブロック(言語指定つき)あり
強調、インラインコード、リンク、画像あり
生HTML、脚注、数式なし

見出しは1段下げる

ページのタイトルがh1を持つので、原稿の#h2##h3として描画します。Libraryのエディタで「見出し1」を付けた行が、記事の中では節の見出しになります。技術記事ではこの見出しにidを付け、目次からリンクしています。

コードブロックの言語

言語指定は<pre data-lang="ts">として持ち、CSSのattr()で右上に表示しています。シンタックスハイライトのライブラリは読み込んでいません。読みやすさより、ページの軽さと依存の少なさを優先しています。

足りなくなったら

対応する記法を増やすときは、変換器のブロック型を1つ足し、描画側にcaseを1つ足します。ここまでで200行ほどなので、必要になった時点で読み直せる大きさに収まっています。

この記事の本文は、当社が開発・運用する文書管理サービスLibraryで管理しています。この きじの ぶんは、わたしたちが つくっている Library という ぶんしょの サービスで かんりしています。 Libraryの原文を開くLibraryの もとの ぶんを みる

相談そうだん

この記事のテーマで、開発を相談する。この きじの テーマで、かいはつを そうだんする。

記事で扱った構成や設計は、ソリューション事業部が実際の案件で使っているものです。同じ課題をお持ちでしたら、業務整理から一緒に進めます。きじに かいた つくりかたは、じっさいの しごとで つかっている ものです。おなじ こまりごとが あれば、いっしょに すすめます。

関連記事かんれんする きじ

同じテーマの記事。おなじ テーマの きじ。

技術記事一覧へ戻るきじの いちらんへ もどる