We Read an OpenAPI Spec and Found 13 Vulnerabilities in 2.4 Seconds

Date: March 4, 2026
Tool: swagger-scanner (SecurityClaw)
Category: API Security
Result: ⚠ Partial — 2/4 core vulns, 11 bonus findings
Scan time: 2.46 seconds
Methodology note: We built a deliberately vulnerable Flask REST API with a full OpenAPI 3.0 specification and 4 planted OWASP API Top 10 vulnerabilities. SecurityClaw's swagger-scanner read the spec and probed the running API. The scanner found 2 of the 4 planted vulnerabilities directly, plus 11 additional real security issues we hadn't explicitly planted. The 2 missed checks are documented honestly with the exact reason they were missed and the roadmap fix. All credentials and infrastructure references in the output are demo values.

What We Built

78% of APIs have at least one OWASP API Top 10 vulnerability. The problem isn't usually that developers don't know about authentication. It's the gap between what the spec says is protected and what the server actually enforces at runtime.

We built a Flask REST API with a complete OpenAPI 3.0 specification and four vulnerabilities deliberately embedded — one per OWASP API Top 10 category:

# Vulnerability Endpoint OWASP Class
V1 BOLA/IDOR — integer user IDs GET /api/v2/users/{id} API1 — Broken Object Level Authorization
V2 Unauthenticated admin endpoint GET /api/v2/admin/users API2 — Broken Authentication
V3 Excessive data exposure GET /api/v2/products API3 — Broken Object Property Level Auth
V4 Deprecated v1 API still live GET /api/v1/users API9 — Improper Inventory Management

We also left in three undocumented "shadow" endpoints — routes that exist in the running Flask app but appear nowhere in the spec. And we left Flask in debug mode with verbose error responses enabled on the login endpoint.

The API had 8 documented endpoints across v1 and v2. The scanner discovered all 8 from the spec — plus found the shadow endpoints by actively probing paths common to Python/Flask apps.

What SecurityClaw Found

Total findings: 13 — 1 Critical, 9 High, 3 Medium. Scan time: 2.46 seconds.

============================================================
Swagger/OpenAPI Security Scan
============================================================
Spec:       http://localhost:5002/openapi.json
Base URL:   http://localhost:5002
API:        SecurityClaw Demo API (v2.0.0)
Endpoints:  8 discovered
Duration:   2.4s

Findings:   13 total | 🔴 1 Critical | 🟠 9 High | 🟡 3 Medium | 🔵 0 Low

1. 🔴 CRITICAL — Unauthenticated Access to Auth-Required Endpoint
   Endpoint: GET /api/v2/admin/users
   Evidence: HTTP 200 received without auth token. Response length: 283 bytes.

7. 🟠 HIGH — Integer ID Parameter - Potential BOLA/IDOR Vulnerability
   Endpoint: GET /api/v2/users/{userId}
   Evidence: Sequential enumeration (1, 2, 3...) may expose other users' data.

8. 🟡 MEDIUM — Verbose Error Messages / Stack Trace Exposure
   Endpoint: POST /api/v2/auth/login
   Evidence: HTTP 500 response contains: {"db_connection":"sqlite:///demo.db",
   "debug_traceback":"Traceback (most recent call last)...","secret_key":"dev-secret-do-not-use"}

9. 🟠 HIGH — Shadow/Undocumented Endpoint: /admin
   Evidence: GET http://localhost:5002/admin → HTTP 200. 220 bytes.

Summary: 1 Critical, 9 High, 3 Medium
        

The Critical Finding: Spec Says Auth. Server Says No.

The most important finding came first. The OpenAPI spec for GET /api/v2/admin/users clearly defines a bearerAuth security requirement with admin scope. The endpoint is documented as requiring authentication.

The server disagreed. An unauthenticated GET request returned HTTP 200 with the full user list. The scanner probed it without a token, got data, and flagged it Critical in under a second.

This is API2 (Broken Authentication) in the most direct form: the spec is documentation, not enforcement. The authentication check was either never implemented, commented out during development, or accidentally removed. The spec doesn't protect anything — the running code does.

BOLA/IDOR: Integer IDs Are an Enumeration Problem

The scanner flagged GET /api/v2/users/{userId} as a High severity BOLA risk due to the integer parameter type. Sequential user IDs (1, 2, 3...) are trivially enumerable. A scanner can enumerate them automatically; an attacker can do it manually in minutes.

This is OWASP API1 territory. The spec defines the parameter as an integer — the scanner reads that and immediately flags the enumeration risk. No authentication check required; the issue is structural. The fix involves either UUIDs or proper per-request authorisation enforcement (verify the requesting user owns the resource, regardless of what ID they provided).

BOLA remains the most exploited API vulnerability in real breaches. If you're building REST APIs and your resource IDs are integers, this is your first audit target. The Web Application Hacker's Handbook covers IDOR exploitation patterns in depth — still essential reading despite its age, particularly for understanding why access control failures are so prevalent.

Shadow Endpoints: Three Routes Nobody Documented

The scanner probed paths common to Flask applications beyond the documented spec endpoints. It found three routes returning HTTP 200 that appeared nowhere in the OpenAPI spec:

  • /admin — returns database path, Redis host URL, user count, environment label
  • /debug — returns Python installation paths, environment variable count, Flask debug mode status
  • /metrics — full Prometheus-style traffic metrics with per-endpoint request counts

The /admin response is worth quoting directly:

{
  "panel": "Admin Dashboard",
  "db_path": "/var/app/demo.db",
  "redis_host": "redis://localhost:6379",
  "users_total": 4,
  "env": "production"
}
        

These routes weren't in any spec, any document, any threat model. They existed in the code and responded with real infrastructure data. The scanner found them by probing — not by reading spec paths, but by actively testing candidate URLs that Flask frameworks commonly expose.

Shadow endpoints are how attackers discover attack surface that your security review missed entirely. Your threat model was built from the spec. The spec was wrong about what was exposed.

The Login Endpoint's Secret: Flask Secret Key in a 500 Response

The scanner sent a malformed request to POST /api/v2/auth/login and triggered an unhandled exception. Flask's debug mode responded with the full stack trace — plus a JSON error body that included the Flask application's secret key:

{
  "error": "Internal server error",
  "debug_traceback": "Traceback (most recent call last):\n  File \"/app/app.py\"...",
  "flask_env": "development",
  "db_connection": "sqlite:///demo.db",
  "secret_key": "dev-secret-do-not-use-in-prod"
}
        

The Flask secret key is used to sign session cookies. If an attacker has the secret key, they can forge any session cookie in the application. This isn't an API design problem — it's "someone left app.debug = True in production." The scanner found it in 2.4 seconds by doing what any competent penetration tester does on day one: send malformed input to authentication endpoints and look at error responses.

Full Finding Breakdown

# Severity Finding Check Type
1 🔴 Critical Unauthenticated access to /api/v2/admin/users (spec requires admin scope) UNAUTHENTICATED_ACCESS
2 🟠 High Write endpoint PUT /api/v2/users/{userId}/preferences — no auth definition in spec MISSING_AUTH_DEFINITION
3 🟠 High /api/v2/admin/users — sensitive/admin endpoint in public spec SENSITIVE_ENDPOINT_EXPOSURE
4 🟠 High /api/v2/debug/health-check — debug endpoint in public spec SENSITIVE_ENDPOINT_EXPOSURE
5 🟠 High /api/v2/internal/reset-demo — internal endpoint in public spec SENSITIVE_ENDPOINT_EXPOSURE
6 🟡 Medium Dev/test endpoints (/debug/..., /internal/...) in production spec DEV_ENDPOINTS_IN_SPEC
7 🟠 High GET /api/v2/users/{userId} — integer userId, enumerable BOLA risk BOLA_INTEGER_IDS
8 🟡 Medium POST /api/v2/auth/login — Python stack trace + secret_key in 500 body VERBOSE_ERRORS
9 🟠 High Shadow endpoint /admin — 200 OK, not in spec. Returns DB path and Redis host. SHADOW_ENDPOINTS
10 🟠 High Shadow endpoint /debug — 200 OK, not in spec. Returns internal platform config. SHADOW_ENDPOINTS
11 🟠 High Shadow endpoint /metrics — 200 OK, not in spec. Full traffic metrics exposed. SHADOW_ENDPOINTS
12 🟠 High /openapi.json — flagged as shadow endpoint (minor FP — it's the spec URL itself) SHADOW_ENDPOINTS
13 🟡 Medium Method override accepted: X-HTTP-Method-Override: DELETE returns 200 METHOD_OVERRIDE

On finding #12 — the scanner flagged /openapi.json itself as a shadow endpoint because it's not listed as a path in the spec's paths object. Technically accurate. In practice, the spec URL is expected to be accessible. We documented this as a known false positive.

The Honest Gaps: What It Missed

We found 13 issues in 2.4 seconds. We also missed 2 of the 4 planted vulnerabilities directly. Here's exactly why.

❌ V3 — Excessive Data Exposure: Not Detected

We planted this in GET /api/v2/products. The endpoint returns internal business fields that have no business being exposed to API consumers: cost_price, internal_margin, supplier_id, warehouse_stock, internal_sku. These fields aren't in the spec's response schema.

The scanner missed it because detecting excessive data exposure requires runtime response analysis: make an authenticated request, parse the JSON body, compare each returned field name against the spec's defined properties in the response schema, flag undocumented fields.

The current scanner does structural and authentication analysis. It doesn't yet perform schema-aware response diffing. It's a meaningful gap — API3 (excessive data exposure) is a top-5 OWASP API finding in real audits, and it requires actually making requests and inspecting what comes back. The fix isn't technically complex; it's just not built yet.

The AI compensating factor: During the analysis phase, the AI layer can flag any response body containing fields matching patterns like cost_price, margin, internal_*, supplier_* as potential API3 candidates. That's a manual review step the AI analysis can handle — but it requires seeing the actual response, which means someone needs to make the authenticated call first.

❌ V4 — Deprecated API Still Live: Not Detected

The OpenAPI spec marks /api/v1/users as deprecated: true with x-sunset-date: 2025-06-01. It was supposed to be retired 9 months ago. It still returns 200 with real data.

The scanner parsed the spec, saw the deprecated annotation, and… moved on. No check that the endpoint was actually responding.

The fix is simple: for each endpoint with deprecated: true, probe it and flag if it returns 2xx instead of 410 Gone. This is a single additional check that would catch every deprecated-but-live endpoint. It's on the roadmap and should be in the next version.

Deprecated APIs that silently stay live are a widespread problem. They miss security patches. They lack monitoring. Nobody owns them. They accumulate vulnerabilities while the new API gets the attention. OWASP API9 (Improper Inventory Management) exists for exactly this reason.

The Score

2 of 4 planted vulnerabilities found directly. 11 additional real issues found as bonus findings. 1 minor FP (/openapi.json as shadow endpoint). The 2 missed checks are on the roadmap with clear implementation paths.

We found 11/13 issues in 2.4 seconds. The 2 we missed require response body analysis and deprecated-endpoint probing — both straightforward to add.

What This Means for Your API

Three takeaways that apply to any API, not just this demo.

1. Your OpenAPI spec is documentation, not enforcement

The spec said /api/v2/admin/users required admin auth. The server didn't care. This gap — between what the spec documents and what the server enforces — is where the majority of API authentication failures live. If your security review process stops at "the spec says auth is required," you're missing the actual check.

Spec-based scanning closes this gap by treating the spec as a test oracle: "the spec claims X; let's verify X is true." That verification takes 2.4 seconds. The traditional alternative is a penetration tester manually probing each endpoint — which takes days and costs significant budget. If you're building APIs professionally, The Web Application Hacker's Handbook remains the best single resource for understanding how these access control failures are found and exploited.

2. Shadow endpoints are everywhere

None of the three shadow endpoints we found were in any spec, any commit message, any design document, any threat model. They existed in code and responded to requests. Your threat model was built from documentation; your attack surface includes everything that responds to an HTTP request.

Framework-default routes (/metrics, /health, /debug, /admin) are particularly common shadow endpoints in Flask, Django, and Spring Boot applications. A scanner that only reads your spec will miss all of them. A scanner that reads your spec and probes common candidate paths will find them in seconds.

3. Debug mode in production is an instant critical

Flask's app.debug = True leaking a secret key in a 500 response is not a subtle issue. It's an immediate session forgery risk for every user on the platform. It takes one malformed login request to expose it. The scanner found it by doing exactly what any manual tester does: send bad input to auth endpoints, look at the error.

Error handling is one of the most consistently under-tested areas in API security reviews. Teams spend significant time on authentication and authorisation logic; they spend far less time systematically checking what their error responses reveal. Black Hat Python (2nd Edition) covers building custom probes for exactly this kind of verbose error hunting — useful background if you're building your own security automation.

What SecurityClaw does: The swagger-scanner is one of 56+ skills in SecurityClaw's automated penetration testing platform. It reads your OpenAPI/Swagger spec, maps your documented API surface, tests authentication enforcement, probes for shadow endpoints, and flags structural vulnerabilities in seconds — not days. Learn more about SecurityClaw →


Back to all SecurityClaw Demos  |  See also: D2 Nikto (web misconfiguration)  |  D5 Nuclei (cloud/web misconfigs)

Advertisement