From: Markus Koschany <apo@debian.org>
Date: Wed, 17 Aug 2022 12:58:27 +0200
Subject: CVE-2022-2047

Origin: https://github.com/eclipse/jetty.project/pull/8146
---
 .../java/org/eclipse/jetty/client/HttpRequest.java |  8 +-
 .../eclipse/jetty/client/HttpClientURITest.java    | 45 ++++++++++
 .../main/java/org/eclipse/jetty/http/HttpURI.java  | 25 +++++-
 .../java/org/eclipse/jetty/http/HttpURITest.java   | 95 ++++++++++++++++++++++
 .../org/eclipse/jetty/proxy/ConnectHandler.java    |  2 +-
 .../java/org/eclipse/jetty/server/Request.java     | 14 +++-
 .../eclipse/jetty/server/HttpConnectionTest.java   | 12 +--
 7 files changed, 187 insertions(+), 14 deletions(-)

diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index f6a453f..2d2afa0 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -177,6 +177,8 @@ public class HttpRequest implements Request
             String rawPath = uri.getRawPath();
             if (rawPath == null)
                 rawPath = "";
+            if (!rawPath.startsWith("/"))
+                rawPath = "/" + rawPath;
             this.path = rawPath;
             String query = uri.getRawQuery();
             if (query != null)
@@ -855,14 +857,14 @@ public class HttpRequest implements Request
         return result;
     }
 
-    private URI newURI(String uri)
+    private URI newURI(String path)
     {
         try
         {
             // Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
-            if ("*".equals(uri))
+            if ("*".equals(path))
                 return null;
-            URI result = new URI(uri);
+            URI result = new URI(path);
             return result.isOpaque() ? null : result;
         }
         catch (URISyntaxException x)
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
index 9c43512..5a97bdd 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpURI.java
@@ -189,7 +189,7 @@ public class HttpURI
         _uri=uri;
 
         if (HttpMethod.CONNECT.is(method))
-            _path=uri;
+            parse(State.HOST, uri, 0, uri.length());
         else
             parse(uri.startsWith("/")?State.PATH:State.START,uri,0,uri.length());
     }
@@ -720,17 +720,30 @@ public class HttpURI
      */
     public void setAuthority(String host, int port)
     {
+        if (host != null && !isPathValidForAuthority(_path))
+            throw new IllegalArgumentException("Relative path with authority");
         _host=host;
         _port=port;
         _uri=null;
     }
 
+    private boolean isPathValidForAuthority(String path)
+    {
+        if (path == null)
+            return true;
+        if (path.isEmpty() || "*".equals(path))
+            return true;
+        return path.startsWith("/");
+    }
+
     /* ------------------------------------------------------------ */
     /**
      * @param path the path
      */
     public void setPath(String path)
     {
+        if (hasAuthority() && !isPathValidForAuthority(path))
+            throw new IllegalArgumentException("Relative path with authority");
         _uri=null;
         _path=path;
         _decodedPath=null;
@@ -739,6 +752,8 @@ public class HttpURI
     /* ------------------------------------------------------------ */
     public void setPathQuery(String path)
     {
+        if (hasAuthority() && !isPathValidForAuthority(path))
+            throw new IllegalArgumentException("Relative path with authority");
         _uri=null;
         _path=null;
         _decodedPath=null;
@@ -747,7 +762,13 @@ public class HttpURI
         if (path!=null)
             parse(State.PATH,path,0,path.length());
     }
-    
+
+    private boolean hasAuthority()
+    {
+        return _host != null;
+    }
+
+
     /* ------------------------------------------------------------ */
     public void setQuery(String query)
     {
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
index 63a58cf..6c3e65f 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpURITest.java
@@ -32,6 +32,15 @@ import java.nio.charset.StandardCharsets;
 import org.eclipse.jetty.util.MultiMap;
 import org.junit.jupiter.api.Test;
 
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.Assumptions.assumeTrue;
+
 public class HttpURITest
 {
     @Test
@@ -100,6 +109,32 @@ public class HttpURITest
         assertThat(uri.getPath(),is("/bar"));
     }
 
+    @Test
+    public void testCONNECT()
+    {
+        HttpURI uri = new HttpURI();
+
+        uri.parseRequestTarget("CONNECT", "host:80");
+        assertThat(uri.getHost(), is("host"));
+        assertThat(uri.getPort(), is(80));
+        assertThat(uri.getPath(), nullValue());
+
+        uri.parseRequestTarget("CONNECT", "host");
+        assertThat(uri.getHost(), is("host"));
+        assertThat(uri.getPort(), is(-1));
+        assertThat(uri.getPath(), nullValue());
+
+        uri.parseRequestTarget("CONNECT", "192.168.0.1:8080");
+        assertThat(uri.getHost(), is("192.168.0.1"));
+        assertThat(uri.getPort(), is(8080));
+        assertThat(uri.getPath(), nullValue());
+
+        uri.parseRequestTarget("CONNECT", "[::1]:8080");
+        assertThat(uri.getHost(), is("[::1]"));
+        assertThat(uri.getPort(), is(8080));
+        assertThat(uri.getPath(), nullValue());
+    }
+
     @Test
     public void testExtB() throws Exception
     {
@@ -222,4 +257,64 @@ public class HttpURITest
         assertEquals(uri.getAuthority(), "example.com:8888");
         assertEquals(uri.getUser(), "user:password");
     }
+
+    @Test
+    public void testRelativePathWithAuthority()
+    {
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setAuthority("host", 0);
+            httpURI.setPath("path");
+        });
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setAuthority("host", 8080);
+            httpURI.setPath(";p=v/url");
+        });
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setAuthority("host", 0);
+            httpURI.setPath(";");
+        });
+
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setPath("path");
+            httpURI.setAuthority("host", 0);
+        });
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setPath(";p=v/url");
+            httpURI.setAuthority("host", 8080);
+        });
+        assertThrows(IllegalArgumentException.class, () ->
+        {
+            HttpURI httpURI = new HttpURI();
+            httpURI.setPath(";");
+            httpURI.setAuthority("host", 0);
+        });
+
+        HttpURI uri = new HttpURI();
+        uri.setPath("*");
+        uri.setAuthority("host", 0);
+        assertEquals("//host*", uri.toString());
+        uri = new HttpURI();
+        uri.setAuthority("host", 0);
+        uri.setPath("*");
+        assertEquals("//host*", uri.toString());
+
+        uri = new HttpURI();
+        uri.setPath("");
+        uri.setAuthority("host", 0);
+        assertEquals("//host", uri.toString());
+        uri = new HttpURI();
+        uri.setAuthority("host", 0);
+        uri.setPath("");
+        assertEquals("//host", uri.toString());
+    }
 }
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
index 6b7d39e..d3dd5c8 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
@@ -197,7 +197,7 @@ public class ConnectHandler extends HandlerWrapper
     {
         if (HttpMethod.CONNECT.is(request.getMethod()))
         {
-            String serverAddress = request.getRequestURI();
+            String serverAddress = baseRequest.getHttpURI().getAuthority();
             if (LOG.isDebugEnabled())
                 LOG.debug("CONNECT request for {}", serverAddress);
 
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
index b15bcc7..5b996bb 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java
@@ -1778,9 +1778,19 @@ public class Request implements HttpServletRequest
         
         setMethod(request.getMethod());
         HttpURI uri = request.getURI();
-        _originalURI = uri.isAbsolute()&&request.getHttpVersion()!=HttpVersion.HTTP_2?uri.toString():uri.getPathQuery();
+        String encoded;
+        if (HttpMethod.CONNECT.is(request.getMethod()))
+        {
+            _originalURI = uri.getAuthority();
+            encoded = "/";
+        }
+        else
+        {
+            _originalURI = uri.isAbsolute() && request.getHttpVersion() != HttpVersion.HTTP_2 ? uri.toString() : uri.getPathQuery();
+            encoded = uri.getPath();
+        }
+
 
-        String encoded = uri.getPath();
         String path;
         if (encoded==null)
         {
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
index 918a112..22391f7 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpConnectionTest.java
@@ -365,7 +365,7 @@ public class HttpConnectionTest
     public void testBadPathDotDotPath() throws Exception
     {
         String response=connector.getResponse("GET /ooops/../../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad URI");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
@@ -380,28 +380,28 @@ public class HttpConnectionTest
     public void testBadPathEncodedDotDotPath() throws Exception
     {
         String response=connector.getResponse("GET /ooops/%2e%2e/%2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad URI");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
     public void testBadDotDotPath() throws Exception
     {
         String response=connector.getResponse("GET ../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad URI");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
     public void testBadSlashDotDotPath() throws Exception
     {
         String response=connector.getResponse("GET /../path HTTP/1.0\r\nHost: localhost:80\r\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad URI");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
     public void testEncodedBadDotDotPath() throws Exception
     {
         String response=connector.getResponse("GET %2e%2e/path HTTP/1.0\r\nHost: localhost:80\r\n\n");
-        checkContains(response,0,"HTTP/1.1 400 Bad URI");
+        checkContains(response,0,"HTTP/1.1 400");
     }
 
     @Test
@@ -1168,7 +1168,7 @@ public class HttpConnectionTest
                                            "12345\r\n"+
                                            "0;\r\n" +
                                            "\r\n");
-            checkContains(response,offset,"HTTP/1.1 400 Bad Request");
+            checkContains(response,offset,"HTTP/1.1 400");
         }
         catch (Exception e)
         {
