[Git][java-team/jetty9][master] 2 commits: Fix CVE-2021-34429

Markus Koschany (@apo) gitlab at salsa.debian.org
Sun Jul 18 18:57:46 BST 2021



Markus Koschany pushed to branch master at Debian Java Maintainers / jetty9


Commits:
bc08e0c1 by Markus Koschany at 2021-07-18T19:37:53+02:00
Fix CVE-2021-34429

Closes: #991188
Thanks: Salvatore Bonaccorso for the report.

- - - - -
dc520b43 by Markus Koschany at 2021-07-18T19:38:26+02:00
Update changelog

- - - - -


3 changed files:

- debian/changelog
- + debian/patches/CVE-2021-34429.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,13 @@
+jetty9 (9.4.39-3) unstable; urgency=high
+
+  * Team upload.
+  * Fix CVE-2021-34429:
+    URIs can be crafted using some encoded characters to access the content of
+    the WEB-INF directory and/or bypass some security constraints.
+    Thanks to Salvatore Bonaccorso for the report. (Closes: #991188)
+
+ -- Markus Koschany <apo at debian.org>  Sun, 18 Jul 2021 19:37:57 +0200
+
 jetty9 (9.4.39-2) unstable; urgency=high
 
   * Team upload.


=====================================
debian/patches/CVE-2021-34429.patch
=====================================
@@ -0,0 +1,1147 @@
+From: Markus Koschany <apo at debian.org>
+Date: Sun, 18 Jul 2021 18:58:17 +0200
+Subject: CVE-2021-34429
+
+Bug-Debian: https://bugs.debian.org/991188
+Origin: https://github.com/eclipse/jetty.project/pull/6477
+---
+ .../main/java/org/eclipse/jetty/http/HttpURI.java  |  13 +-
+ .../java/org/eclipse/jetty/http/HttpURITest.java   | 172 ++++++++-----
+ .../jetty/rewrite/handler/RedirectUtil.java        |   4 +-
+ .../jetty/rewrite/handler/ValidUrlRuleTest.java    |  14 +-
+ .../jetty/server/handler/ContextHandler.java       |  30 +--
+ .../jetty/server/handler/ResourceHandler.java      |   2 +
+ .../eclipse/jetty/server/HttpConnectionTest.java   |   6 +
+ .../handler/ContextHandlerGetResourceTest.java     |  21 ++
+ .../org/eclipse/jetty/servlet/RequestURITest.java  |  42 +++-
+ .../main/java/org/eclipse/jetty/util/URIUtil.java  | 266 +++++++++++----------
+ .../eclipse/jetty/util/resource/FileResource.java  |   7 +-
+ .../eclipse/jetty/util/resource/PathResource.java  |  19 +-
+ .../org/eclipse/jetty/util/resource/Resource.java  |   6 +-
+ .../eclipse/jetty/util/resource/URLResource.java   |   9 +-
+ .../jetty/util/URIUtilCanonicalPathTest.java       |  20 ++
+ .../eclipse/jetty/util/resource/ResourceTest.java  |  18 ++
+ 16 files changed, 412 insertions(+), 237 deletions(-)
+
+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 74c04e0..9538468 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
+@@ -617,10 +617,12 @@ public class HttpURI
+         }
+         else if (_path != null)
+         {
+-            String canonical = URIUtil.canonicalPath(_path);
+-            if (canonical == null)
+-                throw new BadMessageException("Bad URI");
+-            _decodedPath = URIUtil.decodePath(canonical);
++            // The RFC requires this to be canonical before decoding, but this can leave dot segments and dot dot segments
++            // which are not canonicalized and could be used in an attempt to bypass security checks.
++            String decodedNonCanonical = URIUtil.decodePath(_path);
++            _decodedPath = URIUtil.canonicalPath(decodedNonCanonical);
++            if (_decodedPath == null)
++                throw new IllegalArgumentException("Bad URI");
+         }
+     }
+ 
+@@ -670,7 +672,8 @@ public class HttpURI
+     }
+ 
+     /**
+-     * @return True if the URI has either an {@link #hasAmbiguousSegment()} or {@link #hasAmbiguousSeparator()}.
++     * @return True if the URI has either an {@link #hasAmbiguousSegment()} or {@link #hasAmbiguousEmptySegment()}
++     * or {@link #hasAmbiguousSeparator()} or {@link #hasAmbiguousParameter()}
+      */
+     public boolean isAmbiguous()
+     {
+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 f6b95ba..3f3a27d 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
+@@ -230,84 +230,111 @@ public class HttpURITest
+         assertEquals("", uri.toString());
+ 
+         uri.setPath("/path/info");
+-        assertEquals("/path/info", uri.toString());
++    assertEquals("/path/info", uri.toString());
+ 
+-        uri.setAuthority("host", 8080);
+-        assertEquals("//host:8080/path/info", uri.toString());
++    uri.setAuthority("host", 8080);
++    assertEquals("//host:8080/path/info", uri.toString());
+ 
+-        uri.setParam("param");
+-        assertEquals("//host:8080/path/info;param", uri.toString());
++    uri.setParam("param");
++    assertEquals("//host:8080/path/info;param", uri.toString());
+ 
+-        uri.setQuery("a=b");
+-        assertEquals("//host:8080/path/info;param?a=b", uri.toString());
++    uri.setQuery("a=b");
++    assertEquals("//host:8080/path/info;param?a=b", uri.toString());
+ 
+-        uri.setScheme("http");
+-        assertEquals("http://host:8080/path/info;param?a=b", uri.toString());
++    uri.setScheme("http");
++    assertEquals("http://host:8080/path/info;param?a=b", uri.toString());
+ 
+-        uri.setPathQuery("/other;xxx/path;ppp?query");
+-        assertEquals("http://host:8080/other;xxx/path;ppp?query", uri.toString());
++    uri.setPathQuery("/other;xxx/path;ppp?query");
++    assertEquals("http://host:8080/other;xxx/path;ppp?query", uri.toString());
+ 
+-        assertThat(uri.getScheme(), is("http"));
+-        assertThat(uri.getAuthority(), is("host:8080"));
+-        assertThat(uri.getHost(), is("host"));
+-        assertThat(uri.getPort(), is(8080));
+-        assertThat(uri.getPath(), is("/other;xxx/path;ppp"));
+-        assertThat(uri.getDecodedPath(), is("/other/path"));
+-        assertThat(uri.getParam(), is("ppp"));
+-        assertThat(uri.getQuery(), is("query"));
+-        assertThat(uri.getPathQuery(), is("/other;xxx/path;ppp?query"));
++    assertThat(uri.getScheme(), is("http"));
++    assertThat(uri.getAuthority(), is("host:8080"));
++    assertThat(uri.getHost(), is("host"));
++    assertThat(uri.getPort(), is(8080));
++    assertThat(uri.getPath(), is("/other;xxx/path;ppp"));
++    assertThat(uri.getDecodedPath(), is("/other/path"));
++    assertThat(uri.getParam(), is("ppp"));
++    assertThat(uri.getQuery(), is("query"));
++    assertThat(uri.getPathQuery(), is("/other;xxx/path;ppp?query"));
+ 
+-        uri.setPathQuery(null);
+-        assertEquals("http://host:8080?query", uri.toString()); // Yes silly result!
++    uri.setPathQuery(null);
++    assertEquals("http://host:8080?query", uri.toString()); // Yes silly result!
+ 
+-        uri.setQuery(null);
+-        assertEquals("http://host:8080", uri.toString());
++    uri.setQuery(null);
++    assertEquals("http://host:8080", uri.toString());
+ 
+-        uri.setPathQuery("/other;xxx/path;ppp?query");
+-        assertEquals("http://host:8080/other;xxx/path;ppp?query", uri.toString());
++    uri.setPathQuery("/other;xxx/path;ppp?query");
++    assertEquals("http://host:8080/other;xxx/path;ppp?query", uri.toString());
+ 
+-        uri.setScheme(null);
+-        assertEquals("//host:8080/other;xxx/path;ppp?query", uri.toString());
++    uri.setScheme(null);
++    assertEquals("//host:8080/other;xxx/path;ppp?query", uri.toString());
+ 
+-        uri.setAuthority(null, -1);
+-        assertEquals("/other;xxx/path;ppp?query", uri.toString());
++    uri.setAuthority(null, -1);
++    assertEquals("/other;xxx/path;ppp?query", uri.toString());
+ 
+-        uri.setParam(null);
+-        assertEquals("/other;xxx/path?query", uri.toString());
++    uri.setParam(null);
++    assertEquals("/other;xxx/path?query", uri.toString());
+ 
+-        uri.setQuery(null);
+-        assertEquals("/other;xxx/path", uri.toString());
++    uri.setQuery(null);
++    assertEquals("/other;xxx/path", uri.toString());
+ 
+-        uri.setPath(null);
+-        assertEquals("", uri.toString());
+-    }
++    uri.setPath(null);
++    assertEquals("", uri.toString());
++}
+ 
+-    public static Stream<Arguments> decodePathTests()
+-    {
+-        return Arrays.stream(new Object[][]
+-            {
+-                // Simple path example
+-                {"http://host/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"//host/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-
+-                // legal non ambiguous relative paths
+-                {"http://host/../path/info", null, EnumSet.noneOf(Ambiguous.class)},
+-                {"http://host/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"http://host/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"//host/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"//host/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
+-                {"path/../info", "info", EnumSet.noneOf(Ambiguous.class)},
+-                {"path/./info", "path/info", EnumSet.noneOf(Ambiguous.class)},
++public static Stream<Arguments> decodePathTests()
++{
++    return Arrays.stream(new Object[][]
++        {
++            // Simple path example
++            {"http://host/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++            {"//host/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++            {"/path/info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++
++            // legal non ambiguous relative paths
++            {"http://host/../path/info", null, EnumSet.noneOf(Ambiguous.class)},
++            {"http://host/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
++            {"http://host/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++            {"//host/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
++            {"//host/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++            {"/path/../info", "/info", EnumSet.noneOf(Ambiguous.class)},
++            {"/path/./info", "/path/info", EnumSet.noneOf(Ambiguous.class)},
++            {"path/../info", "info", EnumSet.noneOf(Ambiguous.class)},
++            {"path/./info", "path/info", EnumSet.noneOf(Ambiguous.class)},
++
++            // encoded paths
++                {"/f%6f%6F/bar", "/foo/bar", EnumSet.noneOf(Violation.class)},
++                {"/f%u006f%u006F/bar", "/foo/bar", EnumSet.of(Violation.UTF16)},
++                {"/f%u0001%u0001/bar", "/f\001\001/bar", EnumSet.of(Violation.UTF16)},
++                {"/foo/%u20AC/bar", "/foo/\u20AC/bar", EnumSet.of(Violation.UTF16)},
+ 
+                 // illegal paths
+-                {"//host/../path/info", null, EnumSet.noneOf(Ambiguous.class)},
+-                {"/../path/info", null, EnumSet.noneOf(Ambiguous.class)},
+-                {"../path/info", null, EnumSet.noneOf(Ambiguous.class)},
+-                {"/path/%XX/info", null, EnumSet.noneOf(Ambiguous.class)},
+-                {"/path/%2/F/info", null, EnumSet.noneOf(Ambiguous.class)},
++                {"//host/../path/info", null, EnumSet.noneOf(Violation.class)},
++                {"/../path/info", null, EnumSet.noneOf(Violation.class)},
++                {"../path/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/%XX/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/%2/F/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/%/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/%u000X/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/Fo%u0000/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/Fo%00/info", null, EnumSet.noneOf(Violation.class)},
++                {"/path/Foo/info%u0000", null, EnumSet.noneOf(Violation.class)},
++                {"/path/Foo/info%00", null, EnumSet.noneOf(Violation.class)},
++                {"/path/%U20AC", null, EnumSet.noneOf(Violation.class)},
++                {"%2e%2e/info", null, EnumSet.noneOf(Violation.class)},
++                {"%u002e%u002e/info", null, EnumSet.noneOf(Violation.class)},
++                {"%2e%2e;/info", null, EnumSet.noneOf(Violation.class)},
++                {"%u002e%u002e;/info", null, EnumSet.noneOf(Violation.class)},
++                {"%2e.", null, EnumSet.noneOf(Violation.class)},
++                {"%u002e.", null, EnumSet.noneOf(Violation.class)},
++                {".%2e", null, EnumSet.noneOf(Violation.class)},
++                {".%u002e", null, EnumSet.noneOf(Violation.class)},
++                {"%2e%2e", null, EnumSet.noneOf(Violation.class)},
++                {"%u002e%u002e", null, EnumSet.noneOf(Violation.class)},
++                {"%2e%u002e", null, EnumSet.noneOf(Violation.class)},
++                {"%u002e%2e", null, EnumSet.noneOf(Violation.class)},
++                {"..;/info", null, EnumSet.noneOf(Violation.class)},
++                {"..;param/info", null, EnumSet.noneOf(Violation.class)},
+ 
+                 // ambiguous dot encodings
+                 {"scheme://host/path/%2e/info", "/path/./info", EnumSet.of(Ambiguous.SEGMENT)},
+@@ -342,6 +369,13 @@ public class HttpURITest
+                 {"%2F/info", "//info", EnumSet.of(Ambiguous.SEPARATOR)},
+                 {"/path/%2f../info", "/path//../info", EnumSet.of(Ambiguous.SEPARATOR)},
+ 
++                // ambiguous encoding
++                {"/path/%25/info", "/path/%/info", EnumSet.of(Violation.ENCODING)},
++                {"/path/%u0025/info", "/path/%/info", EnumSet.of(Violation.ENCODING, Violation.UTF16)},
++                {"%25/info", "%/info", EnumSet.of(Violation.ENCODING)},
++                {"/path/%25../info", "/path/%../info", EnumSet.of(Violation.ENCODING)},
++                {"/path/%u0025../info", "/path/%../info", EnumSet.of(Violation.ENCODING, Violation.UTF16)},
++
+                 // combinations
+                 {"/path/%2f/..;/info", "/path///../info", EnumSet.of(Ambiguous.SEPARATOR, Ambiguous.PARAM)},
+                 {"/path/%2f/..;/%2e/info", "/path///.././info", EnumSet.of(Ambiguous.SEPARATOR, Ambiguous.PARAM, Ambiguous.SEGMENT)},
+@@ -370,4 +404,20 @@ public class HttpURITest
+             assertThat(decodedPath, nullValue());
+         }
+     }
++
++    public static Stream<Arguments> queryData()
++    {
++        return Stream.of(
++            new String[]{"/path?p=%U20AC", "p=%U20AC"},
++            new String[]{"/path?p=%u20AC", "p=%u20AC"}
++        ).map(Arguments::of);
++    }
++
++    @ParameterizedTest
++    @MethodSource("queryData")
++    public void testEncodedQuery(String input, String expectedQuery)
++    {
++        HttpURI httpURI = new HttpURI(input);
++        assertThat("[" + input + "] .query", httpURI.getQuery(), is(expectedQuery));
++    }
+ }
+diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java
+index 6fc476d..1040018 100644
+--- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java
++++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectUtil.java
+@@ -53,12 +53,12 @@ public final class RedirectUtil
+                 String path = request.getRequestURI();
+                 String parent = (path.endsWith("/")) ? path : URIUtil.parentPath(path);
+                 location = URIUtil.canonicalPath(URIUtil.addEncodedPaths(parent, location));
+-                if (!location.startsWith("/"))
++                if (location != null && !location.startsWith("/"))
+                     url.append('/');
+             }
+ 
+             if (location == null)
+-                throw new IllegalStateException("path cannot be above root");
++                throw new IllegalStateException("redirect path cannot be above root");
+             url.append(location);
+ 
+             location = url.toString();
+diff --git a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
+index fae7150..bf63fe7 100644
+--- a/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
++++ b/jetty-rewrite/src/test/java/org/eclipse/jetty/rewrite/handler/ValidUrlRuleTest.java
+@@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test;
+ 
+ import static org.junit.jupiter.api.Assertions.assertEquals;
+ import static org.junit.jupiter.api.Assertions.assertFalse;
++import static org.junit.jupiter.api.Assertions.assertThrows;
+ import static org.junit.jupiter.api.Assertions.assertTrue;
+ 
+ @SuppressWarnings("unused")
+@@ -75,6 +76,12 @@ public class ValidUrlRuleTest extends AbstractRuleTestCase
+ 
+     @Test
+     public void testInvalidJsp() throws Exception
++    {
++        assertThrows(IllegalArgumentException.class, () -> _request.setURIPathQuery("/jsp/bean1.jsp%00"));
++    }
++
++    @Test
++    public void testInvalidJspWithNullByte() throws Exception
+     {
+         _rule.setCode("405");
+         _rule.setReason("foo");
+@@ -113,6 +120,12 @@ public class ValidUrlRuleTest extends AbstractRuleTestCase
+         assertEquals(200, _response.getStatus());
+     }
+ 
++    @Test
++    public void testInvalidShamrock() throws Exception
++    {
++        assertThrows(IllegalArgumentException.class, () -> _request.setURIPathQuery("/jsp/shamrock-%00%E2%98%98.jsp"));
++    }
++
+     @Test
+     public void testCharacters() throws Exception
+     {
+@@ -124,4 +137,3 @@ public class ValidUrlRuleTest extends AbstractRuleTestCase
+         //@checkstyle-enable-check : IllegalTokenText
+     }
+ }
+-
+diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+index 4e16095..48bebcb 100644
+--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
++++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java
+@@ -1948,7 +1948,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+ 
+         try
+         {
+-            path = URIUtil.canonicalPath(path);
+             Resource resource = _baseResource.addPath(path);
+ 
+             if (checkAlias(path, resource))
+@@ -2135,9 +2134,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return ContextHandler.this;
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getContext(java.lang.String)
+-         */
+         @Override
+         public ServletContext getContext(String uripath)
+         {
+@@ -2226,9 +2222,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return null;
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getMimeType(java.lang.String)
+-         */
+         @Override
+         public String getMimeType(String file)
+         {
+@@ -2237,9 +2230,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return _mimeTypes.getMimeByExtension(file);
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getRequestDispatcher(java.lang.String)
+-         */
+         @Override
+         public RequestDispatcher getRequestDispatcher(String uriInContext)
+         {
+@@ -2253,6 +2243,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+ 
+             try
+             {
++                // The uriInContext will be canonicalized by HttpURI.
+                 HttpURI uri = new HttpURI(null, null, 0, uriInContext);
+ 
+                 String pathInfo = URIUtil.canonicalPath(uri.getDecodedPath());
+@@ -2278,6 +2269,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+         @Override
+         public String getRealPath(String path)
+         {
++            // This is an API call from the application which may have arbitrary non canonical paths passed
++            // Thus we canonicalize here, to avoid the enforcement of only canonical paths in
++            // ContextHandler.this.getResource(path).
++            path = URIUtil.canonicalPath(path);
+             if (path == null)
+                 return null;
+             if (path.length() == 0)
+@@ -2312,9 +2307,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return null;
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getResourceAsStream(java.lang.String)
+-         */
+         @Override
+         public InputStream getResourceAsStream(String path)
+         {
+@@ -2402,9 +2394,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return o;
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getAttributeNames()
+-         */
+         @Override
+         public Enumeration<String> getAttributeNames()
+         {
+@@ -2423,9 +2412,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             return Collections.enumeration(set);
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#setAttribute(java.lang.String, java.lang.Object)
+-         */
+         @Override
+         public void setAttribute(String name, Object value)
+         {
+@@ -2452,9 +2438,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             }
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#removeAttribute(java.lang.String)
+-         */
+         @Override
+         public void removeAttribute(String name)
+         {
+@@ -2471,9 +2454,6 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
+             }
+         }
+ 
+-        /*
+-         * @see javax.servlet.ServletContext#getServletContextName()
+-         */
+         @Override
+         public String getServletContextName()
+         {
+diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
+index b5b0db0..63d94ff 100644
+--- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
++++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java
+@@ -188,7 +188,9 @@ public class ResourceHandler extends HandlerWrapper implements ResourceFactory,
+                 }
+             }
+             else if (_context != null)
++            {
+                 r = _context.getResource(path);
++            }
+ 
+             if ((r == null || !r.exists()) && path.endsWith("/jetty-dir.css"))
+                 r = getStylesheet();
+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 72197e6..ada19a0 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
+@@ -829,6 +829,12 @@ public class HttpConnectionTest
+             "\r\n");
+         checkContains(response, 0, "HTTP/1.1 200"); //now fallback to iso-8859-1
+ 
++        response = connector.getResponse("GET /foo/bar%c0%00 HTTP/1.1\r\n" +
++            "Host: localhost\r\n" +
++            "Connection: close\r\n" +
++            "\r\n");
++        checkContains(response, 0, "HTTP/1.1 400");
++
+         response = connector.getResponse("GET /bad/utf8%c1 HTTP/1.1\r\n" +
+             "Host: localhost\r\n" +
+             "Connection: close\r\n" +
+diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
+index a6a471b..4f575bf 100644
+--- a/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
++++ b/jetty-server/src/test/java/org/eclipse/jetty/server/handler/ContextHandlerGetResourceTest.java
+@@ -245,6 +245,27 @@ public class ContextHandlerGetResourceTest
+         assertNull(url);
+     }
+ 
++    @Test
++    public void testAlias() throws Exception
++    {
++        String path = "/./index.html";
++        Resource resource = context.getResource(path);
++        assertNull(resource);
++        URL resourceURL = context.getServletContext().getResource(path);
++        assertFalse(resourceURL.getPath().contains("/./"));
++
++        path = "/down/../index.html";
++        resource = context.getResource(path);
++        assertNull(resource);
++        resourceURL = context.getServletContext().getResource(path);
++        assertFalse(resourceURL.getPath().contains("/../"));
++
++        path = "//index.html";
++        resource = context.getResource(path);
++        assertNull(resource);
++        resourceURL = context.getServletContext().getResource(path);
++        assertNull(resourceURL);
++
+     @Test
+     public void testDeep() throws Exception
+     {
+diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/RequestURITest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/RequestURITest.java
+index de3814a..2b3e29a 100644
+--- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/RequestURITest.java
++++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/RequestURITest.java
+@@ -196,7 +196,6 @@ public class RequestURITest
+             // Read the response.
+             String response = readResponse(client);
+ 
+-            // TODO: is HTTP/1.1 response appropriate for an HTTP/1.0 request?
+             assertThat(response, Matchers.containsString("HTTP/1.1 200 OK"));
+             assertThat(response, Matchers.containsString("RequestURI: " + expectedReqUri));
+             assertThat(response, Matchers.containsString("QueryString: " + expectedQuery));
+@@ -223,4 +222,45 @@ public class RequestURITest
+             assertThat(response, Matchers.containsString("QueryString: " + expectedQuery));
+         }
+     }
++
++    public static Stream<Arguments> badData()
++    {
++        List<Arguments> ret = new ArrayList<>();
++        ret.add(Arguments.of("/hello\000"));
++        ret.add(Arguments.of("/hello%00"));
++        ret.add(Arguments.of("/hello%u0000"));
++        ret.add(Arguments.of("/hello\000/world"));
++        ret.add(Arguments.of("/hello%00world"));
++        ret.add(Arguments.of("/hello%u0000world"));
++        ret.add(Arguments.of("/hello%GG"));
++        ret.add(Arguments.of("/hello%;/world"));
++        ret.add(Arguments.of("/hello/../../world"));
++        ret.add(Arguments.of("/hello/..;/world"));
++        ret.add(Arguments.of("/hello/..;?/world"));
++        ret.add(Arguments.of("/hello/%#x/../world"));
++        ret.add(Arguments.of("/../hello/world"));
++        ret.add(Arguments.of("/hello%u00u00/world"));
++        ret.add(Arguments.of("hello"));
++
++        return ret.stream();
++    }
++
++    @ParameterizedTest
++    @MethodSource("badData")
++    public void testGetBadRequestsURIHTTP10(String rawpath) throws Exception
++    {
++        try (Socket client = newSocket(serverURI.getHost(), serverURI.getPort()))
++        {
++            OutputStream os = client.getOutputStream();
++
++            String request = String.format("GET %s HTTP/1.0\r\n\r\n", rawpath);
++            os.write(request.getBytes(StandardCharsets.ISO_8859_1));
++            os.flush();
++
++            // Read the response.
++            String response = readResponse(client);
++
++            assertThat(response, Matchers.containsString("HTTP/1.1 400 "));
++        }
++    }
+ }
+diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+index 6818e54..7cf5862 100644
+--- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
++++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+@@ -475,8 +475,8 @@ public class URIUtil
+                             char u = path.charAt(i + 1);
+                             if (u == 'u')
+                             {
+-                                // TODO remove %u support in jetty-10
+-                                // this is wrong. This is a codepoint not a char
++                                // In Jetty-10 UTF16 encoding is only supported with UriCompliance.Violation.UTF16_ENCODINGS.
++                                // This is wrong. This is a codepoint not a char
+                                 builder.append((char)(0xffff & TypeUtil.parseInt(path, i + 2, 4, 16)));
+                                 i += 5;
+                             }
+@@ -537,7 +537,6 @@ public class URIUtil
+         {
+             throw new IllegalArgumentException("cannot decode URI", e);
+         }
+-
+     }
+ 
+     /* Decode a URI path and strip parameters of ISO-8859-1 path
+@@ -562,8 +561,7 @@ public class URIUtil
+                         char u = path.charAt(i + 1);
+                         if (u == 'u')
+                         {
+-                            // TODO remove %u encoding support in jetty-10
+-                            // This is wrong. This is a codepoint not a char
++                            // In Jetty-10 UTF16 encoding is only supported with UriCompliance.Violation.UTF16_ENCODINGS.                            // This is wrong. This is a codepoint not a char
+                             builder.append((char)(0xffff & TypeUtil.parseInt(path, i + 2, 4, 16)));
+                             i += 5;
+                         }
+@@ -782,143 +780,139 @@ public class URIUtil
+     }
+ 
+     /**
+-     * Convert an encoded path to a canonical form.
++     * Convert a partial URI to a canonical form.
+      * <p>
+-     * All instances of "." and ".." are factored out.
++     * All segments of "." and ".." are factored out.
+      * Null is returned if the path tries to .. above its root.
+      * </p>
+      *
+-     * @param path the path to convert, decoded, with path separators '/' and no queries.
++     * @param uri the encoded URI from the path onwards, which may contain query strings and/or fragments
+      * @return the canonical path, or null if path traversal above root.
++     * @see #canonicalPath(String)
++     * @see #canonicalURI(String)
+      */
+-    public static String canonicalPath(String path)
++    public static String canonicalURI(String uri)
+     {
+-        // See https://tools.ietf.org/html/rfc3986#section-5.2.4
+-
+-        if (path == null || path.isEmpty())
+-            return path;
++        if (uri == null || uri.isEmpty())
++            return uri;
+ 
+-        int end = path.length();
++        boolean slash = true;
++        int end = uri.length();
+         int i = 0;
+-        int dots = 0;
+ 
++        // Initially just loop looking if we may need to normalize
+         loop: while (i < end)
+         {
+-            char c = path.charAt(i);
++            char c = uri.charAt(i);
+             switch (c)
+             {
+                 case '/':
+-                    dots = 0;
++                    slash = true;
+                     break;
+ 
+                 case '.':
+-                    if (dots == 0)
+-                    {
+-                        dots = 1;
++                    if (slash)
+                         break loop;
+-                    }
+-                    dots = -1;
++                    slash = false;
+                     break;
+ 
++                case '?':
++                case '#':
++                    // Nothing to normalize so return original path
++                    return uri;
++
+                 default:
+-                    dots = -1;
++                    slash = false;
+             }
+ 
+             i++;
+         }
+ 
++        // Nothing to normalize so return original path
+         if (i == end)
+-            return path;
++            return uri;
+ 
+-        StringBuilder canonical = new StringBuilder(path.length());
+-        canonical.append(path, 0, i);
++        // We probably need to normalize, so copy to path so far into builder
++        StringBuilder canonical = new StringBuilder(uri.length());
++        canonical.append(uri, 0, i);
+ 
++        // Loop looking for single and double dot segments
++        int dots = 1;
+         i++;
+-        while (i <= end)
++        loop : while (i < end)
+         {
+-            char c = i < end ? path.charAt(i) : '\0';
++            char c = uri.charAt(i);
+             switch (c)
+             {
+-                case '\0':
+-                    if (dots == 2)
+-                    {
+-                        if (canonical.length() < 2)
+-                            return null;
+-                        canonical.setLength(canonical.length() - 1);
+-                        canonical.setLength(canonical.lastIndexOf("/") + 1);
+-                    }
+-                    break;
+-
+                 case '/':
+-                    switch (dots)
+-                    {
+-                        case 1:
+-                            break;
+-
+-                        case 2:
+-                            if (canonical.length() < 2)
+-                                return null;
+-                            canonical.setLength(canonical.length() - 1);
+-                            canonical.setLength(canonical.lastIndexOf("/") + 1);
+-                            break;
+-
+-                        default:
+-                            canonical.append(c);
+-                    }
++                    if (doDotsSlash(canonical, dots))
++                        return null;
++                    slash = true;
+                     dots = 0;
+                     break;
+ 
++                case '?':
++                case '#':
++                    // finish normalization at a query
++                    break loop;
++
+                 case '.':
+-                    switch (dots)
+-                    {
+-                        case 0:
+-                            dots = 1;
+-                            break;
+-                        case 1:
+-                            dots = 2;
+-                            break;
+-                        case 2:
+-                            canonical.append("...");
+-                            dots = -1;
+-                            break;
+-                        default:
+-                            canonical.append('.');
+-                    }
++                    // Count dots only if they are leading in the segment
++                    if (dots > 0)
++                        dots++;
++                    else if (slash)
++                        dots = 1;
++                    else
++                        canonical.append('.');
++                    slash = false;
+                     break;
+ 
+                 default:
+-                    switch (dots)
+-                    {
+-                        case 1:
+-                            canonical.append('.');
+-                            break;
+-                        case 2:
+-                            canonical.append("..");
+-                            break;
+-                        default:
+-                    }
++                    // Add leading dots to the path
++                    while (dots-- > 0)
++                        canonical.append('.');
+                     canonical.append(c);
+-                    dots = -1;
++                    dots = 0;
++                    slash = false;
+             }
+-
+             i++;
+         }
++
++        // process any remaining dots
++        if (doDots(canonical, dots))
++            return null;
++
++        // append any query
++        if (i < end)
++            canonical.append(uri, i, end);
++
+         return canonical.toString();
+     }
+ 
+     /**
+-     * Convert a path to a cananonical form.
+-     * <p>
+-     * All instances of "." and ".." are factored out.
+-     * </p>
++     * @param path the encoded URI from the path onwards, which may contain query strings and/or fragments
++     * @return the canonical path, or null if path traversal above root.
++     * @deprecated Use {@link #canonicalURI(String)}
++     */
++    @Deprecated
++    public static String canonicalEncodedPath(String path)
++    {
++        return canonicalURI(path);
++    }
++
++    /**
++     * Convert a decoded URI path to a canonical form.
+      * <p>
++     * All segments of "." and ".." are factored out.
+      * Null is returned if the path tries to .. above its root.
+      * </p>
+      *
+-     * @param path the path to convert (expects URI/URL form, encoded, and with path separators '/')
++     * @param path the decoded URI path to convert. Any special characters (e.g. '?', "#") are assumed to be part of
++     * the path segments.
+      * @return the canonical path, or null if path traversal above root.
++     * @see #canonicalURI(String)
+      */
+-    public static String canonicalEncodedPath(String path)
++    public static String canonicalPath(String path)
+     {
+         if (path == null || path.isEmpty())
+             return path;
+@@ -927,8 +921,8 @@ public class URIUtil
+         int end = path.length();
+         int i = 0;
+ 
+-        loop:
+-        while (i < end)
++        // Initially just loop looking if we may need to normalize
++        loop: while (i < end)
+         {
+             char c = path.charAt(i);
+             switch (c)
+@@ -943,9 +937,6 @@ public class URIUtil
+                     slash = false;
+                     break;
+ 
+-                case '?':
+-                    return path;
+-
+                 default:
+                     slash = false;
+             }
+@@ -953,56 +944,31 @@ public class URIUtil
+             i++;
+         }
+ 
++        // Nothing to normalize so return original path
+         if (i == end)
+             return path;
+ 
++        // We probably need to normalize, so copy to path so far into builder
+         StringBuilder canonical = new StringBuilder(path.length());
+         canonical.append(path, 0, i);
+ 
++        // Loop looking for single and double dot segments
+         int dots = 1;
+         i++;
+-        while (i <= end)
++        while (i < end)
+         {
+-            char c = i < end ? path.charAt(i) : '\0';
++            char c = path.charAt(i);
+             switch (c)
+             {
+-                case '\0':
+                 case '/':
+-                case '?':
+-                    switch (dots)
+-                    {
+-                        case 0:
+-                            if (c != '\0')
+-                                canonical.append(c);
+-                            break;
+-
+-                        case 1:
+-                            if (c == '?')
+-                                canonical.append(c);
+-                            break;
+-
+-                        case 2:
+-                            if (canonical.length() < 2)
+-                                return null;
+-                            canonical.setLength(canonical.length() - 1);
+-                            canonical.setLength(canonical.lastIndexOf("/") + 1);
+-                            if (c == '?')
+-                                canonical.append(c);
+-                            break;
+-                        default:
+-                            while (dots-- > 0)
+-                            {
+-                                canonical.append('.');
+-                            }
+-                            if (c != '\0')
+-                                canonical.append(c);
+-                    }
+-
++                    if (doDotsSlash(canonical, dots))
++                        return null;
+                     slash = true;
+                     dots = 0;
+                     break;
+ 
+                 case '.':
++                    // Count dots only if they are leading in the segment
+                     if (dots > 0)
+                         dots++;
+                     else if (slash)
+@@ -1013,20 +979,66 @@ public class URIUtil
+                     break;
+ 
+                 default:
++                    // Add leading dots to the path
+                     while (dots-- > 0)
+-                    {
+                         canonical.append('.');
+-                    }
+                     canonical.append(c);
+                     dots = 0;
+                     slash = false;
+             }
+-
+             i++;
+         }
++
++        // process any remaining dots
++        if (doDots(canonical, dots))
++            return null;
++
+         return canonical.toString();
+     }
+ 
++    private static boolean doDots(StringBuilder canonical, int dots)
++    {
++        switch (dots)
++        {
++            case 0:
++            case 1:
++                break;
++            case 2:
++                if (canonical.length() < 2)
++                    return true;
++                canonical.setLength(canonical.length() - 1);
++                canonical.setLength(canonical.lastIndexOf("/") + 1);
++                break;
++            default:
++                while (dots-- > 0)
++                    canonical.append('.');
++        }
++        return false;
++    }
++
++    private static boolean doDotsSlash(StringBuilder canonical, int dots)
++    {
++        switch (dots)
++        {
++            case 0:
++                canonical.append('/');
++                break;
++            case 1:
++                break;
++            case 2:
++                if (canonical.length() < 2)
++                    return true;
++                canonical.setLength(canonical.length() - 1);
++                canonical.setLength(canonical.lastIndexOf("/") + 1);
++                break;
++            default:
++                while (dots-- > 0)
++                    canonical.append('.');
++                canonical.append('/');
++        }
++        return false;
++    }
++
+     /**
+      * Convert a path to a compact form.
+      * All instances of "//" and "///" etc. are factored out to single "/"
+diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
+index 9ccfe96..d62b117 100644
+--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
++++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/FileResource.java
+@@ -271,8 +271,11 @@ public class FileResource extends Resource
+         assertValidPath(path);
+         path = org.eclipse.jetty.util.URIUtil.canonicalPath(path);
+ 
+-        if (path == null)
+-            throw new MalformedURLException();
++        // Check that the path is within the root,
++        // but use the original path to create the
++        // resource, to preserve aliasing.
++        if (URIUtil.canonicalPath(path) == null)
++            throw new MalformedURLException(path);
+ 
+         if ("/".equals(path))
+             return this;
+diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
+index af377b1..67a31b3 100644
+--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
++++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java
+@@ -97,6 +97,13 @@ public class PathResource extends Resource
+             abs = path.toAbsolutePath();
+         }
+ 
++        // Any normalization difference means it's an alias,
++        // and we don't want to bother further to follow
++        // symlinks as it's an alias anyway.
++        Path normal = path.normalize();
++        if (!isSameName(abs, normal))
++             return normal;
++
+         try
+         {
+             if (Files.isSymbolicLink(path))
+@@ -104,11 +111,8 @@ public class PathResource extends Resource
+             if (Files.exists(path))
+             {
+                 Path real = abs.toRealPath(FOLLOW_LINKS);
+-
+                 if (!isSameName(abs, real))
+-                {
+                     return real;
+-                }
+             }
+         }
+         catch (IOException e)
+@@ -363,12 +367,13 @@ public class PathResource extends Resource
+     @Override
+     public Resource addPath(final String subpath) throws IOException
+     {
+-        String cpath = URIUtil.canonicalPath(subpath);
+-
+-        if ((cpath == null) || (cpath.length() == 0))
++        // Check that the path is within the root,
++        // but use the original path to create the
++        // resource, to preserve aliasing.
++        if (URIUtil.canonicalPath(subpath) == null)
+             throw new MalformedURLException(subpath);
+ 
+-        if ("/".equals(cpath))
++        if ("/".equals(subpath))
+             return this;
+ 
+         // subpaths are always under PathResource
+diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
+index 4574436..3448e94 100644
+--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
++++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java
+@@ -459,10 +459,12 @@ public abstract class Resource implements ResourceFactory, Closeable
+      * Returns the resource contained inside the current resource with the
+      * given name.
+      *
+-     * @param path The path segment to add, which is not encoded
++     * @param path The path segment to add, which is not encoded.  The path may be non canonical, but if so then
++     * the resulting Resource will return true from {@link #isAlias()}.
+      * @return the Resource for the resolved path within this Resource.
+      * @throws IOException if unable to resolve the path
+-     * @throws MalformedURLException if the resolution of the path fails because the input path parameter is malformed.
++     * @throws MalformedURLException if the resolution of the path fails because the input path parameter is malformed, or
++     * a relative path attempts to access above the root resource.
+      */
+     public abstract Resource addPath(String path)
+         throws IOException, MalformedURLException;
+diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
+index ffd9c1c..40d68ab 100644
+--- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
++++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java
+@@ -271,10 +271,11 @@ public class URLResource extends Resource
+     public Resource addPath(String path)
+         throws IOException
+     {
+-        if (path == null)
+-            return null;
+-
+-        path = URIUtil.canonicalPath(path);
++        // Check that the path is within the root,
++        // but use the original path to create the
++        // resource, to preserve aliasing.
++        if (URIUtil.canonicalPath(path) == null)
++            throw new MalformedURLException(path);
+ 
+         return newResource(URIUtil.addEncodedPaths(_url.toExternalForm(), URIUtil.encodePath(path)), _useCaches);
+     }
+diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilCanonicalPathTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilCanonicalPathTest.java
+index 82f2771..a272926 100644
+--- a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilCanonicalPathTest.java
++++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilCanonicalPathTest.java
+@@ -149,4 +149,24 @@ public class URIUtilCanonicalPathTest
+     {
+         assertThat(URIUtil.canonicalPath(input), is(expectedResult));
+     }
++
++    public static Stream<Arguments> queries()
++    {
++        String[][] data =
++            {
++                {"/ctx/../dir?/../index.html", "/dir?/../index.html"},
++                {"/get-files?file=/etc/passwd", "/get-files?file=/etc/passwd"},
++                {"/get-files?file=../../../../../passwd", "/get-files?file=../../../../../passwd"}
++            };
++        return Stream.of(data).map(Arguments::of);
++    }
++
++    @ParameterizedTest
++    @MethodSource("queries")
++    public void testQuery(String input, String expectedPath)
++    {
++        String actual = URIUtil.canonicalURI(input);
++        assertThat(actual, is(expectedPath));
++    }
++
+ }
+diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
+index c93dc73..5d7c7b2 100644
+--- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
++++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/ResourceTest.java
+@@ -21,6 +21,7 @@ package org.eclipse.jetty.util.resource;
+ import java.io.File;
+ import java.io.IOException;
+ import java.io.InputStream;
++import java.net.MalformedURLException;
+ import java.net.URI;
+ import java.net.URL;
+ import java.nio.file.Path;
+@@ -45,6 +46,8 @@ import static org.hamcrest.Matchers.equalTo;
+ import static org.hamcrest.Matchers.startsWith;
+ import static org.junit.jupiter.api.Assertions.assertEquals;
+ import static org.junit.jupiter.api.Assertions.assertNotNull;
++import static org.junit.jupiter.api.Assertions.assertThrows;
++import static org.junit.jupiter.api.Assertions.assertTrue;
+ 
+ public class ResourceTest
+ {
+@@ -325,4 +328,19 @@ public class ResourceTest
+ 
+         assertEquals(rb, ra);
+     }
++
++    @Test
++    public void testClimbAboveBase() throws Exception
++    {
++        Resource resource = Resource.newResource("/foo/bar");
++        assertThrows(MalformedURLException.class, () -> resource.addPath(".."));
++
++        Resource same = resource.addPath(".");
++        assertNotNull(same);
++        assertTrue(same.isAlias());
++
++        assertThrows(MalformedURLException.class, () -> resource.addPath("./.."));
++
++        assertThrows(MalformedURLException.class, () -> resource.addPath("./../bar"));
++    }
+ }


=====================================
debian/patches/series
=====================================
@@ -7,3 +7,4 @@
 09-tweak-distribution.patch
 CVE-2021-28169.patch
 CVE-2021-34428.patch
+CVE-2021-34429.patch



View it on GitLab: https://salsa.debian.org/java-team/jetty9/-/compare/6c7cc80fcd072e4bb28d4e8ed441d625ea0ecc5c...dc520b433e7761c30ef5defb93ae89ea3463a88f

-- 
View it on GitLab: https://salsa.debian.org/java-team/jetty9/-/compare/6c7cc80fcd072e4bb28d4e8ed441d625ea0ecc5c...dc520b433e7761c30ef5defb93ae89ea3463a88f
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/20210718/c74afff6/attachment.htm>


More information about the pkg-java-commits mailing list