Skip to content

Commit 04c5043

Browse files
committed
fix: Reject NaN and Infinity as Number coercion results
Double.parseDouble accepts "NaN", "Infinity", and "-Infinity", which are not valid JSON numbers. They would slip past NumberSchema coercion and bypass numeric constraint validation (any comparison with NaN is false). Reject non-finite results with the same type error used for other coercion failures.
1 parent 2acc5db commit 04c5043

2 files changed

Lines changed: 31 additions & 1 deletion

File tree

src/main/java/com/retailsvc/http/internal/ValueCoercion.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,18 @@ public static Object coerce(String raw, Schema schema, String pointer) {
2323
}
2424
}
2525
case NumberSchema _ -> {
26+
double d;
2627
try {
27-
yield Double.parseDouble(raw);
28+
d = Double.parseDouble(raw);
2829
} catch (NumberFormatException _) {
2930
throw new ValidationException(
3031
new ValidationError(pointer, "type", "expected number", raw));
3132
}
33+
if (!Double.isFinite(d)) {
34+
throw new ValidationException(
35+
new ValidationError(pointer, "type", "expected number", raw));
36+
}
37+
yield d;
3238
}
3339
case BooleanSchema _ -> {
3440
if ("true".equals(raw)) {

src/test/java/com/retailsvc/http/internal/ValueCoercionTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,30 @@ void numberCoercionFailureThrowsValidationException() {
6060
.isInstanceOf(ValidationException.class);
6161
}
6262

63+
@Test
64+
void rejectsNaNAsNumber() {
65+
assertThatThrownBy(() -> ValueCoercion.coerce("NaN", numSchema, "/x"))
66+
.isInstanceOf(ValidationException.class)
67+
.extracting("error.pointer", "error.keyword")
68+
.containsExactly("/x", "type");
69+
}
70+
71+
@Test
72+
void rejectsPositiveInfinityAsNumber() {
73+
assertThatThrownBy(() -> ValueCoercion.coerce("Infinity", numSchema, "/x"))
74+
.isInstanceOf(ValidationException.class)
75+
.extracting("error.pointer", "error.keyword")
76+
.containsExactly("/x", "type");
77+
}
78+
79+
@Test
80+
void rejectsNegativeInfinityAsNumber() {
81+
assertThatThrownBy(() -> ValueCoercion.coerce("-Infinity", numSchema, "/x"))
82+
.isInstanceOf(ValidationException.class)
83+
.extracting("error.pointer", "error.keyword")
84+
.containsExactly("/x", "type");
85+
}
86+
6387
@Test
6488
void booleanCoercionFailureThrowsValidationException() {
6589
assertThatThrownBy(() -> ValueCoercion.coerce("yes", boolSchema, "/b"))

0 commit comments

Comments
 (0)