Skip to main content

FastAPI scanner

shadowaudit's FastAPI scanner uses regex-based decorator pattern matching to extract route definitions from .py files.

How it works

The scanner reads each .py file line-by-line and matches decorator patterns like @app.get('/path') and @router.post('/path'). No Python runtime is required — shadowaudit reads the source as text.

Detected patterns

Pattern 1 — Direct app decorators

from fastapi import FastAPI

app = FastAPI()

@app.get('/users')
async def get_users():
return []

@app.post('/users')
async def create_user():
return {}

Pattern 2 — APIRouter decorators

from fastapi import APIRouter

router = APIRouter()

@router.get('/products')
async def list_products():
pass

@router.delete('/products/{id}')
async def delete_product(id: int):
pass

Pattern 3 — Path parameters

FastAPI uses {param} syntax (same as OpenAPI) — stored as-is:

@app.get('/users/{user_id}/posts/{post_id}')
async def get_user_post(user_id: int, post_id: int):
pass
# → GET /users/{user_id}/posts/{post_id}

Example scan output

shadowaudit --dir ./src --spec openapi.json --framework fastapi
[INFO] Running FastAPI route scanner...
[✓] Found 7 FastAPI routes in ./src
[INFO] GET /health → main.py:13 [no auth]
[INFO] GET /api/users → main.py:21 [auth]
[INFO] POST /api/users → main.py:26 [auth]
[INFO] GET /api/debug/test-data → main.py:38 [no auth]

Auth detection

shadowaudit detects FastAPI auth by scanning the decorator line and up to 10 lines below (the function signature where Depends() appears):

@router.get('/admin/users')
async def get_admin_users(
current_user: User = Depends(get_current_user) # ← auth detected
):
pass

Also detects inline dependencies:

@router.get('/secure', dependencies=[Depends(verify_token)])
async def secure_endpoint():
pass

Hardcoded FastAPI auth patterns

Depends( Security( oauth2_scheme get_current_user
verify_token HTTPBearer HTTPBasic APIKeyHeader
APIKeyQuery require_auth login_required jwt_required
current_user

Known limitations

  • include_router prefixesapp.include_router(router, prefix='/api/v1') prefixes are detected but not yet applied to routes (tracked in issue #1)
  • @app.api_route() multi-method decorator — not yet supported (regex only matches single-method decorators)
  • APIRouter(prefix='/api/v1') — constructor prefix not yet reconciled