diff --git a/src/main/java/com/networknt/schema/utils/RFC5892.java b/src/main/java/com/networknt/schema/utils/RFC5892.java index a21931cae..6ad2cf473 100644 --- a/src/main/java/com/networknt/schema/utils/RFC5892.java +++ b/src/main/java/com/networknt/schema/utils/RFC5892.java @@ -99,13 +99,16 @@ public static boolean isValid(String value) { if ("".equals(value)) { return false; // empty string should fail } - if (value.length() == 1 && value.matches(LABEL_SEPARATOR_REGEX)) { - return false; // single label separator should fail - } // RFC 5892 calls each segment in a host name a label. They are separated by all the recognized label separators. String[] labels = value.split(LABEL_SEPARATOR_REGEX); + if (labels.length == 0) { + return false; // a value made up only of label separators (e.g. "." or "...") has no labels + } for (String label : labels) { - if (label.isEmpty()) continue; // A DNS entry may contain a trailing '.'. + // String.split() drops trailing empty strings, so a trailing '.' never + // produces an empty label here. A leading or interior empty label + // (e.g. ".example" or "a..b") is invalid. + if (label.isEmpty()) return false; String unicode = label; if (isACE(label)) { diff --git a/src/test/java/com/networknt/schema/FormatValidatorTest.java b/src/test/java/com/networknt/schema/FormatValidatorTest.java index 9dd83591a..421611dfd 100644 --- a/src/test/java/com/networknt/schema/FormatValidatorTest.java +++ b/src/test/java/com/networknt/schema/FormatValidatorTest.java @@ -220,4 +220,25 @@ void draft7DisableFormat() { }); assertEquals(0, messages.size()); } + + @Test + void idnHostnameRejectsEmptyLabels() { + String schemaData = "{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"format\":\"idn-hostname\"}"; + Schema schema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12).getSchema(schemaData); + // A leading or interior empty label (a leading dot, or two adjacent dots) is invalid. + assertFalse(schema.validate("\".example\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + assertFalse(schema.validate("\"a..b\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + // A value made up only of label separators has no labels and is invalid. + assertFalse(schema.validate("\"...\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + assertFalse(schema.validate("\"。。\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + // Ordinary host names remain valid. + assertTrue(schema.validate("\"example\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + assertTrue(schema.validate("\"sub.example.com\"", InputFormat.JSON, + ec -> ec.executionConfig(c -> c.formatAssertionsEnabled(true))).isEmpty()); + } }