Skip to content

Commit 765aa89

Browse files
committed
feat: Validate integer format 'int32' for 32-bit overflow
1 parent 1ad2c00 commit 765aa89

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

src/main/java/com/retailsvc/http/validate/DefaultValidator.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@
3636
import java.util.UUID;
3737
import java.util.concurrent.ConcurrentHashMap;
3838
import java.util.concurrent.ConcurrentMap;
39+
import java.util.function.DoublePredicate;
3940
import java.util.function.Function;
41+
import java.util.function.LongPredicate;
4042
import java.util.function.Predicate;
4143
import java.util.regex.Pattern;
4244
import java.util.regex.PatternSyntaxException;
@@ -47,6 +49,10 @@ public final class DefaultValidator implements Validator {
4749

4850
private record FormatCheck(Predicate<String> isValid, String message) {}
4951

52+
private record IntegerFormatCheck(LongPredicate isValid, String message) {}
53+
54+
private record NumberFormatCheck(DoublePredicate isValid, String message) {}
55+
5056
private static final Pattern EMAIL = Pattern.compile("^[^\\s@]++@[^\\s@.]++\\.[^\\s@]++$");
5157

5258
private static final Pattern HOSTNAME =
@@ -75,6 +81,15 @@ private record FormatCheck(Predicate<String> isValid, String message) {}
7581
Map.entry("binary", new FormatCheck(s -> true, "not valid binary")),
7682
Map.entry("password", new FormatCheck(s -> true, "not valid password")));
7783

84+
private static final Map<String, IntegerFormatCheck> INTEGER_FORMAT_CHECKS =
85+
Map.of(
86+
"int32",
87+
new IntegerFormatCheck(
88+
n -> n >= Integer.MIN_VALUE && n <= Integer.MAX_VALUE,
89+
"value does not fit in int32"));
90+
91+
private static final Map<String, NumberFormatCheck> NUMBER_FORMAT_CHECKS = Map.of();
92+
7893
private final Function<String, Schema> refResolver;
7994
private final ConcurrentMap<String, Pattern> compiledPatterns = new ConcurrentHashMap<>();
8095

@@ -154,6 +169,26 @@ private void validateStringFormat(String str, String format, String pointer) {
154169
}
155170
}
156171

172+
private void validateIntegerFormat(long n, String format, String pointer) {
173+
IntegerFormatCheck check = INTEGER_FORMAT_CHECKS.get(format);
174+
if (check == null) {
175+
return;
176+
}
177+
if (!check.isValid().test(n)) {
178+
fail(pointer, FORMAT_KEYWORD, check.message(), n);
179+
}
180+
}
181+
182+
private void validateNumberFormat(double n, String format, String pointer) {
183+
NumberFormatCheck check = NUMBER_FORMAT_CHECKS.get(format);
184+
if (check == null) {
185+
return;
186+
}
187+
if (!check.isValid().test(n)) {
188+
fail(pointer, FORMAT_KEYWORD, check.message(), n);
189+
}
190+
}
191+
157192
private static boolean isUuid(String s) {
158193
try {
159194
UUID.fromString(s);
@@ -336,6 +371,9 @@ private void validateInteger(Object value, IntegerSchema s, String pointer) {
336371
if (s.multipleOf() != null && n % s.multipleOf() != 0) {
337372
fail(pointer, "multipleOf", "not a multiple of " + s.multipleOf(), n);
338373
}
374+
if (s.format() != null) {
375+
validateIntegerFormat(n, s.format(), pointer);
376+
}
339377
}
340378

341379
private void validateNumber(Object value, NumberSchema s, String pointer) {
@@ -371,6 +409,9 @@ private void validateNumber(Object value, NumberSchema s, String pointer) {
371409
if (s.multipleOf() != null && !isMultipleOf(n, s.multipleOf().doubleValue())) {
372410
fail(pointer, "multipleOf", "not a multiple of " + s.multipleOf(), n);
373411
}
412+
if (s.format() != null) {
413+
validateNumberFormat(n, s.format(), pointer);
414+
}
374415
}
375416

376417
/**

src/test/java/com/retailsvc/http/validate/StringIntegerNumberTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,18 @@ void stringFormatUnknownIsIgnored() {
241241
Set.of(TypeName.STRING), null, null, null, "definitely-not-a-format", null);
242242
assertThatCode(() -> v.validate("anything", s, "/v")).doesNotThrowAnyException();
243243
}
244+
245+
@Test
246+
void integerFormatInt32() {
247+
IntegerSchema s =
248+
new IntegerSchema(Set.of(TypeName.INTEGER), null, null, null, null, null, "int32");
249+
assertThatCode(() -> v.validate(Integer.MAX_VALUE, s, "/v")).doesNotThrowAnyException();
250+
assertThatCode(() -> v.validate(Integer.MIN_VALUE, s, "/v")).doesNotThrowAnyException();
251+
assertThatThrownBy(() -> v.validate(Integer.MAX_VALUE + 1L, s, "/v"))
252+
.extracting(t -> ((ValidationException) t).error().keyword())
253+
.isEqualTo("format");
254+
assertThatThrownBy(() -> v.validate(Integer.MIN_VALUE - 1L, s, "/v"))
255+
.extracting(t -> ((ValidationException) t).error().keyword())
256+
.isEqualTo("format");
257+
}
244258
}

0 commit comments

Comments
 (0)