api-security-review
Skill0 starsChanged 5 months agoMITView on GitHub
Review a REST or GraphQL API for security vulnerabilities — every item of the OWASP API Top 10 2023 (BOLA, broken auth, mass assignment, rate limiting, SSRF, GraphQL depth/complexity), JWT/OAuth pitfalls, CORS misconfig, and security headers. Outputs severity-rated findings with curl test snippets and code-level fixes. Use to vet an OpenAPI spec, audit an endpoint diff, or pre-deploy a service.
- Installs packages
4 other projects use a version of this.
Adopted by
Most recently taken up by
- OmarAlex24/audit-skillstheir own version, first seen Nov 15, 2026
- DigitalExplorers/agent-skillstheir own version, first seen Apr 29, 2026
- thomast1906/github-copilot-agent-skillstheir own version, first seen Apr 22, 2026
- apisec-inc/apisec-skillstheir own version, first seen Mar 12, 2026
Other versions4 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 4
- thomast1906/github-copilot-agent-skills★ 203same name
- apisec-inc/apisec-skills★ 20same name
- DigitalExplorers/agent-skills★ 12same name
- OmarAlex24/audit-skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
The file
Show its rules and text
We split this skill into the rules it gives an agent
13 rules. 0 are common, 6 are distinctive, and 7 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 75
Do not proceed until you know what is in scope, which stack, and the auth model.
- Distinctive: line 96
Use the exact IDs and names.
- Distinctive: line 120
Prefer opaque, unguessable IDs (UUIDv4) over sequential integers so enumeration is harder even if a check is missed.
- Very specific: line 165
Use short-lived access tokens (5–15 min) + refresh tokens with rotation, add a token revocation list (jti), and never put PII in claims you …
- Very specific: line 205
Never return the raw ORM row.
- Very specific: line 222
Spot it: No middleware/limiter on routes.
- Very specific: line 258
Spot it: No route-level role check.
- Distinctive: line 338
What it is: Default credentials, verbose errors, missing TLS, open admin panels, debug mode in production, missing security headers, …
- Distinctive: line 390
Fix: Treat third-party responses as untrusted input:
- Very specific: line 392
Parametrize: every SQL/NoSQL/LDAP/XPath query — never string-format.
- Very specific: line 394
Use yaml.safe_load, not yaml.load.
- Distinctive: line 396
Don't blindly forward error bodies — wrap them.
- Very specific: line 485
Follow with one line of *why* (e.g.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
API Security Review
A disciplined, repeatable methodology for reviewing a REST or GraphQL API against the OWASP API Security Top 10 (2023) — Broken Object Level Authorization, Broken Authentication, Broken Object Property Level Authorization, Unrestricted Resource Consumption, Broken Function Level Authorization, Unrestricted Access to Sensitive Business Flows, Server-Side Request Forgery, Security Misconfiguration, Improper Inventory Management, and Unsafe Consumption of APIs — plus the cross-cutting auth, CORS, and GraphQL checks that account for most real-world breaches. BOLA alone is roughly 40% of observed API attacks.
The skill takes an OpenAPI spec, an endpoint diff, a request/response sample, or a chunk of API source, and returns severity-rated findings with curl test snippets and code-level fixes, plus a coverage matrix so callers see what was actually checked.
Honest scope & limits. I cannot hit a live server. I reason over what you provide — an OpenAPI spec, source code, request/response samples, or descriptions. The output is leads + a hardening plan, not a substitute for a professional penetration test or a live DAST run. Not legal or compliance advice; PCI / HIPAA / SOC 2 attestation requires qualified auditors.
When to Activate
Activate when the user:
- Shares an OpenAPI / Swagger spec (
openapi.yaml,swagger.json) and asks for a security pass. - Pastes REST or GraphQL endpoint source code (FastAPI, Flask, Express, NestJS, Rails, Django REST Framework, Spring, Go) and asks if it's safe to ship.
- Drops a pull-request diff touching API code, routes, auth middleware, an API gateway config, or CORS settings.
- Shares a request/response sample (curl, HAR, Postman) and asks "is this API secure?" or "what's leaking here?".
- Mentions any of: OWASP API Top 10, BOLA, broken auth, mass assignment, JWT, OAuth, OIDC, CORS, SSRF, rate limit, GraphQL depth/complexity, security headers.
Step 1: Intake & Scope
Establish the review's scope before saying anything about safety.
- What are we reviewing?
- (A) An OpenAPI / Swagger spec — full or excerpt.
- (B) Endpoint source code — one route, a module, or the whole service.
- (C) A PR / diff touching API code.
- (D) A request/response sample — curl, HAR, browser network capture.
- (E) An API gateway config — Kong, Apigee, AWS API Gateway, NGINX, Envoy.
- Stack. Language & framework: FastAPI, Flask, Django REST Framework, Express, NestJS, Hapi, Rails, Spring (Boot/MVC), ASP.NET, Go (gin/echo/chi). Different idioms hide different bugs.
- Auth model. JWT (HS / RS / ES) · OAuth 2.0 / OIDC (Authorization Code + PKCE, Client Credentials, Implicit — deprecated) · session cookies · API keys · mTLS · signed requests. Where is auth enforced — gateway, middleware, or per-route?
- REST · GraphQL · both? GraphQL needs extra checks (introspection, depth, complexity, per-resolver authz).
- Exposure. Public internet · partner-only (API key + IP allowlist) · internal (assumed trusted — verify) · machine-to-machine.
- What can you share? The spec? The route handler? A few sample requests? The auth middleware? Without artefacts, ask before guessing.
Do not proceed until you know what is in scope, which stack, and the auth model.
Step 2: Severity Model
Rate every finding by blast radius × likelihood.
| Severity | Meaning |
| 🔴 Critical | Full account or data takeover, mass PII leak, RCE, privilege escalation. Block deploy. |
| 🟠 High | Auth bypass on a single account, partial data leak, exploitable BOLA on a sensitive resource. |
| 🟡 Medium | DoS via missing rate limit, info leak (stack trace, version banner), CSRF on a non-critical flow. |
| 🟢 Low | Hardening gap on top of a strong default (missing one security header, weak cipher coexisting with a strong default). |
| ℹ️ Info | Best-practice nits, documentation gaps, naming hygiene. |
Step 3: OWASP API Top 10 (2023) — Review Passes
Use the exact IDs and names. For each: what it is · how to spot it · exploit scenario · fix. Cite each finding in your report by its OWASP ID.
API1:2023 Broken Object Level Authorization
- What it is. An endpoint operates on
/orders/{id},/users/{userId},/files/{fileId}and authenticates the caller — but never checks that the caller owns that object. ~40% of all API attacks. Unchanged from 2019 because it remains the dominant API bug class. - Spot it. Any path with
{id}/{uuid}/ numeric IDs where the handler loads the object by id alone, with noWHERE owner_id = callerfilter and no explicit ownership check. Sequential or guessable IDs (incrementing integers) amplify it. - Exploit. Alice's token, Bob's
userIdin the URL → Bob's data. Fix. Explicit owner check at the handler, deny-by-default:
# FastAPI / SQLAlchemy obj = db.get(Order, order_id) if obj is None or obj.owner_id != request.user.id: raise HTTPException(status_code=404) # 404, not 403, to avoid id enumerationOr push it into the query:
Order.objects.filter(id=order_id, owner_id=user.id).first(). Prefer opaque, unguessable IDs (UUIDv4) over sequential integers so enumeration is harder even if a check is missed.
API2:2023 Broken Authentication
- What it is. Weak login or token validation lets attackers impersonate users — credential stuffing, brute force, predictable session IDs, weak password reset, and JWT/OAuth misuse.
- JWT pitfalls (check every JWT system).
alg: noneaccepted by the server.- Algorithm confusion — the server uses HMAC verify with the RSA public key as the secret (attacker signs an HS256 token with the public key, server accepts it).
- Weak HS256 secret (dictionary-crackable).
- No
iss,aud,expverification. - Long-lived tokens with no revocation list and no rotation.
- Sensitive PII inside JWT claims (it's returned to the client).
- JWT used as both session and authorization bearer with no scope check.
- OAuth 2.0 / OIDC.
- Authorization Code flow without PKCE for public clients (SPAs, mobile).
- Open-redirect on
redirect_uri(always allowlist exact URIs). - Implicit flow still in use — deprecated.
- Missing
state/nonce→ CSRF on the auth code grant. - Scope check missing at the resource server.
- Sessions / cookies. No
Secure,HttpOnly,SameSite=Lax|Strict. Session ID in the URL. No re-auth for sensitive operations (password change, email change, payment). - Spot it.
jwt.decode(token, key)with noalgorithms=allowlist;verify_signature=False; noaud/issarguments; HS256 in a public-client setup; cookie set withoutSecure/HttpOnly/SameSite. Fix (PyJWT).
import jwt payload = jwt.decode( token, public_key, algorithms=["RS256"], # pin the algorithm — block alg=none + confusion audience="my-api", # verify aud issuer="https://auth.example", # verify iss options={"require": ["exp", "iat", "iss", "aud"]}, )Use short-lived access tokens (5–15 min) + refresh tokens with rotation, add a token revocation list (
jti), and never put PII in claims you wouldn't log.
API3:2023 Broken Object Property Level Authorization
- What it is. Two sub-classes merged: excessive data exposure (the
response includes properties the caller shouldn't see —
password_hash,internal_flags,mfa_secret) and mass assignment (the request body writes properties the caller shouldn't touch —isAdmin,balance,tenant_id). - Spot it.
return user.__dict__/return user.to_dict()/ serializing the whole ORM row.User(**request.json)/user.update(**body)/ Railsuser.update_attributes(params[:user])without strong params. No request/response schema with an explicit allowlist. - Exploit.
PATCH /users/mewith{"isAdmin": true, "tenantId": 42}and the user is now an admin in someone else's tenant. Fix. Explicit allowlist schemas on input and output:
# Pydantic — explicit, allow-list, no extras class UserUpdateIn(BaseModel): display_name: str | None = None email: EmailStr | None = None model_config = ConfigDict(extra="forbid") # reject unknown fields class UserOut(BaseModel): id: UUID display_name: str email: EmailStr # NOTE: no password_hash, no is_admin, no internal flags// Zod — strict() rejects extra keys const UserUpdateIn = z.object({ displayName: z.string().min(1).max(80).optional(), email: z.string().email().optional(), }).strict();Never return the raw ORM row. Map to a response schema and
exclude/omitsensitive fields by default.
API4:2023 Unrestricted Resource Consumption
- What it is. Renamed from Lack of Rate Limiting. No rate limit, no payload size cap, no pagination cap, unbounded queries → DoS or cost blow-up (DB, LLM, third-party APIs).
- Pragmatic checklist.
- Token-keyed > IP-keyed. NAT and CDNs flatten IPs; token-keyed limits track the real principal.
- Separate buckets: per-user, per-IP, per-endpoint, per-tenant.
- Payload size cap (HTTP body), JSON depth and array-length caps.
- Pagination: enforce
limit ≤ 100; never accept arbitrarylimit. - Long-running endpoint timeouts.
- Cost budgets on expensive resources (DB scans, LLM calls, third-party metering).
- Spot it. No middleware/limiter on routes.
limit = int(request.args["limit"])with no max. No body-size middleware. Unlimited file uploads. Heavy joins with no pagination. Fix.
# FastAPI + slowapi — per-user token-keyed limiter from slowapi import Limiter from slowapi.util import get_remote_address def key_by_user(request): return request.state.user.id if hasattr(request.state, "user") else get_remote_address(request) limiter = Limiter(key_func=key_by_user, default_limits=["60/minute"]) @app.get("/search") @limiter.limit("20/minute") def search(q: str, limit: int = Query(20, ge=1, le=100)): # cap pagination ...// Express + express-rate-limit, per-user import rateLimit from "express-rate-limit"; app.use(express.json({ limit: "100kb" })); // body cap app.use("/api/", rateLimit({ windowMs: 60_000, max: 60, keyGenerator: (req) => req.user?.id || req.ip, // token-keyed when possible }));
API5:2023 Broken Function Level Authorization
- What it is. Admin / privileged functions are reachable by non-admins —
the URL is "secret", the role check lives in the UI, or the check is on the
wrong route.
POST /admin/usersaccepts a normal user token. - Spot it. No route-level role check;
if user.is_adminonly in the frontend; admin endpoints sharing the same router as user endpoints with no separate guard; "hidden" admin URLs that nonetheless reach the handler. - Exploit. A regular user calls
POST /admin/usersand creates an admin account, or callsDELETE /users/{id}and erases anyone. Fix. Deny by default, allowlist roles, enforce at the route:
# FastAPI dependency-injected guard, applied per-route def require_admin(user: User = Depends(current_user)) -> User: if "admin" not in user.roles: raise HTTPException(status_code=403) return user @router.post("/admin/users", dependencies=[Depends(require_admin)]) def create_user(...): ...Make the guard the default, then explicitly mark public routes — not the other way around. Cover
DELETE/PUT/PATCHon every admin object.
API6:2023 Unrestricted Access to Sensitive Business Flows
- What it is. New in 2023. Automation abuses the business logic itself — buy-all-tickets bots, reward farming, coupon stacking, mass account creation, mass password reset, mass follow/transfer/withdraw. Each individual request is "legal"; the volume is the abuse.
- Sensitive flows to protect. Account creation, password reset, login, coupon/promo redemption, purchase, review/comment, follow, transfer, withdraw, refer-a-friend rewards.
- Spot it. A
POST /signupwith no CAPTCHA, no device-fingerprint, no velocity check, no business-logic cap. A coupon endpoint with no per-user limit. A withdraw endpoint with no daily cap. - Fix. Defense-in-depth at the business layer, not just rate limit:
- CAPTCHA (hCaptcha, Turnstile) on high-value flows.
- Device fingerprint + behavioural anomaly detection.
- Velocity checks (per-user, per-IP, per-tenant, per-device).
- Hard business-logic caps ("max 1 coupon per account per day", "max 3 password resets per hour per email", "max $X withdrawn per day").
API7:2023 Server-Side Request Forgery
- What it is. New in 2023. A URL or hostname comes from the user, and the
server fetches it. Attackers point it at internal services — cloud metadata
(
169.254.169.254), internal admin panels, internal databases, neighbour pods. - Spot it. Webhook subscription endpoints, "import from URL", PDF/HTML renderers, image proxies, link-preview generators, RSS importers.
- Exploit.
POST /webhooks/fetch {"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/"}→ AWS IAM creds leaked back in the response. Fix. Strict URL allowlist; resolve DNS before fetch; reject private/ link-local/loopback ranges; pin to HTTP(S); use a separate egress network if possible.
import ipaddress, socket from urllib.parse import urlparse BLOCKED_NETS = [ipaddress.ip_network(n) for n in ( "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "169.254.0.0/16", "fc00::/7", "::1/128", "fe80::/10", )] def safe_url(url: str) -> str: u = urlparse(url) if u.scheme not in ("http", "https"): raise ValueError("scheme not allowed") ip = ipaddress.ip_address(socket.gethostbyname(u.hostname)) if any(ip in net for net in BLOCKED_NETS): raise ValueError("private/loopback/link-local not allowed") return urlBonus: defend against DNS rebinding — resolve once, fetch by the resolved IP (with the original Host header), so the second resolution can't flip to a private address after the check.
API8:2023 Security Misconfiguration
- What it is. Default credentials, verbose errors, missing TLS, open admin panels, debug mode in production, missing security headers, permissive CORS, loose content-type handling.
- Spot it.
DEBUG = True,app.run(debug=True), stack traces in production responses,Server: gunicorn/20.x/X-Powered-By: Express, missing HSTS / CSP /X-Content-Type-Options,Access-Control-Allow-Origin: *with credentials. Fix. A baseline header set, strict content-type, generic error pages:
@app.middleware("http") async def security_headers(request, call_next): resp = await call_next(request) resp.headers["Strict-Transport-Security"] = "max-age=63072000; includeSubDomains; preload" resp.headers["X-Content-Type-Options"] = "nosniff" resp.headers["Referrer-Policy"] = "no-referrer" resp.headers["Content-Security-Policy"] = "frame-ancestors 'none'" resp.headers["Permissions-Policy"] = "interest-cohort=()" return respTLS 1.2+ only, modern ciphers, no weak fallbacks. Generic error responses to clients; rich logs to the server side. Strict content-type check — reject anything not
application/jsonon JSON endpoints.
API9:2023 Improper Inventory Management
- What it is. Renamed from Improper Asset Management. Forgotten v1 /
staging / test /
internalendpoints with weaker auth still reachable. Old API versions kept running for "compatibility" with no deprecation plan. Undocumented endpoints that the security team doesn't know exist. - Spot it. Routes labelled
v0,v1,legacy,internal,debug,stagingreachable from the public domain. No OpenAPI spec — or one that drifts from reality. NoSunset/Deprecationheaders on retiring endpoints. - Fix. OpenAPI is the source of truth. Generate it from code (FastAPI,
NestJS, drf-spectacular) so it can't drift. Catalogue every endpoint, every
version, every environment. Send
SunsetandDeprecationheaders on retiring versions and put a hard kill-date on them. Blockinternal/debug/adminpaths at the edge (gateway/WAF) unless explicitly allowed.
API10:2023 Unsafe Consumption of APIs
- What it is. New in 2023. Your API trusts a third-party API's response without validation — and that response can be hostile (compromised vendor, malicious user-controlled webhook, untrusted SaaS). Subsumes much of the old "injection" class.
- Spot it.
requests.get(vendor_url).json()shoved straight into your DB or templated into another query. Deserializing untrusted blobs (pickle, Java native serialization, YAML!!tags). Forwarding third-party error bodies verbatim to clients. SQL/NoSQL/LDAP/XPath/command strings built by string-formatting third-party data. - Fix. Treat third-party responses as untrusted input:
- Validate against a strict response schema (JSON Schema / Pydantic / Zod).
- Parametrize every SQL/NoSQL/LDAP/XPath query — never string-format.
- Never
pickle.loads/ Java native deserialize data you didn't write. - Use
yaml.safe_load, notyaml.load. - Enforce TLS, certificate verification, and timeouts on every outbound call.
- Don't blindly forward error bodies — wrap them.
Step 4: Cross-Cutting Checks
These slice across multiple OWASP items but warrant their own pass.
JWT deep-dive (one more time, explicitly)
- ✅ Pin
algorithms=[…]to exactly what you issue (RS256 or ES256 — not "any"). - ✅ Verify
iss,aud,exp,nbf,iat; require their presence. - ✅ Strong, rotated signing keys; HS256 only for server-to-server with a high-entropy secret stored in a secret manager.
- ✅ Short-lived access tokens + refresh-token rotation; revocation by
jti. - ✅ No PII in claims you wouldn't log. Scopes/roles checked per-route.
- 🚫 No
alg: none. No HMAC verification with the RSA public key. No long-lived bearer tokens with no revocation path.
CORS attack patterns
- 🚫
Access-Control-Allow-Origin: *withAccess-Control-Allow-Credentials: true(browsers reject — but reflection variants slip through anyway). - 🚫 Origin reflection — the server echoes whatever
Originthe client sends. A malicious site's origin gets reflected, and an authenticated cross-origin request is wide open. - 🚫
nullorigin accepted (sandboxed iframes, file://, some redirects sendOrigin: null). - 🚫 Pre-flight not enforced server-side — the server actually executes the unsafe request even though the browser would block the response read.
- ✅ Explicit origin allowlist; per-origin
Allow-Credentials; rejectnull; validate theOriginheader on every state-changing request.
GraphQL specifics
- ✅ Introspection disabled in production (or restricted to authorized operators).
- ✅ Query depth limit (e.g. ≤ 10) and complexity / cost analysis limit.
- ✅ Field-level authorization at every resolver — in GraphQL, BOLA is
per-field, not per-endpoint. A
Usertype'semailmay be public;phoneis not. - ✅ Cap aliases + batching (a single HTTP request can contain many expensive operations).
- ✅ Watch resolver-level N+1 — DataLoader or batch resolvers prevent shaped queries from melting the DB.
- ✅ Persisted queries (or APQ with an allowlist) for high-trust first-party clients; arbitrary queries only for partners that need them.
Step 5: Red-Flags Quick Scan
Any one of these flips the review toward 🟠 or 🔴 — name it and find the others nearby:
GET /users/{id}returns all user fields (PII,password_hash, internal flags). → API3.PATCH /users/{id}accepts an arbitrary body with no schema (mass assignment). → API3.- JWT verified with
verify=False/verify_signature=False, or noalgorithms=allowlist. → API2. - CORS origin reflection (
Access-Control-Allow-Origin: <whatever the request sent>). → API8. - No
401/403on/admin/*for a non-admin token. → API5. - Rate limit only on
/login, nowhere else (DoS, scraping, BOLA enumeration all uncapped). → API4. - Endpoint accepts a URL in the body and fetches it server-side, with no allowlist. → API7.
- GraphQL introspection enabled on prod (
/graphqlreturns__schema). → API9. - Internal endpoints under
/v0/internal/*reachable from the public domain. → API9.
Step 6: Output Format — the Review Report
Lead with a verdict banner, then findings, then a coverage matrix, then suggested test artefacts and the disclaimer.
Verdict banner (pick one)
- ✅ Hardened — no Critical or High findings; baseline headers, auth, and rate limits in place.
- 🟡 Multiple Medium issues — fix before prod scale-up.
- 🟠 High risk — patch the listed items before the next deploy.
- ⛔ Critical — block deploy. One or more 🔴 findings (auth bypass, BOLA on PII, SSRF, mass assignment of privilege).
Follow with one line of why (e.g. "BOLA on /users/{userId} and mass
assignment of isAdmin on PATCH /users/{userId}").
Findings table
| # | OWASP ID | Endpoint / feature | Severity | One-liner |
| 1 | API1:2023 | GET /users/{userId} | 🔴 Critical | No owner check; any authenticated user reads any user. |
| 2 | API3:2023 | PATCH /users/{userId} | 🔴 Critical | Mass assignment — accepts isAdmin. |
| 3 | API8:2023 | global | 🟡 Medium | Missing HSTS / CSP / X-Content-Type-Options. |
Per-finding detail
For each finding, write:
- Description — what's wrong, in one short paragraph.
- Impact — what an attacker gets.
Curl test snippet — a single command another engineer can run to reproduce:
# API1:2023 — BOLA on /users/{userId} curl -i -H "Authorization: Bearer $ALICE_TOKEN" \ https://api.example.com/users/$BOB_USER_ID # Expect: 403 / 404 (object hidden). Vulnerable: 200 + Bob's record.- Code-level fix — the exact snippet from Step 3 for that OWASP item, adapted to the user's stack.
- References — link to the OWASP API Top 10 2023 page for that ID.
Coverage matrix
So the caller knows what was actually checked vs not reviewed:
| OWASP ID | Name | Result |
| API1:2023 | Broken Object Level Authorization | ❌ fail (finding #1) |
| API2:2023 | Broken Authentication | ✅ pass |
| API3:2023 | Broken Object Property Level Authorization | ❌ fail (finding #2) |
| API4:2023 | Unrestricted Resource Consumption | 🟡 partial (rate limit OK, no body cap) |
| API5:2023 | Broken Function Level Authorization | ✅ pass |
| API6:2023 | Unrestricted Access to Sensitive Business Flows | ⚪ not reviewed (need flow inventory) |
| API7:2023 | Server-Side Request Forgery | ⚪ n/a (no outbound fetch in scope) |
| API8:2023 | Security Misconfiguration | ❌ fail (finding #3) |
| API9:2023 | Improper Inventory Management | ⚪ not reviewed (spec only) |
| API10:2023 | Unsafe Consumption of APIs | ⚪ n/a (no third-party calls in scope) |
Suggested test artefacts
A small set the caller can drop into CI right now.
# tests/test_bola.py — pytest regression for finding #1
def test_bola_users_endpoint(client, alice_token, bob_user_id):
r = client.get(f"/users/{bob_user_id}", headers={"Authorization": f"Bearer {alice_token}"})
assert r.status_code in (403, 404), f"BOLA: alice could read bob, got {r.status_code} {r.text}"# .github/workflows/api-security.yml
name: api-security
on: [push, pull_request]
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 42Crunch API Security Audit
uses: 42Crunch/api-security-audit-action@v4
with:
api-token: ${{ secrets.FORTYTWO_CRUNCH_TOKEN }}
min-score: 75
- name: OWASP ZAP baseline
uses: zaproxy/action-baseline@v0.12.0
with:
target: https://staging.api.example.com
- name: Schemathesis property-based tests
run: |
pip install schemathesis
schemathesis run --checks all openapi.yaml \
--base-url https://staging.api.example.comDisclaimer (always include)
Educational security guidance — not a substitute for a professional penetration test or a live DAST run, and not legal or compliance advice. This review is static reasoning over what you provided (spec, source, or samples); it cannot hit a live server, observe runtime behaviour, or attest to PCI / HIPAA / SOC 2 conformance — those require qualified auditors. Fix the findings, then run a live scan (42Crunch, OWASP ZAP, Schemathesis, Burp) and, for high-value services, commission a third-party pen-test.
Related Viprasol Skills
code-review-skill— general correctness / security / performance review of the same PR with prioritized findings.smart-contract-audit— if the API fronts an on-chain protocol, audit the Solidity/EVM contracts behind it.
Not affiliated with or endorsed by Anthropic.
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.

