From b9fff3700b8ab3f13c72d58d89c75f943e98ecec Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 25 Mar 2025 14:11:03 -0400 Subject: [PATCH 1/6] URI/URI ref passing --- protovalidate/internal/extra_func.py | 730 +++++++++++++++++++++- tests/conformance/nonconforming.yaml | 881 --------------------------- tests/extra_func_test.py | 24 + 3 files changed, 735 insertions(+), 900 deletions(-) create mode 100644 tests/extra_func_test.py diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index cf211c75..2625d9a0 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -13,6 +13,7 @@ # limitations under the License. import math +import sys import typing from email.utils import parseaddr from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network, ip_address, ip_network @@ -164,28 +165,13 @@ def is_email(string: celtypes.Value) -> celpy.Result: def is_uri(string: celtypes.Value) -> celpy.Result: - url = urlparse.urlparse(str(string)) - # urlparse correctly reads the scheme from URNs but parses everything - # after (except the query string) as the path. - if url.scheme == "urn": - if not (url.path): - return celtypes.BoolType(False) - elif not all([url.scheme, url.netloc, url.path]): - return celtypes.BoolType(False) - - # If the query string contains percent-encoding, then try to decode it. - # unquote will return the same string if it is improperly encoded. - if "%" in url.query: - return celtypes.BoolType(urlparse.unquote(url.query) != url.query) - - return celtypes.BoolType(True) + valid = Uri(str(string)).uri() + return celtypes.BoolType(valid) def is_uri_ref(string: celtypes.Value) -> celpy.Result: - url = urlparse.urlparse(str(string)) - if not all([url.scheme, url.path]) and url.fragment: - return celtypes.BoolType(False) - return celtypes.BoolType(True) + valid = Uri(str(string)).uri_reference() + return celtypes.BoolType(valid) def is_hostname(string: celtypes.Value) -> celpy.Result: @@ -237,6 +223,712 @@ def unique(val: celtypes.Value) -> celpy.Result: return celtypes.BoolType(len(val) == len(set(val))) +class Uri: + _string: str + _index: int + _pct_encoded_found: bool + + def log(self, string: str): + print("index is {} -- {}".format(self._index, string), file=sys.stderr) + + def __init__(self, string: str): + super().__init__() + self._string = string + self._index = 0 + + def uri(self) -> bool: + """Determines whether string is a valid URI. + + Method parses the rule: + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] + """ + start = self._index + if not (self.__scheme() and self.__take(":") and self.__hier_part()): + self._index = start + return False + + if self.__take("?") and not self.__query(): + return False + + if self.__take("#") and not self.__fragment(): + return False + + if self._index != len(self._string): + self._index = start + return False + + return True + + def uri_reference(self) -> bool: + """Determines whether string is a valid URI reference. + + Method parses the rule: + URI-reference = URI / relative-ref + """ + return self.uri() or self.__relative_ref() + + def __hier_part(self) -> bool: + """Determines whether string contains a valid hier-part. + + Method parses the rule: + + hier-part = "//" authority path-abempty. + / path-absolute + / path-rootless + / path-empty + """ + start = self._index + if self.__take("/") and self.__take("/") and self.__authority() and self.__path_abempty(): + return True + + self._index = start + + self.log("made it here, which is bad") + + return self.__path_absolute() or self.__path_rootless() or self.__path_empty() + + def __relative_ref(self) -> bool: + """Determines whether string contains a valid relative reference. + + Method parses the rule: + + relative-ref = relative-part [ "?" query ] [ "#" fragment ] + """ + start = self._index + if not self.__relative_part(): + return False + + if self.__take("?") and not self.__query(): + self._index = start + return False + + if self.__take("#") and not self.__fragment(): + self._index = start + return False + + if self._index != len(self._string): + self._index = start + return False + + return True + + def __relative_part(self) -> bool: + """Determines whether string contains a valid relative part. + + Method parses the rule: + + relative-part = "//" authority path-abempty + / path-absolute + / path-noscheme + / path-empty + """ + + start = self._index + if self.__take("/") and self.__take("/") and self.__authority() and self.__path_abempty(): + return True + + self._index = start + + return self.__path_absolute() or self.__path_noscheme() or self.__path_empty() + + def __scheme(self) -> bool: + """Determines whether string contains a valid scheme. + + Method parses the rule: + + scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) + + Terminated by ":". + """ + + start = self._index + if self.__alpha(): + while self.__alpha() or self.__digit() or self.__take("+") or self.__take("-") or self.__take("."): + pass + + if self._string[self._index] == ":": + return True + + self._index = start + return False + + def __authority(self) -> bool: + """Determines whether string contains a valid authority. + + Method parses the rule: + + authority = [ userinfo "@" ] host [ ":" port ] + + Lead by double slash ("") and terminated by "/", "?", "#", or end of URI. + """ + + start = self._index + if self.__userinfo(): + if not self.__take("@"): + self._index = start + self.log("done with userinfo") + return False + + self.log("checking host") + if not self.__host(): + self._index = start + self.log("not a host") + return False + + if self.__take(":"): + if not self.__port(): + self._index = start + return False + + self.log("is auth end check") + if not self.__is_authority_end(): + self.log("not a auth end") + self._index = start + return False + + self.log("we passed") + return True + + def __is_authority_end(self) -> bool: + """Reports whether the current position is the end of the authority. + + The authority component [...] is terminated by the next slash ("/"), + question mark ("?"), or number sign ("#") character, or by the + end of the URI. + """ + + return ( + self._index >= len(self._string) + or self._string[self._index] == "?" + or self._string[self._index] == "#" + or self._string[self._index] == "/" + ) + + def __userinfo(self) -> bool: + """Determines whether string contains a valid userinfo. + + Method parses the rule: + + userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) + + Terminated by "@" in authority. + """ + start = self._index + while True: + if self.__unreserved() or self.__pct_encoded() or self.__sub_delims() or self.__take(":"): + continue + + if self._index < len(self._string): + if self._string[self._index] == "@": + return True + + self._index = start + return False + + def __check_host_pct_encoded(self, string: str) -> bool: + """Verifies that string is correctly percent-encoded""" + try: + # unquote defaults to 'UTF-8' encoding. + urlparse.unquote(string, errors="strict") + except UnicodeError: + return False + + return True + + def __host(self) -> bool: + """Determines whether string contains a valid host. + + host parses the rule: + + host = IP-literal / IPv4address / reg-name. + """ + if self._index >= len(self._string): + return False + + start = self._index + self._pct_encoded_found = False + + # Note: IPv4address is a subset of reg-name + if (self._string[self._index] == "[" and self.__ip_literal()) or self.__reg_name(): + if self._pct_encoded_found: + raw_host = self._string[start : self._index] + # RFC 3986: + # > URI producing applications must not use percent-encoding in host + # > unless it is used to represent a UTF-8 character sequence. + if not self.__check_host_pct_encoded(raw_host): + return False + + return True + + return False + + def __port(self) -> bool: + """Determines whether string contains a valid port. + + host parses the rule: + + port = *DIGIT + + Terminated by end of authority. + """ + start = self._index + while True: + if self.__digit(): + continue + + if self.__is_authority_end(): + return True + + self._index = start + return False + + def __ip_literal(self) -> bool: + """Determines whether string contains a valid port. + + ip_literal parses the rule from RFC 6874: + + IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture ) "]" + """ + + start = self._index + + if self.__take("["): + curr_idx = self._index + if self.__ipv6_address() and self.__take("]"): + return True + + self._index = curr_idx + + if self.__ipv6_addrz() and self.__take("]"): + return True + + self._index = curr_idx + + if self.__ip_vfuture() and self.__take("]"): + return True + + self._index = start + return False + + def __ipv6_address(self) -> bool: + """Determines whether string contains a valid ipv6 address. + + Method parses the rule "IPv6address". + + Relies on the implementation of is_ip. + """ + start = self._index + while self.__hex_dig() or self.__take(":"): + pass + + if validate_ip(self._string[start : self._index], 6): + return True + + self._index = start + return False + + def __ipv6_addrz(self) -> bool: + """Determines whether string contains a valid IPv6addrz. + + RFC 6874: + + IPv6addrz = IPv6address "%25" ZoneID + """ + start = self._index + if self.__ipv6_address() and self.__take("%") and self.__take("2") and self.__take("5") and self.__zone_id(): + return True + + self._index = start + + return False + + def __zone_id(self) -> bool: + """Determines whether string contains a valid zone ID. + + RFC 6874: + + ZoneID = 1*( unreserved / pct-encoded ) + """ + + start = self._index + while self.__unreserved() or self.__pct_encoded(): + pass + + if self._index - start > 0: + return True + + self._index = start + + return False + + def __ip_vfuture(self) -> bool: + """Determines whether string contains a valid ipvFuture. + + IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) + """ + start = self._index + + if self.__take("v") and self.__hex_dig(): + while self.__hex_dig(): + pass + + if self.__take("."): + j = 0 + while self.__unreserved() or self.__sub_delims() or self.__take(":"): + j += 1 + + if j >= 1: + return True + + self._index = start + + return False + + def __reg_name(self) -> bool: + """Determines whether string contains a valid reg-name. + + reg-name = *( unreserved / pct-encoded / sub-delims ) + + Terminates on start of port (":") or end of authority. + """ + start = self._index + while True: + if self.__unreserved() or self.__pct_encoded() or self.__sub_delims(): + continue + + if self.__is_authority_end(): + # End of authority + return True + + if self._string[self._index] == ":": + return True + + self._index = start + + return False + + def __is_path_end(self) -> bool: + """Determines whether the current index has reached the end of path. + + > The path is terminated by the first question mark ("?") or + > number sign ("#") character, or by the end of the URI. + """ + return self._index >= len(self._string) or self._string[self._index] == "?" or self._string[self._index] == "#" + + def __path_abempty(self) -> bool: + """Determines whether string contains a path-abempty. + + path-abempty = *( "/" segment ) + + Terminated by end of path: "?", "#", or end of URI. + """ + start = self._index + while self.__take("/") and self.__segment(): + pass + + self.log("done with segment loop") + if self.__is_path_end(): + return True + + self._index = start + + return False + + def __path_absolute(self) -> bool: + """Determines whether string contains a path-absolute. + + path-absolute = "/" [ segment-nz *( "/" segment ) ] + + Terminated by end of path: "?", "#", or end of URI. + """ + start = self._index + + if self.__take("/"): + if self.__segment_nz(): + while self.__take("/") and self.__segment(): + pass + + if self.__is_path_end(): + return True + + self._index = start + + return False + + def __path_noscheme(self) -> bool: + """Determines whether string contains a path-noscheme. + + path-noscheme = segment-nz-nc *( "/" segment ) + + Terminated by end of path: "?", "#", or end of URI. + """ + + start = self._index + if self.__segment_nz_nc(): + while self.__take("/") and self.__segment(): + pass + + if self.__is_path_end(): + return True + + self._index = start + + return True + + def __path_rootless(self) -> bool: + """Determines whether string contains a path-rootless. + + path-rootless = segment-nz *( "/" segment ) + + Terminated by end of path: "?", "#", or end of URI. + """ + start = self._index + + if self.__segment_nz(): + while self.__take("/") and self.__segment(): + pass + + if self.__is_path_end(): + return True + + self._index = start + + return True + + def __path_empty(self) -> bool: + """Determines whether string contains a path-empty. + + path-empty = 0 + + Terminated by end of path: "?", "#", or end of URI. + """ + return self.__is_path_end() + + def __segment(self) -> bool: + """Determines whether string contains a segment. + + segment = *pchar + """ + + while self.__pchar(): + pass + + return True + + def __segment_nz(self) -> bool: + """Determines whether string contains a segment-nz. + + segment-nz = 1*pchar + """ + start = self._index + + if self.__pchar(): + while self.__pchar(): + pass + + return True + + self._index = start + + return False + + def __segment_nz_nc(self) -> bool: + """Determines whether string contains a segment-nz-nc. + + segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) + ; non-zero-length segment without any colon ":" + """ + + start = self._index + + while self.__unreserved() or self.__pct_encoded() or self.__sub_delims() or self.__take("@"): + pass + + if self._index - start > 0: + return True + + self._index = start + + return False + + def __pchar(self) -> bool: + """Determines whether the character at the current index is a pchar. + + pchar = unreserved / pct-encoded / sub-delims / ":" / "@" + """ + return ( + self.__unreserved() or self.__pct_encoded() or self.__sub_delims() or self.__take(":") or self.__take("@") + ) + + def __query(self) -> bool: + """Determines whether string contains a valid query. + + query = *( pchar / "/" / "?" ) + + Terminated by "#" or end of URI. + """ + + start = self._index + + while True: + if self.__pchar() or self.__take("/") or self.__take("?"): + continue + + if self._index == len(self._string) or self._string[self._index] == "#": + return True + + self._index = start + + return False + + def __fragment(self) -> bool: + """Determines whether string contains a valid fragment. + + fragment = *( pchar / "/" / "?" ) + + Terminated by end of URI. + """ + + start = self._index + + while True: + if self.__pchar() or self.__take("/") or self.__take("?"): + continue + + if self._index == len(self._string): + return True + + self._index = start + + return False + + def __pct_encoded(self) -> bool: + """Determines whether string contains a valid percent encoding. + + pct-encoded = "%" HEXDIG HEXDIG + + Sets `_pct_encoded_found` to true if a valid triplet was found + """ + start = self._index + + if self.__take("%") and self.__hex_dig() and self.__hex_dig(): + self._pct_encoded_found = True + return True + + self._index = start + + return False + + def __unreserved(self) -> bool: + """Determines whether the character at the current index is unreserved. + + unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" + """ + return ( + self.__alpha() + or self.__digit() + or self.__take("-") + or self.__take("_") + or self.__take(".") + or self.__take("~") + ) + + def __sub_delims(self) -> bool: + """Determines whether the character at the current index is a sub-delim. + + sub-delims = "!" / "$" / "&" / "'" / "(" / ")" + / "*" / "+" / "," / ";" / "=" + """ + return ( + self.__take("!") + or self.__take("$") + or self.__take("&") + or self.__take("'") + or self.__take("(") + or self.__take(")") + or self.__take("*") + or self.__take("+") + or self.__take(",") + or self.__take(";") + or self.__take("=") + ) + + def __alpha(self) -> bool: + """Determines whether the character at the current index is an alpha char. + + alpha parses the rule: + + ALPHA = %x41-5A / %x61-7A ; A-Z / a-z + """ + + if self._index >= len(self._string): + return False + + c = self._string[self._index] + if ("A" <= c <= "Z") or ("a" <= c <= "z"): + self._index += 1 + return True + + return False + + def __digit(self) -> bool: + """Determines whether the character at the current index is a digit. + + Method parses the rule: + + DIGIT = %x30-39 ; 0-9 + """ + + if self._index >= len(self._string): + return False + + c = self._string[self._index] + if "0" <= c <= "9": + self._index += 1 + return True + + return False + + def __hex_dig(self) -> bool: + """Determines whether the character at the current index is a hex digit. + + Method parses the rule: + + HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" + """ + + if self._index >= len(self._string): + return False + + c = self._string[self._index] + + if ("0" <= c <= "9") or ("a" <= c <= "f") or ("A" <= c <= "F") or ("0" <= c <= "9"): + self._index += 1 + + return True + + return False + + def __take(self, char: str) -> bool: + """Take the given char at the current index. + + If char is at the current index, increment the index. + + Returns: + True if char is at the current index. False if char is not at the + current index or the end of string has been reached. + """ + + if self._index >= len(self._string): + return False + + if self._string[self._index] == char: + self._index += 1 + return True + + return False + + def make_extra_funcs(locale: str) -> dict[str, celpy.CELFunction]: # TODO(#257): Fix types and add tests for StringFormat. # For now, ignoring the type. diff --git a/tests/conformance/nonconforming.yaml b/tests/conformance/nonconforming.yaml index 531cf553..47767dd6 100644 --- a/tests/conformance/nonconforming.yaml +++ b/tests/conformance/nonconforming.yaml @@ -166,887 +166,6 @@ library/is_ip_prefix: # want: validation error (1 violation) # 1. constraint_id: "library.is_ip_prefix" # got: valid -library/is_uri: - - invalid/authority_path-abempty_segment_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo://example.com/^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: valid - - invalid/authority_path-abempty_segment_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo://example.com/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: valid - - invalid/authority_path-abempty_segment_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo://example.com/%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: valid - - invalid/host_ipfuture - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[v1x]"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ('IPvFuture address is invalid',)) - - invalid/host_ipv6/b - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[2001::0370::7334]"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ("'2001::0370::7334' does not appear to be an IPv4 or IPv6 address",)) - - invalid/host_ipv6_zone-id_bad_pct-encoded/a - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25foo%]"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ("'::1%25foo%' does not appear to be an IPv4 or IPv6 address",)) - - invalid/host_ipv6_zone-id_bad_pct-encoded/b - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25foo%2x]"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ("'::1%25foo%2x' does not appear to be an IPv4 or IPv6 address",)) - - invalid/host_ipv6_zone-id_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25foo%c3x%96]"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ("'::1%25foo%c3x%96' does not appear to be an IPv4 or IPv6 address",)) - - invalid/userinfo_reserved_square_bracket_close - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://]@example.com"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ('Invalid IPv6 URL',)) - - invalid/userinfo_reserved_square_bracket_open - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[@example.com"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # got: runtime error: ('return error for overflow', , ('Invalid IPv6 URL',)) - - valid/authority_path-abempty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo://example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/example - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment_pchar_extra - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com#/?"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com#%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com#%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com#%c3%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/fragment_sub-delims - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com#!$&'()*+,;="} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ip4v_bad_octet - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://256.0.0.1"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipfuture_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[vF.-!$&'()*+,;=._~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipfuture_long - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[v1234AF.x]"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipfuture_short - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[v1.x]"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipv4 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://127.0.0.1"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipv6 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipv6_zone-id - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25eth0]"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_ipv6_zone-id_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25foo%61%20%23]"} - # want: valid - # got: runtime error: ('return error for overflow', , ("'::1%25foo%61%20%23' does not appear to be an IPv4 or IPv6 address",)) - - valid/host_ipv6_zone-id_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://[::1%25foo%c3%96]"} - # want: valid - # got: runtime error: ('return error for overflow', , ("'::1%25foo%c3%96' does not appear to be an IPv4 or IPv6 address",)) - - valid/host_reg-name - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://foo"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_reg-name_empty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://:8080"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_reg-name_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://!$&'()*+,;=._~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_reg-name_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://foo%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/host_reg-name_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://foo%c3%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_exhaust_segment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%20!$&'()*+,;=:@%20"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_exhaust_segment-nz - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/@%20!$&()*+,;=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~:"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment-nz-pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment-nz-pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment-nz-pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/%c3%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_segment_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/%c3%96%c3"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_with_empty_pchar - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_with_query_and_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz?q#f"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_with_segment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz/a"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-absolute_with_segments - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:/nz//segment//segment/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-empty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-empty_with_query_and_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:?q#f"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment-nz_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:@%20!$&()*+,;=0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~:"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment-nz_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment-nz_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment-nz_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:%c3%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment_empty_pchar - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%20!$&'()*+,;=:@%20"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_segment_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/%c3%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_with_query_and_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz?q#f"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_with_segment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz/a"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/path-rootless_with_segments - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo:nz//segment//segment/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_0 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:0"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_1 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:1"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_65535 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:65535"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_65536 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:65536"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_8080 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:8080"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_empty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com:"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/port_empty_reg-name_empty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://:"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?baz=quux"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_extra - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?/?"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_pchar_extra - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?:@"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?%61%20%23"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_pct-encoded_invalid_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?%c3x%96"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?%c3%96%c3"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_sub-delim_semicolon - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?;"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_sub-delims - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?!$&'()*+,="} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/query_unusual_key_value_structure - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://example.com?a=b&c&&=1&=="} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/scheme_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"foo0123456789azAZ+-.://example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/scheme_ftp - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"ftp://example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_exhaust - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~!$&'()*+,;=::@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_extra - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://:@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_multiple_colons - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://:::@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_name - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://user@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_name_password - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://user:password@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_pct-encoded_ascii - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://%61%20%23@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_pct-encoded_invalid-utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://%c3x%963@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_pct-encoded_utf8 - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://%c3%963@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_reserved_hash_parses_as_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://#@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_reserved_questionmark_parses_as_query - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://?@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_reserved_slash_parses_as_path-abempty - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https:///@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_sub-delims - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://!$&'()*+,;=@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false - - valid/userinfo_unreserved - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUri]:{val:"https://0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~@example.com"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri" - # message: "" - # for_key: false -library/is_uri_ref: - - invalid/authority_path-abempty_segment_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/bad_relative-part - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:":"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/leading_space - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:" ./foo"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-abempty_query_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host?^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-abempty_query_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host?\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-abempty_query_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host?%"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-abempty_segment_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host/%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/foo/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_query_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/?^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_query_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/?\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_query_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/?%"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment-nz_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment-nz_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment-nz_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/nz/^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/nz/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-absolute_segment_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/nz/%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-empty_query_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"?^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-empty_query_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"?\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-empty_query_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"?%"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./foo/\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_query_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:".?^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_query_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:".?\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_query_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:".?%"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment-bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment-nz_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment-nz_bad_colon - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:":"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment-nz_bad_control_character - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"\x1f"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment-nz_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment_bad_caret - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./^"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/path-noscheme_segment_bad_pct-encoded - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./%x"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/space - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:" "} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/trailing_space - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./foo "} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - invalid/uri_with_bad_scheme - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"1foo://example.com"} - # want: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # got: valid - - valid/authority_path-abempty_with_segment_query_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host/foo?baz=quux#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/extreme - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//userinfo0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~!$&'()*+,;=::@host!$&'()*+,;=._~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789/path0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%20!$&'()*+,;=:@%20//foo/?query0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?#fragment0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-abempty_exhaust_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-abempty_with_fragment/a - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-abempty_with_fragment/b - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"//host/foo/bar#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-absolute_exhaust_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-absolute_with_fragment/a - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-absolute_with_fragment/b - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"/foo/bar#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-empty_exhaust_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-noscheme_exhaust_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:".#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-._~%20!$&'()*+,=;:@?/"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-noscheme_with_fragment/a - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:".#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-noscheme_with_fragment/b - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./foo/bar#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false - - valid/path-noscheme_with_segment_query_fragment - # input: [type.googleapis.com/buf.validate.conformance.cases.IsUriRef]:{val:"./foo/bar?baz=quux#frag"} - # want: valid - # got: validation error (1 violation) - # 1. constraint_id: "library.is_uri_ref" - # message: "" - # for_key: false standard_constraints/required: - proto2/scalar/optional/unset # input: [type.googleapis.com/buf.validate.conformance.cases.RequiredProto2ScalarOptional]:{} diff --git a/tests/extra_func_test.py b/tests/extra_func_test.py new file mode 100644 index 00000000..a10efb6e --- /dev/null +++ b/tests/extra_func_test.py @@ -0,0 +1,24 @@ +# Copyright 2023-2025 Buf Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import unittest + +from protovalidate.internal.extra_func import Uri + + +class TestFunc(unittest.TestCase): + def test_ninf(self): + uri = Uri("https://foo%c3x%96") + is_it = uri.uri() + self.assertFalse(is_it) From b7b07027754f94b31c28d982b479ba26cd0adaab Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 25 Mar 2025 14:28:17 -0400 Subject: [PATCH 2/6] Docs --- protovalidate/internal/extra_func.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index f566322d..5d8d6c98 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -221,6 +221,17 @@ def unique(val: celtypes.Value) -> celpy.Result: class Uri: + """Uri is a class used to validate a given string to determine if it is a valid URI or URI reference. + + Callers can validate a string by constructing an instance of this class and then calling one of its + public methods: + uri() + uri_reference() + + Each method will return True or False depending on whether it passes validation. + + """ + _string: str _index: int _pct_encoded_found: bool @@ -229,6 +240,11 @@ def log(self, string: str): print("index is {} -- {}".format(self._index, string), file=sys.stderr) def __init__(self, string: str): + """Initialize a URI validation class with a given string + + Args: + string (str): String to validate as a URI or URI reference. + """ super().__init__() self._string = string self._index = 0 @@ -366,10 +382,8 @@ def __authority(self) -> bool: self.log("done with userinfo") return False - self.log("checking host") if not self.__host(): self._index = start - self.log("not a host") return False if self.__take(":"): @@ -377,13 +391,10 @@ def __authority(self) -> bool: self._index = start return False - self.log("is auth end check") if not self.__is_authority_end(): - self.log("not a auth end") self._index = start return False - self.log("we passed") return True def __is_authority_end(self) -> bool: From f2b3cf9b405b111a41421391b919dec5a3837630 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 25 Mar 2025 14:29:20 -0400 Subject: [PATCH 3/6] Cleanup --- protovalidate/internal/extra_func.py | 8 -------- tests/extra_func_test.py | 24 ------------------------ 2 files changed, 32 deletions(-) delete mode 100644 tests/extra_func_test.py diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index 5d8d6c98..c18400a0 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -14,7 +14,6 @@ import math import re -import sys import typing from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network, ip_address, ip_network from urllib import parse as urlparse @@ -236,9 +235,6 @@ class Uri: _index: int _pct_encoded_found: bool - def log(self, string: str): - print("index is {} -- {}".format(self._index, string), file=sys.stderr) - def __init__(self, string: str): """Initialize a URI validation class with a given string @@ -296,8 +292,6 @@ def __hier_part(self) -> bool: self._index = start - self.log("made it here, which is bad") - return self.__path_absolute() or self.__path_rootless() or self.__path_empty() def __relative_ref(self) -> bool: @@ -379,7 +373,6 @@ def __authority(self) -> bool: if self.__userinfo(): if not self.__take("@"): self._index = start - self.log("done with userinfo") return False if not self.__host(): @@ -634,7 +627,6 @@ def __path_abempty(self) -> bool: while self.__take("/") and self.__segment(): pass - self.log("done with segment loop") if self.__is_path_end(): return True diff --git a/tests/extra_func_test.py b/tests/extra_func_test.py deleted file mode 100644 index a10efb6e..00000000 --- a/tests/extra_func_test.py +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2023-2025 Buf Technologies, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import unittest - -from protovalidate.internal.extra_func import Uri - - -class TestFunc(unittest.TestCase): - def test_ninf(self): - uri = Uri("https://foo%c3x%96") - is_it = uri.uri() - self.assertFalse(is_it) From 3bc457ed96d862c038ccbc4c8db3f48921b2f4a7 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 25 Mar 2025 14:37:47 -0400 Subject: [PATCH 4/6] Cleanup --- protovalidate/internal/extra_func.py | 45 ++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index c18400a0..2fe289bb 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -220,7 +220,7 @@ def unique(val: celtypes.Value) -> celpy.Result: class Uri: - """Uri is a class used to validate a given string to determine if it is a valid URI or URI reference. + """Uri is a class used to parse a given string to determine if it is a valid URI or URI reference. Callers can validate a string by constructing an instance of this class and then calling one of its public methods: @@ -228,7 +228,6 @@ class Uri: uri_reference() Each method will return True or False depending on whether it passes validation. - """ _string: str @@ -241,6 +240,7 @@ def __init__(self, string: str): Args: string (str): String to validate as a URI or URI reference. """ + super().__init__() self._string = string self._index = 0 @@ -251,6 +251,7 @@ def uri(self) -> bool: Method parses the rule: URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] """ + start = self._index if not (self.__scheme() and self.__take(":") and self.__hier_part()): self._index = start @@ -274,6 +275,7 @@ def uri_reference(self) -> bool: Method parses the rule: URI-reference = URI / relative-ref """ + return self.uri() or self.__relative_ref() def __hier_part(self) -> bool: @@ -286,6 +288,7 @@ def __hier_part(self) -> bool: / path-rootless / path-empty """ + start = self._index if self.__take("/") and self.__take("/") and self.__authority() and self.__path_abempty(): return True @@ -301,6 +304,7 @@ def __relative_ref(self) -> bool: relative-ref = relative-part [ "?" query ] [ "#" fragment ] """ + start = self._index if not self.__relative_part(): return False @@ -414,6 +418,7 @@ def __userinfo(self) -> bool: Terminated by "@" in authority. """ + start = self._index while True: if self.__unreserved() or self.__pct_encoded() or self.__sub_delims() or self.__take(":"): @@ -443,6 +448,7 @@ def __host(self) -> bool: host = IP-literal / IPv4address / reg-name. """ + if self._index >= len(self._string): return False @@ -466,12 +472,13 @@ def __host(self) -> bool: def __port(self) -> bool: """Determines whether string contains a valid port. - host parses the rule: + Method parses the rule: port = *DIGIT Terminated by end of authority. """ + start = self._index while True: if self.__digit(): @@ -486,7 +493,7 @@ def __port(self) -> bool: def __ip_literal(self) -> bool: """Determines whether string contains a valid port. - ip_literal parses the rule from RFC 6874: + Method parses the rule from RFC 6874: IP-literal = "[" ( IPv6address / IPv6addrz / IPvFuture ) "]" """ @@ -516,8 +523,9 @@ def __ipv6_address(self) -> bool: Method parses the rule "IPv6address". - Relies on the implementation of is_ip. + Relies on the implementation of validate_ip. """ + start = self._index while self.__hex_dig() or self.__take(":"): pass @@ -535,6 +543,7 @@ def __ipv6_addrz(self) -> bool: IPv6addrz = IPv6address "%25" ZoneID """ + start = self._index if self.__ipv6_address() and self.__take("%") and self.__take("2") and self.__take("5") and self.__zone_id(): return True @@ -567,6 +576,7 @@ def __ip_vfuture(self) -> bool: IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) """ + start = self._index if self.__take("v") and self.__hex_dig(): @@ -592,6 +602,7 @@ def __reg_name(self) -> bool: Terminates on start of port (":") or end of authority. """ + start = self._index while True: if self.__unreserved() or self.__pct_encoded() or self.__sub_delims(): @@ -614,6 +625,7 @@ def __is_path_end(self) -> bool: > The path is terminated by the first question mark ("?") or > number sign ("#") character, or by the end of the URI. """ + return self._index >= len(self._string) or self._string[self._index] == "?" or self._string[self._index] == "#" def __path_abempty(self) -> bool: @@ -623,6 +635,7 @@ def __path_abempty(self) -> bool: Terminated by end of path: "?", "#", or end of URI. """ + start = self._index while self.__take("/") and self.__segment(): pass @@ -641,6 +654,7 @@ def __path_absolute(self) -> bool: Terminated by end of path: "?", "#", or end of URI. """ + start = self._index if self.__take("/"): @@ -682,6 +696,7 @@ def __path_rootless(self) -> bool: Terminated by end of path: "?", "#", or end of URI. """ + start = self._index if self.__segment_nz(): @@ -702,6 +717,7 @@ def __path_empty(self) -> bool: Terminated by end of path: "?", "#", or end of URI. """ + return self.__is_path_end() def __segment(self) -> bool: @@ -720,6 +736,7 @@ def __segment_nz(self) -> bool: segment-nz = 1*pchar """ + start = self._index if self.__pchar(): @@ -752,10 +769,11 @@ def __segment_nz_nc(self) -> bool: return False def __pchar(self) -> bool: - """Determines whether the character at the current index is a pchar. + """Reports whether the current position is a pchar. pchar = unreserved / pct-encoded / sub-delims / ":" / "@" """ + return ( self.__unreserved() or self.__pct_encoded() or self.__sub_delims() or self.__take(":") or self.__take("@") ) @@ -809,6 +827,7 @@ def __pct_encoded(self) -> bool: Sets `_pct_encoded_found` to true if a valid triplet was found """ + start = self._index if self.__take("%") and self.__hex_dig() and self.__hex_dig(): @@ -820,10 +839,11 @@ def __pct_encoded(self) -> bool: return False def __unreserved(self) -> bool: - """Determines whether the character at the current index is unreserved. + """Reports whether the current position is an unreserved character. unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" """ + return ( self.__alpha() or self.__digit() @@ -834,11 +854,12 @@ def __unreserved(self) -> bool: ) def __sub_delims(self) -> bool: - """Determines whether the character at the current index is a sub-delim. + """Reports whether the current position is a sub-delim. sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" """ + return ( self.__take("!") or self.__take("$") @@ -854,9 +875,9 @@ def __sub_delims(self) -> bool: ) def __alpha(self) -> bool: - """Determines whether the character at the current index is an alpha char. + """Reports whether the current position is an alpha character. - alpha parses the rule: + Method parses the rule: ALPHA = %x41-5A / %x61-7A ; A-Z / a-z """ @@ -872,7 +893,7 @@ def __alpha(self) -> bool: return False def __digit(self) -> bool: - """Determines whether the character at the current index is a digit. + """Reports whether the current position is a digit. Method parses the rule: @@ -890,7 +911,7 @@ def __digit(self) -> bool: return False def __hex_dig(self) -> bool: - """Determines whether the character at the current index is a hex digit. + """Reports whether the current position is a hex digit. Method parses the rule: From 8707a6c65aef58d94e6559de4e6dc2fb529ea18f Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Tue, 25 Mar 2025 14:43:52 -0400 Subject: [PATCH 5/6] Cleanup --- protovalidate/internal/extra_func.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index 2fe289bb..6d25c6d9 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -161,11 +161,13 @@ def is_email(string: celtypes.Value) -> celpy.Result: def is_uri(string: celtypes.Value) -> celpy.Result: + """is_uri validates whether string is a valid URI.""" valid = Uri(str(string)).uri() return celtypes.BoolType(valid) def is_uri_ref(string: celtypes.Value) -> celpy.Result: + """is_uri_reference validates whether string is a valid URI reference.""" valid = Uri(str(string)).uri_reference() return celtypes.BoolType(valid) From b55fc7d92a1ab588c9f2fdca0a187283357a2ff3 Mon Sep 17 00:00:00 2001 From: Steve Ayers Date: Wed, 26 Mar 2025 10:54:47 -0400 Subject: [PATCH 6/6] Fix docs --- protovalidate/internal/extra_func.py | 162 ++++++++++++++++++++------- 1 file changed, 120 insertions(+), 42 deletions(-) diff --git a/protovalidate/internal/extra_func.py b/protovalidate/internal/extra_func.py index 6d25c6d9..09ba69e3 100644 --- a/protovalidate/internal/extra_func.py +++ b/protovalidate/internal/extra_func.py @@ -145,12 +145,21 @@ def is_ip_prefix(val: celtypes.Value, *args) -> celpy.Result: def is_email(string: celtypes.Value) -> celpy.Result: - """Returns true if the string is an email address, for example "foo@example.com". + """Validate whether string is a valid email address. Conforms to the definition for a valid email address from the HTML standard. Note that this standard willfully deviates from RFC 5322, which allows many unexpected forms of email addresses and will easily match a typographical error. + + Args: + string (celTypes.Value): The string to validate. + + Returns: + True if the string is an email address, for example "foo@example.com". False otherwise. + + Raises: + celpy.CELEvalError: If string is not an instance of celtypes.StringType. """ if not isinstance(string, celtypes.StringType): @@ -161,13 +170,48 @@ def is_email(string: celtypes.Value) -> celpy.Result: def is_uri(string: celtypes.Value) -> celpy.Result: - """is_uri validates whether string is a valid URI.""" + """Validate whether string is a valid URI. + + URI is defined in the internet standard RFC 3986. + Zone Identifiers in IPv6 address literals are supported (RFC 6874). + + Args: + string (celTypes.Value): The string to validate. + + Returns: + True if the string is a URI, for example "https://example.com/foo/bar?baz=quux#frag". False otherwise. + + Raises: + celpy.CELEvalError: If string is not an instance of celtypes.StringType. + """ + + if not isinstance(string, celtypes.StringType): + msg = "invalid argument, expected string" + raise celpy.CELEvalError(msg) valid = Uri(str(string)).uri() return celtypes.BoolType(valid) def is_uri_ref(string: celtypes.Value) -> celpy.Result: - """is_uri_reference validates whether string is a valid URI reference.""" + """Validate whether string is a valid URI reference. + + URI, URI Reference, and Relative Reference are defined in the internet standard RFC 3986. + Zone Identifiers in IPv6 address literals are supported (RFC 6874). + + Args: + string (celTypes.Value): The string to validate. + + Returns: + True if the string is a URI Reference - a URI such as "https://example.com/foo/bar?baz=quux#frag" + or a Relative Reference such as "./foo/bar?query". False otherwise. + + Raises: + celpy.CELEvalError: If string is not an instance of celtypes.StringType. + """ + + if not isinstance(string, celtypes.StringType): + msg = "invalid argument, expected string" + raise celpy.CELEvalError(msg) valid = Uri(str(string)).uri_reference() return celtypes.BoolType(valid) @@ -248,9 +292,10 @@ def __init__(self, string: str): self._index = 0 def uri(self) -> bool: - """Determines whether string is a valid URI. + """Determine whether string is a valid URI. Method parses the rule: + URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] """ @@ -272,16 +317,17 @@ def uri(self) -> bool: return True def uri_reference(self) -> bool: - """Determines whether string is a valid URI reference. + """Determine whether string is a valid URI reference. Method parses the rule: + URI-reference = URI / relative-ref """ return self.uri() or self.__relative_ref() def __hier_part(self) -> bool: - """Determines whether string contains a valid hier-part. + """Determine whether string contains a valid hier-part. Method parses the rule: @@ -300,7 +346,7 @@ def __hier_part(self) -> bool: return self.__path_absolute() or self.__path_rootless() or self.__path_empty() def __relative_ref(self) -> bool: - """Determines whether string contains a valid relative reference. + """Determine whether string contains a valid relative reference. Method parses the rule: @@ -326,7 +372,7 @@ def __relative_ref(self) -> bool: return True def __relative_part(self) -> bool: - """Determines whether string contains a valid relative part. + """Determine whether string contains a valid relative part. Method parses the rule: @@ -345,7 +391,7 @@ def __relative_part(self) -> bool: return self.__path_absolute() or self.__path_noscheme() or self.__path_empty() def __scheme(self) -> bool: - """Determines whether string contains a valid scheme. + """Determine whether string contains a valid scheme. Method parses the rule: @@ -366,7 +412,7 @@ def __scheme(self) -> bool: return False def __authority(self) -> bool: - """Determines whether string contains a valid authority. + """Determine whether string contains a valid authority. Method parses the rule: @@ -397,7 +443,7 @@ def __authority(self) -> bool: return True def __is_authority_end(self) -> bool: - """Reports whether the current position is the end of the authority. + """Report whether the current position is the end of the authority. The authority component [...] is terminated by the next slash ("/"), question mark ("?"), or number sign ("#") character, or by the @@ -412,7 +458,7 @@ def __is_authority_end(self) -> bool: ) def __userinfo(self) -> bool: - """Determines whether string contains a valid userinfo. + """Determine whether string contains a valid userinfo. Method parses the rule: @@ -434,7 +480,7 @@ def __userinfo(self) -> bool: return False def __check_host_pct_encoded(self, string: str) -> bool: - """Verifies that string is correctly percent-encoded""" + """Verify that string is correctly percent-encoded""" try: # unquote defaults to 'UTF-8' encoding. urlparse.unquote(string, errors="strict") @@ -444,9 +490,9 @@ def __check_host_pct_encoded(self, string: str) -> bool: return True def __host(self) -> bool: - """Determines whether string contains a valid host. + """Determine whether string contains a valid host. - host parses the rule: + Method parses the rule: host = IP-literal / IPv4address / reg-name. """ @@ -472,7 +518,7 @@ def __host(self) -> bool: return False def __port(self) -> bool: - """Determines whether string contains a valid port. + """Determine whether string contains a valid port. Method parses the rule: @@ -493,7 +539,7 @@ def __port(self) -> bool: return False def __ip_literal(self) -> bool: - """Determines whether string contains a valid port. + """Determine whether string contains a valid port. Method parses the rule from RFC 6874: @@ -521,7 +567,7 @@ def __ip_literal(self) -> bool: return False def __ipv6_address(self) -> bool: - """Determines whether string contains a valid ipv6 address. + """Determine whether string contains a valid ipv6 address. Method parses the rule "IPv6address". @@ -539,9 +585,9 @@ def __ipv6_address(self) -> bool: return False def __ipv6_addrz(self) -> bool: - """Determines whether string contains a valid IPv6addrz. + """Determine whether string contains a valid IPv6addrz. - RFC 6874: + Method parses the rule from RFC 6874: IPv6addrz = IPv6address "%25" ZoneID """ @@ -555,9 +601,9 @@ def __ipv6_addrz(self) -> bool: return False def __zone_id(self) -> bool: - """Determines whether string contains a valid zone ID. + """Determine whether string contains a valid zone ID. - RFC 6874: + Method parses the rule from RFC 6874: ZoneID = 1*( unreserved / pct-encoded ) """ @@ -574,7 +620,9 @@ def __zone_id(self) -> bool: return False def __ip_vfuture(self) -> bool: - """Determines whether string contains a valid ipvFuture. + """Determine whether string contains a valid ipvFuture. + + Method parses the rule: IPvFuture = "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ) """ @@ -598,7 +646,9 @@ def __ip_vfuture(self) -> bool: return False def __reg_name(self) -> bool: - """Determines whether string contains a valid reg-name. + """Determine whether string contains a valid reg-name. + + Method parses the rule: reg-name = *( unreserved / pct-encoded / sub-delims ) @@ -622,7 +672,7 @@ def __reg_name(self) -> bool: return False def __is_path_end(self) -> bool: - """Determines whether the current index has reached the end of path. + """Determine whether the current index has reached the end of path. > The path is terminated by the first question mark ("?") or > number sign ("#") character, or by the end of the URI. @@ -631,7 +681,9 @@ def __is_path_end(self) -> bool: return self._index >= len(self._string) or self._string[self._index] == "?" or self._string[self._index] == "#" def __path_abempty(self) -> bool: - """Determines whether string contains a path-abempty. + """Determine whether string contains a path-abempty. + + Method parses the rule: path-abempty = *( "/" segment ) @@ -650,7 +702,9 @@ def __path_abempty(self) -> bool: return False def __path_absolute(self) -> bool: - """Determines whether string contains a path-absolute. + """Determine whether string contains a path-absolute. + + Method parses the rule: path-absolute = "/" [ segment-nz *( "/" segment ) ] @@ -672,7 +726,9 @@ def __path_absolute(self) -> bool: return False def __path_noscheme(self) -> bool: - """Determines whether string contains a path-noscheme. + """Determine whether string contains a path-noscheme. + + Method parses the rule: path-noscheme = segment-nz-nc *( "/" segment ) @@ -692,7 +748,9 @@ def __path_noscheme(self) -> bool: return True def __path_rootless(self) -> bool: - """Determines whether string contains a path-rootless. + """Determine whether string contains a path-rootless. + + Method parses the rule: path-rootless = segment-nz *( "/" segment ) @@ -713,7 +771,9 @@ def __path_rootless(self) -> bool: return True def __path_empty(self) -> bool: - """Determines whether string contains a path-empty. + """Determine whether string contains a path-empty. + + Method parses the rule: path-empty = 0 @@ -723,7 +783,9 @@ def __path_empty(self) -> bool: return self.__is_path_end() def __segment(self) -> bool: - """Determines whether string contains a segment. + """Determine whether string contains a segment. + + Method parses the rule: segment = *pchar """ @@ -734,7 +796,9 @@ def __segment(self) -> bool: return True def __segment_nz(self) -> bool: - """Determines whether string contains a segment-nz. + """Determine whether string contains a segment-nz. + + Method parses the rule: segment-nz = 1*pchar """ @@ -752,7 +816,9 @@ def __segment_nz(self) -> bool: return False def __segment_nz_nc(self) -> bool: - """Determines whether string contains a segment-nz-nc. + """Determine whether string contains a segment-nz-nc. + + Method parses the rule: segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" ) ; non-zero-length segment without any colon ":" @@ -771,7 +837,9 @@ def __segment_nz_nc(self) -> bool: return False def __pchar(self) -> bool: - """Reports whether the current position is a pchar. + """Report whether the current position is a pchar. + + Method parses the rule: pchar = unreserved / pct-encoded / sub-delims / ":" / "@" """ @@ -781,7 +849,9 @@ def __pchar(self) -> bool: ) def __query(self) -> bool: - """Determines whether string contains a valid query. + """Determine whether string contains a valid query. + + Method parses the rule: query = *( pchar / "/" / "?" ) @@ -802,7 +872,9 @@ def __query(self) -> bool: return False def __fragment(self) -> bool: - """Determines whether string contains a valid fragment. + """Determine whether string contains a valid fragment. + + Method parses the rule: fragment = *( pchar / "/" / "?" ) @@ -823,7 +895,9 @@ def __fragment(self) -> bool: return False def __pct_encoded(self) -> bool: - """Determines whether string contains a valid percent encoding. + """Determine whether string contains a valid percent encoding. + + Method parses the rule: pct-encoded = "%" HEXDIG HEXDIG @@ -841,7 +915,9 @@ def __pct_encoded(self) -> bool: return False def __unreserved(self) -> bool: - """Reports whether the current position is an unreserved character. + """Report whether the current position is an unreserved character. + + Method parses the rule: unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" """ @@ -856,7 +932,9 @@ def __unreserved(self) -> bool: ) def __sub_delims(self) -> bool: - """Reports whether the current position is a sub-delim. + """Report whether the current position is a sub-delim. + + Method parses the rule: sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" @@ -877,7 +955,7 @@ def __sub_delims(self) -> bool: ) def __alpha(self) -> bool: - """Reports whether the current position is an alpha character. + """Report whether the current position is an alpha character. Method parses the rule: @@ -895,7 +973,7 @@ def __alpha(self) -> bool: return False def __digit(self) -> bool: - """Reports whether the current position is a digit. + """Report whether the current position is a digit. Method parses the rule: @@ -913,7 +991,7 @@ def __digit(self) -> bool: return False def __hex_dig(self) -> bool: - """Reports whether the current position is a hex digit. + """Report whether the current position is a hex digit. Method parses the rule: