@awesomate/hosting-mcp
Version:
Awesomate MCP server — lets Claude manage your Awesomate WordPress hosting, plan, limits, n8n automations, and build Node/static apps + databases
58 lines (48 loc) • 3.01 kB
Markdown
# Security checklist — walk this before promoting to prod
Two layers guard prod. The **automated gate** runs in every app's deploy
workflow (`scripts/security-check.mjs` — report-only on dev/staging,
BLOCKING on `main`, plus `npm audit --audit-level=critical`). This checklist
is the **human layer**: the things a scanner cannot see. Walk it with the
user in plain language before merging staging → main.
## What the automated gate already catches (don't re-litigate)
Committed secrets / a tracked `.env` / private keys · SQL built by
interpolation or concatenation · `eval` / `new Function` · shell exec with
interpolated input · critical dependency advisories. If the prod deploy
fails on the gate, read the Actions log — every finding prints its fix.
**Never remove or bypass the gate step to make a deploy pass.**
## The human checks (scanner-blind spots)
1. **Authorization, not just authentication.** Logged-in is not allowed.
For every route that reads/writes data: could user A pass user B's id and
touch B's data? Filter by the session's user id server-side, never trust
an id from the request body.
2. **Every input validated at the edge.** Each POST/PATCH handler starts
with a zod `safeParse`, with sane max lengths. No handler touches
`await c.req.json()` raw.
3. **Secrets flow one way.** Values live in env vars (set via
`awesomate_app_set_env`), are never logged, never echoed in responses,
never sent to the browser.
4. **Rendering is escape-safe.** Browser code uses `textContent` /
`createElement` for user data; server HTML never string-interpolates
user input without escaping.
5. **The n8n webhook is not world-callable.** If n8n is attached, the
workflow checks the `X-Awesomate-Webhook-Secret` header (set during
attach). Test: call the webhook without the header — it must refuse.
6. **Public API surface is intentional.** api-only starter: is `API_KEY`
set for prod, or is a public API genuinely intended? CORS open on
purpose?
7. **Sensitive data is minimized.** Only collect what the app needs; if it
stores personal data, tell the user plainly: connections are TLS and
secrets are encrypted, but the DATABASE is not blanket-encrypted at
rest — sensitive fields deserve app-level encryption (ask before adding).
8. **Errors don't leak.** 500 responses return a generic message, not stack
traces or SQL (the starters already do this — keep it).
## Static sites
No server code, so the gate doesn't run — the risks are the lead form's
webhook (point 5) and third-party scripts (don't add any that aren't
needed). Never put an API key in page JavaScript: anything in a static page
is public.
## How to talk about it
Non-technical user — say what you checked, not the jargon: *"Before making
it live I checked that people can only see their own data, that the contact
form can't be abused, and that your API key never appears anywhere public.
The deploy pipeline also scans every release automatically."*