[Git][java-team/netty][master] 3 commits: CVE-2025-67735

Bastien Roucariès (@rouca) gitlab at salsa.debian.org
Sun Jan 25 14:00:52 GMT 2026



Bastien Roucariès pushed to branch master at Debian Java Maintainers / netty


Commits:
580aa6d5 by Bastien Roucariès at 2026-01-12T00:30:00+01:00
CVE-2025-67735

- - - - -
46a9f8e6 by Bastien Roucariès at 2026-01-12T00:44:05+01:00
Update changelog

- - - - -
7ba92f50 by Bastien Roucariès at 2026-01-25T15:00:13+01:00
Depends on junit 5

- - - - -


4 changed files:

- debian/changelog
- debian/maven.rules
- + debian/patches/CVE-2025-67735.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,8 +1,16 @@
 netty (1:4.1.48-15) unstable; urgency=medium
 
   * Split package for preparing upgrade
-
- -- Bastien Roucariès <rouca at debian.org>  Mon, 05 Jan 2026 12:46:47 +0100
+  * Fix CVE-2025-67735 (Closes: #1123606)
+    `io.netty.handler.codec.http.HttpRequestEncoder`
+    has a CRLF injection with the request URI when constructing
+    a request. This leads to request smuggling when
+    `HttpRequestEncoder` is used without proper sanitization
+    of the URI. Any application / framework using `HttpRequestEncoder`
+    can be subject to be abused to perform request smuggling using
+    CRLF injection
+
+ -- Bastien Roucariès <rouca at debian.org>  Mon, 12 Jan 2026 00:43:40 +0100
 
 netty (1:4.1.48-14) unstable; urgency=medium
 


=====================================
debian/maven.rules
=====================================
@@ -27,4 +27,4 @@ log4j log4j * s/1\.2\..*/1.2.x/ * *
 org.bouncycastle s/bcpkix-jdk15on/bcpkix/ * s/.*/debian/ * *
 s/ant/org.apache.ant/ * * s/.*/debian/ * *
 s/org.javassist/javassist/ * * s/.*/debian/ * *
-junit junit * s/.*/4.x/ * *
+junit junit * s/.*/5.x/ * *


=====================================
debian/patches/CVE-2025-67735.patch
=====================================
@@ -0,0 +1,328 @@
+From: Chris Vest <christianvest_hansen at apple.com>
+Date: Thu, 11 Dec 2025 09:20:08 -0800
+Subject: Merge commit from fork * Reject encoding of HTTP URIs that have
+ line-breaks
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+Motivation:
+Line-breaks in user-supplied data can cause security issues like request/response splitting, request smuggling, and parser desynchronization.
+The URI was not being checked for containing line-breaks before encoding.
+
+Modification:
+When encoding the URI in HttpRequestEncoder, we now also check if it contains any line-break characters, and if so, throw an IllegalArgumentException.
+
+Result:
+Line-breaks are now being properly neutralized from the URI in HttpRequestEncoder.
+
+Unfortunately, the performance drops a bit from this check.
+
+Before:
+
+```
+Benchmark                                      Mode  Cnt         Score        Error  Units
+HttpRequestEncoderInsertBenchmark.newEncoder  thrpt   40  10169070.498 ±  27016.445  ops/s
+```
+
+Now:
+
+```
+Benchmark                                      Mode  Cnt         Score       Error  Units
+HttpRequestEncoderInsertBenchmark.newEncoder  thrpt   40   7984846.328 ± 29959.587  ops/s
+```
+
+* Move the request line encoding safety checks to DefaultHttpRequest
+
+origin: backport, https://github.com/netty/netty/commit/77e81f1e5944d98b3acf887d3aa443b252752e94
+---
+ .../handler/codec/http/DefaultFullHttpRequest.java | 10 ++-
+ .../handler/codec/http/DefaultHttpRequest.java     | 16 +++++
+ .../java/io/netty/handler/codec/http/HttpUtil.java | 57 ++++++++++++++++-
+ .../handler/codec/http/DefaultHttpRequestTest.java | 74 ++++++++++++++++++++++
+ .../handler/codec/http/HttpRequestEncoderTest.java |  2 -
+ .../io/netty/handler/codec/http/HttpUtilTest.java  | 26 ++++++++
+ 6 files changed, 179 insertions(+), 6 deletions(-)
+
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java
+index 117e6db..d599241 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultFullHttpRequest.java
+@@ -53,7 +53,15 @@ public class DefaultFullHttpRequest extends DefaultHttpRequest implements FullHt
+ 
+     public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri,
+             ByteBuf content, HttpHeaders headers, HttpHeaders trailingHeader) {
+-        super(httpVersion, method, uri, headers);
++        this(httpVersion, method, uri, content, headers, trailingHeader, true);
++    }
++
++    /**
++     * Create a full HTTP response with the given HTTP version, method, URI, contents, and header and trailer objects.
++     */
++    public DefaultFullHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri,
++            ByteBuf content, HttpHeaders headers, HttpHeaders trailingHeader, boolean validateRequestLine) {
++        super(httpVersion, method, uri, headers, validateRequestLine);
+         this.content = checkNotNull(content, "content");
+         this.trailingHeader = checkNotNull(trailingHeader, "trailingHeader");
+     }
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java
+index dbc7dd3..d0df1c0 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultHttpRequest.java
+@@ -61,9 +61,25 @@ public class DefaultHttpRequest extends DefaultHttpMessage implements HttpReques
+      * @param headers           the Headers for this Request
+      */
+     public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, HttpHeaders headers) {
++        this(httpVersion, method, uri, headers, true);
++    }
++
++    /**
++     * Creates a new instance.
++     *
++     * @param httpVersion       the HTTP version of the request
++     * @param method            the HTTP method of the request
++     * @param uri               the URI or path of the request
++     * @param headers           the Headers for this Request
++     */
++    public DefaultHttpRequest(HttpVersion httpVersion, HttpMethod method, String uri, HttpHeaders headers,
++                              boolean validateRequestLine) {
+         super(httpVersion, headers);
+         this.method = checkNotNull(method, "method");
+         this.uri = checkNotNull(uri, "uri");
++        if (validateRequestLine) {
++            HttpUtil.validateRequestLineTokens(httpVersion, method, uri);
++        }
+     }
+ 
+     @Override
+diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
+index afa3ec4..512d841 100644
+--- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
++++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpUtil.java
+@@ -41,12 +41,13 @@ public final class HttpUtil {
+     private static final AsciiString CHARSET_EQUALS = AsciiString.of(HttpHeaderValues.CHARSET + "=");
+     private static final AsciiString SEMICOLON = AsciiString.cached(";");
+     private static final String COMMA_STRING = String.valueOf(COMMA);
++    private static final long ILLEGAL_REQUEST_LINE_TOKEN_OCTET_MASK = 1L << '\n' | 1L << '\r' | 1L << ' ';
+ 
+     private HttpUtil() { }
+ 
+     /**
+      * Determine if a uri is in origin-form according to
+-     * <a href="https://tools.ietf.org/html/rfc7230#section-5.3">rfc7230, 5.3</a>.
++     * <a href="https://datatracker.ietf.org/doc/html/rfc9112#section-3.2.1">RFC 9112, 3.2.1</a>.
+      */
+     public static boolean isOriginForm(URI uri) {
+         return uri.getScheme() == null && uri.getSchemeSpecificPart() == null &&
+@@ -54,8 +55,8 @@ public final class HttpUtil {
+     }
+ 
+     /**
+-     * Determine if a uri is in asterisk-form according to
+-     * <a href="https://tools.ietf.org/html/rfc7230#section-5.3">rfc7230, 5.3</a>.
++     * Determine if a string uri is in origin-form according to
++     * <a href="https://datatracker.ietf.org/doc/html/rfc9112#section-3.2.1">RFC 9112, 3.2.1</a>.
+      */
+     public static boolean isAsteriskForm(URI uri) {
+         return "*".equals(uri.getPath()) &&
+@@ -475,6 +476,56 @@ public final class HttpUtil {
+         return null;
+     }
+ 
++    static void validateRequestLineTokens(HttpVersion httpVersion, HttpMethod method, String uri) {
++        // The HttpVersion class does its own validation, and it's not possible for subclasses to circumvent it.
++        // The HttpMethod class does its own validation, but subclasses might circumvent it.
++        if (method.getClass() != HttpMethod.class) {
++            if (!isEncodingSafeStartLineToken(method.asciiName())) {
++                throw new IllegalArgumentException(
++                        "The HTTP method name contain illegal characters: " + method.asciiName());
++            }
++        }
++
++        if (!isEncodingSafeStartLineToken(uri)) {
++            throw new IllegalArgumentException("The URI contain illegal characters: " + uri);
++        }
++    }
++
++    /**
++     * Validate that the given request line token is safe for verbatim encoding to the network.
++     * This does not fully check that the token – HTTP method, version, or URI – is valid and formatted correctly.
++     * Only that the token does not contain characters that would break or
++     * desynchronize HTTP message parsing of the start line wherein the token would be included.
++     * <p>
++     * See <a href="https://datatracker.ietf.org/doc/html/rfc9112#name-request-line">RFC 9112, 3.</a>
++     *
++     * @param token The token to check.
++     * @return {@code true} if the token is safe to encode verbatim into the HTTP message output stream,
++     * otherwise {@code false}.
++     */
++    public static boolean isEncodingSafeStartLineToken(CharSequence token) {
++        int i = 0;
++        int lenBytes = token.length();
++        int modulo = lenBytes % 4;
++        int lenInts = modulo == 0 ? lenBytes : lenBytes - modulo;
++        for (; i < lenInts; i += 4) {
++            long chars = 1L << token.charAt(i) |
++                    1L << token.charAt(i + 1) |
++                    1L << token.charAt(i + 2) |
++                    1L << token.charAt(i + 3);
++            if ((chars & ILLEGAL_REQUEST_LINE_TOKEN_OCTET_MASK) != 0) {
++                return false;
++            }
++        }
++        for (; i < lenBytes; i++) {
++            long ch = 1L << token.charAt(i);
++            if ((ch & ILLEGAL_REQUEST_LINE_TOKEN_OCTET_MASK) != 0) {
++                return false;
++            }
++        }
++        return true;
++    }
++
+     /**
+      * Fetch MIME type part from message's Content-Type header as a char sequence.
+      *
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java
+index cf0fa92..cd15472 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/DefaultHttpRequestTest.java
+@@ -21,8 +21,82 @@ import org.junit.Test;
+ import static io.netty.handler.codec.http.HttpHeadersTestUtils.of;
+ import static org.junit.Assert.assertNull;
+ import static org.junit.Assert.assertTrue;
++import static org.junit.Assert.assertThrows;
+ 
+ public class DefaultHttpRequestTest {
++    @ParameterizedTest
++    @ValueSource(strings = {
++            "http://localhost/\r\n",
++            "/r\r\n?q=1",
++            "http://localhost/\r\n?q=1",
++            "/r\r\n/?q=1",
++            "http://localhost/\r\n/?q=1",
++            "/r\r\n",
++            "http://localhost/ HTTP/1.1\r\n\r\nPOST /p HTTP/1.1\r\n\r\n",
++            "/r HTTP/1.1\r\n\r\nPOST /p HTTP/1.1\r\n\r\n",
++            "/ path",
++            "/path ",
++            " /path",
++            "http://localhost/ ",
++            " http://localhost/",
++            "http://local host/",
++    })
++    void constructorMustRejectIllegalUrisByDefault(String uri) {
++        assertThrows(IllegalArgumentException.class, () ->
++                new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri));
++    }
++
++    @ParameterizedTest
++    @ValueSource(strings = {
++            "GET ",
++            " GET",
++            "G ET",
++            " GET ",
++            "GET\r",
++            "GET\n",
++            "GET\r\n",
++            "GE\rT",
++            "GE\nT",
++            "GE\r\nT",
++            "\rGET",
++            "\nGET",
++            "\r\nGET",
++            " \r\nGET",
++            "\r \nGET",
++            "\r\n GET",
++            "\r\nGET ",
++            "\nGET ",
++            "\rGET ",
++            "\r GET",
++            " \rGET",
++            "\nGET ",
++            "\n GET",
++            " \nGET",
++            "GET \n",
++            "GET \r",
++            " GET\r",
++            " GET\r",
++            "GET \n",
++            " GET\n",
++            " GET\n",
++            "GE\nT ",
++            "GE\rT ",
++            " GE\rT",
++            " GE\rT",
++            "GE\nT ",
++            " GE\nT",
++            " GE\nT",
++    })
++    void constructorMustRejectIllegalHttpMethodByDefault(String method) {
++        assertThrows(IllegalArgumentException.class, () -> {
++            new DefaultHttpRequest(HttpVersion.HTTP_1_0, new HttpMethod("GET") {
++                @Override
++                public AsciiString asciiName() {
++                    return new AsciiString(method);
++                }
++            }, "/");
++        });
++    }
+ 
+     @Test
+     public void testHeaderRemoval() {
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java
+index 2f866f7..7008b4b 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestEncoderTest.java
+@@ -32,8 +32,6 @@ import static org.hamcrest.Matchers.instanceOf;
+ import static org.hamcrest.Matchers.is;
+ import static org.junit.Assert.*;
+ 
+-/**
+- */
+ public class HttpRequestEncoderTest {
+ 
+     @SuppressWarnings("deprecation")
+diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java
+index 186b498..87268c6 100644
+--- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java
++++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpUtilTest.java
+@@ -59,6 +59,12 @@ public class HttpUtilTest {
+         assertEquals("2", values.get(1));
+     }
+ 
++    @Test
++    public void testRecognizesAsteriskForm() {
++        // Asterisk form: https://tools.ietf.org/html/rfc7230#section-5.3.4
++        assertTrue(HttpUtil.isAsteriskForm(URI.create("*")));
++        // Origin form: https://tools.ietf.org/html/rfc7230#section-5.3.1
++
+     @Test
+     public void testGetCharsetAsRawCharSequence() {
+         String QUOTES_CHARSET_CONTENT_TYPE = "text/html; charset=\"utf8\"";
+@@ -74,6 +80,26 @@ public class HttpUtilTest {
+         assertNull(HttpUtil.getCharsetAsSequence(SIMPLE_CONTENT_TYPE));
+     }
+ 
++    @ParameterizedTest
++    @ValueSource(strings = {
++            "http://localhost/\r\n",
++            "/r\r\n?q=1",
++            "http://localhost/\r\n?q=1",
++            "/r\r\n/?q=1",
++            "http://localhost/\r\n/?q=1",
++            "/r\r\n",
++            "http://localhost/ HTTP/1.1\r\n\r\nPOST /p HTTP/1.1\r\n\r\n",
++            "/r HTTP/1.1\r\n\r\nPOST /p HTTP/1.1\r\n\r\n",
++            "GET ",
++            " GET",
++            "HTTP/ 1.1",
++            "HTTP/\r0.9",
++            "HTTP/\n1.1",
++    })
++    public void requestLineTokenValidationMustRejectInvalidTokens(String token) throws Exception {
++        assertFalse(HttpUtil.isEncodingSafeStartLineToken(token));
++    }
++
+     @Test
+     public void testGetCharset() {
+         String NORMAL_CONTENT_TYPE = "text/html; charset=utf-8";


=====================================
debian/patches/series
=====================================
@@ -31,3 +31,4 @@ CVE-2025-55163_1.patch
 CVE-2025-55163_2.patch
 CVE-2025-58057.patch
 CVE-2025-58056.patch
+CVE-2025-67735.patch



View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/4467715516e1a81020f133caed0409b40b713e88...7ba92f50feef173d8043de0fe50206dd4a0e2aac

-- 
View it on GitLab: https://salsa.debian.org/java-team/netty/-/compare/4467715516e1a81020f133caed0409b40b713e88...7ba92f50feef173d8043de0fe50206dd4a0e2aac
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20260125/929e0e87/attachment.htm>


More information about the pkg-java-commits mailing list