
CISA added seven CVEs to the Known Exploited Vulnerabilities catalog on September 2, 2026, in catalog version 2026.09.02. Four of the seven are failures of authentication or authorization. Two are titled that way — "BerriAI LiteLLM Improper Authentication Vulnerability" and "JFrog Artifactory Improper Authentication Vulnerability." The other two are not. CVE-2026-48710 in Starlette is listed as "HTTP Request/Response Smuggling," its lone CWE the 444 that goes with that name, and CVE-2026-49869 in Kestra OSS as "OS Command Injection." Both titles name what an attacker achieved rather than the defect that let them. The defect is recoverable if you read on — CISA's description of the Starlette bug does reach the words "authentication bypass," and Kestra's CWE list does include 287 — but the title is the field that gets grepped, pasted into a ticket and rendered on a dashboard.
The two defects
Starlette rebuilds request.url from the HTTP Host header. Routing does not — it matches on the raw path in the ASGI scope. Before version 1.0.1 the header was not validated, so a Host value containing / or ? made request.url.path disagree with the path the router actually served. X41 D-Sec found it on January 27, 2026 during an unrelated source-code audit. The fix, published May 21, validates the header against RFC 9112 §3.2 and falls back to scope["server"] when it is malformed.
That leaves a fork inside code you wrote:
# bypassable — path reconstructed from an attacker-controlled header
if request.url.path.startswith("/admin"):
require_auth(request)
# not bypassable — the path the router actually matched
if request.scope["path"].startswith("/admin"):
require_auth(request)
Same Starlette version, same dependency tree, opposite answers. NVD scores the CVE 6.5 with confidentiality and integrity impact both rated Low — the base score conceding it cannot know what sits on top of the framework. In the exploited case what sat on top was LiteLLM. CISA's own note says CVE-2026-48710 "could be chaned with CVE-2026-42271," a LiteLLM command injection gated behind nothing but a valid API key, in KEV since June 8.
Kestra is the same class with no dependency involved. Its AuthenticationFilter exempted the public config endpoint from Basic Auth using request.getPath().endsWith("/configs") — a suffix match where an exact match belonged. Kestra addresses resources by path segments the caller chooses, so an anonymous caller picks configs as the last one. The advisory lists PUT /api/v1/{tenant}/namespaces/{namespace}/kv/configs, which writes secrets, and DELETE /api/v1/{tenant}/logs/{namespace}/configs, which destroys the log you would investigate with. The flow-create and execution-trigger routes inherit the bypass, and the shell and Python plugins shipped enabled turn that into root inside the worker container. CVSS 10.0, one string method. Fixed in 1.0.45 and 1.3.21.

The practice: resolve reachability against your own code before you triage on the title
An SCA tool reports the version in your lockfile. For Starlette it cannot report anything else, because the answer is not in the dependency tree — it is in which of two attributes your middleware reads. A team that routes the ticket from the KEV title files a request-smuggling ticket with whoever owns the reverse proxy, and the middleware is never opened.
So add one step between the alert and the ticket: read the advisory text for a conditional about caller code, and when it contains one, turn it into a grep. Starlette's is explicit — "middleware and endpoints that apply security restrictions based on request.url (rather than the raw scope path) could therefore be bypassed." That sentence names the grep. This is the same move as resolving a vendor precondition clause against your running configuration before you look at severity, except the configuration that decides it here is your own source.
The cost is roughly a person-hour per advisory, and it does not scale to a whole dependency tree, so scope it. Only advisories that make exploitability conditional on how the caller uses the library earn the hour; most state their impact unconditionally and go straight into the normal patch cycle. None of this defers the upgrade. Getting to Starlette 1.0.1 is cheaper than the analysis; the grep decides something else — whether you also need to look for evidence someone already used it, and how hard to argue for an out-of-cycle window.
The two deadlines showed the same reasoning applied above you. Starlette drew September 16, Kestra September 5 — three days, now past. BOD 26-04 sets that interval from exposure, KEV status, automatability and technical impact, not from a severity score, and Kestra's unauthenticated path to root scores worse on every one of them.
What to check this week
grep -rn "request\.url\.path" --include=*.pyacross your FastAPI and Starlette services, and read every hit that feeds an allow or deny decision. Those are the ones that needrequest.scope["path"]. Hits that only build a log line or a redirect are fine.- Grep your authentication filters and middleware for
endsWith,startsWith,endswith,startswithandin path. Any authorization allowlist matched by substring rather than against the routed path is the Kestra defect wearing a different language. - Confirm Starlette is at 1.0.1 or later, including where it arrives transitively under FastAPI, and Kestra at 1.0.45 or 1.3.21.
- If a Kestra instance was reachable and unpatched, treat flows and KV as having been writable: inventory flows nobody on your team authored, and note the KEV entry marks this one as requiring forensic triage, with the audit log among the deletable endpoints.
- Check whether your SCA output carries a reachability verdict at all, or only a version comparison. If it is only the version, the reachability step is yours and needs an owner.
North InfoSec runs AI-assisted penetration testing and security assessments, including source-level review of the authorization paths described above. northinfosec.com