This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/json-nan in repository https://gitbox.apache.org/repos/asf/cxf.git
commit d4ee0a41964437e3621284cdedb2563046d7f6c5 Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri May 22 15:31:07 2026 +0100 Handle NaN or infinite times in JSON/JWT --- .../json/basic/JsonMapObjectReaderWriter.java | 6 +++- .../json/basic/JsonMapObjectReaderWriterTest.java | 22 ++++++++++++ .../apache/cxf/rs/security/jose/jwt/JwtUtils.java | 37 +++++++++++++++----- .../cxf/rs/security/jose/jwt/JwtUtilsTest.java | 40 ++++++++++++++++++++++ 4 files changed, 95 insertions(+), 10 deletions(-) diff --git a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java index 88445b17e68..a99e7c43e73 100644 --- a/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java +++ b/rt/rs/extensions/json-basic/src/main/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriter.java @@ -258,7 +258,11 @@ public class JsonMapObjectReaderWriter { try { value = Long.valueOf(valueStr); } catch (NumberFormatException ex) { - value = Double.valueOf(valueStr); + Double doubleValue = Double.valueOf(valueStr); + if (doubleValue.isInfinite() || doubleValue.isNaN()) { + throw new NumberFormatException("Non-finite numeric value is not allowed"); + } + value = doubleValue; } } diff --git a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java index 424eb72c5dd..9c66add08c1 100644 --- a/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java +++ b/rt/rs/extensions/json-basic/src/test/java/org/apache/cxf/jaxrs/json/basic/JsonMapObjectReaderWriterTest.java @@ -33,6 +33,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class JsonMapObjectReaderWriterTest { @@ -210,4 +211,25 @@ public class JsonMapObjectReaderWriterTest { assertEquals("a\\", entry.getValue()); } + @Test + public void testRejectInfinityNumericValue() { + assertInvalidNumericLiteral("Infinity"); + assertInvalidNumericLiteral("-Infinity"); + } + + @Test + public void testRejectNaNNumericValue() { + assertInvalidNumericLiteral("NaN"); + } + + private void assertInvalidNumericLiteral(String value) { + JsonMapObjectReaderWriter jsonMapObjectReaderWriter = new JsonMapObjectReaderWriter(); + try { + jsonMapObjectReaderWriter.fromJson("{\"exp\":" + value + "}"); + fail("Expected NumberFormatException for invalid numeric value: " + value); + } catch (NumberFormatException ex) { + // expected + } + } + } diff --git a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java index 31653190db8..10b08220a93 100644 --- a/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java +++ b/rt/rs/security/jose-parent/jose/src/main/java/org/apache/cxf/rs/security/jose/jwt/JwtUtils.java @@ -50,9 +50,14 @@ public final class JwtUtils { return; } Instant now = Instant.now(); - Instant expires = Instant.ofEpochMilli(expiryTime * 1000L); - if (clockOffset != 0) { - expires = expires.plusSeconds(clockOffset); + Instant expires; + try { + expires = Instant.ofEpochSecond(expiryTime); + if (clockOffset != 0) { + expires = expires.plusSeconds(clockOffset); + } + } catch (RuntimeException ex) { + throw new JwtException("The token has expired", ex); } if (expires.isBefore(now)) { throw new JwtException("The token has expired"); @@ -69,10 +74,15 @@ public final class JwtUtils { } Instant validCreation = Instant.now(); - if (clockOffset != 0) { - validCreation = validCreation.plusSeconds(clockOffset); + Instant notBeforeDate; + try { + if (clockOffset != 0) { + validCreation = validCreation.plusSeconds(clockOffset); + } + notBeforeDate = Instant.ofEpochSecond(notBeforeTime); + } catch (RuntimeException ex) { + throw new JwtException("The token cannot be accepted yet", ex); } - Instant notBeforeDate = Instant.ofEpochMilli(notBeforeTime * 1000L); // Check to see if the not before time is in the future if (notBeforeDate.isAfter(validCreation)) { @@ -89,11 +99,20 @@ public final class JwtUtils { return; } - Instant createdDate = Instant.ofEpochMilli(issuedAtInSecs * 1000L); + Instant createdDate; + try { + createdDate = Instant.ofEpochSecond(issuedAtInSecs); + } catch (RuntimeException ex) { + throw new JwtException("Invalid issuedAt", ex); + } Instant validCreation = Instant.now(); - if (clockOffset != 0) { - validCreation = validCreation.plusSeconds(clockOffset); + try { + if (clockOffset != 0) { + validCreation = validCreation.plusSeconds(clockOffset); + } + } catch (RuntimeException ex) { + throw new JwtException("Invalid issuedAt", ex); } // Check to see if the IssuedAt time is in the future diff --git a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwt/JwtUtilsTest.java b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwt/JwtUtilsTest.java index e53a47060c8..af58d2d0e7c 100644 --- a/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwt/JwtUtilsTest.java +++ b/rt/rs/security/jose-parent/jose/src/test/java/org/apache/cxf/rs/security/jose/jwt/JwtUtilsTest.java @@ -174,4 +174,44 @@ public class JwtUtilsTest { } } + @org.junit.Test + public void testInfiniteExpiryTokenRejected() { + try { + String claimsJson = "{\"sub\":\"alice\",\"iss\":\"DoubleItSTSIssuer\"," + + "\"exp\":Infinity}"; + JwtClaims claims = JwtUtils.jsonToClaims(claimsJson); + JwtUtils.validateJwtExpiry(claims, 0, true); + fail("Failure expected on a token with infinite expiry"); + } catch (JwtException | NumberFormatException ex) { + // expected + } + } + + @org.junit.Test + public void testInfiniteNotBeforeTokenRejected() { + try { + String claimsJson = "{\"sub\":\"alice\",\"iss\":\"DoubleItSTSIssuer\"," + + "\"nbf\":Infinity}"; + JwtClaims claims = JwtUtils.jsonToClaims(claimsJson); + JwtUtils.validateJwtNotBefore(claims, 0, true); + fail("Failure expected on a token with infinite not before"); + } catch (JwtException | NumberFormatException ex) { + // expected + } + } + + @org.junit.Test + public void testInfiniteIssuedAtTokenRejected() { + try { + String claimsJson = "{\"sub\":\"alice\",\"iss\":\"DoubleItSTSIssuer\"," + + "\"iat\":Infinity}"; + JwtClaims claims = JwtUtils.jsonToClaims(claimsJson); + JwtUtils.validateJwtIssuedAt(claims, 0, 0, true); + fail("Failure expected on a token with infinite issued at"); + } catch (JwtException | NumberFormatException ex) { + // expected + } + } + + }
