I plugin sandboxed sono isolati per impostazione predefinita. Per fare qualcosa oltre la lettura e la scrittura del proprio KV e storage, un plugin deve dichiarare una capability nel suo manifest. Il bridge sandbox controlla ogni API fornita dall’host in base a tali dichiarazioni — un plugin che non ha dichiarato content:read non ottiene un ctx.content, e uno che non ha dichiarato network:request non ottiene ctx.http.
Questa pagina copre cosa garantisce ogni capability, come la sandbox le impone e cosa non è applicabile.
Dichiarare le capabilities
Le capabilities si trovano in emdash-plugin.jsonc, insieme a slug e al resto del contratto di fiducia:
{
"slug": "plugin-hello",
// ...identity + profile...
"capabilities": ["content:read", "network:request"],
"allowedHosts": ["api.example.com"]
}
Dichiara solo ciò di cui il plugin ha effettivamente bisogno. Le dichiarazioni di capabilities sono anche ciò che il marketplace mostra agli operatori del sito nel dialogo di consenso — le capabilities aggiuntive sono attrito al momento dell’installazione e un flag di sicurezza negli audit.
Riferimento capabilities
| Capability | Concede accesso a |
|---|---|
content:read | ctx.content.get(), ctx.content.list() |
content:write | ctx.content.create(), ctx.content.update(), ctx.content.delete() (implica content:read) |
media:read | ctx.media.get(), ctx.media.list() |
media:write | ctx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (implica media:read) |
network:request | ctx.http.fetch() — limitato a allowedHosts |
network:request:unrestricted | ctx.http.fetch() senza restrizione di host (solo per URL configurati dall’utente) |
users:read | ctx.users.get(), ctx.users.getByEmail(), ctx.users.list() |
email:send | ctx.email.send() (richiede un plugin provider email configurato) |
hooks.email-transport:register | Permette di registrare l’hook esclusivo email:deliver (provider di trasporto) |
hooks.email-events:register | Permette di registrare gli hook email:beforeSend / email:afterSend |
hooks.page-fragments:register | Permette di registrare l’hook page:fragments (solo plugin nativi) |
Alcune cose da sapere:
- Implicazioni.
content:writeimplica automaticamentecontent:read;media:writeimplicamedia:read;network:request:unrestrictedimplicanetwork:request. Non è necessario elencare entrambi. network:request:unrestrictedesiste per URL configurati dall’utente. Un plugin webhook in cui l’operatore digita l’URL di destinazione deve raggiungere host che non sono nel manifest. I plugin che chiamano sempre API note dovrebbero usarenetwork:request+allowedHosts.email:sendè controllato dalla configurazione, non solo dalla capability. Un plugin può dichiarareemail:send, mactx.emailsarà popolato solo se qualche altro plugin ha registrato un trasportoemail:deliver.
Liste di host di rete consentiti
I plugin con network:request possono recuperare solo gli host elencati in allowedHosts. I wildcard sono supportati per i sottodomini:
"capabilities": ["network:request"],
"allowedHosts": [
"api.example.com", // host esatto
"*.cdn.example.com" // qualsiasi sottodominio di cdn.example.com
]
Il bridge controlla l’host dell’URL della richiesta rispetto alla lista consentita prima di inoltrare la richiesta. Una richiesta a un host che non è stato dichiarato lancia un errore all’interno del plugin senza mai lasciare la sandbox.
network:request:unrestricted salta completamente il controllo della lista consentita. È destinato ai plugin in cui l’operatore configura l’URL di destinazione a runtime (mittenti webhook, forwarder HTTP generici). Evitalo per i plugin in cui la destinazione fa parte del design del plugin — dichiara invece network:request con host espliciti, in modo che il dialogo di consenso dica agli operatori esattamente dove il plugin chiamerà.
Cosa impone la sandbox
Quando un esecutore sandbox è attivo, il runtime impone:
-
Controllo delle capabilities. La factory PluginContext popola solo
ctx.content,ctx.media,ctx.http,ctx.users,ctx.emailquando viene dichiarata la capability corrispondente. Chiamare un metodo su una capability non dichiarata non è possibile — non c’è alcun oggetto lì. -
Ambito di storage e KV. Ogni operazione di storage e KV è limitata allo slug del plugin. Un plugin non può leggere il KV o le collezioni di storage di un altro plugin, e può accedere solo alle collezioni di storage che ha dichiarato nel manifest.
-
Isolamento di rete. Il
fetch()diretto e altre primitive di rete sono bloccati dall’esecutore. L’unico modo per raggiungere la rete èctx.http.fetch(), che passa attraverso la validazione dell’host del bridge. -
Nessun binding host. I plugin sandboxed non vedono variabili d’ambiente, il filesystem o binding di piattaforma — anche se il tuo worker host li ha. Il runtime del plugin è un isolato pulito con solo il bridge e le capabilities dichiarate.
-
Limiti di risorse. L’esecutore può imporre limiti di CPU, subrequest, wall-clock e memoria per invocazione. I limiti esatti dipendono dall’esecutore che stai usando; l’esecutore Cloudflare usa i limiti del Worker Loader della piattaforma (50ms CPU per invocazione, 10 subrequest, 30 secondi wall-clock, ~128MB memoria). Gli hook che superano i limiti dell’esecutore vengono interrotti; il timeout dell’hook EmDash (
timeoutnella configurazione dell’hook) impone un tetto più rigoroso in aggiunta a quello.
Cosa la sandbox non impone
Alcune cose che il sistema di capabilities non copre e non può coprire:
- Comportamento all’interno di una capability concessa. Un plugin con
content:writepuò modificare qualsiasi contenuto, non solo il proprio. Le capabilities sono grossolane — dicono “questo plugin può scrivere contenuto”, non “questo plugin può scrivere solo il contenuto che ha creato”. La revisione in fase di audit è l’unico controllo su ciò che un plugin fa effettivamente all’interno della sua concessione. - Fiducia dell’operatore su Node.js. Quando l’esecutore sandbox configurato segnala non disponibile (nessun Cloudflare Worker Loader, nessun esecutore lato Node installato, ecc.), i plugin
sandboxed: []vengono saltati all’avvio. Puoi spostarli inplugins: []per eseguirli in-process — ma allora non c’è isolato V8, nessun limite di risorse, e il plugin può chiamarefetch()direttamente o leggere variabili d’ambiente. Trattalo come fiducia a livello nativo. - Canali laterali. Il timing, l’output del log e i dati memorizzati sono tutti visibili a chiunque abbia accesso appropriato all’ambiente host. Non usare la sandbox come confine di riservatezza contro l’operatore che la esegue.
Consenso delle capabilities
Quando un operatore installa un plugin sandboxed dal marketplace, EmDash mostra un dialogo di consenso che elenca le capabilities dichiarate. Gli aggiornamenti che aggiungono capabilities — per esempio, un plugin che in precedenza leggeva solo contenuto ora vuole fare richieste di rete — appaiono come una differenza di capabilities e richiedono una nuova approvazione prima che la nuova versione abbia effetto.
Ecco perché dichiarare capabilities extra conta anche se “potresti usarle più tardi”. Appaiono come attrito a ogni installazione e aggiornamento, e gli audit di sicurezza segnalano i plugin che chiedono più di quanto hanno ovviamente bisogno. Elenca esattamente ciò che il plugin usa, e aggiungi nuove capabilities in una vera versione quando il plugin inizia effettivamente a usarle.
Validazione in fase di bundle
emdash-plugin bundle e emdash-plugin publish eseguono controlli aggiuntivi:
- Ogni capability dichiarata deve essere nel set riconosciuto (gli errori di battitura fanno fallire la build).
network:requestrichiede unallowedHostsnon vuoto;network:request:unrestrictedrichiede che sia vuoto. Vedi il riferimento del manifest.- Il
backend.jsin bundle non può importare built-in Node.js (fs,path,child_process, ecc.) — i runtime sandbox non li forniscono.
Vedi Bundling and publishing per l’elenco completo dei controlli.