]> Cypherpunks.ru repositories - pyderasn.git/commitdiff
NumericString sanitizes its value against numbers
authorSergey Matveev <stargrave@stargrave.org>
Mon, 16 Apr 2018 08:45:36 +0000 (11:45 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Mon, 16 Apr 2018 12:56:14 +0000 (15:56 +0300)
VERSION
doc/news.rst
pyderasn.py
tests/test_pyderasn.py

diff --git a/VERSION b/VERSION
index eb39e5382f4f035e4d71c7f67712cdbfa6c0c335..2f4b60750dc3500b0e4cf08f316a960a7ca42b40 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-3.3
+3.4
index ad0284c0c2f27ed9300442680f25455b718466cb..db063a67b2232b791b1d51d631f7263d3448df73 100644 (file)
@@ -1,6 +1,14 @@
 News
 ====
 
+.. _release3.4:
+
+3.4
+---
+* Strict NumericString's value sanitation
+* Invalid encoding in string types will raise ``DecodeError`` exception,
+  instead of ``Unicode*Error``
+
 .. _release3.3:
 
 3.3
index 885f816b2097c93589d9cc2e44af0cc2c5ac5961..8924d07ed384c44914194616883464ef70d3133e 100755 (executable)
@@ -473,6 +473,7 @@ from collections import OrderedDict
 from datetime import datetime
 from math import ceil
 from os import environ
+from string import digits
 
 from six import add_metaclass
 from six import binary_type
@@ -2255,6 +2256,13 @@ class OctetString(Obj):
                 optional=self.optional,
                 _decoded=(offset, llen, l),
             )
+        except DecodeError as err:
+            raise DecodeError(
+                msg=err.msg,
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
         except BoundsError as err:
             raise DecodeError(
                 msg=str(err),
@@ -2811,7 +2819,7 @@ class CommonString(OctetString):
 
     >>> PrintableString("привет мир")
     Traceback (most recent call last):
-    UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
+    pyderasn.DecodeError: 'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)
 
     >>> BMPString("ада", bounds=(2, 2))
     Traceback (most recent call last):
@@ -2867,14 +2875,17 @@ class CommonString(OctetString):
             value_raw = value
         else:
             raise InvalidValueType((self.__class__, text_type, binary_type))
-        value_raw = (
-            value_decoded.encode(self.encoding)
-            if value_raw is None else value_raw
-        )
-        value_decoded = (
-            value_raw.decode(self.encoding)
-            if value_decoded is None else value_decoded
-        )
+        try:
+            value_raw = (
+                value_decoded.encode(self.encoding)
+                if value_raw is None else value_raw
+            )
+            value_decoded = (
+                value_raw.decode(self.encoding)
+                if value_decoded is None else value_decoded
+            )
+        except (UnicodeEncodeError, UnicodeDecodeError) as err:
+            raise DecodeError(str(err))
         if not self._bound_min <= len(value_decoded) <= self._bound_max:
             raise BoundsError(
                 self._bound_min,
@@ -2936,6 +2947,13 @@ class NumericString(CommonString):
     tag_default = tag_encode(18)
     encoding = "ascii"
     asn1_type_name = "NumericString"
+    allowable_chars = set(digits.encode("ascii"))
+
+    def _value_sanitize(self, value):
+        value = super(NumericString, self)._value_sanitize(value)
+        if not set(value) <= self.allowable_chars:
+            raise DecodeError("non-numeric value")
+        return value
 
 
 class PrintableString(CommonString):
index f655d973dcb39cd7b3c80e68b1876daeacb8af98..f8377e8881e6e4454758372c28196edac0f57c80 100644 (file)
@@ -18,6 +18,7 @@
 
 from datetime import datetime
 from string import ascii_letters
+from string import digits
 from string import printable
 from string import whitespace
 from unittest import TestCase
@@ -48,6 +49,7 @@ from six import int2byte
 from six import iterbytes
 from six import PY2
 from six import text_type
+from six import unichr as six_unichr
 
 from pyderasn import _pp
 from pyderasn import abs_decode_path
@@ -2802,35 +2804,110 @@ class TestUTF8String(StringMixin, CommonMixin, TestCase):
     base_klass = UTF8String
 
 
+class UnicodeDecodeErrorMixin(object):
+    @given(text(
+        alphabet=''.join(six_unichr(i) for i in list(range(0x0410, 0x044f + 1))),
+        min_size=1,
+        max_size=5,
+    ))
+    def test_unicode_decode_error(self, cyrillic_text):
+        with self.assertRaises(DecodeError):
+            self.base_klass(cyrillic_text)
+
+
 class TestNumericString(StringMixin, CommonMixin, TestCase):
     base_klass = NumericString
 
+    def text_alphabet(self):
+        return digits
+
+    @given(text(alphabet=ascii_letters, min_size=1, max_size=5))
+    def test_non_numeric(self, cyrillic_text):
+        with assertRaisesRegex(self, DecodeError, "non-numeric"):
+            self.base_klass(cyrillic_text)
+
+    @given(
+        sets(integers(min_value=0, max_value=10), min_size=2, max_size=2),
+        integers(min_value=0),
+        lists(integers()),
+    )
+    def test_invalid_bounds_while_decoding(self, ints, offset, decode_path):
+        decode_path = tuple(str(i) for i in decode_path)
+        value, bound_min = list(sorted(ints))
+
+        class String(self.base_klass):
+            bounds = (bound_min, bound_min)
+        with self.assertRaises(DecodeError) as err:
+            String().decode(
+                self.base_klass(b"1" * value).encode(),
+                offset=offset,
+                decode_path=decode_path,
+            )
+        repr(err.exception)
+        self.assertEqual(err.exception.offset, offset)
+        self.assertEqual(err.exception.decode_path, decode_path)
+
 
-class TestPrintableString(StringMixin, CommonMixin, TestCase):
+class TestPrintableString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = PrintableString
 
 
-class TestTeletexString(StringMixin, CommonMixin, TestCase):
+class TestTeletexString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = TeletexString
 
 
-class TestVideotexString(StringMixin, CommonMixin, TestCase):
+class TestVideotexString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = VideotexString
 
 
-class TestIA5String(StringMixin, CommonMixin, TestCase):
+class TestIA5String(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = IA5String
 
 
-class TestGraphicString(StringMixin, CommonMixin, TestCase):
+class TestGraphicString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = GraphicString
 
 
-class TestVisibleString(StringMixin, CommonMixin, TestCase):
+class TestVisibleString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = VisibleString
 
 
-class TestGeneralString(StringMixin, CommonMixin, TestCase):
+class TestGeneralString(
+        UnicodeDecodeErrorMixin,
+        StringMixin,
+        CommonMixin,
+        TestCase,
+):
     base_klass = GeneralString