diff --git a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java index 115aea08d5aaa65876639d7130b7463125c899b6..0a245314b06f5ffbf255d0d83796c7512430adca 100644 --- a/spring-web/src/main/java/org/springframework/http/ContentDisposition.java +++ b/spring-web/src/main/java/org/springframework/http/ContentDisposition.java @@ -42,6 +42,9 @@ import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; */ public final class ContentDisposition { + private static final String INVALID_HEADER_FIELD_PARAMETER_FORMAT = + "Invalid header field parameter format (as defined in RFC 5987)"; + @Nullable private final String type; @@ -357,7 +360,7 @@ public final class ContentDisposition { } /** - * Decode the given header field param as describe in RFC 5987. + * Decode the given header field param as described in RFC 5987. *

Only the US-ASCII, UTF-8 and ISO-8859-1 charsets are supported. * @param input the header field param * @return the encoded header field param @@ -383,13 +386,18 @@ public final class ContentDisposition { bos.write((char) b); index++; } - else if (b == '%') { - char[] array = { (char)value[index + 1], (char)value[index + 2]}; - bos.write(Integer.parseInt(String.valueOf(array), 16)); + else if (b == '%' && index < value.length - 2) { + char[] array = new char[]{(char) value[index + 1], (char) value[index + 2]}; + try { + bos.write(Integer.parseInt(String.valueOf(array), 16)); + } + catch (NumberFormatException ex) { + throw new IllegalArgumentException(INVALID_HEADER_FIELD_PARAMETER_FORMAT, ex); + } index+=3; } else { - throw new IllegalArgumentException("Invalid header field parameter format (as defined in RFC 5987)"); + throw new IllegalArgumentException(INVALID_HEADER_FIELD_PARAMETER_FORMAT); } } return new String(bos.toByteArray(), charset); diff --git a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java index 13acce90a02913058855ff38809ea8c9b116111d..ddf4f8867b7bdbc8da220d4a0c3faf252135ee40 100644 --- a/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java +++ b/spring-web/src/test/java/org/springframework/http/ContentDispositionTests.java @@ -28,6 +28,7 @@ import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Unit tests for {@link ContentDisposition} @@ -36,7 +37,6 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException */ public class ContentDispositionTests { - @Test public void parseTest() { ContentDisposition disposition = ContentDisposition @@ -198,4 +198,22 @@ public class ContentDispositionTests { ReflectionUtils.invokeMethod(decode, null, "UTF-16''test")); } + @Test + public void decodeHeaderFieldParamShortInvalidEncodedFilename() { + Method decode = ReflectionUtils.findMethod(ContentDisposition.class, + "decodeHeaderFieldParam", String.class); + ReflectionUtils.makeAccessible(decode); + assertThatIllegalArgumentException().isThrownBy(() -> + ReflectionUtils.invokeMethod(decode, null, "UTF-8''%A")); + } + + @Test + public void decodeHeaderFieldParamLongerInvalidEncodedFilename() { + Method decode = ReflectionUtils.findMethod(ContentDisposition.class, + "decodeHeaderFieldParam", String.class); + ReflectionUtils.makeAccessible(decode); + assertThatIllegalArgumentException().isThrownBy(() -> + ReflectionUtils.invokeMethod(decode, null, "UTF-8''%A.txt")); + } + }