Crowi

レンダラパイプライン

Markdown を HTML にするパイプラインの構造と、レンダラプラグインの拡張点

Crowi のページ本文は、サーバ側の Markdown パイプラインで HTML (と保存済みの AST) に変換されます。レンダラプラグインはこのパイプラインに変換段を足すもので、運用者向けの有効化と設定は レンダラプラグイン にあります。

3 つの約束

  • SSR ファースト。 すべてのレンダラプラグインはサーバ側で HTML を生成します。プラグインコンテンツのクライアントサイドレンダリングはレイアウトシフトを起こすため禁止です。
  • 編集中は外部 API を叩かない。 外部データを取得するプラグインは、編集中はプレースホルダで領域を確保し、ページ保存など明示的なタイミングでのみ取得します。
  • 新しい記法を基本的に増やさない。 プラグインは振る舞いを拡張するもので、文法を拡張するものではありません。

コアパイプライン (TOC / wikilink / メンション / コードブロック) はプラグインより 先に 登録されます。プラグインはその上に追記する形で動作します。

拡張点

登録口何ができるか
コードブロックレンダラ```plantuml のように、言語名で選ばれるフェンスブロックを描画する
埋め込みタグ@[name](url) の形の埋め込みを描画する
remark / rehype プラグインパイプラインの任意の段に変換を挿し込む

埋め込みタグの card はコア自身が予約しており、第三者プラグインから上書きできません。絵文字ショートコードとリンクカードはプラグインではなくコアの機能として常時有効で、crowi.config.jsonplugins にも現れません。

キャッシュ

埋め込みとコードブロック系プラグインのレンダリング結果はキャッシュされます。方式は stale-while-revalidate で、キャッシュ済みの結果を即座に返しつつバックグラウンドで再レンダリングします。stale ウィンドウを大きく過ぎたエントリだけ、新しいレンダリングを待ってから返します。

失敗も種類ごとの間隔つきでキャッシュされます (一時的な上流障害は約 5 分、恒久的な失敗は約 1 時間、認可の失敗は 1 分、レート制限は上流の Retry-After に従い最大 24 時間)。一時的な失敗では、直前に成功した表示を最大 24 時間そのまま残します — 上流の不調で表示済みの図やカードが即座にエラー表示へ置き換わらないようにするためです。ポリシーやアクセス権の変化 (SSRF ガードで新たにブロックされた URL、認可の失敗) は例外で、次の再レンダリングで即座に反映します。

並行数制御を持つプラグイン (現状は Mermaid) では、子プロセスの timeout / crash や並行数超過による未完了はエラーキャッシュを経由せず、「未レンダリング」マークを保存して次の表示時に自動で再試行します。インフラ都合の失敗を、入力が悪いという結論に固定してしまわないための区別です。

SVG のサニタイズ

SVG を生成・中継するレンダラは、SDK が提供する sanitizeSvg (と寸法を読む extractSvgDimensions) を使います。ファーストパーティの PlantUML / Mermaid が使っているものと同じ実装で、script / イベントハンドラ / javascript: などを除去します。自前でサニタイズを実装しないでください。依存に足すのは SDK パッケージだけです。

Mermaid の隔離

Mermaid はブラウザではなく、fork() した隔離子プロセス内で、外部リソースを一切ロードしない strict 設定で描画されます。生成された SVG はサニタイズしたうえで base64 の data: URL として <img> に埋め込みます。図の設定を上書きするディレクティブや外部リソースを参照する構文は、入力段階でソース全体を対象に拒否されます。子プロセスには fetch / XMLHttpRequest / node:net などネットワーク API を削除・遮断する deny-by-default の境界があり、サニタイズより手前の層でも外部到達を防ぎます。

並行描画はプロセスごとに最大 4 個、1 ユーザーあたり同一プロセス内で最大 2 個に制限されます。

リンクカードの取得ガード

@[card](url) の OGP 取得には SSRF ガードがあります。取得先ホストの DNS 解決結果が private / loopback / link-local / unique-local / メタデータ用アドレスであれば拒否し、IP リテラル・DNS 経由・redirect 誘導のいずれでも同じ検証を行います (redirect は自動追従せず、最大 3 hop まで手動でたどって各 hop を再検証します)。http(s) 以外の scheme は即座に拒否、取得は 5 秒でタイムアウトし、応答は先頭 512KB までしか読みません。

設計文書

関連ページ

On this page