Skip to content

Fix ipv6 validation for addresses with brackets #653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 16 additions & 38 deletions src/saml2/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import struct
import base64
import time
from ipaddress import AddressValueError
from ipaddress import IPv4Address
from ipaddress import IPv6Address

from saml2 import time_util

Expand Down Expand Up @@ -112,55 +115,30 @@ def validate_before(not_before, slack):


def valid_address(address):
"""Validate IPv4/IPv6 addresses."""
if not (valid_ipv4(address) or valid_ipv6(address)):
raise NotValid("address")
return True


def valid_ipv4(address):
parts = address.split(".")
if len(parts) != 4:
"""Validate IPv4 addresses."""
try:
IPv4Address(address)
except AddressValueError:
return False
for item in parts:
try:
if not 0 <= int(item) <= 255:
raise NotValid("ipv4")
except ValueError:
return False
return True

#
IPV6_PATTERN = re.compile(r"""
^
\s* # Leading whitespace
(?!.*::.*::) # Only a single wildcard allowed
(?:(?!:)|:(?=:)) # Colon iff it would be part of a wildcard
(?: # Repeat 6 times:
[0-9a-f]{0,4} # A group of at most four hexadecimal digits
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
){6} #
(?: # Either
[0-9a-f]{0,4} # Another group
(?:(?<=::)|(?<!::):) # Colon unless preceeded by wildcard
[0-9a-f]{0,4} # Last group
(?: (?<=::) # Colon iff preceeded by exacly one colon
| (?<!:) #
| (?<=:) (?<!::) : #
) # OR
| # A v4 address with NO leading zeros
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
(?: \.
(?:25[0-4]|2[0-4]\d|1\d\d|[1-9]?\d)
){3}
)
\s* # Trailing whitespace
$
""", re.VERBOSE | re.IGNORECASE | re.DOTALL)


def valid_ipv6(address):
"""Validates IPv6 addresses. """
return IPV6_PATTERN.match(address) is not None
"""Validate IPv6 addresses."""
is_enclosed_in_brackets = address.startswith("[") and address.endswith("]")
address_raw = address[1:-1] if is_enclosed_in_brackets else address
try:
IPv6Address(address_raw)
except AddressValueError:
return False
return True


def valid_boolean(val):
Expand Down
25 changes: 25 additions & 0 deletions tests/test_13_validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from saml2.validate import valid_any_uri
from saml2.validate import NotValid
from saml2.validate import valid_anytype
from saml2.validate import valid_address

from pytest import raises

Expand Down Expand Up @@ -120,3 +121,27 @@ def test_valid_anytype():
assert valid_anytype("P1Y2M3DT10H30M")
assert valid_anytype("urn:oasis:names:tc:SAML:2.0:attrname-format:uri")


def test_valid_address():
assert valid_address("130.239.16.3")
assert valid_address("2001:8003:5555:9999:555a:5555:c77:d5c5")
assert valid_address("2001:8003:5555::555a:5555:c77:d5c5")

# See https://tools.ietf.org/html/rfc4038#section-5.1 regarding
# the inclusion of brackets in the ipv6 address below.
assert valid_address("[2001:8003:5555:9999:555a:5555:c77:d5c5]")

with raises(NotValid):
assert valid_address("127.0.0.256")
with raises(NotValid):
assert valid_address("127.0.0.")
with raises(NotValid):
assert valid_address("127.0.0")
with raises(NotValid):
assert valid_address("2001::5555:9999::5555:c77:d5c5]")
with raises(NotValid):
assert valid_address("2001:8003:5555:9999:555a:5555:c77:d5c5]")
with raises(NotValid):
assert valid_address("[2001:8003:5555:9999:555a:5555:c77:d5c5")
with raises(NotValid):
assert valid_address("[[2001:8003:5555:9999:555a:5555:c77:d5c5]")