サンドボックスプラグインはデフォルトで隔離されています。自身のKVとストレージの読み書き以上のことをするには、プラグインはマニフェストでケイパビリティを宣言する必要があります。サンドボックスブリッジは、これらの宣言に基づいてホスト提供の各APIを制御します — content:readを宣言していないプラグインはctx.contentを取得できず、network:requestを宣言していないプラグインはctx.httpを取得できません。
このページでは、各ケイパビリティが何を許可するか、サンドボックスがそれらをどのように強制するか、そして何が強制できないかを説明します。
ケイパビリティの宣言
ケイパビリティはemdash-plugin.jsoncに、slugや信頼契約の残りの部分と一緒に記載します:
{
"slug": "plugin-hello",
// ...アイデンティティ + プロフィール...
"capabilities": ["content:read", "network:request"],
"allowedHosts": ["api.example.com"]
}
プラグインが実際に必要とするものだけを宣言してください。ケイパビリティの宣言は、Marketplaceが同意ダイアログでオペレーターに表示するものでもあります — 余分なケイパビリティはインストール時の摩擦となり、監査時のセキュリティシグナルとなります。
ケイパビリティリファレンス
| ケイパビリティ | アクセスを許可するもの |
|---|---|
content:read | ctx.content.get()、ctx.content.list() |
content:write | ctx.content.create()、ctx.content.update()、ctx.content.delete()(content:readを含む) |
taxonomies:read | ctx.taxonomies.getAll()、ctx.taxonomies.getTerms()、ctx.taxonomies.getEntryTerms() |
media:read | ctx.media.get()、ctx.media.list() |
media:write | ctx.media.getUploadUrl()、ctx.media.upload()、ctx.media.delete()(media:readを含む) |
network:request | ctx.http.fetch() — allowedHostsに制限 |
network:request:unrestricted | ctx.http.fetch()(ホスト制限なし、ユーザー設定URLのみ) |
users:read | ctx.users.get()、ctx.users.getByEmail()、ctx.users.list() |
email:send | ctx.email.send()(設定済みのメールプロバイダープラグインが必要) |
hooks.email-transport:register | 排他的なemail:deliverフック(トランスポートプロバイダー)の登録を許可 |
hooks.email-events:register | email:beforeSend / email:afterSendフックの登録を許可 |
hooks.page-fragments:register | page:fragmentsフックの登録を許可(ネイティブプラグインのみ) |
いくつかの注意点:
- 含意。
content:writeは自動的にcontent:readを含みます。media:writeはmedia:readを含みます。network:request:unrestrictedはnetwork:requestを含みます。両方をリストする必要はありません。 - タクソノミーは別の読み取り専用サーフェスです。
taxonomies:readは、ctx.taxonomiesを通じてタクソノミー定義、その用語、およびエントリに割り当てられた用語へのアクセスを許可します。content:readとは独立しています — プラグインがコンテンツとその分類を読む場合は両方を宣言してください。プラグインからのタクソノミーへの書き込みアクセスはありません。 network:request:unrestrictedはユーザー設定URLのために存在します。 オペレーターが宛先URLを入力するWebhookプラグインは、マニフェストにないホストに到達する必要があります。常に既知のAPIを呼び出すプラグインはnetwork:request+allowedHostsを使用するべきです。email:sendはケイパビリティだけでなく設定によって制御されます。 プラグインはemail:sendを宣言できますが、ctx.emailは別のプラグインがemail:deliverトランスポートを登録した場合にのみ設定されます。
ネットワークホスト許可リスト
network:requestを持つプラグインは、allowedHostsにリストされたホストにのみフェッチできます。サブドメインにはワイルドカードがサポートされています:
"capabilities": ["network:request"],
"allowedHosts": [
"api.example.com", // 正確なホスト
"*.cdn.example.com" // cdn.example.comの任意のサブドメイン
]
ブリッジはリクエストURLのホストを許可リストと照合してからリクエストを転送します。宣言されていないホストへのリクエストは、サンドボックスを離れることなくプラグイン内で例外をスローします。
network:request:unrestrictedは許可リストのチェックを完全にスキップします。オペレーターが実行時に宛先URLを設定するプラグイン(Webhook送信者、汎用HTTPフォワーダー)向けです。宛先がプラグイン設計の一部であるプラグインには使用しないでください — 代わりに明示的なホストでnetwork:requestを宣言して、同意ダイアログがオペレーターにプラグインがどこを呼び出すかを正確に伝えるようにしてください。
サンドボックスが強制すること
サンドボックスランナーがアクティブな場合、ランタイムは以下を強制します:
-
ケイパビリティゲーティング。 PluginContextファクトリは、対応するケイパビリティが宣言されている場合にのみ
ctx.content、ctx.taxonomies、ctx.media、ctx.http、ctx.users、ctx.emailを設定します。宣言されていないケイパビリティのメソッドを呼び出すことはできません — そこにオブジェクトがありません。 -
ストレージとKVのスコーピング。 すべてのストレージとKV操作はプラグインのslugにスコープされます。プラグインは他のプラグインのKVやストレージコレクションを読み取ることができず、マニフェストで宣言したストレージコレクションにのみアクセスできます。
-
ネットワーク隔離。 直接的な
fetch()やその他のネットワークプリミティブはランナーによってブロックされます。ネットワークへの唯一のパスはctx.http.fetch()で、ブリッジのホスト検証を通過します。 -
ホストバインディングなし。 サンドボックスプラグインは環境変数、ファイルシステム、プラットフォームバインディングを見ることができません — ホストワーカーがそれらを持っていても。プラグインランタイムはブリッジと宣言されたケイパビリティのみを持つクリーンなアイソレートです。
-
リソース制限。 ランナーは呼び出しごとにCPU、サブリクエスト、ウォールクロック、メモリの制限を強制できます。正確な制限は使用しているランナーによって異なります。Cloudflareランナーはプラットフォームのワーカーローダー制限を使用します(呼び出しごとにCPU 50ms、サブリクエスト10件、ウォールクロック30秒、メモリ約128MB)。Node.js workerdランナー(
@emdash-cms/sandbox-workerd)はPromise.raceでウォールクロック時間を強制します。CPUとメモリの制限はCloudflareプラットフォームの機能であり、スタンドアロンworkerdでは強制されません。ランナーの制限を超えるフックはキャンセルされます。EmDashのフックタイムアウト(フック設定のtimeout)はさらに厳しい上限を強制します。
サンドボックスが強制しないこと
ケイパビリティシステムがカバーしない、またはカバーできないいくつかの事項:
- 許可されたケイパビリティ内での動作。
content:writeを持つプラグインは、自身のものだけでなく任意のコンテンツを編集できます。ケイパビリティは粗粒度です —「このプラグインはコンテンツを書き込める」と言いますが、「このプラグインは自分が作成したコンテンツのみ書き込める」とは言いません。監査時のレビューが、プラグインが許可内で実際に何をしているかの唯一のチェックです。 - Node.jsでのオペレーターの信頼。 設定されたサンドボックスランナーが利用不可と報告した場合(Cloudflareワーカーローダーなし、Node側のランナーがインストールされていないなど)、
sandboxed: []プラグインは起動時にスキップされます。インプロセスで実行するためにplugins: []に移動できますが、V8アイソレートなし、リソース制限なし、プラグインが直接fetch()を呼び出したり環境変数を読み取ったりできます。これをネイティブレベルの信頼として扱ってください。 - サイドチャネル。 タイミング、ログ出力、保存されたデータは、ホスト環境への合理的なアクセスを持つ誰にでも見えます。サンドボックスを、それを実行するオペレーターに対する機密性の境界として使用しないでください。
ケイパビリティの同意
オペレーターがMarketplaceからサンドボックスプラグインをインストールすると、EmDashは宣言されたケイパビリティを含む同意ダイアログを表示します。ケイパビリティを追加する更新 — たとえば、以前はコンテンツの読み取りのみだったプラグインがネットワークリクエストを行いたい場合 — はケイパビリティdiffとして表示され、新しいバージョンが有効になる前に新たな承認が必要です。
これが、「後で必要になるかもしれない」場合でも余分なケイパビリティを宣言することが重要な理由です。それらはすべてのインストールとアップデートで摩擦として表示され、セキュリティ監査は明らかに必要以上のものを要求するプラグインにフラグを立てます。プラグインが使用するものを正確にリストし、プラグインが実際に使用を開始する際に実際のバージョンで新しいケイパビリティを追加してください。
ビルド時の検証
emdash-plugin bundleとemdash-plugin publishは追加のチェックを実行します:
- 宣言された各ケイパビリティは認識されたセットに含まれている必要があります(タイプミスはビルドを失敗させます)。
network:requestは空でないallowedHostsが必要です。network:request:unrestrictedは空であることが必要です。マニフェストリファレンスを参照してください。- バンドルされた
backend.jsはNode.jsビルトイン(fs、path、child_processなど)をインポートできません — サンドボックスランタイムはそれらを提供しません。
チェックの完全なリストについてはバンドルと公開を参照してください。