Skip to content

Commit 97586b5

Browse files
Artur-jhoeller
authored andcommitted
fix: Fix PathMatchingResourcePatternResolver to handle absolute paths in JAR manifests
When JAR manifest Class-Path entries contain absolute paths (as Gradle creates on Windows for long classpaths), PathMatchingResourcePatternResolver incorrectly rejected them. Fixes #35730 Signed-off-by: Artur Signell <[email protected]>
1 parent f89737e commit 97586b5

File tree

2 files changed

+53
-2
lines changed

2 files changed

+53
-2
lines changed

spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -630,8 +630,15 @@ private Set<ClassPathManifestEntry> getClassPathManifestEntriesFromJar(File jar)
630630
// See jdk.internal.loader.URLClassPath.JarLoader.tryResolveFile(URL, String)
631631
continue;
632632
}
633-
File candidate = new File(parent, path);
634-
if (candidate.isFile() && candidate.getCanonicalPath().contains(parent.getCanonicalPath())) {
633+
634+
// Handle absolute paths correctly - don't use parent for absolute paths
635+
File pathFile = new File(path);
636+
File candidate = pathFile.isAbsolute() ? pathFile : new File(parent, path);
637+
638+
// For relative paths, enforce security check (must be under parent)
639+
// For absolute paths, just verify file exists (matching JVM behavior)
640+
if (candidate.isFile() &&
641+
(pathFile.isAbsolute() || candidate.getCanonicalPath().contains(parent.getCanonicalPath()))) {
635642
manifestEntries.add(ClassPathManifestEntry.of(candidate, this.useCaches));
636643
}
637644
}

spring-core/src/test/java/org/springframework/core/io/support/PathMatchingResourcePatternResolverTests.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,21 @@ void javaDashJarFindsClassPathManifestEntries() throws Exception {
337337
assertThat(result.replace("\\", "/")).contains("!!!!").contains("/lib/asset.jar!/assets/file.txt");
338338
}
339339

340+
@Test
341+
void javaDashJarFindsAbsoluteClassPathManifestEntries() throws Exception {
342+
Path assetJar = this.temp.resolve("dependency").resolve("asset.jar");
343+
Files.createDirectories(assetJar.getParent());
344+
writeAssetJar(assetJar);
345+
writeApplicationJarWithAbsolutePath(this.temp.resolve("app.jar"), assetJar);
346+
String java = ProcessHandle.current().info().command().get();
347+
Process process = new ProcessBuilder(java, "-jar", "app.jar")
348+
.directory(this.temp.toFile())
349+
.start();
350+
assertThat(process.waitFor()).isZero();
351+
String result = StreamUtils.copyToString(process.getInputStream(), StandardCharsets.UTF_8);
352+
assertThat(result.replace("\\", "/")).contains("!!!!").contains("asset.jar!/assets/file.txt");
353+
}
354+
340355
private void writeAssetJar(Path path) throws Exception {
341356
try (JarOutputStream jar = new JarOutputStream(new FileOutputStream(path.toFile()))) {
342357
jar.putNextEntry(new ZipEntry("assets/"));
@@ -392,6 +407,35 @@ private void writeApplicationJar(Path path) throws Exception {
392407
assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue();
393408
}
394409

410+
private void writeApplicationJarWithAbsolutePath(Path path, Path assetJar) throws Exception {
411+
Manifest manifest = new Manifest();
412+
Attributes mainAttributes = manifest.getMainAttributes();
413+
mainAttributes.put(Name.CLASS_PATH, buildSpringClassPath() + assetJar.toAbsolutePath());
414+
mainAttributes.put(Name.MAIN_CLASS, ClassPathManifestEntriesTestApplication.class.getName());
415+
mainAttributes.put(Name.MANIFEST_VERSION, "1.0");
416+
try (JarOutputStream jar = new JarOutputStream(new FileOutputStream(path.toFile()), manifest)) {
417+
String appClassResource = ClassUtils.convertClassNameToResourcePath(
418+
ClassPathManifestEntriesTestApplication.class.getName()) + ClassUtils.CLASS_FILE_SUFFIX;
419+
String folder = "";
420+
for (String name : appClassResource.split("/")) {
421+
if (!name.endsWith(ClassUtils.CLASS_FILE_SUFFIX)) {
422+
folder += name + "/";
423+
jar.putNextEntry(new ZipEntry(folder));
424+
jar.closeEntry();
425+
}
426+
else {
427+
jar.putNextEntry(new ZipEntry(folder + name));
428+
try (InputStream in = getClass().getResourceAsStream(name)) {
429+
in.transferTo(jar);
430+
}
431+
jar.closeEntry();
432+
}
433+
}
434+
}
435+
assertThat(new FileSystemResource(path).exists()).isTrue();
436+
assertThat(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + path + ResourceUtils.JAR_URL_SEPARATOR).exists()).isTrue();
437+
}
438+
395439
private String buildSpringClassPath() throws Exception {
396440
return copyClasses(PathMatchingResourcePatternResolver.class, "spring-core") +
397441
copyClasses(LogFactory.class, "commons-logging");

0 commit comments

Comments
 (0)