GHSA-pf96-p4fj-6566 is a medium-severity (CVSS 5.6) CWE-444 vulnerability in httpx2. O3 Security confirms whether GHSA-pf96-p4fj-6566 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.
HTTPX2: Conflicting Content-Length and Transfer-Encoding headers can be auto-generated
Exploitation Status
No confirmed exploitation observed yet
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-pf96-p4fj-6566.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
GHSA-pf96-p4fj-6566 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 370,894 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
httpx2Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
HTTPX2 can automatically add a Content-Length header to a request that already contains a caller-supplied Transfer-Encoding header. The resulting HTTP/1.1 request contains both framing headers, which can create an ambiguous message boundary and enable request smuggling or connection desynchronization when processed by intermediaries that disagree about which header takes precedence.
Details
When a request body has a known size, HTTPX2's content encoder returns a default Content-Length. Request._prepare() applies each default header with setdefault(), which only checks whether that same header is already present. It does not check whether the mutually exclusive Transfer-Encoding header is present.
For example:
import httpx2
request = httpx2.Request(
"POST",
"http://example.com/",
headers={"Transfer-Encoding": "chunked"},
content=b"test 123",
)
print(request.headers)
The request contains both:
Transfer-Encoding: chunked
Content-Length: 8
On an HTTP/1.1 connection, the body is serialized using chunked transfer coding while both headers are sent on the wire. This violates HTTP message-framing requirements. Fixed-size byte, JSON, form, and known-length multipart bodies can reach the affected path.
Streaming bodies with an explicit Content-Length are not affected in current HTTPX2 releases because the automatically generated Transfer-Encoding is already suppressed in that direction.
Impact
An attacker may be able to use the conflicting framing headers as a request-smuggling or desynchronization primitive. Exploitation requires an application to pass attacker-controlled request framing headers and associated body data to HTTPX2, use HTTP/1.1, and communicate through a proxy or origin that accepts conflicting headers and interprets them differently from another hop.
Depending on the downstream infrastructure, successful exploitation could interfere with requests sharing a persistent connection, bypass front-end routing or authorization decisions, or poison responses or caches. Applications that do not forward attacker-controlled Transfer-Encoding headers are not directly exposed.
Mitigation
Upgrade to HTTPX2 2.11.0 or later. Patched versions treat Content-Length and Transfer-Encoding as mutually exclusive when applying automatically generated request headers.
If upgrading is not immediately possible, remove Transfer-Encoding and other hop-by-hop framing headers from untrusted input before constructing outbound requests. Applications acting as proxies should derive outbound framing from the body rather than forwarding inbound Content-Length or Transfer-Encoding headers.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | httpx2 | all versions | 2.11.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for httpx2. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.
Fix
Update httpx2 to 2.11.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-pf96-p4fj-6566 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 pinpoints whether GHSA-pf96-p4fj-6566 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.
Tailored to GHSA-pf96-p4fj-6566. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is GHSA-pf96-p4fj-6566 in your dependencies?
O3 detects GHSA-pf96-p4fj-6566 across PyPI dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.