diff --git a/designate/objects/recordset.py b/designate/objects/recordset.py index 807293d26..42b067e44 100755 --- a/designate/objects/recordset.py +++ b/designate/objects/recordset.py @@ -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: diff --git a/designate/objects/rrdata_spf.py b/designate/objects/rrdata_spf.py index 1202bc77f..7fc1be3c2 100644 --- a/designate/objects/rrdata_spf.py +++ b/designate/objects/rrdata_spf.py @@ -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 diff --git a/designate/objects/rrdata_txt.py b/designate/objects/rrdata_txt.py index d22581f8a..44e13fff5 100644 --- a/designate/objects/rrdata_txt.py +++ b/designate/objects/rrdata_txt.py @@ -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 diff --git a/designate/tests/unit/test_objects/test_rrdata_spf.py b/designate/tests/unit/test_objects/test_rrdata_spf.py new file mode 100644 index 000000000..f11f05efa --- /dev/null +++ b/designate/tests/unit/test_objects/test_rrdata_spf.py @@ -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() diff --git a/designate/tests/unit/test_objects/test_rrdata_txt.py b/designate/tests/unit/test_objects/test_rrdata_txt.py new file mode 100644 index 000000000..f6ac899b1 --- /dev/null +++ b/designate/tests/unit/test_objects/test_rrdata_txt.py @@ -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() diff --git a/releasenotes/notes/bug-1755788-txt-spf-validation-d18e43c12691132a.yaml b/releasenotes/notes/bug-1755788-txt-spf-validation-d18e43c12691132a.yaml new file mode 100644 index 000000000..d5574be50 --- /dev/null +++ b/releasenotes/notes/bug-1755788-txt-spf-validation-d18e43c12691132a.yaml @@ -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