- **Every `/__void/inbox` route requires the dev-trigger token; the worker's capture POST carries it via a var.** Vite's host validation (`server.allowedHosts`, runs before plugin middlewares) only rejects DNS-rebinding hostnames — any IP-literal `Host` passes — and its default CORS reflects every `localhost` origin, so an unauthenticated inbox middleware hands every peer under `--host` (and every other local dev server) the raw MIME with Bcc and attachments, a DELETE, and a capture endpoint to plant messages. `plugin-email-dev-inbox.ts` therefore checks the same per-project token the cron/queue dev routes and the pages dev-CSS loopback use (`getOrCreateDevTriggerToken`, `.void/dev-trigger-token`), accepted as the `x-void-dev-trigger` header (worker, curl) or `?token=` (browser — the plugin prints the tokenized URL on `listening` and renders its own links with it; responses already send `Referrer-Policy: no-referrer`). Missing or wrong → `401` with a one-line body naming both forms and no ack header. The compiled router embeds the token as a module-local const that `runtime/email.ts` cannot import, so `mergeBindings` injects `__VOID_EMAIL_DEV_INBOX_TOKEN` next to `__VOID_EMAIL_DEV_INBOX` under the one `shouldInjectEmailDevInboxVar` gate (dev only — never in a build's `wrangler.json`); `sendEmail` sends it and, on a non-2xx, echoes the first line of the dev server's body so a missing token is diagnosable. Nothing on the worker side ever skips the inbox for a real transport because the token is absent. Because that file is now the inbox's whole credential and loopback needs no `--host`, `getOrCreateDevTriggerToken` creates it `0600` (`openSync(.., 'wx', 0o600)`) and best-effort `chmod`s a pre-existing file whose group/other bits are set, never rotating it (skipped on Windows, which has no such bits) — a default-umask `0644` file would hand every other account on a shared host the raw MIME, the DELETE, and the capture endpoint. `.void/dev-triggers.ts` (`plugin-framework-triggers.ts`) embeds the same token for Class A frameworks and is written `0600` the same way. `/__void/ping` stays open: it exposes no data. `/__void/restart` answers `403` unless the POST is same-origin (`Sec-Fetch-Site`, else `Origin`) and carries this server instance's random token in `x-void-restart` — browsers send no Fetch Metadata to plain-HTTP LAN origins (`--host`), so the token, embedded only in the `virtual:void/binding-hint` toast (which navigates with `&t=`; Vite's default CORS exposes it to localhost origins only, like the HMR token), is what stops a foreign page from opening `/__void/restarting` and riding its auto-POST. `/__void/restarting` refuses a navigation whose `Sec-Fetch-Site` is not `same-origin` or `none`, sends `Referrer-Policy: no-referrer`, and only navigates to a same-origin path from `?return=`, falling back to `/`.
0 commit comments