Overview
CVE-2026-2975 describes a security flaw impacting FastApiAdmin up to version 2.2.0, specifically the Custom Documentation Endpoint and its reset_api_docs function. An unauthenticated remote actor could trigger information disclosure by invoking the endpoint that builds and returns a full view of the application's registered routes and related metadata. The exposed data includes route paths, HTTP methods, and handler names, enabling an attacker to map the API surface and identify potential targets. This vulnerability is tied to the information disclosure category (CWE-200) and can also be described as an authorization leakage (CWE-284) where object-level details are exposed without proper access control.
Affected Versions
FastApiAdmin <= 2.2.0 (Custom Documentation Endpoint: reset_api_docs)
Code Fix Example
FastAPI API Security Remediation
from fastapi import FastAPI, APIRoute, HTTPException, Depends, Header
app = FastAPI()
# Example public endpoint to illustrate the surface
@app.get("/public")
async def public_endpoint():
return {"ok": True}
# ---------------------- Vulnerable pattern ----------------------
def collect_docs_vulnerable():
docs = {}
for route in app.routes:
if isinstance(route, APIRoute):
docs[route.path] = {
"methods": list(route.methods),
"endpoint": getattr(route.endpoint, "__name__", str(route.endpoint)),
"dependencies": getattr(route, "dependant", None)
}
return docs
@app.get("/vuln/reset_api_docs")
async def vuln_reset_api_docs():
# Exposes all route metadata without any authorization checks
return collect_docs_vulnerable()
# ---------------------- Fixed pattern ----------------------
def get_current_user_role(x_role: str = Header(None)) -> str:
# Require an explicit admin role to access docs
if x_role != "admin":
raise HTTPException(status_code=403, detail="Forbidden")
return x_role
def collect_docs_secure():
docs = {}
for route in app.routes:
if isinstance(route, APIRoute):
# Exclude internal/admin endpoints from docs to avoid leakage
if route.path.startswith("/internal") or route.path.startswith("/admin"):
continue
docs[route.path] = {
"methods": list(route.methods),
"endpoint": getattr(route.endpoint, "__name__", str(route.endpoint))
}
return docs
@app.get("/secure/reset_api_docs")
async def secure_reset_api_docs(role: str = Depends(get_current_user_role)):
return collect_docs_secure()