Reject empty labels in idn-hostname format - #1274
Conversation
RFC5892.isValid skipped empty labels with continue, so a leading or interior empty label -- ".example" or "a..b" -- validated as a valid idn-hostname. String.split already drops trailing empty labels, so the skip only ever hit leading/interior empties, which are invalid. Reject them.
There was a problem hiding this comment.
Pull request overview
Updates idn-hostname format validation to reject hostnames containing leading or interior empty labels (e.g. .example, a..b), aligning behavior with the JSON-Schema-Test-Suite and reference validators.
Changes:
- Change
RFC5892.isValidto fail fast on empty labels produced by splitting hostnames into labels. - Add a unit test asserting
.exampleanda..bare invalid while ordinary hostnames remain valid.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| src/main/java/com/networknt/schema/utils/RFC5892.java | Tightens IDN hostname validation by rejecting empty labels during label iteration. |
| src/test/java/com/networknt/schema/FormatValidatorTest.java | Adds regression coverage for invalid empty-label idn-hostname inputs and confirms valid hostnames still pass. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| 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; |
| // 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()); |
String.split returns an empty array for a value that is all separators
("..." or "。。"), so the loop was skipped and it validated as true. Reject
when there are no labels; this also subsumes the single-separator case.
|
Good catch. A value that is only separators ( |
|
@copilot resolve the merge conflicts in this pull request |
| // 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. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
src/test/java/com/networknt/schema/FormatValidatorTest.java:242
- The new test exercises leading/interior empty labels, but it doesn’t cover the edge case where adjacent separators occur at the end (e.g.
"a.."), which can be mishandled if the implementation drops trailing empty split tokens. Also, since the implementation explicitly allows a trailing separator, adding a regression assertion for a trailing-dot hostname (e.g."example.") would lock in the intended behavior described in the PR.
// 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());
src/main/java/com/networknt/schema/utils/RFC5892.java:106
String.split(regex)with the default limit drops all trailing empty strings. That means inputs like"a.."(adjacent separators at the end) will be split to["a"], so the newlabel.isEmpty()check never runs and the empty label is not rejected. To reliably detect empty labels while still allowing exactly one trailing separator, preserve trailing empties withsplit(..., -1)and explicitly ignore only a single final empty label.
// 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
}
The
idn-hostnameformat accepts host names with a leading or interior empty label:RFC5892.isValidskips empty labels withcontinue, with a comment about a trailing.. ButString.splitalready drops trailing empty strings, so the skip only ever hits a leading or interior empty label, both of which are invalid. Rejecting them matches the JSON-Schema-Test-Suite (idn-hostname.json: "leading dot" and "empty label between two dots is invalid") and the reference validator. Ordinary host names (and trailing dots) are unaffected; the full suite stays green (added a test).