fix: harden auth middleware against Starlette BadHost (CVE-2026-48710)#2375
Merged
Conversation
The serverless and dedicated-deployment auth middlewares in http_api.py
gated their public-path allowlist on request.url.path, which vulnerable
Starlette (< 1.0.1) derives from the unvalidated Host header. A request
with a crafted Host (e.g. `Host: x/docs?`) could make request.url.path
read as an allowlisted path while ASGI routed to an authenticated
handler — bypassing API-key auth.
Defense in depth:
- Bump fastapi to a release line that ships patched Starlette and add
an explicit `starlette>=1.0.1` floor.
- Read the path from `request.scope["path"]` (the raw ASGI path,
untouched by Host header) inside both auth middlewares.
Logging/telemetry uses of request.url.path are left as-is; they are
informational and not authorization decisions.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5101e201fa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
FastAPI < 0.133 caps its starlette dependency below 1.0 (e.g. 0.119.x requires `starlette<0.49.0,>=0.40.0`), so `fastapi<0.120` together with the `starlette>=1.0.1` security floor was an unsatisfiable resolver problem and broke CI installs. FastAPI 0.133.0 dropped the starlette upper bound (`starlette>=0.40.0`, no cap), letting Starlette 1.0.1+ resolve cleanly. Verified locally: unit tests pass on both fastapi==0.133.0 + starlette==1.1.0 (the new floor) and fastapi==0.135.4 + starlette==1.1.0. on_event is still deprecated-but-functional in this range. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
grzegorz-roboflow
previously approved these changes
May 27, 2026
…textVar and auth span Addresses reviewer items #1-#3. Test: adds test_dedicated_deployment_auth_middleware_rejects_host_header_path_injection (plus a _build_dedicated_deployment_interface helper) so the check_authorization middleware has symmetric coverage with check_authorization_serverless. Verified under vulnerable Starlette 0.37.2: reverting just the dedicated middleware to request.url.path makes this test fail on the very first injection variant ("Host-injection bypass for header 'testserver/docs?': expected 401, got 200"). Code: two more request.url.path → request.scope["path"] swaps in the same spirit as the auth middlewares: - set_request_path_context middleware: the current_request_path ContextVar flows into ModelManagerBase._model_request_paths and is reported back in model-info responses (base.py:644). Reviewer flagged this as the "leaks into model_load_info / request_model_ids" case. - check_authorization_serverless OTel span: the span records the auth decision itself ("serverless.authorization.check"), so its http.target attribute must not be Host-forgeable. The remaining informational request.url.path call sites (_log_serverless_authorization_denial L479, _log_serverless_request_received L501, the logging at L1120 / L1155) are deliberately left for a follow-up: they're outside the auth middleware and the original remediation scope explicitly excluded "logging usages". Worth a separate sweep that updates the helpers' signatures consistently rather than wedging the change here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Pushed an update based on internal code review:
Re: log-forging coverage in the deferred follow-up — I'll open a tracking issue for items 4-6 once this lands. |
PawelPeczek-Roboflow
approved these changes
May 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Patches CVE-2026-48710 ("BadHost"), a Starlette < 1.0.1 vulnerability where
request.url.pathis derived from the unvalidatedHostheader. A crafted Host (e.g.Host: x/docs?) makesrequest.url.pathread as an allowlisted route while ASGI routes the request to an authenticated handler — bypassing the API-key check.Both API-key auth middlewares in inference/core/interfaces/http/http_api.py gate their public-path allowlist on
request.url.path, so both were affected:Defense in depth:
fastapi>=0.116.0,<0.120plus an explicitstarlette>=1.0.1floor (belt-and-suspenders in case a transitive dep narrows FastAPI's resolution).request.scope["path"](the raw ASGI path, untouched by Host) inside both middlewares. Allowlist contents andstartswithlogic preserved exactly — only the source of the path string changed.Logging/telemetry uses of
request.url.path(lines 479, 501, 580, 795, 1120, 1155) are informational, not authorization decisions, and are intentionally left as-is per the remediation scope.Test plan
Added
test_serverless_auth_middleware_rejects_host_header_path_injectionwhich sends six Host-injection variants (testserver/docs?,testserver?/docs,testserver/healthz?,testserver/_next/x,testserver/static/x,testserver#/docs) to a protected endpoint and asserts each returns 401, not 200.scope["path"]defense holds independent of the Starlette version.tests/inference/unit_tests/core/interfaces/http/test_http_api.pypass. The 5 pre-existing failures intest_run_uvicorn_script.py(test-pollution when run together withtest_http_api.py) are present onmainand unrelated to this change.Follow-ups (deliberately out of scope)
TrustedHostMiddleware— needs per-service decision once legitimate Host variance is enumerated across GCP Serverless and Dedicated Deployment.References: badhost.org, CVE-2026-48710, OSTIF disclosure.
🤖 Generated with Claude Code