api-hardening
Skill3 starsChanged 10 days agoMITView on GitHub
Use this skill when building or reviewing a server-side endpoint, route handler, server action, webhook, cron job, or background worker in any language. Covers per-route authorization and IDOR, input validation at the trust boundary, rate limiting, SSRF, file uploads, JWT and session handling, tenant isolation, and error or log leakage.
- Sends data out
In this repository72 other files it gives its agents.
- workflow/AGENTS.mdAGENTS.md
- docs/llms.txtllms.txt
- llms.txtllms.txt
- ai-video-prompt-engineerSkill
- api-contract-designSkill
- brainstormingSkill
- cavecrewSkill
- caveman-commitSkill
- caveman-compressSkill
- caveman-helpSkill
- caveman-reviewSkill
- cavemanSkill
- councilSkill
- database-migrationsSkill
- deep-researchSkill
- dependency-auditSkill
- deployment-patternsSkill
- e2e-testingSkill
- error-handlingSkill
- exa-searchSkill
- finishing-a-development-branchSkill
- frontend-securitySkill
- github-opsSkill
- graphifySkill
- graphyloop-wavesSkill
- graphyloop-workflowSkill
- gsap-coreSkill
- gsap-frameworksSkill
- gsap-performanceSkill
- gsap-pluginsSkill
- gsap-reactSkill
- gsap-scrolltriggerSkill
- gsap-timelineSkill
- gsap-utilsSkill
- high-end-visual-designSkill
- image-to-codeSkill
- last30daysSkill
- minimalist-uiSkill
- postgres-patternsSkill
- prisma-patternsSkill
- receiving-code-reviewSkill
- redesign-existing-projectsSkill
- remotion-video-creationSkill
- requesting-code-reviewSkill
- search-firstSkill
- secrets-hygieneSkill
- security-reviewSkill
- security-scanSkill
- short-video-productionSkill
- story-engineeringSkill
- supabase-setupSkill
- swarm-memorySkill
- systematic-debuggingSkill
- tdd-workflowSkill
- terminal-opsSkill
- threejs-animationSkill
- threejs-fundamentalsSkill
- threejs-geometrySkill
- threejs-interactionSkill
- threejs-lightingSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 3 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Before a deploy that exposes a new surface publicly.
- Very specific: line 65
Never interpolate input into SQL, NoSQL filters, shell commands, file paths, template strings, or regexes.
- Distinctive: line 111
Never log, echo, or return a token, password hash, or full connection string.
- Very specific: line 117
Never return more fields than the caller needs — serialize with an explicit allow-list (select the columns, or map to a response DTO).
- Distinctive: line 134
Run the suite. A security fix without a regression test comes back.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
API hardening
Most breached endpoints were authenticated. The pattern is not "no login" — it is "logged in, and the handler never checked that this row belongs to this caller." Authorization is per route, per object, every time. This skill is the server-side pass that goes route by route.
When to activate
- Any new or changed endpoint: REST route, GraphQL resolver, RPC/tRPC procedure, server action, webhook receiver, queue consumer, cron entry.
- Anything touching auth, sessions, roles, tenants, payments, uploads, or PII.
- A bug report shaped like "user A can see user B's data" or "it 500s with a weird message".
- Before a deploy that exposes a new surface publicly.
Route inventory first
Enumerate what exists before judging it. Grep the router, not your memory:
# Node/Next
rg -n "app\.(get|post|put|patch|delete)|router\.(get|post|put|patch|delete)" src
rg -n "export async function (GET|POST|PUT|PATCH|DELETE)" app
rg -n "'use server'" -l app src
# Python / Go / PHP
rg -n "@app\.(route|get|post)|@router\.(get|post)" .
rg -n "http.HandleFunc|mux.Handle" .
rg -n "Route::(get|post|put|delete)" routesBuild a table: method+path · auth required? · role/scope · object-ownership check · input validated? · rate limited? Every empty cell is a finding.
1. Authentication vs authorization
- Authentication answers who; authorization answers may they, on this object. Middleware usually gives you the first and never the second.
- Default deny. Public routes are an explicit allow-list, not "everything the
middleware matcher forgot". Verify the matcher: a regex that misses
/api/admin/../usersor a trailing slash is a bypass. - IDOR / BOLA — the top one. Any handler taking an id from the request must
scope the query by the caller:
where id = :id and owner_id = :caller(or tenant/org id). Filtering after the fetch, or trusting auserIdsent in the body/JWT payload the client can re-sign, is not a check. - Return 404 for objects the caller may not see, 403 only where existence is not sensitive. A 403 confirms the id exists.
- Role checks server-side only, from the session/token verified this request — never from a header, query param, or hidden form field.
- Multi-tenant: the tenant id comes from the session, never from the payload. In
Postgres/Supabase, RLS is the second wall, not the only one — see
supabase-setup.
2. Input validation at the boundary
- Validate at the edge with a schema (zod/valibot, pydantic, JSON Schema, DTO +
validator) and pass typed data inward. Reject unknown keys — a permissive parser
is how mass assignment reaches
role: "admin". - Allow-list, never deny-list: types, enums, ranges, lengths, formats, array size,
total body size. Cap pagination
limit; unboundedlimitis a free DoS. - Never interpolate input into SQL, NoSQL filters, shell commands, file paths,
template strings, or regexes. Parameterized queries or an ORM's binding API;
path.resolve+ a prefix check for paths; no user-built regex (ReDoS). - Sort/filter/include params: map names to an allow-list of columns/relations, do not pass them through.
- Webhooks are untrusted input with a signature: verify the HMAC over the raw body before parsing (a body parser that rewrites bytes breaks the check), enforce a timestamp window, and make handling idempotent by event id.
3. Rate limiting and abuse
- Limit by identity where you have one, by IP otherwise, and always on: login, signup, password reset, OTP/MFA, token refresh, search, export, upload, and any endpoint that sends email/SMS or costs money per call.
- Store counters where every instance sees them (Redis/Upstash/DB), not in process memory behind a load balancer.
- Progressive cost on auth failure (delay, lockout window, CAPTCHA) plus constant-time credential comparison and identical failure messages, so the endpoint does not become a user-enumeration oracle.
- Cap payload size and request timeout at the edge before the handler runs.
4. SSRF, uploads, and outbound calls
- Server-side fetch of a user-supplied URL: allow-list host or scheme, resolve DNS
and reject private/loopback/link-local ranges (
127/8,10/8,172.16/12,192.168/16,169.254/16,::1,fc00::/7), disable redirect following or re-validate each hop, and set a timeout. Cloud metadata endpoints (169.254.169.254) are the classic target. - Uploads: validate real content type by magic bytes, not the extension or the
client's
Content-Type; cap size; generate a random stored filename; store outside the web root or in object storage; serve withContent-Disposition: attachmentandnosniff; neverchmod +x. Prefer pre-signed direct-to-storage uploads with a short TTL. - Any outbound HTTP: explicit timeout, bounded retries with backoff, and a circuit breaker for the dependency you cannot trust to stay up.
5. Tokens, sessions, cookies
- Sessions: server-side store or signed cookie with rotation on login and privilege change; invalidate on logout and password change.
- JWT: verify signature and
alg(rejectnone, pin the expected algorithm),iss,aud,exp; short TTL; refresh tokens rotated and revocable. A JWT you cannot revoke is a password with an expiry date. - Cookies:
httpOnly,secure,sameSite, narrowpath, no wildcarddomain. State-changing routes need CSRF protection unless they are strictlysameSite=strict|lax+ non-form content type, and you verified that. - Never log, echo, or return a token, password hash, or full connection string.
6. Errors, logs, and responses
- Client gets a stable code + safe message; the stack trace, SQL, and driver error stay in the server log. A 500 body that names the table is reconnaissance.
- Never return more fields than the caller needs — serialize with an explicit
allow-list (
selectthe columns, or map to a response DTO).SELECT *into JSON is how password hashes and internal flags leak. - Redact secrets/PII in logs; log the actor, action, object id, and outcome for security-relevant events (login, permission change, delete, export).
Verification before reporting
- For each route you touched, show the ownership check and the validation call — file:line, not prose.
- Prove the control with a request: same endpoint, another user's id, expect
404/403; malformed body, expect 422; N+1 rapid calls, expect 429.
curl -si -X GET host/api/orders/<other-users-id> -H "authorization: Bearer $A" # expect 404/403 curl -si -X POST host/api/orders -H "content-type: application/json" -d '{"role":"admin"}' # rejected - Unauthenticated call to every new route: expect 401, never data.
- Run the suite. A security fix without a regression test comes back.
Reporting
severity · route · file:line · exploit path · fix, ordered by exploitability.
State the controls you verified with output, and separately the ones you could not
test and why. Contradicting a claim of "already handled by middleware" requires
naming the middleware and the matcher line — otherwise treat it as unverified.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

