
convex-security-check
Ten minute security pass over a Convex backend: public functions that should be internal, missing auth checks, unvalidated args, IDs from the client trusted without ownership checks, secrets in code. Use before merging a pull request, after adding new public functions, or when the user says 'quick security check'.
- Installs
- 2.7K
- Last 24h
- —
- Trending rank
- —
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/waynesutton/builder-skills --skill convex-security-check- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads convex-security-check automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Convex security check
A ten minute pass over convex/ that catches the mistakes that ship most often. The one rule: every exported query, mutation, and action is a public endpoint anyone can call with any arguments, so each one must check identity and ownership or be intentionally anonymous.
Run each grep, read what it surfaces, fix or file. This skill finds problems; it does not map the whole system. For that, hand off to convex-security-audit.
When to reach for this
- Before merging a pull request that touches
convex/ - After adding or renaming public functions
- The user says "quick security check", "sanity check the backend", or "anything obviously wrong here"
- Before a dev to prod push, when there is no time for a full audit
Checklist
Run the commands from the project root. Every grep excludes _generated.
1. Public vs internal
Anything only called by other Convex functions, the scheduler, crons, or webhooks should be internalQuery, internalMutation, or internalAction.
rg -n "export const \w+ = (query|mutation|action)\(" convex --glob '!**/_generated/**'
rg -n "\bapi\.\w+\.\w+" convex --glob '!**/_generated/**'
The second grep finds api.* used inside the backend. Server code scheduling or running a public function is almost always a sign that function should be internal.
- Every public function has a client that needs to call it
- No
api.*insidectx.scheduler.*,ctx.run*, orcrons.* - Admin only operations (role changes, credit grants, deletions across users) are internal
2. Auth
rg -l "= (query|mutation|action)\(" convex --glob '!**/_generated/**' \
| xargs rg -L "getUserIdentity|getCurrentUser|authedQuery|authedMutation"
Files listed contain public functions and no auth call at all. Open each one. A public post list is fine anonymous. Anything that returns or writes user data is not.
- Every public function checks identity or is documented as intentionally anonymous
- Auth helpers throw or return early when identity is missing
- Roles come from the
userstable, never from client arguments
3. Validation
rg -nU "(query|mutation|action)\(\{\s*handler" convex --glob '!**/_generated/**'
rg -n "v\.any\(\)" convex --glob '!**/_generated/**'
The first grep finds functions whose definition starts with handler, meaning no args. The second finds v.any(), which turns off validation for that field.
- Every function has
argsandreturnsvalidators - No
v.any()on arguments that reach the database or an external API - Return validators list fields explicitly so
passwordHash,stripeCustomerId, and internal flags cannot leak
4. Ownership and IDs
v.id("tasks") proves the string is a valid ID for that table. It does not prove the caller owns the document.
rg -n "ctx\.db\.(get|patch|delete|replace)\(args\." convex --glob '!**/_generated/**'
rg -n "userId: v\.(id|string)\(" convex --glob '!**/_generated/**'
For each hit in the first grep, find the ownership comparison between the read and the write. For the second, a public function that accepts the caller's own userId as an argument is trusting the client to say who it is.
- Every
get,patch,delete,replaceby a client supplied ID is followed by an ownership or membership check - Caller identity is derived from
ctx.auth, never accepted as an argument - Lists use
withIndexon the owner field, not a full scan plus filter
5. Secrets
rg -n -i "(sk_live|sk_test|whsec_|AKIA[0-9A-Z]{16}|-----BEGIN|api[_-]?key\s*[:=]\s*['\"][A-Za-z0-9])" convex src --glob '!**/_generated/**'
rg -n "process\.env\." src
Anything in src/ that reads process.env or import.meta.env ships to the browser. Only deployment URLs and public client IDs belong there.
- No secret literals in
convex/,src/, tests, or fixtures - Secrets read from
process.envinside the action or HTTP action that uses them - Dev and prod deployments use different keys
6. HTTP and storage
rg -n "http\.route|httpAction\(" convex/http.ts
rg -n "storage\.(generateUploadUrl|getUrl)" convex --glob '!**/_generated/**'
- Every
http.tsroute verifies its caller (webhook signature, bearer token, orgetUserIdentity) before parsing the body - Routes call
internal.*, notapi.* -
generateUploadUrlrequires auth -
getUrlis called on a storage ID read from a document the caller owns, never on a storage ID passed by the client
One fix, before and after
The most common finding: auth is checked, ownership is not.
Before:
export const updateTask = mutation({
args: { taskId: v.id("tasks"), title: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity();
if (!identity) throw new ConvexError("Sign in required");
// any signed in user can rename any task
await ctx.db.patch(args.taskId, { title: args.title });
return null;
},
});
After:
import { mutation } from "./_generated/server";
import { v, ConvexError } from "convex/values";
import { getCurrentUser } from "./lib/auth";
export const updateTask = mutation({
args: { taskId: v.id("tasks"), title: v.string() },
returns: v.null(),
handler: async (ctx, args) => {
const user = await getCurrentUser(ctx); // throws when signed out
const task = await ctx.db.get(args.taskId);
// same error for missing and not owned, so IDs cannot be probed
if (!task || task.userId !== user._id) {
throw new ConvexError({ code: "NOT_FOUND", message: "Task not found" });
}
await ctx.db.patch(args.taskId, { title: args.title });
return null;
},
});
getCurrentUser looks the user up through a by_tokenIdentifier index using identity.tokenIdentifier and throws when there is no identity. Define it once in convex/lib/auth.ts and use it everywhere.
Common mistakes
| Mistake | Why it breaks | Do this instead |
|---|---|---|
| Checking auth in the React component only | Anyone can call the function from the dashboard or a script | Check in the handler |
v.id("users") argument for "the current user" | Client can pass any user's ID | Derive from ctx.auth.getUserIdentity() |
Comparing ownership to identity.email | Emails can be reused or unverified | Compare to user._id |
internalMutation treated as safe on its own | The public caller may pass unverified IDs | Check the call site too |
| Fixing one hit and moving on | The same pattern usually appears in siblings | Fix all hits from the grep |
Hand off to convex-security-audit
Stop and run the full audit when any of these are true:
- More than two or three findings in steps 2 or 4; the pattern is systemic
- The app has multi tenant data (orgs, teams, workspaces)
http.tshas webhook routes or routes that return user data- Files are uploaded and served
- The user asks for a report, a launch review, or a post incident review
The audit maps auth per function, data access per table, HTTP exposure, storage, scheduler trust, rate limiting, and produces a written findings report. This check does not.
Docs
FAQ
What does the convex-security-check skill do?
Ten minute security pass over a Convex backend: public functions that should be internal, missing auth checks, unvalidated args, IDs from the client trusted without ownership checks, secrets in code. Use before merging a pull request, after adding new public functions, or when the user says 'quick security check'.
How do I install convex-security-check?
Run npx skills add https://github.com/waynesutton/builder-skills --skill convex-security-check in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/convex-security-check/ (personal) or .claude/skills/convex-security-check/ (project).
Which AI agents can use convex-security-check?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is convex-security-check safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains convex-security-check?
It is published by waynesutton in waynesutton/builder-skills. It has 2,718 recorded installs.
More from waynesutton/builder-skills
convex
waynesutton/builder-skills
Routes Convex work to the right builder skill and covers the handful of rules that apply everywhere: validators on every function, withIndex over filter, idempotent mutations, internal functions for scheduling. Use when a task mentions Convex and no more specific skill matches, or as the first stop before picking one.
convex-best-practices
waynesutton/builder-skills
Production patterns for Convex apps and the rules the @convex-dev/eslint-plugin enforces: validators, indexes, idempotent mutations, avoiding OCC conflicts, thin function wrappers, error handling. Use when reviewing Convex code, asking whether a pattern is right, setting up ESLint, or fixing write conflicts and slow queries.
convex-functions
waynesutton/builder-skills
Writes Convex queries, mutations, actions, and internal functions in the object form with args and returns validators, correct ctx usage, runtime boundaries, and error handling. Use when adding or changing anything in convex/.ts that exports a function, or when deciding between query, mutation, and action.
convex-schema-validator
waynesutton/builder-skills
Designs convex/schema.ts tables, validators, indexes, and relationships, and keeps the schema honest as data evolves. Use when creating tables, adding fields, choosing index fields, modeling relationships, or when a validator error appears at deploy time.
convex-realtime
waynesutton/builder-skills
Builds reactive UIs on Convex subscriptions: useQuery patterns, optimistic updates, pagination that stays live, presence, and avoiding subscription churn. Use when wiring the frontend to Convex, when data does not update live, or when a page rerenders too much.
convex-cron-jobs
waynesutton/builder-skills
Schedules work in Convex: cron jobs in convex/crons.ts, one off scheduled functions with runAfter and runAt, batching large jobs, and cancelling or inspecting the queue. Use when something needs to run on a timer, later, or in the background, or when a cron is not firing.
Popular security skills
azure-compliance
microsoft/azure-skills
Run Azure compliance and security audits with azqr plus Key Vault expiration checks. Covers best-practice assessment, resource review, policy/compliance validation, and security posture checks. WHEN: compliance scan, security audit, BEFORE running azqr (compliance cli tool), Azure best practices, Key Vault expiration check, expired certificates, expiring secrets, orphaned resources, compliance assessment.
minimal-run-and-audit
lllllllama/rigorpilot-skills
Rigor Run skill for README-first deep learning repo reproduction. Use when the task is specifically to capture or normalize evidence from the selected smoke test or documented inference or evaluation command and write standardized reprooutputs/ files, including patch notes when repository files changed. Do not use for training execution, initial repo intake, generic environment setup, paper lookup, target selection, hidden scientific-meaning changes, or end-to-end orchestration by itself.
seo-audit
coreyhaines31/marketingskills
When the user wants to audit, review, or diagnose SEO issues on their site. Also use when the user mentions "SEO audit," "technical SEO," "why am I not ranking," "SEO issues," "on-page SEO," "meta tags review," "SEO health check," "my traffic dropped," "lost rankings," "not showing up in Google," "site isn't ranking," "Google update hit me," "page speed," "core web vitals," "crawl errors," "indexing issues," "stuck on page 2," "striking distance keywords," "title tag rewrite," "low CTR," "local SEO," "Google Business Profile," or "map pack." Use this even if the user just says something vague like "my SEO is bad" or "help with SEO" — start with an audit. For building pages at scale to target keywords, see programmatic-seo. For adding structured data, see schema. For AI search optimization, see ai-seo.
firebase-security-rules-auditor
firebase/agent-skills
Audits Firebase (Firestore, Cloud Storage) security rules for vulnerabilities, privilege escalation, role bypasses, create vs update inconsistencies, resource exhaustion, type safety, size limits, and hasOnly ownership checks. Use when auditing/reviewing rules, running red-team rule assessments, or scoring against auditor checklists. Don't use for Firebase CLI (login, deploy), Auth, Crashlytics, Remote Config, or database queries.
audit
pbakaus/impeccable
Run technical quality checks across accessibility, performance, theming, responsive design, and anti-patterns. Generates a scored report with P0-P3 severity ratings and actionable plan. Use when the user wants an accessibility check, performance audit, or technical quality review.
competitors
coreyhaines31/marketingskills
When the user wants to create competitor comparison or alternative pages for SEO and buyer-facing use. Also use when the user mentions 'alternative page,' 'vs page,' 'competitor comparison,' 'comparison page,' '[Product] vs [Product],' '[Product] alternative,' 'competitive landing pages,' 'how do we compare to X,' 'competitor teardown,' 'audit our competitor pages,' 'are our comparison pages out of date,' or 'competitive asset audit.' Use this for any content that positions your product against competitors. Covers four formats: singular alternative, plural alternatives, you vs competitor, and competitor vs competitor. For auditing existing claims (not researching competitors from scratch, which is competitor-profiling; not technical SEO on these pages, which is seo-audit), use the asset audit here. For internal battle cards and sales-specific competitor docs, see sales-enablement.