GitHub Security Advisory: GHSA-xxqh-mfjm-7mv9
Package: io.netty:netty-codec-http (MAVEN)
Severity: MODERATE (CVSS 5.8)
CWE: CWE-444: Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
Vulnerable versions: >= 4.2.0.Alpha1, <= 4.2.12.Final
Patched in: 4.2.13.Final

Summary: Netty HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization

Details:
# NETTY HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization

| Field     | Value |
|-----------|-------|
| Library   | `io.netty:netty-codec-http` |
| Component | `codec-http` — `HttpObjectDecoder` |
| Severity  | **HIGH** |
| Affects   | HEAD, commit `4f3533ae` confirmed |

---

## Summary

`HttpObjectDecoder` strips a conflicting `Content-Length` header when a request carries both `Transfer-Encoding: chunked` and `Content-Length`, but only for HTTP/1.1 messages. The guard is absent for HTTP/1.0. An attacker that sends an HTTP/1.0 request with both headers causes Netty to decode the body as chunked while leaving `Content-Length` intact in the forwarded `HttpMessage`. Any downstream proxy or handler that trusts `Content-Length` over `Transfer-Encoding` will disagree on message boundaries, enabling request smuggling.

---

## Root Cause

```java
// HttpObjectDecoder.java:828-833
if (HttpUtil.isTransferEncodingChunked(message)) {
    this.chunked = true;
    if (!contentLengthFields.isEmpty() && message.protocolVersion() == HttpVersion.HTTP_1_1) {
        handleTransferEncodingChunkedWithContentLength(message);  // strips CL — HTTP/1.1 only
    }
    return State.READ_CHUNK_SIZE;
}

// HttpObjectDecoder.java:870-873
protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
    message.headers().remove(HttpHeaderNames.CONTENT_LENGTH);
    contentLength = Long.MIN_VALUE;
}
```

The conflict-resolution path is gated on `message.protocolVersion() == HttpVersion.HTTP_1_1`. When the request declares `HTTP/1.0`, the condition is false, `handleTransferEncodingChunkedWithContentLength` is never called, and the `Content-Length` header survives into the forwarded message. Netty still processes the body as chunked; a downstream component that is CL-first interprets the same bytes as a separate request.

---

## Proof of Concept

```
POST /api HTTP/1.0\r\n
Host: internal.example.com\r\n
Transfer-Encoding: chunked\r\n
Content-Length: 0\r\n
\r\n
5\r\n
GPOST\r\n
0\r\n
\r\n
```

Netty consumes the full chunked body (5 bytes + terminator). A downstream CL-first proxy reads `Content-Length: 0`, considers the request complete at the blank line, and treats `5\r\nGPOST\r\n0\r\n\r\n` as the start of a second request.

---

## Conditions Required

1. Netty is deployed behind a reverse proxy or load balancer that is `Content-Length`-first (nginx, some HAProxy configs, AWS ALB in certain modes).
2. Attacker can send HTTP/1.0 requests (either directly or by downgrading via connection manipulation).
3. No additional HTTP/1.0 stripping layer between attacker and Netty.

---

## Impact

Request smuggling at the Netty edge. Allows cache poisoning, session fixation against other users, unauthorized access to internal endpoints, and bypassing of WAF or authentication layers that inspect only the first logical request.

---

## Confirmed PoC Test

Verified against HEAD (`4f3533ae`) using `EmbeddedChannel`. Both tests pass, confirming the vulnerabil