Security fixes are applied to the latest release on the main branch.
If you discover a security issue, please do not open a public GitHub issue.
Instead, use GitHub Security Advisories to report it privately, or email the repository owner via their GitHub profile.
We will acknowledge receipt within a reasonable timeframe and work on a fix before public disclosure when appropriate.
This worker exposes a public HTTP endpoint by default. Browser access control is the operator’s responsibility via Cloudflare Zero Trust / Access. The worker does not implement a login page.
- Cloudflare Access for the dashboard — Put the Worker behind Cloudflare Access with an Allow policy for your identity (one-click Workers Access or a self-hosted app). Without Access (or equivalent edge auth),
GET /,/admin, and/api/*(includingPOST /api/simulate,/api/alert-config,/api/test-alert) are reachable by anyone who knows the URL. You may optionally apply a stricter Access policy to/admin. - Second Access app to Bypass
/devicesfor Home Assistant — Worker-destination Access apps have no path field; do not add Bypass there. Create a separate self-hosted Access application with apublicdestination ofyour-hostname/devicesand a Bypass (Everyone) policy. Cloudflare givespublicdestinations precedence over Worker destinations for that path. See SETUP.md for the API recipe. API_KEYfor Home Assistant only — Required forGET /devicesand deprecatedGET /?json=true. Fail closed when unset. Clients must sendAuthorization: Bearerorx-api-key(query-string keys are not accepted). After the/devicesbypass,API_KEYis the sole guard on that endpoint — use a strong secret and rotate if exposed. BrowserGET /api/dashboarddoes not useAPI_KEY(Access only). Do not Bypass/.- Inbound email identity — Envelope MAIL FROM must be exactly
notification@myq.comfor direct myQ → Cloudflare Email Routing. For Gmail (or similar) forwards, setALLOWED_FORWARD_FROMto the forwarder’s envelope address; the Worker then also requires headerFromto benotification@myq.com. Unknown senders are dropped without SMTP reject (avoids bounce noise). Prefer settingALLOWED_EMAIL_TOto your secret Email Routing alias so only that RCPT TO is accepted. WhenAuthentication-Resultsis present, DKIM/DMARCfailresults are rejected. Message-ID deduplication is enforced with a unique D1 index ondoor_events.message_id_hash. - Protect your webhook URL — Alert settings are stored in D1. Use a secret ntfy topic name or private Apprise endpoint; anyone who knows that URL can send notifications to it directly. The dashboard redacts stored webhook paths in API responses. Alert latching is best-effort across overlapping cron invocations.
- Dashboard mutation throttles —
POST /api/alert-configandPOST /api/test-alertuse a soft D1 rate limit (fail-open). Treat it as UX only. For abuse prevention, add Cloudflare Rate Limiting / WAF rules on those routes. - Content Security Policy — Static assets and JSON responses use a CSP without
'unsafe-inline'(script-src 'self'; style-src 'self'). - Rotate secrets — If
API_KEY,ALLOWED_EMAIL_TO,ALLOWED_FORWARD_FROM, or your Cloudflare API token is exposed, rotate them immediately in the Cloudflare dashboard and GitHub repository secrets. - Limit API token scope — Use a Cloudflare API token scoped only to the Workers, D1, and (if managing Access via API) Access Apps and Policies resources this project needs.
The worker stores garage door state, event history, and alert webhook settings in Cloudflare D1. It does not store myQ account credentials — state is derived from forwarded notification emails only.