avoid-feature-creep
Keeps a change scoped to what was asked. Flags extra features, refactors, and 'while we are here' edits, and moves them to a…
Deep security review of a Convex app: authorization model, data access paths per table, HTTP action exposure, rate limiting, file storage access, scheduled function trust, and a written findings report. Use before launch, after an incident, or when the user asks for a full audit
$ npx -y skills add waynesutton/builder-skills --skill convex-security-audit --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/convex-security-auditContext preview
The summary Claude sees to decide when to auto-load this skill.
Deep security review of a Convex app: authorization model, data access paths per table, HTTP action exposure, rate limiting, file storage access, scheduled function trust, and a written findings report. Use before launch, after an incident, or when the user asks for a full audit
name: convex-security-audit description: Deep security review of a Convex app: authorization model, data access paths per table, HTTP action exposure, rate limiting, file storage access, scheduled function trust, and a written findings report. Use before launch, after an incident, or when the user asks for a full audit rather than a quick check.
Produces a written findings report covering every public function, every table, and every entry point (HTTP routes, file storage, scheduler). The one rule: every exported `query`, `mutation`, and `action` is a public endpoint anyone on the internet can call with any arguments, so each one must prove who the caller is and what they may touch.
For a ten minute pass, use convex-security-check instead. This skill is the slow, complete version.
Work through the steps in order. Record every finding as you go. Do not fix anything until the inventory is complete; fixing early hides the shape of the problem.
List every exported `query`, `mutation`, and `action`. These are the attack surface.
rg -n "export const \w+ = (query|mutation|action)\(" convex --glob '!**/_generated/**'For each one write down: file, name, type, whether a client needs to call it, and whether it is costly (sends email, calls an LLM, creates records) and so needs a rate limit. Anything only called by other Convex functions, crons, or the scheduler should be `internalQuery`, `internalMutation`, or `internalAction`. Flag each of those as a finding.
For every public function from step 1, answer: does it call `ctx.auth.getUserIdentity()` (or a helper that does) before reading or writing? Which functions are intentionally anonymous?
rg -l "= (query|mutation|action)\(" convex --glob '!**/_generated/**' \
| xargs rg -L "getUserIdentity|getCurrentUser|authedQuery|authedMutation"Files that print here contain public functions with no auth call anywhere in the file. Read each one. Anonymous is fine for a public blog list. It is a critical finding for anything that returns or writes user data.
Check that auth helpers throw or return early on a missing identity, and that role checks read the role from your `users` table, not from client arguments.
For every table in `convex/schema.ts`, list which public functions read it, which write it, and what condition scopes the access (owner ID, org membership, role, public flag). Fill in the data access matrix from the report template.
Look for:
rg -n "v\.any\(\)" convex --glob '!**/_generated/**' rg -n "ctx\.db\.(get|patch|delete|replace)\(args\." convex --glob '!**/_generated/**'
Every route in `convex/http.ts` is reachable without a Convex client. For each route check: how is the caller verified (webhook signature, bearer token, `ctx.auth.getUserIdentity()`), is the body validated before use, and does it call only `internal.*` functions with data it has already verified?
rg -n "http\.route|httpAction\(" convex/http.tsWebhook handlers that skip signature verification are critical. Routes that call `api.*` functions from inside an `httpAction` may be bypassing auth those functions assume they have.
Find where upload URLs are generated and where file URLs are returned.
rg -n "storage\.(generateUploadUrl|getUrl|delete)" convex --glob '!**/_generated/**'
`generateUploadUrl` in a public mutation with no auth lets anyone fill your storage. A query that returns `ctx.storage.getUrl(args.fileId)` for any ID the client passes is a data leak; the URL works for anyone who holds it.
Internal functions skip auth by design. The question is whether anything schedules them with unverified input.
rg -n "(runAfter|runAt|runMutation|runAction|interval|cron)\([^)]*\bapi\." convex --glob '!**/_generated/**'
Scheduling an `api.*` function is a finding: use `internal.*`. Then trace each `internal.*` call site back to the public function or route that triggered it and confirm the caller validated ownership before scheduling.
rg -n -i "(sk_live|sk_test|whsec_|AKIA[0-9A-Z]{16}|-----BEGIN|api[_-]?key\s*[:=]\Agent skills for builders shipping Convex apps. By Wayne Sutton. Seventeen skills.
Keeps a change scoped to what was asked. Flags extra features, refactors, and 'while we are here' edits, and moves them to a…
Builds AI agents on the Convex agent component: threads, messages, tools that call queries and mutations, streaming, RAG with…
Production patterns for Convex apps and the rules the @convex-dev/eslint-plugin enforces: validators, indexes, idempotent…
Creates reusable Convex components with defineComponent, a clean client wrapper, their own schema, and an npm publish setup. Use…
Schedules work in Convex: cron jobs in convex/crons.ts, one off scheduled functions with runAfter and runAt, batching large jobs,…
Handles files in Convex: upload URLs, storing blobs from actions, serving with getUrl or an HTTP action, metadata from the…