From 472ede48cd35748c4d3d7a5206b96bc068757f57 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 16 Aug 2019 15:48:42 -0500 Subject: [PATCH] Issue #3983 - JarFileResource directory listing is invalid + Correcting encoded path searching + Adding more unit tests to ensure no regression Signed-off-by: Joakim Erdfelt --- .../jetty/util/resource/JarFileResource.java | 14 ++-- .../jetty/util/resource/JarResourceTest.java | 78 +++++++++++++++++- .../src/test/resources/jar-file-resource.jar | Bin 0 -> 3172 bytes 3 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 jetty-util/src/test/resources/jar-file-resource.jar diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java index 044a244ecaf..3ceb0a55cd4 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java @@ -29,7 +29,6 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -116,9 +115,11 @@ public class JarFileResource extends JarResource _jarFile = null; _list = null; - int sep = _urlString.lastIndexOf("!/"); - _jarUrl = _urlString.substring(0, sep + 2); - _path = URIUtil.decodePath(_urlString.substring(sep + 2)); + // Work with encoded URL path + String urlFilePath = _url.getPath(); + int sep = urlFilePath.lastIndexOf("!/"); + _jarUrl = urlFilePath.substring(0, sep + 2); + _path = URIUtil.decodePath(urlFilePath.substring(sep + 2)); if (_path.length() == 0) _path = null; _jarFile = _jarConnection.getJarFile(); @@ -316,11 +317,12 @@ public class JarFileResource extends JarResource } Enumeration e = jarFile.entries(); - String dir = _urlString.substring(_urlString.lastIndexOf("!/") + 2); + String encodedDir = _urlString.substring(_urlString.lastIndexOf("!/") + 2); + String dir = URIUtil.decodePath(encodedDir); while (e.hasMoreElements()) { JarEntry entry = e.nextElement(); - String name = StringUtil.replace(entry.getName(), '\\', '/'); + String name = entry.getName(); if (!name.startsWith(dir) || name.length() == dir.length()) { continue; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java index 7efcd6754b6..1fc6f4afe5e 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JarResourceTest.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.util.resource; import java.io.IOException; +import java.net.URI; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -41,6 +42,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -138,9 +140,9 @@ public class JarResourceTest @Test public void testJarFileCopyToDirectoryTraversal() throws Exception { - Path testZip = MavenTestingUtils.getTestResourcePathFile("TestData/extract.zip"); + Path extractZip = MavenTestingUtils.getTestResourcePathFile("TestData/extract.zip"); - String s = "jar:" + testZip.toUri().toASCIIString() + "!/"; + String s = "jar:" + extractZip.toUri().toASCIIString() + "!/"; Resource r = Resource.newResource(s); assertThat(r, instanceOf(JarResource.class)); @@ -188,6 +190,78 @@ public class JarResourceTest assertTrue(r.exists()); } + @Test + public void testJarFileResourceList() throws Exception + { + Path testJar = MavenTestingUtils.getTestResourcePathFile("jar-file-resource.jar"); + String uri = "jar:" + testJar.toUri().toASCIIString() + "!/"; + + Resource resource = new JarFileResource(URI.create(uri).toURL()); + Resource rez = resource.addPath("rez/"); + + assertThat("path /rez/ is a dir", rez.isDirectory(), is(true)); + + List actual = Arrays.asList(rez.list()); + String[] expected = new String[]{ + "one", + "aaa", + "bbb", + "oddities/", + "another dir/", + "ccc", + "deep/", + }; + assertThat("Dir contents", actual, containsInAnyOrder(expected)); + } + + /** + * Test getting a file listing of a Directory in a JAR + * Where the JAR entries contain names that are URI encoded / escaped + */ + @Test + public void testJarFileResourceList_PreEncodedEntries() throws Exception + { + Path testJar = MavenTestingUtils.getTestResourcePathFile("jar-file-resource.jar"); + String uri = "jar:" + testJar.toUri().toASCIIString() + "!/"; + + Resource resource = new JarFileResource(URI.create(uri).toURL()); + Resource rez = resource.addPath("rez/oddities/"); + + assertThat("path /rez/oddities/ is a dir", rez.isDirectory(), is(true)); + + List actual = Arrays.asList(rez.list()); + String[] expected = new String[]{ + ";", + "#hashcode", + "index.html#fragment", + "other%2fkind%2Fof%2fslash", // pre-encoded / escaped + "a file with a space", + ";\" onmousedown=\"alert(document.location)\"", + "some\\slash\\you\\got\\there" // not encoded, stored as backslash native + }; + assertThat("Dir contents", actual, containsInAnyOrder(expected)); + } + + @Test + public void testJarFileResourceList_DirWithSpace() throws Exception + { + Path testJar = MavenTestingUtils.getTestResourcePathFile("jar-file-resource.jar"); + String uri = "jar:" + testJar.toUri().toASCIIString() + "!/"; + + Resource resource = new JarFileResource(URI.create(uri).toURL()); + Resource anotherDir = resource.addPath("rez/another dir/"); + + assertThat("path /rez/another dir/ is a dir", anotherDir.isDirectory(), is(true)); + + List actual = Arrays.asList(anotherDir.list()); + String[] expected = new String[]{ + "a file.txt", + "another file.txt", + "..\\a different file.txt", + }; + assertThat("Dir contents", actual, containsInAnyOrder(expected)); + } + private List listFiles(Path dir) throws IOException { return Files.list(dir).collect(Collectors.toList()); diff --git a/jetty-util/src/test/resources/jar-file-resource.jar b/jetty-util/src/test/resources/jar-file-resource.jar new file mode 100644 index 0000000000000000000000000000000000000000..de93283a8373dd95b7b35282f208069a47447aef GIT binary patch literal 3172 zcmb7FQD|FL7{2WiSL4*KX0nOswOwq=(iE!Ghw9TdO}#cvUD`b?3b)D4k}bKnBq?hG zzD%Pm2tt_;g+9pG!%_l6=Zm-rlLT(eZGxrF4LXo6Q&0wqC{D`o|L?u+Irl6n`j-PY z<^11w{{Q^v`vSran}g%JIL?2vdz_=f#@V=(lpDJLEyo35my_%2>f(Mm^s%4X#O;J1 zxJ!|xnrF&gRc*VAj(geSUm-}^*>zwkd9xd~?1n`^ZnXgsMe#sY^-OW+%jLsf9pJO+ zYnOIGGA;seyA8nf^t1_Z;lh3cpu#_Ujs`G_oly#wG9CqZy^KgCo{3B8A;R^0(z^9A zcI$``RBH`k(C_#7#wOhU<>fc)jcVia&z*V&omYRWJOw#>EfqW`TT!26QTt+IIu=$U zk`VOOw9!d-!&43Tf~wC`_tnQoCspmJ>KT|)1#Rq(LA3Vi>(^ZX(`O+XV2F5@C@x2& zId3eJO!P%l;)$drXG}`pvQX88V+^y&_D%PmuMq1QSfp8OU!P~$luS%YxrU>sfYvqq zni7TYbOK7P4OM-EZ>e=nm{7-$y6dB!`j19k*}}EAu{}-;(+YDfJj*2V(Rf1QPscMc zUgXm=Vi;QKu4{wp=%lY^>$$Vw+UKa)&sK{S$3oWO+E-jS;>e!;vS>`M_+Ip`8!&N^ z`Z%VWFR`p6y}TkPmD#iuQBKRR_KFE9m2pRu@GLggn^3}HCa%Z>z2=2(Ln-&au8&Uf<;$9(X`AeMTto zeIu19>zB(%;wkF+3^Tj&DX_bZtMX>%%*iuHta7{Y&cm=BHHK9BG6TrB0yK|;6QILt z#(3dNGQn>YZq^=$RDAjE!TVc)d%YnMii}2~dvZpfvj4^LQycLLhr{M&G@B9QuCG6x zd+|Z>`ZZwH4(t)OG$KheROkO-co1l7+uYooNmg2c@j9#zSpG_RZPl4C6dVTqjgjMT z;QO||@q63h=5jfKYWDXNU);6heEq;;v@js#CLQW$UA*Xlx$jVt(gN48KvulAmDT+ e$}z32*31v-Oc3XQV2A&3+#dML!Fg8oU;hHK@Z9nM literal 0 HcmV?d00001