Skip to content

Commit 19011c3

Browse files
fduttonFaron Dutton
andauthored
Adds support for validating an IRI (#768)
Resolves #767 Co-authored-by: Faron Dutton <[email protected]>
1 parent cb7c53b commit 19011c3

File tree

12 files changed

+51
-28
lines changed

12 files changed

+51
-28
lines changed

src/main/java/com/networknt/schema/JsonMetaSchema.java

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.fasterxml.jackson.databind.JsonNode;
2020
import com.networknt.schema.format.DateFormat;
2121
import com.networknt.schema.format.EmailFormat;
22+
import com.networknt.schema.format.IriFormat;
2223
import com.networknt.schema.format.IriReferenceFormat;
2324
import com.networknt.schema.format.PatternFormat;
2425
import com.networknt.schema.format.RegexFormat;
@@ -49,27 +50,30 @@ static PatternFormat pattern(String name, String regex) {
4950

5051
// this section contains formats common to all dialects.
5152
static {
52-
COMMON_BUILTIN_FORMATS.add(pattern("alpha", "^[a-zA-Z]+$"));
53-
COMMON_BUILTIN_FORMATS.add(pattern("alphanumeric", "^[a-zA-Z0-9]+$"));
54-
COMMON_BUILTIN_FORMATS.add(pattern("color", "(#?([0-9A-Fa-f]{3,6})\\b)|(aqua)|(black)|(blue)|(fuchsia)|(gray)|(green)|(lime)|(maroon)|(navy)|(olive)|(orange)|(purple)|(red)|(silver)|(teal)|(white)|(yellow)|(rgb\\(\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*\\))|(rgb\\(\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*\\))"));
55-
COMMON_BUILTIN_FORMATS.add(pattern("hostname", "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$"));
56-
COMMON_BUILTIN_FORMATS.add(pattern("ip-address", "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"));
57-
COMMON_BUILTIN_FORMATS.add(pattern("ipv4", "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$"));
58-
COMMON_BUILTIN_FORMATS.add(pattern("ipv6", "^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$"));
59-
COMMON_BUILTIN_FORMATS.add(pattern("json-pointer", "^(/([^/#~]|[~](?=[01]))*)*$"));
60-
COMMON_BUILTIN_FORMATS.add(pattern("phone", "^\\+(?:[0-9] ?){6,14}[0-9]$"));
61-
COMMON_BUILTIN_FORMATS.add(pattern("relative-json-pointer", "^(0|([1-9]\\d*))(#|(/([^/#~]|[~](?=[01]))*)*)$"));
62-
COMMON_BUILTIN_FORMATS.add(pattern("style", "\\s*(.+?):\\s*([^;]+);?"));
63-
COMMON_BUILTIN_FORMATS.add(pattern("uri-template", "^([^\\p{Cntrl}\"'%<>\\^`\\{|\\}]|%\\p{XDigit}{2}|\\{[+#./;?&=,!@|]?((\\w|%\\p{XDigit}{2})(\\.?(\\w|%\\p{XDigit}{2}))*(:[1-9]\\d{0,3}|\\*)?)(,((\\w|%\\p{XDigit}{2})(\\.?(\\w|%\\p{XDigit}{2}))*(:[1-9]\\d{0,3}|\\*)?))*\\})*$"));
64-
COMMON_BUILTIN_FORMATS.add(pattern("utc-millisec", "^[0-9]+(\\.?[0-9]+)?$"));
65-
COMMON_BUILTIN_FORMATS.add(pattern("uuid", "^\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}$"));
53+
COMMON_BUILTIN_FORMATS.add(pattern("hostname", "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$", "must be a valid RFC 1123 host name"));
54+
COMMON_BUILTIN_FORMATS.add(pattern("ipv4", "^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])$", "must be a valid RFC 2673 IP address"));
55+
COMMON_BUILTIN_FORMATS.add(pattern("ipv6", "^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$", "must be a valid RFC 4291 IP address"));
56+
COMMON_BUILTIN_FORMATS.add(pattern("json-pointer", "^(/([^/#~]|[~](?=[01]))*)*$", "must be a valid RFC 6901 JSON Pointer"));
57+
COMMON_BUILTIN_FORMATS.add(pattern("relative-json-pointer", "^(0|([1-9]\\d*))(#|(/([^/#~]|[~](?=[01]))*)*)$", "must be a valid IETF Relative JSON Pointer"));
58+
COMMON_BUILTIN_FORMATS.add(pattern("uri-template", "^([^\\p{Cntrl}\"'%<>\\^`\\{|\\}]|%\\p{XDigit}{2}|\\{[+#./;?&=,!@|]?((\\w|%\\p{XDigit}{2})(\\.?(\\w|%\\p{XDigit}{2}))*(:[1-9]\\d{0,3}|\\*)?)(,((\\w|%\\p{XDigit}{2})(\\.?(\\w|%\\p{XDigit}{2}))*(:[1-9]\\d{0,3}|\\*)?))*\\})*$", "must be a valid RFC 6570 URI Template"));
59+
COMMON_BUILTIN_FORMATS.add(pattern("uuid", "^\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}$", "must be a valid RFC 4122 UUID"));
6660
COMMON_BUILTIN_FORMATS.add(new DateFormat());
6761
COMMON_BUILTIN_FORMATS.add(new EmailFormat());
62+
COMMON_BUILTIN_FORMATS.add(new IriFormat());
6863
COMMON_BUILTIN_FORMATS.add(new IriReferenceFormat());
6964
COMMON_BUILTIN_FORMATS.add(new RegexFormat());
7065
COMMON_BUILTIN_FORMATS.add(new TimeFormat());
7166
COMMON_BUILTIN_FORMATS.add(new UriFormat());
7267
COMMON_BUILTIN_FORMATS.add(new UriReferenceFormat());
68+
69+
// The following formats do not appear in any draft
70+
COMMON_BUILTIN_FORMATS.add(pattern("alpha", "^[a-zA-Z]+$"));
71+
COMMON_BUILTIN_FORMATS.add(pattern("alphanumeric", "^[a-zA-Z0-9]+$"));
72+
COMMON_BUILTIN_FORMATS.add(pattern("color", "(#?([0-9A-Fa-f]{3,6})\\b)|(aqua)|(black)|(blue)|(fuchsia)|(gray)|(green)|(lime)|(maroon)|(navy)|(olive)|(orange)|(purple)|(red)|(silver)|(teal)|(white)|(yellow)|(rgb\\(\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*,\\s*\\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\b\\s*\\))|(rgb\\(\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*,\\s*(\\d?\\d%|100%)+\\s*\\))"));
73+
COMMON_BUILTIN_FORMATS.add(pattern("ip-address", "^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"));
74+
COMMON_BUILTIN_FORMATS.add(pattern("phone", "^\\+(?:[0-9] ?){6,14}[0-9]$"));
75+
COMMON_BUILTIN_FORMATS.add(pattern("style", "\\s*(.+?):\\s*([^;]+);?"));
76+
COMMON_BUILTIN_FORMATS.add(pattern("utc-millisec", "^[0-9]+(\\.?[0-9]+)?$"));
7377
}
7478

7579
public static class Builder {

src/main/java/com/networknt/schema/format/AbstractRFC3339Format.java renamed to src/main/java/com/networknt/schema/format/AbstractRFC3986Format.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import java.net.URI;
44
import java.net.URISyntaxException;
55

6-
public abstract class AbstractRFC3339Format extends AbstractFormat {
6+
public abstract class AbstractRFC3986Format extends AbstractFormat {
77

8-
public AbstractRFC3339Format(String name, String errorMessageDescription) {
8+
public AbstractRFC3986Format(String name, String errorMessageDescription) {
99
super(name, errorMessageDescription);
1010
}
1111

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.networknt.schema.format;
2+
3+
import java.net.URI;
4+
5+
public class IriFormat extends AbstractRFC3986Format {
6+
7+
public IriFormat() {
8+
super("iri", "must be a valid RFC 3987 IRI");
9+
}
10+
11+
@Override
12+
protected boolean validate(URI uri) {
13+
return uri.isAbsolute();
14+
}
15+
16+
}

src/main/java/com/networknt/schema/format/IriReferenceFormat.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import java.net.URI;
44

5-
public class IriReferenceFormat extends AbstractRFC3339Format {
5+
public class IriReferenceFormat extends AbstractRFC3986Format {
66

77
public IriReferenceFormat() {
8-
super("iri-reference", "must be a valid RFC 3986 IRI-reference");
8+
super("iri-reference", "must be a valid RFC 3987 IRI-reference");
99
}
1010

1111
@Override

src/main/java/com/networknt/schema/format/RegexFormat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
public class RegexFormat extends AbstractFormat {
2323

2424
public RegexFormat() {
25-
super("regex", "must be a valid regex");
25+
super("regex", "must be a valid ECMA-262 regular expression");
2626
}
2727

2828
@Override

src/main/java/com/networknt/schema/format/TimeFormat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class TimeFormat extends AbstractFormat {
3939
.toFormatter();
4040

4141
public TimeFormat() {
42-
super("time", "^(?:(?:[01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9])(?:\\.\\d+)?(?:Z|[+-](?:(?:0[0-9]|2[0-3]):[0-5][0-9]))$");
42+
super("time", "must be a valid RFC 3339 time");
4343
}
4444

4545
@Override

src/main/java/com/networknt/schema/format/UriFormat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.net.URI;
44

5-
public class UriFormat extends AbstractRFC3339Format {
5+
public class UriFormat extends AbstractRFC3986Format {
66

77
public UriFormat() {
88
super("uri", "must be a valid RFC 3986 URI");

src/main/java/com/networknt/schema/format/UriReferenceFormat.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.net.URI;
44

5-
public class UriReferenceFormat extends AbstractRFC3339Format {
5+
public class UriReferenceFormat extends AbstractRFC3986Format {
66

77
public UriReferenceFormat() {
88
super("uri-reference", "must be a valid RFC 3986 URI-reference");

src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ private void disableV202012Tests() {
8080
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json"));
8181
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/idn-email.json"));
8282
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json"));
83-
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/iri.json"));
8483
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/ref.json"));
8584
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/refRemote.json"));
8685
this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json"));
@@ -94,7 +93,6 @@ private void disableV201909Tests() {
9493
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/float-overflow.json"));
9594
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/idn-email.json"));
9695
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json"));
97-
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/iri.json"));
9896
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/recursiveRef.json"));
9997
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/ref.json"));
10098
this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/refRemote.json"));
@@ -109,7 +107,6 @@ private void disableV7Tests() {
109107
this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/float-overflow.json"));
110108
this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/idn-email.json"));
111109
this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/idn-hostname.json"));
112-
this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/iri.json"));
113110
this.disabled.add(Paths.get("src/test/suite/tests/draft7/ref.json"));
114111
this.disabled.add(Paths.get("src/test/suite/tests/draft7/refRemote.json"));
115112
}

src/test/suite/tests/draft2019-09/optional/format/iri.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@
6464
{
6565
"description": "an invalid IRI based on IPv6",
6666
"data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334",
67-
"valid": false
67+
"valid": false,
68+
"disabled": true,
69+
"reason": "URI syntax cannot always distinguish a malformed server-based authority from a legitimate registry-based authority"
6870
},
6971
{
7072
"description": "an invalid relative IRI Reference",

0 commit comments

Comments
 (0)