Check TXT/SPF records for RFC1035 sec. 5.1

If record data has empty spaces it should be surrounded by double quotes.
This patch will raise an error during validation if record has whitespaces,
empty spaces, tabs, etc., but not wrapped in " " (double quotes).

Corresponding RFC part:

<character-string> is expressed in one or two ways: as a contiguous set
of characters without interior spaces, or as a string beginning with a "
and ending with a ".  Inside a " delimited string any character can
occur, except for a " itself, which must be quoted using \ (back slash).

Closes-Bug: 1755788
Depends-On: https://review.openstack.org/#/c/617809/
Change-Id: I159d0732688ddf1337ab3602a84a43fd043dcaa2
This commit is contained in:
Dmitry Galkin 2018-03-22 17:53:27 +00:00
parent 59d8cab9bb
commit 02ef0350af
6 changed files with 111 additions and 4 deletions

View File

@ -187,10 +187,8 @@ class RecordSet(base.DesignateObject, base.DictObjectMixin,
error_indexes.append(i)
except Exception as e:
error_message = str.format(
'Provided object is not valid. '
'Got a %s error with message %s' %
(type(e).__name__, six.text_type(e)))
error_message = ('Provided object is not valid. Got a %s error'
' with message %s' % (type(e).__name__, six.text_type(e)))
raise exceptions.InvalidObject(error_message)
else:

View File

@ -16,6 +16,7 @@ from designate.objects.record import Record
from designate.objects.record import RecordList
from designate.objects import base
from designate.objects import fields
from designate.exceptions import InvalidObject
@base.DesignateRegistry.register
@ -32,6 +33,23 @@ class SPF(Record):
return self.txt_data
def _from_string(self, value):
if (not value.startswith('"') and not value.endswith('"')):
# value with spaces should be quoted as per RFC1035 5.1
for element in value:
if element.isspace():
err = ("Empty spaces are not allowed in SPF record, "
"unless wrapped in double quotes.")
raise InvalidObject(err)
else:
# quotes within value should be escaped with backslash
strip_value = value.strip('"')
for index, char in enumerate(strip_value):
if char == '"':
if strip_value[index - 1] != "\\":
err = ("Quotation marks should be escaped with "
"backslash.")
raise InvalidObject(err)
self.txt_data = value
# The record type is defined in the RFC. This will be used when the record

View File

@ -16,6 +16,7 @@ from designate.objects.record import Record
from designate.objects.record import RecordList
from designate.objects import base
from designate.objects import fields
from designate.exceptions import InvalidObject
@base.DesignateRegistry.register
@ -32,6 +33,23 @@ class TXT(Record):
return self.txt_data
def _from_string(self, value):
if (not value.startswith('"') and not value.endswith('"')):
# value with spaces should be quoted as per RFC1035 5.1
for element in value:
if element.isspace():
err = ("Empty spaces are not allowed in TXT record, "
"unless wrapped in double quotes.")
raise InvalidObject(err)
else:
# quotes within value should be escaped with backslash
strip_value = value.strip('"')
for index, char in enumerate(strip_value):
if char == '"':
if strip_value[index - 1] != "\\":
err = ("Quotation marks should be escaped with "
"backslash.")
raise InvalidObject(err)
self.txt_data = value
# The record type is defined in the RFC. This will be used when the record

View File

@ -0,0 +1,33 @@
# 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.
from oslo_log import log as logging
import oslotest.base
import testtools
from designate import exceptions
from designate import objects
LOG = logging.getLogger(__name__)
class RRDataSPFTest(oslotest.base.BaseTestCase):
def test_reject_non_quoted_spaces(self):
record = objects.SPF(data='foo bar')
with testtools.ExpectedException(exceptions.InvalidObject):
record.validate()
def test_reject_non_escaped_quotes(self):
record = objects.SPF(data='foo"bar')
with testtools.ExpectedException(exceptions.InvalidObject):
record.validate()

View File

@ -0,0 +1,33 @@
# 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.
from oslo_log import log as logging
import oslotest.base
import testtools
from designate import exceptions
from designate import objects
LOG = logging.getLogger(__name__)
class RRDataTXTTest(oslotest.base.BaseTestCase):
def test_reject_non_quoted_spaces(self):
record = objects.TXT(data='foo bar')
with testtools.ExpectedException(exceptions.InvalidObject):
record.validate()
def test_reject_non_escaped_quotes(self):
record = objects.TXT(data='foo"bar')
with testtools.ExpectedException(exceptions.InvalidObject):
record.validate()

View File

@ -0,0 +1,7 @@
---
fixes:
- |
TXT and SPF records are now validated for empty spaces in the values.
If record value has empty space it should use "" quotation according to
RFC-1035 section 5.1. Use of single quotation mark within record value
requires quote symbol to be escaped with backslash. Bug-1755788