From 7a87c69dd0abf5f1605515ba0be35edb53fff8a1 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 3 Aug 2017 19:43:44 +0100 Subject: [PATCH] Normalize spec when creating jar URL by removing /../ and /./ Previously when Handler was creating a URL from a context URL and a spec, any occurrances of /../ or /./ in the spec would be left as-is. This differed from the JDK's Handler implementation which normalizes the URL by modifying the path to remove any occurrences of /../ or /./ This commit updates our Handler implementation to align it with the JDK's. Tests have been added to assert that, given the same inputs, the two Handler classes produce the same output. Closes gh-9917 --- .../boot/loader/jar/Handler.java | 29 +++++++++++++++++- .../boot/loader/jar/HandlerTests.java | 30 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java index 4a64223cc0..917f35706d 100644 --- a/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java +++ b/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/jar/Handler.java @@ -203,7 +203,34 @@ public class Handler extends URLStreamHandler { } private void setFile(URL context, String file) { - setURL(context, JAR_PROTOCOL, null, -1, null, null, file, null, null); + setURL(context, JAR_PROTOCOL, null, -1, null, null, normalize(file), null, null); + } + + private String normalize(String file) { + int afterLastSeparatorIndex = file.lastIndexOf(SEPARATOR) + SEPARATOR.length(); + String afterSeparator = file.substring(afterLastSeparatorIndex); + afterSeparator = replaceParentDir(afterSeparator); + afterSeparator = replaceCurrentDir(afterSeparator); + return file.substring(0, afterLastSeparatorIndex) + afterSeparator; + } + + private String replaceParentDir(String file) { + int parentDirIndex; + while ((parentDirIndex = file.indexOf("/../")) >= 0) { + int precedingSlashIndex = file.lastIndexOf('/', parentDirIndex - 1); + if (precedingSlashIndex >= 0) { + file = file.substring(0, precedingSlashIndex) + + file.substring(parentDirIndex + 3); + } + else { + file = file.substring(parentDirIndex + 4); + } + } + return file; + } + + private String replaceCurrentDir(String file) { + return file.replace("/./", "/"); } @Override diff --git a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java index 2fc34b35b1..4744e94199 100644 --- a/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java +++ b/spring-boot-tools/spring-boot-loader/src/test/java/org/springframework/boot/loader/jar/HandlerTests.java @@ -136,6 +136,36 @@ public class HandlerTests { new URL("jar:file:/test.jar!/BOOT-INF/classes/foo.txt"))); } + @Test + public void urlWithSpecReferencingParentDirectory() throws MalformedURLException { + assertStandardAndCustomHandlerUrlsAreEqual( + "file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd", + "../folderB/b.xsd"); + } + + @Test + public void urlWithSpecReferencingAncestorDirectoryOutsideJarStopsAtJarRoot() + throws MalformedURLException { + assertStandardAndCustomHandlerUrlsAreEqual( + "file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd", + "../../../../../../folderB/b.xsd"); + } + + @Test + public void urlWithSpecReferencingCurrentDirectory() throws MalformedURLException { + assertStandardAndCustomHandlerUrlsAreEqual( + "file:/test.jar!/BOOT-INF/classes!/xsd/folderA/a.xsd", + "./folderB/./b.xsd"); + } + + private void assertStandardAndCustomHandlerUrlsAreEqual(String context, String spec) + throws MalformedURLException { + URL standardUrl = new URL(new URL("jar:" + context), spec); + URL customHandlerUrl = new URL(new URL("jar", null, -1, context, this.handler), + spec); + assertThat(customHandlerUrl.toString()).isEqualTo(standardUrl.toString()); + } + private URL createUrl(String file) throws MalformedURLException { return new URL("jar", null, -1, file, this.handler); }