Capabilities とセキュリティ

このページ

サンドボックス化されたプラグインはデフォルトで隔離されています。自身の KV とストレージの読み書き以上のことを行うには、プラグインはデスクリプタ上で capability を宣言 する必要があります。サンドボックスブリッジは、これらの宣言に基づいてホストが提供するすべての API を制御します — content:read を宣言しなかったプラグインは ctx.content を取得できず、network:request を宣言しなかったプラグインは ctx.http を取得できません。

このページでは、各 capability が何を付与するか、サンドボックスがそれらをどのように強制するか、そして何が強制できないかについて説明します。

Capabilities の宣言

Capabilities はデスクリプタ(astro.config.mjs によってインポートされるファイル)上に存在し、idversionentrypoint と並んでいます:

export function helloPlugin(): PluginDescriptor {
	return {
		id: "plugin-hello",
		version: "0.1.0",
		format: "standard",
		entrypoint: "@my-org/plugin-hello/sandbox",

		capabilities: ["content:read", "network:request"],
		allowedHosts: ["api.example.com"],
	};
}

プラグインが実際に必要とするものだけを宣言してください。Capability の宣言は、マーケットプレイスがサイト運営者に同意ダイアログで表示するものでもあります — 余分な capabilities はインストール時の摩擦となり、監査におけるセキュリティフラグとなります。

Capability リファレンス

Capabilityアクセスを付与
content:readctx.content.get(), ctx.content.list()
content:writectx.content.create(), ctx.content.update(), ctx.content.delete() (content:read を含む)
media:readctx.media.get(), ctx.media.list()
media:writectx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (media:read を含む)
network:requestctx.http.fetch()allowedHosts に制限
network:request:unrestrictedctx.http.fetch() ホスト制限なし(ユーザー設定 URL のみ)
users:readctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (設定されたメールプロバイダープラグインが必要)
hooks.email-transport:register排他的な email:deliver フックの登録を許可(トランスポートプロバイダー)
hooks.email-events:registeremail:beforeSend / email:afterSend フックの登録を許可
hooks.page-fragments:registerpage:fragments フックの登録を許可(ネイティブプラグインのみ)

いくつか知っておくべきこと:

  • 含意。 content:write は自動的に content:read を含みます;media:writemedia:read を含みます;network:request:unrestrictednetwork:request を含みます。両方をリストする必要はありません。
  • network:request:unrestricted はユーザー設定 URL のために存在します。 運営者が宛先 URL を入力する webhook プラグインは、マニフェストにないホストに到達する必要があります。常に既知の API を呼び出すプラグインは、network:request + allowedHosts を使用する必要があります。
  • email:send は capability だけでなく設定によって制御されます。 プラグインは 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 を宣言して、同意ダイアログが運営者にプラグインが正確にどこに呼び出すかを伝えるようにします。

サンドボックスが強制するもの

サンドボックスランナーがアクティブな場合、ランタイムは以下を強制します:

  1. Capability ゲーティング。 PluginContext ファクトリは、対応する capability が宣言されている場合にのみ、ctx.contentctx.mediactx.httpctx.usersctx.email を入力します。宣言されていない capability のメソッドを呼び出すことは不可能です — そこにはオブジェクトがありません。

  2. ストレージと KV のスコーピング。 すべてのストレージおよび KV 操作は、プラグインの id にスコープされます。プラグインは別のプラグインの KV やそのストレージコレクションを読み取ることができず、デスクリプタで宣言したストレージコレクションにのみアクセスできます。

  3. ネットワーク分離。 直接的な fetch() およびその他のネットワークプリミティブはランナーによってブロックされます。ネットワークに到達する唯一の方法は、ブリッジのホスト検証を通過する ctx.http.fetch() です。

  4. ホストバインディングなし。 サンドボックス化されたプラグインは、環境変数、ファイルシステム、またはプラットフォームバインディングを見ることができません — ホストワーカーがそれらを持っていても同様です。プラグインランタイムは、ブリッジと宣言された capabilities のみを持つクリーンな分離です。

  5. リソース制限。 ランナーは、呼び出しごとに CPU、サブリクエスト、ウォールクロック、メモリの制限を強制できます。正確な制限は、使用しているランナーに依存します;Cloudflare ランナーはプラットフォームの Worker Loader 制限を使用します(呼び出しあたり 50ms CPU、10 サブリクエスト、30 秒ウォールクロック、約 128MB メモリ)。ランナーの制限を超えるフックは中止されます;EmDash フックタイムアウト(フック設定の timeout)は、その上にさらに厳格な上限を強制します。

サンドボックスが強制しないもの

capability システムがカバーできないいくつかのこと:

  • 付与された capability 内での動作。 content:write を持つプラグインは、自分のコンテンツだけでなく、すべてのコンテンツを編集できます。Capabilities は粗いです — 「このプラグインはコンテンツを書き込むことができる」と言いますが、「このプラグインは作成したコンテンツのみを書き込むことができる」とは言いません。監査時のレビューは、プラグインが実際に付与内で何をするかの唯一のチェックです。
  • Node.js での運営者の信頼。 設定されたサンドボックスランナーが利用不可と報告する場合(Cloudflare Worker Loader なし、Node 側のランナーがインストールされていないなど)、sandboxed: [] プラグインは起動時にスキップされます。それらを plugins: [] に移動してインプロセスで実行できますが、その場合、V8 アイソレートはなく、リソース制限もなく、プラグインは直接 fetch() を呼び出したり環境変数を読み取ったりできます。これをネイティブレベルの信頼として扱ってください。
  • サイドチャネル。 タイミング、ログ出力、保存されたデータはすべて、ホスト環境への適切なアクセス権を持つ人にとって可視です。サンドボックスを、それを実行している運営者に対する機密性の境界として使用しないでください。

Capability の同意

運営者がマーケットプレイスからサンドボックス化されたプラグインをインストールすると、EmDash は宣言された capabilities をリストする同意ダイアログを表示します。capabilities を追加する更新 — たとえば、以前はコンテンツを読み取るだけだったプラグインが今ではネットワークリクエストを行いたい — は capability diff として表示され、新しいバージョンが有効になる前に新たな承認が必要です。

これが、「後で使うかもしれない」場合でも余分な capabilities を宣言することが重要な理由です。それらはすべてのインストールと更新で摩擦として表示され、セキュリティ監査は明らかに必要以上を求めるプラグインにフラグを立てます。プラグインが使用するものを正確にリストし、プラグインが実際に使用し始めたときに実際のバージョンで新しい capabilities を追加してください。

バンドル時の検証

emdash plugin bundleemdash plugin publish は追加のチェックを実行します:

  • 宣言されたすべての capability は既知のセットに含まれている必要があります(タイプミスはビルドを失敗させます)。
  • allowedHosts を設定せずに network:request を宣言するプラグインは警告をトリガーします — ホストを宣言するか、network:request:unrestricted に切り替えて理由を文書化してください。
  • 非推奨の capability 名は bundle/validate 中に警告をトリガーし、publish でハード失敗します。
  • バンドルされた backend.js は Node.js の組み込みモジュール(fspathchild_process など)をインポートできません — サンドボックスランタイムはそれらを提供しません。

チェックの完全なリストについては、バンドルと公開 を参照してください。