diff --git a/src/main/java/com/networknt/schema/JsonMetaSchema.java b/src/main/java/com/networknt/schema/JsonMetaSchema.java index 199410b2f..8c0444029 100644 --- a/src/main/java/com/networknt/schema/JsonMetaSchema.java +++ b/src/main/java/com/networknt/schema/JsonMetaSchema.java @@ -19,6 +19,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.networknt.schema.format.DateFormat; import com.networknt.schema.format.EmailFormat; +import com.networknt.schema.format.IriFormat; import com.networknt.schema.format.IriReferenceFormat; import com.networknt.schema.format.PatternFormat; import com.networknt.schema.format.RegexFormat; @@ -49,27 +50,30 @@ static PatternFormat pattern(String name, String regex) { // this section contains formats common to all dialects. static { - COMMON_BUILTIN_FORMATS.add(pattern("alpha", "^[a-zA-Z]+$")); - COMMON_BUILTIN_FORMATS.add(pattern("alphanumeric", "^[a-zA-Z0-9]+$")); - 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*\\))")); - 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]))*$")); - 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]?)$")); - 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])$")); - 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*$")); - COMMON_BUILTIN_FORMATS.add(pattern("json-pointer", "^(/([^/#~]|[~](?=[01]))*)*$")); - COMMON_BUILTIN_FORMATS.add(pattern("phone", "^\\+(?:[0-9] ?){6,14}[0-9]$")); - COMMON_BUILTIN_FORMATS.add(pattern("relative-json-pointer", "^(0|([1-9]\\d*))(#|(/([^/#~]|[~](?=[01]))*)*)$")); - COMMON_BUILTIN_FORMATS.add(pattern("style", "\\s*(.+?):\\s*([^;]+);?")); - 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}|\\*)?))*\\})*$")); - COMMON_BUILTIN_FORMATS.add(pattern("utc-millisec", "^[0-9]+(\\.?[0-9]+)?$")); - COMMON_BUILTIN_FORMATS.add(pattern("uuid", "^\\p{XDigit}{8}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{4}-\\p{XDigit}{12}$")); + 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")); + 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")); + 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")); + COMMON_BUILTIN_FORMATS.add(pattern("json-pointer", "^(/([^/#~]|[~](?=[01]))*)*$", "must be a valid RFC 6901 JSON Pointer")); + COMMON_BUILTIN_FORMATS.add(pattern("relative-json-pointer", "^(0|([1-9]\\d*))(#|(/([^/#~]|[~](?=[01]))*)*)$", "must be a valid IETF Relative JSON Pointer")); + 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")); + 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")); COMMON_BUILTIN_FORMATS.add(new DateFormat()); COMMON_BUILTIN_FORMATS.add(new EmailFormat()); + COMMON_BUILTIN_FORMATS.add(new IriFormat()); COMMON_BUILTIN_FORMATS.add(new IriReferenceFormat()); COMMON_BUILTIN_FORMATS.add(new RegexFormat()); COMMON_BUILTIN_FORMATS.add(new TimeFormat()); COMMON_BUILTIN_FORMATS.add(new UriFormat()); COMMON_BUILTIN_FORMATS.add(new UriReferenceFormat()); + + // The following formats do not appear in any draft + COMMON_BUILTIN_FORMATS.add(pattern("alpha", "^[a-zA-Z]+$")); + COMMON_BUILTIN_FORMATS.add(pattern("alphanumeric", "^[a-zA-Z0-9]+$")); + 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*\\))")); + 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]?)$")); + COMMON_BUILTIN_FORMATS.add(pattern("phone", "^\\+(?:[0-9] ?){6,14}[0-9]$")); + COMMON_BUILTIN_FORMATS.add(pattern("style", "\\s*(.+?):\\s*([^;]+);?")); + COMMON_BUILTIN_FORMATS.add(pattern("utc-millisec", "^[0-9]+(\\.?[0-9]+)?$")); } public static class Builder { diff --git a/src/main/java/com/networknt/schema/format/AbstractRFC3339Format.java b/src/main/java/com/networknt/schema/format/AbstractRFC3986Format.java similarity index 78% rename from src/main/java/com/networknt/schema/format/AbstractRFC3339Format.java rename to src/main/java/com/networknt/schema/format/AbstractRFC3986Format.java index 18619d6ab..c839bd3e3 100644 --- a/src/main/java/com/networknt/schema/format/AbstractRFC3339Format.java +++ b/src/main/java/com/networknt/schema/format/AbstractRFC3986Format.java @@ -3,9 +3,9 @@ import java.net.URI; import java.net.URISyntaxException; -public abstract class AbstractRFC3339Format extends AbstractFormat { +public abstract class AbstractRFC3986Format extends AbstractFormat { - public AbstractRFC3339Format(String name, String errorMessageDescription) { + public AbstractRFC3986Format(String name, String errorMessageDescription) { super(name, errorMessageDescription); } diff --git a/src/main/java/com/networknt/schema/format/IriFormat.java b/src/main/java/com/networknt/schema/format/IriFormat.java new file mode 100644 index 000000000..4e24ad381 --- /dev/null +++ b/src/main/java/com/networknt/schema/format/IriFormat.java @@ -0,0 +1,16 @@ +package com.networknt.schema.format; + +import java.net.URI; + +public class IriFormat extends AbstractRFC3986Format { + + public IriFormat() { + super("iri", "must be a valid RFC 3987 IRI"); + } + + @Override + protected boolean validate(URI uri) { + return uri.isAbsolute(); + } + +} diff --git a/src/main/java/com/networknt/schema/format/IriReferenceFormat.java b/src/main/java/com/networknt/schema/format/IriReferenceFormat.java index c3b5b299a..adaaf312e 100644 --- a/src/main/java/com/networknt/schema/format/IriReferenceFormat.java +++ b/src/main/java/com/networknt/schema/format/IriReferenceFormat.java @@ -2,10 +2,10 @@ import java.net.URI; -public class IriReferenceFormat extends AbstractRFC3339Format { +public class IriReferenceFormat extends AbstractRFC3986Format { public IriReferenceFormat() { - super("iri-reference", "must be a valid RFC 3986 IRI-reference"); + super("iri-reference", "must be a valid RFC 3987 IRI-reference"); } @Override diff --git a/src/main/java/com/networknt/schema/format/RegexFormat.java b/src/main/java/com/networknt/schema/format/RegexFormat.java index 84d5e46cb..34376a16f 100644 --- a/src/main/java/com/networknt/schema/format/RegexFormat.java +++ b/src/main/java/com/networknt/schema/format/RegexFormat.java @@ -22,7 +22,7 @@ public class RegexFormat extends AbstractFormat { public RegexFormat() { - super("regex", "must be a valid regex"); + super("regex", "must be a valid ECMA-262 regular expression"); } @Override diff --git a/src/main/java/com/networknt/schema/format/TimeFormat.java b/src/main/java/com/networknt/schema/format/TimeFormat.java index ee42488dc..012c47267 100644 --- a/src/main/java/com/networknt/schema/format/TimeFormat.java +++ b/src/main/java/com/networknt/schema/format/TimeFormat.java @@ -39,7 +39,7 @@ public class TimeFormat extends AbstractFormat { .toFormatter(); public TimeFormat() { - 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]))$"); + super("time", "must be a valid RFC 3339 time"); } @Override diff --git a/src/main/java/com/networknt/schema/format/UriFormat.java b/src/main/java/com/networknt/schema/format/UriFormat.java index 9ffb54b65..805499119 100644 --- a/src/main/java/com/networknt/schema/format/UriFormat.java +++ b/src/main/java/com/networknt/schema/format/UriFormat.java @@ -2,7 +2,7 @@ import java.net.URI; -public class UriFormat extends AbstractRFC3339Format { +public class UriFormat extends AbstractRFC3986Format { public UriFormat() { super("uri", "must be a valid RFC 3986 URI"); diff --git a/src/main/java/com/networknt/schema/format/UriReferenceFormat.java b/src/main/java/com/networknt/schema/format/UriReferenceFormat.java index 8e33e7539..8c23e5212 100644 --- a/src/main/java/com/networknt/schema/format/UriReferenceFormat.java +++ b/src/main/java/com/networknt/schema/format/UriReferenceFormat.java @@ -2,7 +2,7 @@ import java.net.URI; -public class UriReferenceFormat extends AbstractRFC3339Format { +public class UriReferenceFormat extends AbstractRFC3986Format { public UriReferenceFormat() { super("uri-reference", "must be a valid RFC 3986 URI-reference"); diff --git a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java index def6d1fe5..0e34ba1fb 100644 --- a/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java +++ b/src/test/java/com/networknt/schema/JsonSchemaTestSuiteTest.java @@ -80,7 +80,6 @@ private void disableV202012Tests() { this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format-assertion.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/idn-email.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/idn-hostname.json")); - this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/optional/format/iri.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/ref.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/refRemote.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2020-12/vocabulary.json")); @@ -94,7 +93,6 @@ private void disableV201909Tests() { this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/float-overflow.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/idn-email.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/idn-hostname.json")); - this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/optional/format/iri.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/recursiveRef.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/ref.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft2019-09/refRemote.json")); @@ -109,7 +107,6 @@ private void disableV7Tests() { this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/float-overflow.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/idn-email.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/idn-hostname.json")); - this.disabled.add(Paths.get("src/test/suite/tests/draft7/optional/format/iri.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft7/ref.json")); this.disabled.add(Paths.get("src/test/suite/tests/draft7/refRemote.json")); } diff --git a/src/test/suite/tests/draft2019-09/optional/format/iri.json b/src/test/suite/tests/draft2019-09/optional/format/iri.json index ad4c79e83..808c3c1eb 100644 --- a/src/test/suite/tests/draft2019-09/optional/format/iri.json +++ b/src/test/suite/tests/draft2019-09/optional/format/iri.json @@ -64,7 +64,9 @@ { "description": "an invalid IRI based on IPv6", "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "valid": false + "valid": false, + "disabled": true, + "reason": "URI syntax cannot always distinguish a malformed server-based authority from a legitimate registry-based authority" }, { "description": "an invalid relative IRI Reference", diff --git a/src/test/suite/tests/draft2020-12/optional/format/iri.json b/src/test/suite/tests/draft2020-12/optional/format/iri.json index 311c9ef08..4b91f154e 100644 --- a/src/test/suite/tests/draft2020-12/optional/format/iri.json +++ b/src/test/suite/tests/draft2020-12/optional/format/iri.json @@ -64,7 +64,9 @@ { "description": "an invalid IRI based on IPv6", "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "valid": false + "valid": false, + "disabled": true, + "reason": "URI syntax cannot always distinguish a malformed server-based authority from a legitimate registry-based authority" }, { "description": "an invalid relative IRI Reference", diff --git a/src/test/suite/tests/draft7/optional/format/iri.json b/src/test/suite/tests/draft7/optional/format/iri.json index a0d12aed6..4bef9ebb2 100644 --- a/src/test/suite/tests/draft7/optional/format/iri.json +++ b/src/test/suite/tests/draft7/optional/format/iri.json @@ -61,7 +61,9 @@ { "description": "an invalid IRI based on IPv6", "data": "http://2001:0db8:85a3:0000:0000:8a2e:0370:7334", - "valid": false + "valid": false, + "disabled": true, + "reason": "URI syntax cannot always distinguish a malformed server-based authority from a legitimate registry-based authority" }, { "description": "an invalid relative IRI Reference",