Capabilities e sicurezza

In questa pagina

I plugin in sandbox sono isolati per impostazione predefinita. Per fare qualcosa oltre a leggere e scrivere il proprio KV e storage, un plugin deve dichiarare una capability sul suo descrittore. Il ponte del sandbox controlla ogni API fornita dall’host in base a queste 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 concede ogni capability, come il sandbox le applica e cosa non è applicabile.

Dichiarare le capabilities

Le capabilities risiedono sul descrittore (il file importato da astro.config.mjs), insieme a id, version ed entrypoint:

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"],
	};
}

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 nella finestra di consenso — capabilities extra sono attrito al momento dell’installazione e un segnale di sicurezza negli audit.

Riferimento delle capabilities

CapabilityConcede l’accesso a
content:readctx.content.get(), ctx.content.list()
content:writectx.content.create(), ctx.content.update(), ctx.content.delete() (implica content:read)
media:readctx.media.get(), ctx.media.list()
media:writectx.media.getUploadUrl(), ctx.media.upload(), ctx.media.delete() (implica media:read)
network:requestctx.http.fetch() — limitato a allowedHosts
network:request:unrestrictedctx.http.fetch() senza restrizioni di host (solo per URL configurati dall’utente)
users:readctx.users.get(), ctx.users.getByEmail(), ctx.users.list()
email:sendctx.email.send() (richiede un plugin provider email configurato)
hooks.email-transport:registerConsente di registrare l’hook esclusivo email:deliver (provider di trasporto)
hooks.email-events:registerConsente di registrare gli hook email:beforeSend / email:afterSend
hooks.page-fragments:registerConsente di registrare l’hook page:fragments (solo plugin nativi)

Alcune cose da sapere:

  • Implicazioni. content:write implica automaticamente content:read; media:write implica media:read; network:request:unrestricted implica network:request. Non è necessario elencare entrambi.
  • network:request:unrestricted esiste per URL configurati dall’utente. Un plugin webhook in cui l’operatore digita l’URL di destinazione deve raggiungere host che non sono nel manifesto. I plugin che chiamano sempre API note dovrebbero usare network:request + allowedHosts.
  • email:send è controllato dalla configurazione, non solo dalla capability. Un plugin può dichiarare email:send, ma ctx.email sarà popolato solo se qualche altro plugin ha registrato un trasporto email:deliver.

Liste di host di rete consentiti

I plugin con network:request possono recuperare solo gli host elencati in allowedHosts. I caratteri jolly sono supportati per i sottodomini:

capabilities: ["network:request"],
allowedHosts: [
	"api.example.com",          // host esatto
	"*.cdn.example.com",        // qualsiasi sottodominio di cdn.example.com
],

Il ponte controlla l’host dell’URL della richiesta rispetto alla lista dei consentiti prima di inoltrare la richiesta. Una richiesta a un host che non è stato dichiarato genera un errore all’interno del plugin senza mai lasciare il sandbox.

network:request:unrestricted salta completamente il controllo della lista dei consentiti. È 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 la finestra di consenso dica agli operatori esattamente dove il plugin chiamerà.

Cosa applica il sandbox

Quando un esecutore di sandbox è attivo, il runtime applica:

  1. Controllo delle capabilities. La factory PluginContext popola solo ctx.content, ctx.media, ctx.http, ctx.users, ctx.email quando viene dichiarata la capability corrispondente. Chiamare un metodo su una capability non dichiarata non è possibile — non c’è nessun oggetto lì.

  2. Ambito di storage e KV. Ogni operazione di storage e KV è limitata all’id del plugin. Un plugin non può leggere il KV di un altro plugin o le sue collezioni di storage, e può accedere solo alle collezioni di storage che ha dichiarato sul descrittore.

  3. Isolamento di rete. Il fetch() diretto e altre primitive di rete sono bloccate dall’esecutore. L’unico modo per raggiungere la rete è ctx.http.fetch(), che passa attraverso la validazione dell’host del ponte.

  4. Nessun binding host. I plugin in sandbox non vedono variabili d’ambiente, il filesystem o alcun binding di piattaforma — anche se il tuo worker host li ha. Il runtime del plugin è un isolato pulito con solo il ponte e le capabilities dichiarate.

  5. Limiti di risorse. L’esecutore può applicare limiti di CPU, sottorichieste, tempo di esecuzione e memoria per invocazione. I limiti esatti dipendono da quale esecutore stai usando; l’esecutore Cloudflare usa i limiti del Worker Loader della piattaforma (50ms di CPU per invocazione, 10 sottorichieste, 30 secondi di tempo di esecuzione, ~128MB di memoria). Gli hook che superano i limiti dell’esecutore vengono interrotti; il timeout dell’hook EmDash (timeout nella configurazione dell’hook) applica un limite più rigoroso in aggiunta a quello.

Cosa il sandbox non applica

Alcune cose che il sistema di capabilities non può coprire:

  • Comportamento all’interno di una capability concessa. Un plugin con content:write può 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 al momento dell’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 di sandbox configurato segnala non disponibile (nessun Cloudflare Worker Loader, nessun esecutore lato Node installato, ecc.), i plugin sandboxed: [] vengono saltati all’avvio. Puoi spostarli in plugins: [] per eseguirli in-process — ma allora non c’è isolato V8, nessun limite di risorse, e il plugin può chiamare fetch() direttamente o leggere variabili d’ambiente. Trattalo come fiducia a livello nativo.
  • Canali laterali. I tempi, l’output dei log e i dati memorizzati sono tutti visibili a chiunque abbia accesso appropriato all’ambiente host. Non usare il sandbox come un confine di riservatezza contro l’operatore che lo esegue.

Consenso delle capabilities

Quando un operatore installa un plugin in sandbox dal marketplace, EmDash mostra una finestra di consenso che elenca le capabilities dichiarate. Gli aggiornamenti che aggiungono capabilities — ad esempio, un plugin che in precedenza leggeva solo contenuto ora vuole fare richieste di rete — emergono come un diff di capabilities e richiedono una nuova approvazione prima che la nuova versione abbia effetto.

Ecco perché dichiarare capabilities extra è importante 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 evidentemente necessitano. Elenca esattamente ciò che il plugin usa, e aggiungi nuove capabilities in una versione reale quando il plugin inizia effettivamente a usarle.

Validazione al momento del bundle

emdash plugin bundle e emdash plugin publish eseguono controlli aggiuntivi:

  • Ogni capability dichiarata deve essere nel set noto (gli errori di battitura fanno fallire la build).
  • Un plugin che dichiara network:request senza popolare allowedHosts attiva un avviso — dichiara gli host o passa a network:request:unrestricted e documenta perché.
  • I nomi di capabilities deprecati attivano avvisi durante bundle/validate e un fallimento hard su publish.
  • Il backend.js impacchettato non può importare built-in di Node.js (fs, path, child_process, ecc.) — i runtime di sandbox non li forniscono.

Vedi Bundling e publishing per l’elenco completo dei controlli.