Ensure validations account for trailing newlines

The regex's used for validation allowed "\n" terminated strings
to pass, allowing invalid data through.

Change-Id: Iae4c40ffab9951280bb6d67c2a7cb779dc7ddf0f
Closes-Bug: 1471158
This commit is contained in:
Kiall Mac Innes 2015-07-03 14:56:31 +01:00
parent 5cac3a602f
commit 33510373af
2 changed files with 180 additions and 10 deletions

View File

@ -23,29 +23,34 @@ from oslo_log import log as logging
LOG = logging.getLogger(__name__)
RE_DOMAINNAME = r'^(?!.{255,})(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+$'
RE_HOSTNAME = r'^(?!.{255,})(?:(?:^\*|(?!\-)[A-Za-z0-9_\-]{1,63})(?<!\-)\.)+$'
# NOTE(kiall): All of the below regular expressions are termined with
# "\Z", rather than simply "$" to ensure a string with a
# trailing newline is NOT matched. See bug #1471158.
RE_DOMAINNAME = r'^(?!.{255,})(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+\Z'
RE_HOSTNAME = r'^(?!.{255,})(?:(?:^\*|(?!\-)[A-Za-z0-9_\-]{1,63})(?<!\-)\.)+\Z'
RE_SRV_HOST_NAME = r'^(?:(?!\-)(?:\_[A-Za-z0-9_\-]{1,63}\.){2})(?!.{255,})' \
r'(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+$'
r'(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)\.)+\Z'
# The TLD name will not end in a period.
RE_TLDNAME = r'^(?!.{255,})(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-))' \
r'(?:\.(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)))*$'
r'(?:\.(?:(?!\-)[A-Za-z0-9_\-]{1,63}(?<!\-)))*\Z'
RE_UUID = r'^(?:[0-9a-fA-F]){8}-(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){4}-' \
r'(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){12}$'
r'(?:[0-9a-fA-F]){4}-(?:[0-9a-fA-F]){12}\Z'
RE_IP_AND_PORT = r'^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}' \
r'(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' \
r'(?::(6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}' \
r'|[1-5]\d{4}|[1-9]\d{0,3}|0))?$'
r'(?::(?:6553[0-5]|655[0-2]\d|65[0-4]\d\d|6[0-4]\d{3}' \
r'|[1-5]\d{4}|[1-9]\d{0,3}|0))?\Z'
RE_FIP_ID = r'^(?P<region>[A-Za-z0-9\\.\\-_]{1,100}):' \
RE_FIP_ID = r'^(?P<region>[A-Za-z0-9\.\-_]{1,100}):' \
r'(?P<id>[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-' \
r'[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$'
r'[0-9a-fA-F]{4}-[0-9a-fA-F]{12})\Z'
RE_SSHFP = r'^[0-9A-Fa-f]{40}\Z'
RE_SSHFP = r'^[0-9A-Fa-f]{40}$'
draft3_format_checker = jsonschema.draft3_format_checker
draft4_format_checker = jsonschema.draft4_format_checker
@ -152,6 +157,9 @@ def is_email(instance):
@draft4_format_checker.checks("sshfp")
def is_sshfp(instance):
# TODO(kiall): This isn't actually validating an SSH FP, It's trying to
# validate *part* of a SSHFP, we should either rename this
# or actually validate a SSHFP rdata in it's entireity.
if not isinstance(instance, compat.str_types):
return True
@ -176,6 +184,8 @@ def is_uuid(instance):
@draft3_format_checker.checks("floating-ip-id")
@draft4_format_checker.checks("floating-ip-id")
def is_floating_ip_id(instance):
# TODO(kiall): Apparantly, this is used in exactly zero places outside the
# tests. Determine if we should remove this code...
if not isinstance(instance, compat.str_types):
return True

View File

@ -47,6 +47,8 @@ class SchemaFormatTest(TestCase):
'ABCDEF',
'ABC/DEF',
'ABC\\DEF',
# Trailing newline - Bug 1471158
"127.0.0.1\n",
]
for ipaddress in valid_ipaddresses:
@ -55,6 +57,29 @@ class SchemaFormatTest(TestCase):
for ipaddress in invalid_ipaddresses:
self.assertFalse(format.is_ipv4(ipaddress))
def test_is_ipv6(self):
valid_ipaddresses = [
'2001:db8::0',
'2001:0db8:85a3:0000:0000:8a2e:0370:7334',
'2001:db8:85a3:0000:0000:8a2e:0370:7334',
'2001:db8:85a3::8a2e:0370:7334',
]
invalid_ipaddresses = [
# Invalid characters
'hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh:hhhh'
# Trailing newline - Bug 1471158
"2001:db8::0\n",
]
for ipaddress in valid_ipaddresses:
self.assertTrue(format.is_ipv6(ipaddress),
'Expected Valid: %s' % ipaddress)
for ipaddress in invalid_ipaddresses:
self.assertFalse(format.is_ipv6(ipaddress),
'Expected Invalid: %s' % ipaddress)
def test_is_hostname(self):
valid_hostnames = [
'example.com.',
@ -114,6 +139,8 @@ class SchemaFormatTest(TestCase):
'-abc-.',
'abc.-def-.',
'abc.-def-.ghi.',
# Trailing newline - Bug 1471158
"www.example.com.\n",
]
for hostname in valid_hostnames:
@ -184,6 +211,8 @@ class SchemaFormatTest(TestCase):
'-abc-.',
'abc.-def-.',
'abc.-def-.ghi.',
# Trailing newline - Bug 1471158
"example.com.\n",
]
for domainname in valid_domainnames:
@ -192,6 +221,54 @@ class SchemaFormatTest(TestCase):
for domainname in invalid_domainnames:
self.assertFalse(format.is_domainname(domainname))
def test_is_srv_hostname(self):
valid_hostnames = [
'_sip._tcp.example.com.',
'_sip._udp.example.com.',
]
invalid_hostnames = [
# Invalid Formats
'_tcp.example.com.',
'sip._udp.example.com.',
'_sip.udp.example.com.',
'sip.udp.example.com.',
# Trailing newline - Bug 1471158
"_sip._tcp.example.com.\n",
]
for hostname in valid_hostnames:
self.assertTrue(format.is_srv_hostname(hostname),
'Expected Valid: %s' % hostname)
for hostname in invalid_hostnames:
self.assertFalse(format.is_srv_hostname(hostname),
'Expected Invalid: %s' % hostname)
def test_is_tldname(self):
valid_tldnames = [
'com',
'net',
'org',
'co.uk',
]
invalid_tldnames = [
# Invalid Formats
'com.',
'.com',
# Trailing newline - Bug 1471158
"com\n",
]
for tldname in valid_tldnames:
self.assertTrue(format.is_tldname(tldname),
'Expected Valid: %s' % tldname)
for tldname in invalid_tldnames:
self.assertFalse(format.is_tldname(tldname),
'Expected Invalid: %s' % tldname)
def test_is_email(self):
valid_emails = [
'user@example.com',
@ -240,3 +317,86 @@ class SchemaFormatTest(TestCase):
for email in invalid_emails:
LOG.debug('Expecting failure for: %s' % email)
self.assertFalse(format.is_email(email))
def test_is_sshfp(self):
valid_sshfps = [
'72d30d211ce8c464de2811e534de23b9be9b4dc4',
]
invalid_sshfps = [
# Invalid Formats
'P2d30d211ce8c464de2811e534de23b9be9b4dc4', # "P" !IN [A-F]
'72d30d211',
# Trailing newline - Bug 1471158
"72d30d211ce8c464de2811e534de23b9be9b4dc4\n",
]
for sshfp in valid_sshfps:
self.assertTrue(format.is_sshfp(sshfp),
'Expected Valid: %s' % sshfp)
for sshfp in invalid_sshfps:
self.assertFalse(format.is_sshfp(sshfp),
'Expected Invalid: %s' % sshfp)
def test_is_uuid(self):
valid_uuids = [
'd3693ef8-2188-11e5-bf77-676ff9eb39dd',
]
invalid_uuids = [
# Invalid Formats
'p3693ef8-2188-11e5-bf77-676ff9eb39dd', # "p" !IN [A-F]
'd3693ef8218811e5bf77676ff9eb39dd',
# Trailing newline - Bug 1471158
"d3693ef8-2188-11e5-bf77-676ff9eb39dd\n",
]
for uuid in valid_uuids:
self.assertTrue(format.is_uuid(uuid),
'Expected Valid: %s' % uuid)
for uuid in invalid_uuids:
self.assertFalse(format.is_uuid(uuid),
'Expected Invalid: %s' % uuid)
def test_is_fip_id(self):
valid_fip_ids = [
'region-a:d3693ef8-2188-11e5-bf77-676ff9eb39dd',
]
invalid_fip_ids = [
# Invalid Formats
'region-a:p3693ef8-2188-11e5-bf77-676ff9eb39dd', # "p" !IN [A-F]
# Trailing newline - Bug 1471158
"region-a:d3693ef8-2188-11e5-bf77-676ff9eb39dd\n",
]
for fip_id in valid_fip_ids:
self.assertTrue(format.is_floating_ip_id(fip_id),
'Expected Valid: %s' % fip_id)
for fip_id in invalid_fip_ids:
self.assertFalse(format.is_floating_ip_id(fip_id),
'Expected Invalid: %s' % fip_id)
def test_is_ip_and_port(self):
valid_ip_and_ports = [
'192.0.2.1:80',
'192.0.2.1:1',
'192.0.2.1:65535',
]
invalid_ip_and_ports = [
'192.0.2.1:65536',
# Trailing newline - Bug 1471158
"192.0.2.1:80\n",
]
for ip_and_port in valid_ip_and_ports:
self.assertTrue(format.is_ip_and_port(ip_and_port),
'Expected Valid: %s' % ip_and_port)
for ip_and_port in invalid_ip_and_ports:
self.assertFalse(format.is_ip_and_port(ip_and_port),
'Expected Invalid: %s' % ip_and_port)