From 1900a11f8ab121bd0e2c7c7b490277ce0dd981e1 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Thu, 16 Sep 2021 09:52:44 +0100 Subject: [PATCH] Fix Undertow's resource loading when filename contains reserved chars Fixes gh-9283 --- .../embedded/undertow/JarResourceManager.java | 16 ++-- .../undertow/JarResourceManagerTests.java | 73 ++++++++++++------- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/JarResourceManager.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/JarResourceManager.java index d118d99412..fd7424ea8c 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/JarResourceManager.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/JarResourceManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.boot.web.embedded.undertow; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; import java.net.URL; import io.undertow.UndertowMessages; @@ -39,16 +40,17 @@ class JarResourceManager implements ResourceManager { private final String jarPath; JarResourceManager(File jarFile) { - this(jarFile.getAbsolutePath()); - } - - JarResourceManager(String jarPath) { - this.jarPath = jarPath; + try { + this.jarPath = jarFile.getAbsoluteFile().toURI().toURL().toString(); + } + catch (MalformedURLException ex) { + throw new IllegalArgumentException(ex); + } } @Override public Resource getResource(String path) throws IOException { - URL url = new URL("jar:file:" + this.jarPath + "!" + (path.startsWith("/") ? path : "/" + path)); + URL url = new URL("jar:" + this.jarPath + "!" + (path.startsWith("/") ? path : "/" + path)); URLResource resource = new URLResource(url, path); if (StringUtils.hasText(path) && !"/".equals(path) && resource.getContentLength() < 0) { return null; diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/JarResourceManagerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/JarResourceManagerTests.java index 08785965dc..131b254a6d 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/JarResourceManagerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/undertow/JarResourceManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,14 +19,23 @@ package org.springframework.boot.web.embedded.undertow; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Arrays; +import java.util.List; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import io.undertow.server.handlers.resource.Resource; import io.undertow.server.handlers.resource.ResourceManager; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -37,46 +46,58 @@ import static org.assertj.core.api.Assertions.assertThat; */ class JarResourceManagerTests { - private ResourceManager resourceManager; + @TempDir + static File tempDir; - @BeforeEach - void createJar(@TempDir File tempDir) throws IOException { - File jar = new File(tempDir, "test.jar"); - try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar))) { - out.putNextEntry(new ZipEntry("hello.txt")); - out.write("hello".getBytes()); - } - this.resourceManager = new JarResourceManager(jar); - } - - @Test - void emptyPathIsHandledCorrectly() throws IOException { - Resource resource = this.resourceManager.getResource(""); + @ResourceManagersTest + void emptyPathIsHandledCorrectly(String filename, ResourceManager resourceManager) throws IOException { + Resource resource = resourceManager.getResource(""); assertThat(resource).isNotNull(); assertThat(resource.isDirectory()).isTrue(); } - @Test - void rootPathIsHandledCorrectly() throws IOException { - Resource resource = this.resourceManager.getResource("/"); + @ResourceManagersTest + void rootPathIsHandledCorrectly(String filename, ResourceManager resourceManager) throws IOException { + Resource resource = resourceManager.getResource("/"); assertThat(resource).isNotNull(); assertThat(resource.isDirectory()).isTrue(); } - @Test - void resourceIsFoundInJarFile() throws IOException { - Resource resource = this.resourceManager.getResource("/hello.txt"); + @ResourceManagersTest + void resourceIsFoundInJarFile(String filename, ResourceManager resourceManager) throws IOException { + Resource resource = resourceManager.getResource("/hello.txt"); assertThat(resource).isNotNull(); assertThat(resource.isDirectory()).isFalse(); assertThat(resource.getContentLength()).isEqualTo(5); } - @Test - void resourceIsFoundInJarFileWithoutLeadingSlash() throws IOException { - Resource resource = this.resourceManager.getResource("hello.txt"); + @ResourceManagersTest + void resourceIsFoundInJarFileWithoutLeadingSlash(String filename, ResourceManager resourceManager) + throws IOException { + Resource resource = resourceManager.getResource("hello.txt"); assertThat(resource).isNotNull(); assertThat(resource.isDirectory()).isFalse(); assertThat(resource.getContentLength()).isEqualTo(5); } + static List resourceManagers() throws IOException { + File jar = new File(tempDir, "test.jar"); + try (JarOutputStream out = new JarOutputStream(new FileOutputStream(jar))) { + out.putNextEntry(new ZipEntry("hello.txt")); + out.write("hello".getBytes()); + } + File troublesomeNameJar = new File(tempDir, "test##1.0.jar"); + FileCopyUtils.copy(jar, troublesomeNameJar); + return Arrays.asList(Arguments.of(jar.getName(), new JarResourceManager(jar)), + Arguments.of(troublesomeNameJar.getName(), new JarResourceManager(troublesomeNameJar))); + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource("resourceManagers") + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + private @interface ResourceManagersTest { + + } + }