agentleFS
Viprasol-TechViprasol-Tech/api-security-review/skill.md

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

  • Other versions4 projects use a version of this.

    Word for word · 0

    None found.

    Edited · 0

    None found.

    Wrote their own version · 4

    1. thomast1906/github-copilot-agent-skills★ 203same name
    2. apisec-inc/apisec-skills★ 20same name
    3. DigitalExplorers/agent-skills★ 12same name
    4. 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
  1. Distinctive:

    Do not proceed until you know what is in scope, which stack, and the auth model.

    line 75
  2. Distinctive:

    Use the exact IDs and names.

    line 96
  3. Distinctive:

    Prefer opaque, unguessable IDs (UUIDv4) over sequential integers so enumeration is harder even if a check is missed.

    line 120
  4. Very specific:

    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 …

    line 165
  5. Very specific:

    Never return the raw ORM row.

    line 205
  6. Very specific:

    Spot it: No middleware/limiter on routes.

    line 222
  7. Very specific:

    Spot it: No route-level role check.

    line 258
  8. Distinctive:

    What it is: Default credentials, verbose errors, missing TLS, open admin panels, debug mode in production, missing security headers, …

    line 338
  9. Distinctive:

    Fix: Treat third-party responses as untrusted input:

    line 390
  10. Very specific:

    Parametrize: every SQL/NoSQL/LDAP/XPath query — never string-format.

    line 392
  11. Very specific:

    Use yaml.safe_load, not yaml.load.

    line 394
  12. Distinctive:

    Don't blindly forward error bodies — wrap them.

    line 396
  13. Very specific:

    Follow with one line of *why* (e.g.

    line 485

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

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

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.

  1. 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.
  2. 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.
  3. 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?
  4. REST · GraphQL · both? GraphQL needs extra checks (introspection, depth, complexity, per-resolver authz).
  5. Exposure. Public internet · partner-only (API key + IP allowlist) · internal (assumed trusted — verify) · machine-to-machine.
  6. 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.

SeverityMeaning
🔴 CriticalFull account or data takeover, mass PII leak, RCE, privilege escalation. Block deploy.
🟠 HighAuth bypass on a single account, partial data leak, exploitable BOLA on a sensitive resource.
🟡 MediumDoS via missing rate limit, info leak (stack trace, version banner), CSRF on a non-critical flow.
🟢 LowHardening gap on top of a strong default (missing one security header, weak cipher coexisting with a strong default).
ℹ️ InfoBest-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 no WHERE owner_id = caller filter and no explicit ownership check. Sequential or guessable IDs (incrementing integers) amplify it.
  • Exploit. Alice's token, Bob's userId in 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 enumeration

    Or 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: none accepted 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, exp verification.
    • 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 no algorithms= allowlist; verify_signature=False; no aud/iss arguments; HS256 in a public-client setup; cookie set without Secure/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) / Rails user.update_attributes(params[:user]) without strong params. No request/response schema with an explicit allowlist.
  • Exploit. PATCH /users/me with {"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 / omit sensitive 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 arbitrary limit.
    • 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/users accepts a normal user token.
  • Spot it. No route-level role check; if user.is_admin only 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/users and creates an admin account, or calls DELETE /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 / PATCH on 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 /signup with 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 url

    Bonus: 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 resp

    TLS 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/json on JSON endpoints.

API9:2023 Improper Inventory Management

  • What it is. Renamed from Improper Asset Management. Forgotten v1 / staging / test / internal endpoints 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, staging reachable from the public domain. No OpenAPI spec — or one that drifts from reality. No Sunset / Deprecation headers 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 Sunset and Deprecation headers on retiring versions and put a hard kill-date on them. Block internal / debug / admin paths 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, not yaml.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: * with Access-Control-Allow-Credentials: true (browsers reject — but reflection variants slip through anyway).
  • 🚫 Origin reflection — the server echoes whatever Origin the client sends. A malicious site's origin gets reflected, and an authenticated cross-origin request is wide open.
  • 🚫 null origin accepted (sandboxed iframes, file://, some redirects send Origin: 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; reject null; validate the Origin header 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 User type's email may be public; phone is 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 no algorithms= allowlist. → API2.
  • CORS origin reflection (Access-Control-Allow-Origin: <whatever the request sent>). → API8.
  • No 401 / 403 on /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 (/graphql returns __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 IDEndpoint / featureSeverityOne-liner
1API1:2023GET /users/{userId}🔴 CriticalNo owner check; any authenticated user reads any user.
2API3:2023PATCH /users/{userId}🔴 CriticalMass assignment — accepts isAdmin.
3API8:2023global🟡 MediumMissing HSTS / CSP / X-Content-Type-Options.

Per-finding detail

For each finding, write:

  1. Description — what's wrong, in one short paragraph.
  2. Impact — what an attacker gets.
  3. 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.
  4. Code-level fix — the exact snippet from Step 3 for that OWASP item, adapted to the user's stack.
  5. 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 IDNameResult
API1:2023Broken Object Level Authorization❌ fail (finding #1)
API2:2023Broken Authentication✅ pass
API3:2023Broken Object Property Level Authorization❌ fail (finding #2)
API4:2023Unrestricted Resource Consumption🟡 partial (rate limit OK, no body cap)
API5:2023Broken Function Level Authorization✅ pass
API6:2023Unrestricted Access to Sensitive Business Flows⚪ not reviewed (need flow inventory)
API7:2023Server-Side Request Forgery⚪ n/a (no outbound fetch in scope)
API8:2023Security Misconfiguration❌ fail (finding #3)
API9:2023Improper Inventory Management⚪ not reviewed (spec only)
API10:2023Unsafe 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.com

Disclaimer (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.

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.