Summary
Netty's HTTP/2 and HTTP/3 HTTP-object conversion paths collapse Extended CONNECT requests into ordinary HTTP/1.1 CONNECT requests.
An Extended CONNECT request such as:
:method: CONNECT
:protocol: websocket
:scheme: https
:path: /admin/ws
:authority: ws.example:443
is converted to an HTTP/1.1 object shaped like:
CONNECT ws.example:443 HTTP/1.1
host: ws.example:443
x-http2-scheme: https
or, for HTTP/3:
CONNECT ws.example:443 HTTP/1.1
host: ws.example:443
x-http3-scheme: https
The converted request no longer carries the :protocol value or the Extended-CONNECT :path. A downstream handler using Netty's HTTP-object API therefore cannot distinguish this request from a regular CONNECT tunnel to ws.example:443.
That is a semantic security boundary. RFC 8441 defines Extended CONNECT as a different CONNECT mode: the :protocol pseudo-header selects the protocol for the stream, :path remains part of the target URI, and the server must not treat :authority as the host to tunnel to in the same way it would for a regular CONNECT request. RFC 9220 applies the same pseudo-header and setting semantics to HTTP/3.
Technical Details
HTTP/2:
codec-http2/.../HttpConversionUtil.java:272-280 uses :authority as the HTTP/1 request URI for every CONNECT request.
codec-http2/.../HttpConversionUtil.java:328-337 creates a DefaultHttpRequest with that URI, then translates headers.
codec-http2/.../HttpConversionUtil.java:806-813 maps :authority to Host and :scheme to x-http2-scheme, but does not preserve request :path; unrecognized pseudo-headers such as :protocol are dropped.
HTTP/3:
codec-http3/.../Http3HeadersSink.java:79-94 recognizes Extended CONNECT and requires :method, :scheme, :authority, :path, and :protocol.
codec-http3/.../HttpConversionUtil.java:201-209 uses :authority as the HTTP/1 request URI for every CONNECT request.
codec-http3/.../HttpConversionUtil.java:259-268 creates a DefaultHttpRequest with that URI, then translates headers.
codec-http3/.../HttpConversionUtil.java:578-585 maps :authority to Host and :scheme to x-http3-scheme, but does not preserve request :path; :protocol is dropped as a pseudo-header.
The resulting object-level shape is security-relevant because applications and gateway integrations commonly authorize CONNECT differently from WebSocket, WebTransport, MASQUE, or other Extended CONNECT protocols. With Netty's conversion path, a policy that permits regular CONNECT to an allowlisted authority can be applied to an Extended CONNECT request whose actual protocol and path were erased before application code sees it.
Impact
A remote HTTP/2 or HTTP/3 client can send an Extended CONNECT request that Netty presents to downstream HTTP-object handlers as a regular CONNECT tunnel.
In gateway, proxy, application-server, or protocol-bridging deployments that use Netty's HTTP-object conversion path, this can bypass routing or authorization logic that is meant to distinguish:
- regular CONNECT tunnels from WebSocket/WebTransport/MASQUE-style Extended CONNECT streams;
- allowed CONNECT authorities from protected Extended-CONNECT paths;
- enabled/registered Extended CONNECT protocols from ordinary CONNECT traffic.
This report does not claim memory corruption, code execution, confidentiality impact, or impact on applications that never route HTTP/2 or HTTP/3 requests through Netty's HTTP-object adapters. The demonstrated impact is integrity loss through protocol-state confusion before application policy runs.
Suggested Fix
Recommended fix direction:
- do not convert Extended CONNECT into an ordinary HTTP/1
CONNECT object without preserving the Extended-CONNECT state;
- either reject Extended CONNECT in
HttpConversionUtil.toHttpRequest(...) unless Netty can expose the state safely, or preserve it with explicit extension headers such as x-http2-protocol / x-http3-protocol and x-http2-path / x-http3-path for request conversion;
- keep regular CONNECT behavior unchanged;
- add regression tests covering direct conversion and the frame-to-object adapters for both HTTP/2 and HTTP/3;
- add controls proving regular CONNECT and Extended CONNECT no longer produce the same HTTP-object shape.
If maintainers prefer not to support Extended CONNECT through the HTTP-object API, the safest fix is to fail closed and require users to handle it at the native HTTP/2 or HTTP/3 frame/header layer.
References
Affected Products
io.netty:netty-codec-http2
io.netty:netty-codec-http3
Confirmed affected:
- current
4.2 branch at 7bae566a93e69409697fe57fa807910ba5c9720e
netty-4.2.15.Final at a41f7b289ce1d697c50846f3ade3983e22b2ed40
netty-4.2.2.Final at 660edeaefa for HTTP/2
netty-4.2.0.Final at 09e64d259c99 for HTTP/2
netty-4.1.135.Final at f05f765d81460799c53123a207f665bf3b465171 for HTTP/2
Suggested affected ranges:
io.netty:netty-codec-http2 >= 4.1.64.Final, <= 4.1.135.Final
io.netty:netty-codec-http2 >= 4.2.0.Final, <= 4.2.15.Final
io.netty:netty-codec-http3 >= 4.2.8.Final, <= 4.2.15.Final
The HTTP/2 lower bound is the first 4.1 tag containing Netty's Http2Headers.PseudoHeaderName.PROTOCOL support. The HTTP/3 lower bound is the first 4.2 tag containing Netty's HTTP/3 :protocol support.
CWE and CVSS
Suggested CWEs:
CWE-20: Improper Input Validation
CWE-436: Interpretation Conflict
CWE-444: Inconsistent Interpretation of HTTP Requests
Suggested CVSS v3.1:
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
Suggested severity: High, score 7.5.
Rationale: a network peer can trigger the issue without authentication or user interaction, and the failure occurs before downstream policy receives the request. The claimed impact is integrity because the converted HTTP object can cause authorization or routing policy to evaluate the wrong protocol state. Medium may also be defensible if maintainers scope HTTP-object Extended CONNECT handling as an opt-in compatibility feature rather than a default security boundary.
Duplicate Boundary
Live Netty private-advisory research found adjacent authority-family reports, but no report covering Extended CONNECT :protocol / :path erasure:
GHSA-w424-c27v-r9mr: HTTP/2 :authority userinfo forwarded as Host.
GHSA-xf5f-3m33-p8m3: HTTP/2 :authority plus Host duplicate downgrade.
GHSA-w2m9-rvg6-gfxg: HTTP/3 :authority userinfo forwarded as Host.
GHSA-qf63-3vvr-7272: HTTP/3 :authority plus Host duplicate downgrade.
GHSA-h235-6gmg-jv93: HTTP/3 absolute-form :path authority confusion.
This report is distinct because the root cause is not malformed authority text or conflicting Host values. The root cause is that a valid Extended CONNECT state is normalized into a regular CONNECT state by dropping :protocol and the Extended-CONNECT path.
Local PoVs
PoV files:
pov/Http2ExtendedConnectDowngradePovTest.java
pov/Http3ExtendedConnectDowngradePovTest.java
Run from a Netty checkout after copying each PoV to the matching test package:
./mvnw -q -pl codec-http2 \
-Dtest=Http2ExtendedConnectDowngradePovTest \
-Dsurefire.failIfNoSpecifiedTests=false \
-DskipNativeTests -DskipAutobahnTests -Dmaven.antrun.skip=true test
./mvnw -q -pl codec-http3 \
-Dtest=Http3ExtendedConnectDowngradePovTest \
-Dsurefire.failIfNoSpecifiedTests=false \
-DskipNativeTests -DskipAutobahnTests -Dmaven.antrun.skip=true test
The PoVs are local-only. They do not contact any third-party host.
Each PoV proves:
- an Extended CONNECT request with
:protocol=websocket, :path=/admin/ws, and :authority=ws.example:443 becomes CONNECT ws.example:443;
- Host is also
ws.example:443;
- the converted HTTP object lacks both the Extended-CONNECT path and protocol;
- a regular-CONNECT allowlist sees the converted object as acceptable;
- a protocol-aware policy using the original pseudo-headers would reject the same request.
Observed results:
codec-http2 current 4.2 head: Tests run: 2, Failures: 0, Errors: 0
codec-http2 4.2.15.Final: Tests run: 2, Failures: 0, Errors: 0
codec-http2 4.2.2.Final: Tests run: 2, Failures: 0, Errors: 0
codec-http2 4.2.0.Final: Tests run: 2, Failures: 0, Errors: 0
codec-http2 4.1.135.Final: Tests run: 2, Failures: 0, Errors: 0
codec-http3 current 4.2 head: Tests run: 2, Failures: 0, Errors: 0
codec-http3 4.2.15.Final: Tests run: 2, Failures: 0, Errors: 0
codec-http3 4.2.2.Final: not applicable; HTTP/3 :protocol support absent
Patched-control evidence is included in:
evidence/minimal-fail-closed-patched-control.diff
pov/Http2ExtendedConnectFixedControlTest.java
pov/Http3ExtendedConnectFixedControlTest.java
evidence/patched-control-results.txt
The patched control uses a minimal fail-closed guard: if a CONNECT request contains :protocol, the HTTP/1 conversion path rejects it instead of creating an ordinary CONNECT object. Regular CONNECT remains accepted. Both fixed-control tests passed on current 4.2 head:
codec-http2 patched control: Tests run: 2, Failures: 0, Errors: 0
codec-http3 patched control: Tests run: 2, Failures: 0, Errors: 0
Why This Is Not Intended Safe Behavior
This is not merely an application trust-model issue. Netty exposes HTTP/2 and HTTP/3 adapters that convert protocol-native request metadata into the long-standing HTTP-object API. Those adapters already preserve selected protocol metadata in extension headers such as x-http2-scheme, x-http3-scheme, stream ids, and pushed-stream ids. For Extended CONNECT, the adapter silently discards the metadata that defines the security-sensitive difference between regular CONNECT and Extended CONNECT.
RFC 8441 explicitly changes CONNECT semantics when :protocol is present: the server is not supposed to create a normal tunnel to the host indicated by :authority. RFC 9220 says HTTP/3 uses the same pseudo-header and setting semantics. Netty's conversion does the opposite at the object layer by producing an ordinary CONNECT request-target equal to :authority.
The control case confirms regular CONNECT itself is not the issue. A regular CONNECT request without :protocol and :path produces the same HTTP/1 object shape that the Extended CONNECT request produces. The bug is that these two different protocol states become indistinguishable after conversion.
Summary
Netty's HTTP/2 and HTTP/3 HTTP-object conversion paths collapse Extended CONNECT requests into ordinary HTTP/1.1
CONNECTrequests.An Extended CONNECT request such as:
is converted to an HTTP/1.1 object shaped like:
or, for HTTP/3:
The converted request no longer carries the
:protocolvalue or the Extended-CONNECT:path. A downstream handler using Netty's HTTP-object API therefore cannot distinguish this request from a regular CONNECT tunnel tows.example:443.That is a semantic security boundary. RFC 8441 defines Extended CONNECT as a different CONNECT mode: the
:protocolpseudo-header selects the protocol for the stream,:pathremains part of the target URI, and the server must not treat:authorityas the host to tunnel to in the same way it would for a regular CONNECT request. RFC 9220 applies the same pseudo-header and setting semantics to HTTP/3.Technical Details
HTTP/2:
codec-http2/.../HttpConversionUtil.java:272-280uses:authorityas the HTTP/1 request URI for everyCONNECTrequest.codec-http2/.../HttpConversionUtil.java:328-337creates aDefaultHttpRequestwith that URI, then translates headers.codec-http2/.../HttpConversionUtil.java:806-813maps:authorityto Host and:schemetox-http2-scheme, but does not preserve request:path; unrecognized pseudo-headers such as:protocolare dropped.HTTP/3:
codec-http3/.../Http3HeadersSink.java:79-94recognizes Extended CONNECT and requires:method,:scheme,:authority,:path, and:protocol.codec-http3/.../HttpConversionUtil.java:201-209uses:authorityas the HTTP/1 request URI for everyCONNECTrequest.codec-http3/.../HttpConversionUtil.java:259-268creates aDefaultHttpRequestwith that URI, then translates headers.codec-http3/.../HttpConversionUtil.java:578-585maps:authorityto Host and:schemetox-http3-scheme, but does not preserve request:path;:protocolis dropped as a pseudo-header.The resulting object-level shape is security-relevant because applications and gateway integrations commonly authorize CONNECT differently from WebSocket, WebTransport, MASQUE, or other Extended CONNECT protocols. With Netty's conversion path, a policy that permits regular CONNECT to an allowlisted authority can be applied to an Extended CONNECT request whose actual protocol and path were erased before application code sees it.
Impact
A remote HTTP/2 or HTTP/3 client can send an Extended CONNECT request that Netty presents to downstream HTTP-object handlers as a regular CONNECT tunnel.
In gateway, proxy, application-server, or protocol-bridging deployments that use Netty's HTTP-object conversion path, this can bypass routing or authorization logic that is meant to distinguish:
This report does not claim memory corruption, code execution, confidentiality impact, or impact on applications that never route HTTP/2 or HTTP/3 requests through Netty's HTTP-object adapters. The demonstrated impact is integrity loss through protocol-state confusion before application policy runs.
Suggested Fix
Recommended fix direction:
CONNECTobject without preserving the Extended-CONNECT state;HttpConversionUtil.toHttpRequest(...)unless Netty can expose the state safely, or preserve it with explicit extension headers such asx-http2-protocol/x-http3-protocolandx-http2-path/x-http3-pathfor request conversion;If maintainers prefer not to support Extended CONNECT through the HTTP-object API, the safest fix is to fail closed and require users to handle it at the native HTTP/2 or HTTP/3 frame/header layer.
References
Affected Products
io.netty:netty-codec-http2io.netty:netty-codec-http3Confirmed affected:
4.2branch at7bae566a93e69409697fe57fa807910ba5c9720enetty-4.2.15.Finalata41f7b289ce1d697c50846f3ade3983e22b2ed40netty-4.2.2.Finalat660edeaefafor HTTP/2netty-4.2.0.Finalat09e64d259c99for HTTP/2netty-4.1.135.Finalatf05f765d81460799c53123a207f665bf3b465171for HTTP/2Suggested affected ranges:
io.netty:netty-codec-http2 >= 4.1.64.Final, <= 4.1.135.Finalio.netty:netty-codec-http2 >= 4.2.0.Final, <= 4.2.15.Finalio.netty:netty-codec-http3 >= 4.2.8.Final, <= 4.2.15.FinalThe HTTP/2 lower bound is the first
4.1tag containing Netty'sHttp2Headers.PseudoHeaderName.PROTOCOLsupport. The HTTP/3 lower bound is the first4.2tag containing Netty's HTTP/3:protocolsupport.CWE and CVSS
Suggested CWEs:
CWE-20: Improper Input ValidationCWE-436: Interpretation ConflictCWE-444: Inconsistent Interpretation of HTTP RequestsSuggested CVSS v3.1:
Suggested severity: High, score 7.5.
Rationale: a network peer can trigger the issue without authentication or user interaction, and the failure occurs before downstream policy receives the request. The claimed impact is integrity because the converted HTTP object can cause authorization or routing policy to evaluate the wrong protocol state. Medium may also be defensible if maintainers scope HTTP-object Extended CONNECT handling as an opt-in compatibility feature rather than a default security boundary.
Duplicate Boundary
Live Netty private-advisory research found adjacent authority-family reports, but no report covering Extended CONNECT
:protocol/:patherasure:GHSA-w424-c27v-r9mr: HTTP/2:authorityuserinfo forwarded as Host.GHSA-xf5f-3m33-p8m3: HTTP/2:authorityplus Host duplicate downgrade.GHSA-w2m9-rvg6-gfxg: HTTP/3:authorityuserinfo forwarded as Host.GHSA-qf63-3vvr-7272: HTTP/3:authorityplus Host duplicate downgrade.GHSA-h235-6gmg-jv93: HTTP/3 absolute-form:pathauthority confusion.This report is distinct because the root cause is not malformed authority text or conflicting Host values. The root cause is that a valid Extended CONNECT state is normalized into a regular CONNECT state by dropping
:protocoland the Extended-CONNECT path.Local PoVs
PoV files:
pov/Http2ExtendedConnectDowngradePovTest.javapov/Http3ExtendedConnectDowngradePovTest.javaRun from a Netty checkout after copying each PoV to the matching test package:
The PoVs are local-only. They do not contact any third-party host.
Each PoV proves:
:protocol=websocket,:path=/admin/ws, and:authority=ws.example:443becomesCONNECT ws.example:443;ws.example:443;Observed results:
Patched-control evidence is included in:
evidence/minimal-fail-closed-patched-control.diffpov/Http2ExtendedConnectFixedControlTest.javapov/Http3ExtendedConnectFixedControlTest.javaevidence/patched-control-results.txtThe patched control uses a minimal fail-closed guard: if a CONNECT request contains
:protocol, the HTTP/1 conversion path rejects it instead of creating an ordinary CONNECT object. Regular CONNECT remains accepted. Both fixed-control tests passed on current4.2head:Why This Is Not Intended Safe Behavior
This is not merely an application trust-model issue. Netty exposes HTTP/2 and HTTP/3 adapters that convert protocol-native request metadata into the long-standing HTTP-object API. Those adapters already preserve selected protocol metadata in extension headers such as
x-http2-scheme,x-http3-scheme, stream ids, and pushed-stream ids. For Extended CONNECT, the adapter silently discards the metadata that defines the security-sensitive difference between regular CONNECT and Extended CONNECT.RFC 8441 explicitly changes CONNECT semantics when
:protocolis present: the server is not supposed to create a normal tunnel to the host indicated by:authority. RFC 9220 says HTTP/3 uses the same pseudo-header and setting semantics. Netty's conversion does the opposite at the object layer by producing an ordinary CONNECT request-target equal to:authority.The control case confirms regular CONNECT itself is not the issue. A regular CONNECT request without
:protocoland:pathproduces the same HTTP/1 object shape that the Extended CONNECT request produces. The bug is that these two different protocol states become indistinguishable after conversion.