diff --git a/tests/test_ipv6.py b/tests/test_ipv6.py index 3f321404..286f1fb5 100644 --- a/tests/test_ipv6.py +++ b/tests/test_ipv6.py @@ -5,11 +5,15 @@ @pytest.mark.parametrize(('address',), [ + ('::',), ('::1',), - ('dead:beef:0:0:0:0:42:1',), + ('1::',), + ('dead:beef:0:0:0:0000:42:1',), ('abcd:ef::42:1',), ('0:0:0:0:0:ffff:1.2.3.4',), ('::192.168.30.2',), + ('0000:0000:0000:0000:0000::',), + ('0:a:b:c:d:e:f::',), ]) def test_returns_true_on_valid_ipv6_address(address): assert ipv6(address) @@ -20,7 +24,19 @@ def test_returns_true_on_valid_ipv6_address(address): ('abc.0.0.1',), ('abcd:1234::123::1',), ('1:2:3:4:5:6:7:8:9',), + ('1:2:3:4:5:6:7:8::',), + ('1:2:3:4:5:6:7::8:9',), ('abcd::1ffff',), + ('1111:',), + (':8888',), + (':1.2.3.4',), + ('18:05',), + (':',), + (':1:2:',), + (':1:2::',), + ('::1:2::',), + ('8::1:2::9',), + ('02001:0000:1234:0000:0000:C1C0:ABCD:0876',), ]) def test_returns_failed_validation_on_invalid_ipv6_address(address): assert isinstance(ipv6(address), ValidationFailure) diff --git a/validators/ip_address.py b/validators/ip_address.py index ba36cdc6..f8b138d1 100644 --- a/validators/ip_address.py +++ b/validators/ip_address.py @@ -4,7 +4,7 @@ @validator def ipv4(value): """ - Return whether or not given value is a valid IP version 4 address. + Return whether a given value is a valid IP version 4 address. This validator is based on `WTForms IPAddress validator`_ @@ -32,7 +32,7 @@ def ipv4(value): @validator def ipv4_cidr(value): """ - Return whether or not given value is a valid CIDR-notated IP version 4 + Return whether a given value is a valid CIDR-notated IP version 4 address range. This validator is based on RFC4632 3.1. @@ -57,7 +57,7 @@ def ipv4_cidr(value): @validator def ipv6(value): """ - Return whether or not given value is a valid IP version 6 address + Return whether a given value is a valid IP version 6 address (including IPv4-mapped IPv6 addresses). This validator is based on `WTForms IPAddress validator`_. @@ -95,10 +95,6 @@ def ipv6(value): else: ipv4_groups = [] - max_groups = 6 if ipv4_groups else 8 - if len(ipv6_groups) > max_groups: - return False - count_blank = 0 for part in ipv6_groups: if not part: @@ -109,12 +105,24 @@ def ipv6(value): except ValueError: return False else: - if not 0 <= num <= 65536: + if not 0 <= num <= 65536 or len(part) > 4: return False - if count_blank < 2: + max_groups = 6 if ipv4_groups else 8 + part_count = len(ipv6_groups) - count_blank + if count_blank == 0 and part_count == max_groups: + # no :: -> must have size of max_groups + return True + elif count_blank == 1 and ipv6_groups[-1] and ipv6_groups[0] and part_count < max_groups: + # one :: inside the address or prefix or suffix : -> filter least two cases + return True + elif count_blank == 2 and part_count < max_groups and ( + ((ipv6_groups[0] and not ipv6_groups[-1]) or (not ipv6_groups[0] and ipv6_groups[-1])) or ipv4_groups): + # leading or trailing :: or : at end and begin -> filter last case + # Check if it has ipv4 groups because they get removed from the ipv6_groups return True - elif count_blank == 2 and not ipv6_groups[0] and not ipv6_groups[1]: + elif count_blank == 3 and part_count == 0: + # :: is the address -> filter everything else return True return False @@ -122,7 +130,7 @@ def ipv6(value): @validator def ipv6_cidr(value): """ - Returns whether or not given value is a valid CIDR-notated IP version 6 + Returns whether a given value is a valid CIDR-notated IP version 6 address range. This validator is based on RFC4632 3.1.