Sensitive Data Exposure

How to Fix Sensitive Data Exposure in FastAPI [March 2026] [CVE-2026-2975]

[Updated June 2026] Updated CVE-2026-2975

Overview

The two CVEs describe information disclosure flaws in FastApiAdmin up to version 2.2.0. CVE-2026-2975 targets the custom documentation endpoint by mishandling the reset_api_docs function in /backend/app/plugin/init_app.py, enabling an attacker to disclose internal docs or config content via crafted input. CVE-2026-2976 targets the download endpoint in /backend/app/api/v1/module_common/file/controller.py (download_controller), where a user-supplied file_path parameter allows remote attackers to fetch arbitrary files. Both flaws enable remote exploitation and rely on weak input validation, highlighting how sensitive data can be leaked through misconfigured admin utilities in a FastAPI ecosystem. These issues correspond to CWE-200 ( Information Exposure ) and CWE-284 (Improper Access Control) with an additional CWE-434 (Unrestricted Upload or Download of Files) tendency in the second case. The remediation requires strict input validation, path normalization, and constraining file access to a safe, controlled directory, plus upgrading to patches where provided by the vendor.

Affected Versions

FastApiAdmin <= 2.2.0

Code Fix Example

FastAPI API Security Remediation
VULNERABLE PATTERN AND FIX (side-by-side)

# Vulnerable pattern demonstrating both flaws (CVE-2026-2975 and CVE-2026-2976)
from fastapi import FastAPI, HTTPException
from fastapi.responses import FileResponse
import os

app = FastAPI()

# CVE-2026-2976: vulnerable download endpoint (directly exposes user-supplied path)
@app.get("/download")
def download_controller(file_path: str):
    # Vulnerable: uses unvalidated user input to serve a file
    return FileResponse(file_path)

# CVE-2026-2975: vulnerable docs reset endpoint (reads arbitrary file)
@app.post("/admin/reset_docs")
async def reset_api_docs(path: str = None):
    if path:
        with open(path, 'r', encoding='utf-8') as f:
            return {"docs_content": f.read()}
    return {"docs": "default"}

# FIXED PATTERN: constrain access to safe directories only
BASE_DOCS_DIR = "/app/docs"
BASE_DOWNLOAD_DIR = "/app/downloads"
base_docs = os.path.realpath(BASE_DOCS_DIR)
base_downloads = os.path.realpath(BASE_DOWNLOAD_DIR)

# Safe download
@app.get("/safe_download")
def safe_download_controller(file_path: str):
    target = os.path.realpath(os.path.join(base_downloads, file_path))
    if not target.startswith(base_downloads) or not os.path.isfile(target):
        raise HTTPException(status_code=404, detail="Not found")
    return FileResponse(target)

# Safe reset docs
@app.post("/admin/safe_reset_docs")
async def safe_reset_api_docs(path: str = None):
    if path:
        requested = os.path.realpath(os.path.join(base_docs, path))
        if not requested.startswith(base_docs) or not os.path.exists(requested) or not os.path.isfile(requested):
            raise HTTPException(status_code=400, detail="Invalid path")
        with open(requested, 'r', encoding='utf-8') as f:
            return {"docs_content": f.read()}
    return {"docs": "default"}

CVE References

Choose which optional cookies to allow. You can change this any time.