Sandboxed Plugins sind standardmäßig isoliert. Um über das Lesen und Schreiben des eigenen KV und Storage hinauszugehen, muss ein Plugin eine Fähigkeit in seinem Manifest deklarieren. Die Sandbox-Bridge kontrolliert jede vom Host bereitgestellte API basierend auf diesen Deklarationen — ein Plugin, das content:read nicht deklariert hat, erhält kein ctx.content, und eines ohne network:request erhält kein ctx.http.
Diese Seite behandelt, was jede Fähigkeit gewährt, wie die Sandbox sie durchsetzt und was nicht durchsetzbar ist.
Fähigkeiten deklarieren
Fähigkeiten befinden sich in emdash-plugin.jsonc, neben slug und dem Rest des Vertrauensvertrags:
{
"slug": "plugin-hello",
// ...identity + profile...
"capabilities": ["content:read", "network:request"],
"allowedHosts": ["api.example.com"]
}
Deklarieren Sie nur, was das Plugin tatsächlich benötigt. Fähigkeitsdeklarationen sind auch das, was der Marketplace Site-Betreibern im Zustimmungsdialog anzeigt — zusätzliche Fähigkeiten sind Reibung bei der Installation und ein Sicherheitsflag bei Audits.
Fähigkeitsreferenz
| Fähigkeit | Gewährt Zugriff auf |
|---|---|
content:read | ctx.content.get(), ctx.content.list() |
content:write | ctx.content.create(), ctx.content.update(), ctx.content.delete() (impliziert content:read) |
media:read | ctx.media.get(), ctx.media.list() |
media:write | ctx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (impliziert media:read) |
network:request | ctx.http.fetch() — beschränkt auf allowedHosts |
network:request:unrestricted | ctx.http.fetch() ohne Host-Beschränkung (nur für benutzerkonfigurierte URLs) |
users:read | ctx.users.get(), ctx.users.getByEmail(), ctx.users.list() |
email:send | ctx.email.send() (erfordert ein konfiguriertes E-Mail-Provider-Plugin) |
hooks.email-transport:register | Erlaubt die Registrierung des exklusiven email:deliver-Hooks (Transport-Provider) |
hooks.email-events:register | Erlaubt die Registrierung von email:beforeSend / email:afterSend-Hooks |
hooks.page-fragments:register | Erlaubt die Registrierung des page:fragments-Hooks (nur native Plugins) |
Einige Dinge, die Sie wissen sollten:
- Implikationen.
content:writeimpliziert automatischcontent:read;media:writeimpliziertmedia:read;network:request:unrestrictedimpliziertnetwork:request. Sie müssen nicht beide auflisten. network:request:unrestrictedexistiert für benutzerkonfigurierte URLs. Ein Webhook-Plugin, bei dem der Betreiber die Ziel-URL eingibt, muss Hosts erreichen können, die nicht im Manifest stehen. Plugins, die immer bekannte APIs aufrufen, solltennetwork:request+allowedHostsverwenden.email:sendwird durch Konfiguration gegated, nicht nur durch die Fähigkeit. Ein Plugin kannemail:senddeklarieren, aberctx.emailwird nur befüllt, wenn ein anderes Plugin einenemail:deliver-Transport registriert hat.
Netzwerk-Host-Allowlists
Plugins mit network:request können nur Hosts abrufen, die in allowedHosts aufgeführt sind. Wildcards werden für Subdomains unterstützt:
"capabilities": ["network:request"],
"allowedHosts": [
"api.example.com", // exact host
"*.cdn.example.com" // any subdomain of cdn.example.com
]
Die Bridge prüft den Host der Anfrage-URL gegen die Allowlist, bevor die Anfrage weitergeleitet wird. Eine Anfrage an einen nicht deklarierten Host wirft innerhalb des Plugins, ohne die Sandbox jemals zu verlassen.
network:request:unrestricted überspringt die Allowlist-Prüfung vollständig. Es ist für Plugins gedacht, bei denen der Betreiber die Ziel-URL zur Laufzeit konfiguriert (Webhook-Sender, generische HTTP-Weiterleiter). Vermeiden Sie es für Plugins, bei denen das Ziel Teil des Plugin-Designs ist — deklarieren Sie stattdessen network:request mit expliziten Hosts, damit der Zustimmungsdialog den Betreibern genau zeigt, wohin das Plugin Anfragen senden wird.
Was die Sandbox durchsetzt
Wenn ein Sandbox-Runner aktiv ist, setzt die Runtime durch:
-
Fähigkeits-Gating. Die PluginContext-Factory befüllt
ctx.content,ctx.media,ctx.http,ctx.users,ctx.emailnur, wenn die entsprechende Fähigkeit deklariert ist. Das Aufrufen einer Methode auf einer nicht deklarierten Fähigkeit ist nicht möglich — es gibt kein Objekt dort. -
Storage- und KV-Scoping. Jede Storage- und KV-Operation ist auf den Slug des Plugins beschränkt. Ein Plugin kann nicht das KV oder die Storage-Collections eines anderen Plugins lesen und nur auf Storage-Collections zugreifen, die es im Manifest deklariert hat.
-
Netzwerkisolation. Direktes
fetch()und andere Netzwerk-Primitive werden vom Runner blockiert. Der einzige Weg zum Netzwerk istctx.http.fetch(), das durch die Host-Validierung der Bridge geht. -
Keine Host-Bindings. Sandboxed Plugins sehen keine Umgebungsvariablen, das Dateisystem oder Plattform-Bindings — auch wenn Ihr Host-Worker sie hat. Die Plugin-Runtime ist ein sauberes Isolate mit nur der Bridge und den deklarierten Fähigkeiten.
-
Ressourcenlimits. Der Runner kann CPU-, Subrequest-, Wall-Clock- und Speicherlimits pro Aufruf durchsetzen. Die genauen Limits hängen davon ab, welchen Runner Sie verwenden; der Cloudflare-Runner verwendet die Worker Loader-Limits der Plattform (50ms CPU pro Aufruf, 10 Subrequests, 30 Sekunden Wall-Clock, ~128MB Speicher). Der Node.js workerd-Runner (
@emdash-cms/sandbox-workerd) setzt Wall-Clock-Zeit überPromise.racedurch; CPU- und Speicherlimits sind Cloudflare-Plattformfunktionen und werden von standalone workerd nicht durchgesetzt. Hooks, die die Limits des Runners überschreiten, werden abgebrochen; das EmDash-Hook-Timeout (timeoutin der Hook-Konfiguration) setzt eine strengere Obergrenze darüber hinaus durch.
Was die Sandbox nicht durchsetzt
Einige Dinge, die das Fähigkeitssystem nicht abdeckt und nicht abdecken kann:
- Verhalten innerhalb einer gewährten Fähigkeit. Ein Plugin mit
content:writekann jeden Inhalt bearbeiten, nicht nur seinen eigenen. Fähigkeiten sind grob — sie sagen „dieses Plugin kann Inhalte schreiben“, nicht „dieses Plugin kann nur die von ihm erstellten Inhalte schreiben“. Audit-Zeit-Review ist die einzige Prüfung dessen, was ein Plugin tatsächlich innerhalb seiner Gewährung tut. - Operator-Vertrauen auf Node.js. Wenn der konfigurierte Sandbox-Runner als nicht verfügbar gemeldet wird (kein Cloudflare Worker Loader, kein Node-seitiger Runner installiert usw.), werden
sandboxed: []-Plugins beim Start übersprungen. Sie können sie inplugins: []verschieben, um sie im Prozess auszuführen — aber dann gibt es kein V8-Isolate, keine Ressourcenlimits, und das Plugin kannfetch()direkt aufrufen oder Umgebungsvariablen lesen. Behandeln Sie das als native Vertrauensstufe. - Nebenkanäle. Timing, Log-Ausgabe und gespeicherte Daten sind für jeden mit entsprechendem Zugriff auf die Host-Umgebung sichtbar. Verwenden Sie die Sandbox nicht als Vertraulichkeitsgrenze gegen den sie betreibenden Operator.
Fähigkeitszustimmung
Wenn ein Betreiber ein sandboxed Plugin vom Marketplace installiert, zeigt EmDash einen Zustimmungsdialog mit den deklarierten Fähigkeiten. Updates, die Fähigkeiten hinzufügen — zum Beispiel ein Plugin, das zuvor nur Inhalte las und jetzt Netzwerkanfragen stellen möchte — erscheinen als Fähigkeits-Diff und erfordern eine neue Genehmigung, bevor die neue Version wirksam wird.
Deshalb ist es wichtig, zusätzliche Fähigkeiten zu deklarieren, auch wenn Sie sie „später vielleicht brauchen“. Sie erscheinen als Reibung bei jeder Installation und jedem Update, und Sicherheitsaudits markieren Plugins, die mehr verlangen, als sie offensichtlich benötigen. Listen Sie genau auf, was das Plugin verwendet, und fügen Sie neue Fähigkeiten in einer echten Version hinzu, wenn das Plugin sie tatsächlich zu nutzen beginnt.
Bundle-Zeit-Validierung
emdash-plugin bundle und emdash-plugin publish führen zusätzliche Prüfungen durch:
- Jede deklarierte Fähigkeit muss in der anerkannten Menge sein (Tippfehler führen zum Build-Fehler).
network:requesterfordert eine nicht leereallowedHosts;network:request:unrestrictederfordert, dass sie leer ist. Siehe die Manifest-Referenz.- Das gebündelte
backend.jsdarf keine Node.js-Built-ins importieren (fs,path,child_processusw.) — Sandbox-Runtimes stellen sie nicht bereit.
Siehe Bundling und Publishing für die vollständige Liste der Prüfungen.