This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit cca07d50838395b658524414278288b1b00bfac0 Author: opencode <[email protected]> AuthorDate: Wed Oct 14 11:17:54 2026 +0200 Preserve separator slash when escaping custom WAR separator in JAR URLs When a custom WAR separator is configured via the org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR system property, PATTERN_CUSTOM matches the full separator (custom + "/") but REPLACE_CUSTOM percent-encoded only the custom part. The replaceAll in makeSafeForJarUrl therefore deleted the trailing '/' of the separator, silently rewriting any file path containing the separator sequence to a different, non-existent path and producing a broken jar:file: URL. Fix this by appending a literal '/' to REPLACE_CUSTOM so the escaped replacement keeps the separator's trailing slash, consistent with the default handling where "*/" is replaced with "%2a/". As a side effect, an empty custom separator now behaves as a no-op instead of stripping every '/' from the path. Add a regression test asserting the full expected JAR URL with the '@' separator, using the String overload of buildJarUrl so the expected value is independent of platform-specific File.toURI() behaviour. --- java/org/apache/tomcat/util/buf/UriUtil.java | 6 +++++- test/org/apache/tomcat/util/buf/TestUriUtil40.java | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/java/org/apache/tomcat/util/buf/UriUtil.java b/java/org/apache/tomcat/util/buf/UriUtil.java index 0d6bb83aba..0f084560e1 100644 --- a/java/org/apache/tomcat/util/buf/UriUtil.java +++ b/java/org/apache/tomcat/util/buf/UriUtil.java @@ -48,7 +48,7 @@ public final class UriUtil { } else { WAR_SEPARATOR = custom + "/"; PATTERN_CUSTOM = Pattern.compile(Pattern.quote(WAR_SEPARATOR)); - StringBuilder sb = new StringBuilder(custom.length() * 3); + StringBuilder sb = new StringBuilder(custom.length() * 3 + 1); // Deliberately use the platform's default encoding byte[] ba = custom.getBytes(); for (byte toEncode : ba) { @@ -59,6 +59,10 @@ public final class UriUtil { sb.append(HEX[high]); sb.append(HEX[low]); } + // The pattern matches custom + "/" so the trailing '/' has to be + // preserved by the replacement. This is consistent with the + // default handling of "*/" which is replaced with "%2a/". + sb.append('/'); REPLACE_CUSTOM = sb.toString(); } } diff --git a/test/org/apache/tomcat/util/buf/TestUriUtil40.java b/test/org/apache/tomcat/util/buf/TestUriUtil40.java index 10dcaf7a03..297c6e23f1 100644 --- a/test/org/apache/tomcat/util/buf/TestUriUtil40.java +++ b/test/org/apache/tomcat/util/buf/TestUriUtil40.java @@ -16,9 +16,26 @@ */ package org.apache.tomcat.util.buf; +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; + public class TestUriUtil40 extends TesterUriUtilBase { public TestUriUtil40() { super("@"); } + + @Test + public void testBuildJarUrlPreservesSeparatorSlash() throws IOException { + // Use the String overload with a literal file: URL so the expected + // value does not depend on File.toURI() behaviour (which varies + // with platform) + String result = UriUtil.buildJarUrl("file:/patha/pathb@/pathc").toString(); + // The custom separator must be escaped but the trailing '/' of the + // separator must be preserved, consistent with the default handling + // of "*/" being replaced with "%2a/" + Assert.assertEquals("jar:file:/patha/pathb%40/pathc!/", result); + } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
