fastapi/fastapi
As a library, the dangerous surfaces are file IO (the new frontend static server), auth scheme edge cases, and the streaming/exit-stack machinery where resource teardown is subtle. The frontend server is the newest and widest-blast-radius addition: it reads arbitrary files from a user-supplied directory on every unmatched request. The included-router rework is the deepest structural change and introduces a per-request handler rebuild plus a ContextVar hand-off that the code itself flags as temporary. Two caches — `_endpoint_context_cache` and the `lru_cache`d callable classifiers — are keyed by object identity, which is correct only as long as the keyed objects outlive the cache. Repo-level supply-chain risk is actively managed: all Actions are SHA-pinned, `permissions: {}` is the default, `persist-credentials: false` on every checkout, and zizmor lints the workflows.
- `_FrontendStaticFiles` serves files from a user-supplied directory on every request that no API route matched; it mitigates traversal by normalizing the path (`os.path.normpath(os.path.join(*path.split("/")))`), delegating to Starlette's `lookup_path`, and passing `follow_symlink=False`, but the blast radius of a bug here is arbitrary file disclosure.
- `_lookup_path` maps a `PermissionError` on the filesystem to an HTTP 401 response, which conflates a server-side filesystem permission problem with a client authentication failure and can leak the existence of unreadable paths.
- `APIRoute.handle` rebuilds the entire request handler — `request_response(self.get_route_handler())`, which re-runs `get_request_handler` — on every single request whenever an `_EffectiveRouteContext` is present, i.e. for all routes reached through `include_router`.
- The effective-route context is passed from `handle()` to `get_route_handler()` through a module-level ContextVar rather than an argument, and the code carries a `TODO: Replace or deprecate this no-scope hook` — any subclass overriding `get_route_handler()` interacts with this implicitly.
- `_endpoint_context_cache` is an unbounded module-level `dict[int, EndpointContext]` keyed by `id(func)`; entries are never evicted, so a process that creates endpoint callables dynamically grows the dict without bound and could in principle collide on a recycled `id`.
- The callable classifiers `_is_gen_callable`/`_is_async_gen_callable`/`_is_coroutine_callable` use `lru_cache(maxsize=4096)` keyed by a `_CallIdentity` whose `__hash__` is `id(self.call)`, so the cache holds a strong reference to every classified callable and identity reuse after GC is the correctness assumption.
- `HTTPBasic.__call__` base64-decodes the Authorization header and splits on `:`; it returns the raw username and password with no timing-safe comparison anywhere in the framework — constant-time credential checking is left entirely to the application.
- Every security scheme has an `auto_error=False` mode that returns `None` instead of raising 401; a dependency written against that mode that forgets to check for `None` silently authenticates everyone.
- The default Swagger UI and ReDoc pages load JavaScript and CSS from `cdn.jsdelivr.net` (and Google Fonts for ReDoc) with no SRI hashes, so `/docs` and `/redoc` execute third-party CDN code by default in every FastAPI app.
- Swagger UI parameter injection is defended by `_html_safe_json`, which escapes `<`, `>` and `&` before interpolating user-supplied `swagger_ui_parameters` and `init_oauth` into a `<script>` block — but `oauth2_redirect_url` is interpolated into the same script with no escaping.
- `_extract_endpoint_context` calls `inspect.getsourcefile` and `getsourcelines`, embedding absolute server file paths into `ValidationException.__str__`; these appear in logs and in `ResponseValidationError` tracebacks but not in the 422 JSON body, which only serializes `exc.errors()`.
- `request_response` detects a swallowed exception in a `yield` dependency only after the fact, by checking a `response_awaited` flag and raising a `FastAPIError` with a docs link — a heuristic guard rather than a structural fix.
- `scripts/translate.py` sends documentation content to OpenAI through `pydantic_ai.Agent("openai-chat:gpt-5.5")`, and `.github/workflows/translate.yml` supplies `OPENAI_API_KEY` from repo secrets with two `zizmor: ignore[secrets-outside-env]` suppressions.
- Two workflows use elevated triggers with explicit zizmor suppressions: `guard-dependencies.yml` runs on `pull_request_target` (mitigated by never checking out PR code and only reading `context.payload`), and `deploy-docs.yml`/`smokeshow.yml` run on `workflow_run`.
- Legacy/deprecated code still present: `openapi_prefix` (warns and aliases to `root_path`), `on_event`, `_DefaultLifespan` re-vendored after Starlette deleted it, and the deprecated `ORJSONResponse`/`UJSONResponse` pair kept only for compatibility.