agentleFS
nandhu-44nandhu-44/secure-vibe-coding/skills/secure-web/SKILL.md

secure-web

Skill0 starsChanged 15 days agoApache-2.0View on GitHub

Secure patterns for web apps and APIs. Use when building or editing routes/endpoints/controllers, REST or GraphQL APIs, login/signup/password reset/OAuth/sessions/JWT, forms, file uploads, webhooks, payments/checkout/coupons, admin panels, multi-tenant data access, CORS/CSP/cookies, or server-side URL fetching, in Express, Next.js, FastAPI, Django, Flask, Spring, Go, Laravel, Rails and similar. Prevents IDOR/broken access control, injection, XSS, CSRF, SSRF, missing rate limits, mass assignment, and business-logic flaws.

The file

We split this skill into the rules it gives an agent

11 rules. About 0 are common, 5 are distinctive, and 6 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈5

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    Use this together with the secure-coding core card.

    line 14
  2. Very specific:

    Put authentication in middleware or a dependency applied to the router, deny by default.

    line 19
  3. Very specific:

    Field-level (mass assignment, BOPLA): use explicit input DTOs and schemas with only writable fields.

    line 34
  4. Distinctive:

    Use non-guessable IDs (UUIDv4/v7) for externally visible resources.

    line 35
  5. Very specific:

    Use a maintained auth library or provider (Auth.js/NextAuth, Lucia-style sessions, Django auth, Spring Security, Devise, Laravel …

    line 40
  6. Distinctive:

    Every public app needs these.

    line 69
  7. Distinctive:

    Read business-logic.md for any flow involving money, credits, inventory, roles, invites, or multi-step processes.

    line 82
  8. Distinctive:

    Webhooks: verify the signature over the raw body, check the timestamp (replay), dedupe by event ID, then re-fetch the object from the …

    line 88
  9. Distinctive:

    Enumeration & scraping: no sequential IDs in URLs for private resources, generic auth errors, rate limits.

    line 89
  10. Very specific:

    Never execute, include, or unzip uploads without checks.

    line 94
  11. Very specific:

    Run python3 ${CLAUDE_SKILL_DIR}/../secure-coding/scripts/secscan.py <changed files> on changed files and walk the API section of the core …

    line 113

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Secure Web & API

Use this together with the secure-coding core card. Build the feature first, using these patterns as the default way to write it. Don't treat them as a later hardening pass.

1. Access control: the #1 web risk (A01)

Every route needs answers to two questions: who is calling? and may they touch this specific object?

  • Put authentication in middleware or a dependency applied to the router, deny by default. Public routes must be explicit (@public, allowlist).
  • Object-level authz (IDOR/BOLA): scope every query by the caller's identity or tenant.
    # ❌ any logged-in user can read any order
    order = db.get(Order, order_id)
    # ✅ scoped by owner; 404 for "not yours" to avoid leaking existence
    order = db.scalar(select(Order).where(Order.id == order_id, Order.user_id == current_user.id))
    if order is None: raise HTTPException(404)
    // ✅ Prisma
    const doc = await prisma.document.findFirst({ where: { id, orgId: session.user.orgId } });
    if (!doc) return res.status(404).end();
  • Function-level authz: admin routes check the role server-side, taken from the session or DB. Never use a client flag, a hidden button, or a role field in the JWT payload the user can edit.
  • Field-level (mass assignment, BOPLA): use explicit input DTOs and schemas with only writable fields. Never accept role, isAdmin, ownerId, price, balance, status, verified, or tenantId from the body. Shape the output too: return DTOs, not ORM rows (no passwordHash, internal flags, or other users' emails).
  • Use non-guessable IDs (UUIDv4/v7) for externally visible resources. This is defense in depth, not a replacement for authz checks.
  • Multi-tenant apps: put a tenant filter in a shared repository/query helper (or Postgres RLS) so forgetting it is hard.
  • Next.js/RSC: Server Actions and Route Handlers are public endpoints. Authenticate and authorize inside each action; middleware alone is not enough.

2. Authentication & sessions (A07)

  • Use a maintained auth library or provider (Auth.js/NextAuth, Lucia-style sessions, Django auth, Spring Security, Devise, Laravel Breeze/Fortify, Clerk/Auth0/Cognito/Supabase Auth). Don't hand-roll sessions or OAuth.
  • Passwords: argon2id or bcrypt(12). Minimum length 8 (prefer 12+, max ≥64). Check against breached-password lists (HIBP k-anonymity) when possible. No composition rules or forced rotation (NIST SP 800-63B).
  • Login, signup, and reset return identical responses for unknown user vs wrong password.
  • Rate-limit and lock out: per-IP and per-account (e.g. 5 failures/15 min → exponential backoff/CAPTCHA). See rate-limiting.md.
  • Session cookies: HttpOnly; Secure; SameSite=Lax; Path=/, using the __Host- prefix when possible. Rotate the session ID on login and privilege change. Use idle and absolute timeouts. Invalidate server-side on logout and password change.
  • JWT: short-lived access tokens (≤15 min) plus rotating refresh tokens stored HttpOnly. Pin algorithms=[...], verify exp/iss/aud, keep the signing key in a secret manager. Never put JWTs in localStorage for high-value apps.
  • Password reset: single-use, random 32-byte token, store only its hash, expiry ≤30 min, invalidate other sessions after reset. Never put the user ID or email in the token.
  • MFA: TOTP/WebAuthn/passkeys. Rate-limit OTP attempts (≤5) because 6-digit codes are brute-forceable.
  • OAuth/OIDC: Authorization Code + PKCE, validate state and nonce, exact-match redirect URIs, and never trust the email from a provider unless email_verified.

3. Input handling & injection (A05)

  • Validate at the edge with a schema: zod/valibot (TS), pydantic (FastAPI), Django forms/DRF serializers, Bean Validation (@Valid), go-playground/validator. Check type, length, range, format, and enum. Reject unknown fields (.strict(), extra="forbid").
  • SQL: parameterized queries or the ORM only. For dynamic ORDER BY/column names, map them through an allowlist dict. ORMs' raw APIs ($queryRawUnsafe, .raw(), text() with f-strings, extra()) are injection sinks.
  • NoSQL: cast types (String(req.body.email)), reject $-prefixed keys, never use $where. Use mongo-sanitize or the schema to strip operators.
  • OS commands: avoid them. If needed, execFile/subprocess.run([...]) with a fixed binary, an allowlist, and -- before user args.
  • Templates: never compile user input as a template (SSTI).
  • XML: use defusedxml or disable DTD/external entities. YAML: safe_load.
  • GraphQL: depth/complexity limits, disable introspection in prod (or accept the risk), authz in resolvers, cap batching/aliases.

4. Output & browser security

  • XSS: rely on framework auto-escaping. Avoid dangerouslySetInnerHTML/v-html/|safe/innerHTML. If rich HTML is required, sanitize with DOMPurify (client) or nh3/bleach/sanitize-html (server). Validate URLs for href/src (only https:/mailto:; block javascript:).
  • Security headers (helmet / django-csp / Spring Security / middleware): Content-Security-Policy: default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none' (use nonces for scripts, avoid unsafe-inline), Strict-Transport-Security: max-age=63072000; includeSubDomains, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy minimal.
  • CSRF: cookie-authenticated, state-changing requests need a CSRF token (framework built-ins) or a strict SameSite cookie plus Origin check. GET must never change state.
  • CORS: explicit origin allowlist from config. credentials: true only with explicit origins. Never reflect Origin, never * for authenticated APIs. CORS is not authz.
  • Clickjacking: frame-ancestors 'none' (or a specific allowlist).
  • Open redirects: only relative paths starting with a single / (reject // and /\), or a host allowlist.

5. Rate limiting, quotas & resource limits (A06 / API4)

Every public app needs these. Defaults to apply unless the user specifies otherwise:

EndpointSuggested limit (tune)Key
Login / OTP verify5 / 15 minIP + account
Signup, password reset request, email/SMS send3–5 / hourIP + target email/phone
General authenticated API100–1000 / minuser ID / API key
Expensive: LLM calls, search, exports, image processingper-user quota + concurrency capuser ID
Public unauthenticated API60 / minIP

Also: request body limit (e.g. 100 KB JSON, explicit upload limits), pagination limit max (≤100), server timeouts, query timeouts, and caps on array lengths and nesting depth. Use a shared store (Redis) when running more than one instance. Return 429 with Retry-After. Code for each framework: rate-limiting.md.

6. Business logic: what scanners can't catch

Read business-logic.md for any flow involving money, credits, inventory, roles, invites, or multi-step processes. Key rules:

  • Recompute prices, totals, discounts, and permissions on the server from authoritative data. Ignore client-sent totals.
  • Validate numbers: positive integers for quantities, upper bounds, integer cents or decimals for money (never floats), currency fixed server-side.
  • State machines: enforce allowed transitions (pending → paid → shipped). Steps can't be skipped or repeated (e.g. no /confirm without /pay).
  • Race conditions/double-spend: wrap check-and-update in one transaction with SELECT … FOR UPDATE or a conditional update (UPDATE … SET balance = balance - :x WHERE id = :id AND balance >= :x). Use unique constraints for one-time actions and idempotency keys for payment APIs.
  • One-time things stay one-time: coupons, invites, reset tokens, and email verification links are marked consumed atomically.
  • Webhooks: verify the signature over the raw body, check the timestamp (replay), dedupe by event ID, then re-fetch the object from the provider before granting value.
  • Enumeration & scraping: no sequential IDs in URLs for private resources, generic auth errors, rate limits.

7. File uploads & downloads

  • Enforce max size at the proxy and app level. Allowlist extensions and check magic bytes (file-type, python-magic). Re-encode images (strip EXIF, kill polyglots).
  • Store with random names (UUID) outside the web root or in object storage with private ACLs. Serve via signed URLs or an authz-checked handler with Content-Disposition: attachment and the correct Content-Type, plus nosniff.
  • Never execute, include, or unzip uploads without checks. Zip-slip: validate every entry path. Zip bombs: cap the total uncompressed size and entry count.
  • Downloads by name: map an ID to a server-stored path. Never path.join(base, req.query.name) without a resolve-and-prefix check.

8. SSRF (server fetches a URL)

Webhooks-to-user-URL, link previews, image-from-URL, PDF renderers, and import-from-URL all need: https only → resolve DNS → reject private/loopback/link-local/metadata IPs (10/8, 172.16/12, 192.168/16, 127/8, 169.254/16, ::1, fc00::/7, fd00:ec2::254) → connect to the resolved IP (prevents DNS rebinding) → no redirects or re-validate each hop → timeout and size caps. In the cloud, require IMDSv2. A good option is an egress proxy (e.g. smokescreen) for all user-URL fetches.

9. Errors, logging, deployment

  • Generic errors to the client, details in server logs with a correlation ID. Debug off in prod.
  • Log auth events, authz denials, and admin actions. Never log secrets or tokens (see core logging-errors.md).
  • Behind a proxy: configure trusted proxies correctly before using X-Forwarded-For for rate limiting (app.set('trust proxy', 1), uvicorn --forwarded-allow-ips).
  • HTTPS everywhere, with HSTS. Redirect HTTP→HTTPS at the edge.

Framework quick references

Load the one matching the project: Express/Node · Next.js · FastAPI · Django · Flask · Spring Boot · Go net/http · Laravel/PHP · Supabase/Firebase (BaaS)

Standards mapping: owasp-top10-2025.md · api-top10.md

Before you reply

Run python3 ${CLAUDE_SKILL_DIR}/../secure-coding/scripts/secscan.py <changed files> on changed files and walk the API section of the core self-review.md. Then state in 1–3 lines: auth model, authz scoping, rate limits added, and anything the user must configure.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.