Skip to content

Commit 0912497

Browse files
committed
Merge branch '6.2.x'
2 parents 324b254 + 15563ee commit 0912497

File tree

2 files changed

+55
-2
lines changed

2 files changed

+55
-2
lines changed

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -614,10 +614,12 @@ private Set<ClassPathManifestEntry> getClassPathManifestEntries() {
614614
private Set<ClassPathManifestEntry> getClassPathManifestEntriesFromJar(File jar) throws IOException {
615615
URL base = jar.toURI().toURL();
616616
File parent = jar.getAbsoluteFile().getParentFile();
617+
617618
try (JarFile jarFile = new JarFile(jar)) {
618619
Manifest manifest = jarFile.getManifest();
619620
Attributes attributes = (manifest != null ? manifest.getMainAttributes() : null);
620621
String classPath = (attributes != null ? attributes.getValue(Name.CLASS_PATH) : null);
622+
621623
Set<ClassPathManifestEntry> manifestEntries = new LinkedHashSet<>();
622624
if (StringUtils.hasLength(classPath)) {
623625
StringTokenizer tokenizer = new StringTokenizer(classPath);
@@ -627,8 +629,15 @@ private Set<ClassPathManifestEntry> getClassPathManifestEntriesFromJar(File jar)
627629
// See jdk.internal.loader.URLClassPath.JarLoader.tryResolveFile(URL, String)
628630
continue;
629631
}
630-
File candidate = new File(parent, path);
631-
if (candidate.isFile() && candidate.getCanonicalPath().contains(parent.getCanonicalPath())) {
632+
633+
// Handle absolute paths correctly: do not apply parent to absolute paths.
634+
File pathFile = new File(path);
635+
File candidate = (pathFile.isAbsolute() ? pathFile : new File(parent, path));
636+
637+
// For relative paths, enforce security check: must be under parent.
638+
// For absolute paths, just verify file exists (matching JVM behavior).
639+
if (candidate.isFile() && (pathFile.isAbsolute() ||
640+
candidate.getCanonicalPath().contains(parent.getCanonicalPath()))) {
632641
manifestEntries.add(ClassPathManifestEntry.of(candidate, this.useCaches));
633642
}
634643
}

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)