CVE-2026-48710: A Maintainer's Perspective

kludex2 pts0 comments

CVE-2026-48710: A Maintainer's Perspective - Marcelo Trylesinski

Skip to content

Initializing search

Kludex/monorepo

Blog

CVE-2026-48710: A Maintainer's Perspective

Last week, I published the security advisory GHSA-86qp-5c8j-p5mr<br>on GitHub, and now it seems the project is being aimed at by a barrage of negative press. I want to take a moment to share my perspective on this, and to share my point of view on the vulnerability, and share a bit about the process itself.

What is the vulnerability?

Routing uses the raw HTTP path, but request.url is reconstructed by concatenating http://{host}{path},<br>where host comes from the Host header. Since the client controls that header, request.url.path can be made<br>to differ from the path the request was actually routed on.

This matters when a middleware uses request.url.path to guard a route:

from starlette.applications import Starlette<br>from starlette.middleware import Middleware<br>from starlette.middleware.base import BaseHTTPMiddleware<br>from starlette.responses import PlainTextResponse<br>from starlette.routing import Route

class AuthMiddleware(BaseHTTPMiddleware):<br>async def dispatch(self, request, call_next):<br>if request.url.path.startswith("/admin"):<br>return PlainTextResponse("Forbidden", status_code=403)<br>return await call_next(request)

async def potato(request):<br>return PlainTextResponse("Secret potato")

app = Starlette(<br>routes=[Route("/admin/potato", potato)],<br>middleware=[Middleware(AuthMiddleware)],

Now send a request to /admin/potato with Host: example.com/?. The router still dispatches to the potato<br>endpoint, because routing uses the raw path. But request.url is reconstructed as http://example.com/?/admin/potato,<br>so request.url.path is /. The middleware's check never fires, and the secret leaks.

You may look at this and say: "uau, that's a pretty bad bug". It can be. But the assumptions matter when you reason<br>about where the vulnerability actually lives:

An application uses path-based authorization in middleware. This is an application pattern built on top of<br>Starlette, not something Starlette does for you.

Routing itself is never fooled. The router dispatches on the raw HTTP path, so the endpoint that runs is always<br>the correct one. The divergence only bites code that re-derives authorization from the reconstructed URL.

There's no CDN, load balancer, API gateway, or fronting web server validating the Host header. Any of those<br>neutralizes the attack by rejecting malformed values.

There's a more fundamental point here, and Giovanni Barillari put it well in the advisory<br>thread: authorization and authentication should not be based on the request's path, host, or query string in the first<br>place. That's a fragile pattern regardless of this bug. Trailing slashes, case sensitivity, percent-encoding, and path<br>normalization all bite the same code in the same way. The Host header is just one more thing that can make the<br>string you matched on differ from the request you actually served.

In short, the vulnerability came from the application pattern and the deployment, never from something Starlette intended .

Why was there even a CVE then?

I think the article OSTIF did explains this well:

This bug is a classic “responsibility gap” where if this maintainer didn’t patch, thousands of exposed projects would have to individually secure their projects. In doing this work, they’ve voluntarily taken on the responsibility to protect the ecosystem from long-term systemic harm. As with all open source projects, they owed us nothing and could have left this to be everyone else’s problem and took the extraordinary steps of helping the ecosystem. Please consider donating to Kludex: https://github.com/sponsors/Kludex

I think this summarizes it well.

On the disclosure process

I want to be fair: the people at X41 D-Sec were mostly polite, apologized when I pushed back, and were<br>clearly acting in good faith. But there are a few things about the process I disagree with.

The first was the initial deadline. The very first message imposed a roughly one-month disclosure window and<br>offered a joint call "in 2 or 5 days". That treats unpaid maintainers like a corporation with an on-call security<br>team. Everyone involved in the internal discussion has a full-time job and maintains these<br>projects in their free time. To their credit, X41 walked the deadline back once this was raised.

The part I find hardest to accept is, near the end, they suggested publishing the advisory before a patch<br>was available. That is horrible practice. A public advisory with no available fix leaves every affected user<br>exposed with nothing to do but wait, while attackers get the exact same information. The whole point of<br>coordinated disclosure is that the fix lands first. Suggesting the reverse, on a package this widely deployed,<br>is the opposite of protecting users.

The other thing that bothered me is that X41 built badhost.org, a branded landing page<br>with a logo, a name, and an Internet-wide scanner. That...

request path from starlette middleware host

Related Articles