From da52155b7b716424cbdf09317520ffc508cdeec1 Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Thu, 31 Aug 2023 16:39:14 -0700 Subject: [PATCH 1/2] Limit timestamp string length --- .../java/software/amazon/awssdk/utils/DateUtils.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/utils/src/main/java/software/amazon/awssdk/utils/DateUtils.java b/utils/src/main/java/software/amazon/awssdk/utils/DateUtils.java index 6dda13367d1a..16c437034b3e 100644 --- a/utils/src/main/java/software/amazon/awssdk/utils/DateUtils.java +++ b/utils/src/main/java/software/amazon/awssdk/utils/DateUtils.java @@ -200,6 +200,8 @@ public static Instant parseUnixTimestampInstant(String dateString) throws Number if (dateString == null) { return null; } + + validateTimestampLength(dateString); BigDecimal dateValue = new BigDecimal(dateString); return Instant.ofEpochMilli(dateValue.scaleByPowerOfTen(MILLI_SECOND_PRECISION).longValue()); } @@ -225,4 +227,13 @@ public static String formatUnixTimestampInstant(Instant instant) { return dateValue.scaleByPowerOfTen(0 - MILLI_SECOND_PRECISION) .toPlainString(); } + + private static void validateTimestampLength(String timestamp) { + // Helps avoid BigDecimal parsing unnecessarily large numbers, since it's unbounded + // Long has a max value of 9,223,372,036,854,775,807, which is 19 digits. Assume that a valid timestamp is no + // no longer than 20 characters long (+1 for decimal) + if (timestamp.length() > 20) { + throw new RuntimeException("Input timestamp string must be no longer than 20 characters"); + } + } } From fbad45099a301d5de6cdc4cef9aa564b12c777bc Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Fri, 1 Sep 2023 11:23:14 -0700 Subject: [PATCH 2/2] Add test --- .../software/amazon/awssdk/utils/DateUtilsTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/utils/src/test/java/software/amazon/awssdk/utils/DateUtilsTest.java b/utils/src/test/java/software/amazon/awssdk/utils/DateUtilsTest.java index 20d21a01d0ba..884999681aa0 100644 --- a/utils/src/test/java/software/amazon/awssdk/utils/DateUtilsTest.java +++ b/utils/src/test/java/software/amazon/awssdk/utils/DateUtilsTest.java @@ -20,6 +20,7 @@ import static java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME; import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static software.amazon.awssdk.utils.DateUtils.ALTERNATE_ISO_8601_DATE_FORMAT; @@ -39,6 +40,8 @@ import java.util.Locale; import java.util.TimeZone; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.Stream; import org.junit.Test; public class DateUtilsTest { @@ -295,4 +298,12 @@ public void testUnixTimestampRoundtrip() throws Exception { }); } + @Test + public void parseUnixTimestampInstant_longerThan20Char_throws() { + String largeNum = Stream.generate(() -> "9").limit(21).collect(Collectors.joining()); + assertThatThrownBy(() -> DateUtils.parseUnixTimestampInstant(largeNum)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("20"); + } + }