Skip to content

Commit

Permalink
Convert TypeError/ValueError for invalid regex/MIME/UUID values
Browse files Browse the repository at this point in the history
  • Loading branch information
agronholm committed Jan 15, 2024
1 parent 7d8b776 commit 98bdde3
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
21 changes: 18 additions & 3 deletions cbor2/_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,19 +632,34 @@ def decode_rational(self) -> Fraction:

def decode_regexp(self) -> re.Pattern[str]:
# Semantic tag 35
return self.set_shareable(re.compile(self._decode()))
try:
value = re.compile(self._decode())
except re.error as exc:
raise CBORDecodeValueError("error decoding regular expression") from exc

return self.set_shareable(value)

def decode_mime(self) -> Message:
# Semantic tag 36
from email.parser import Parser

return self.set_shareable(Parser().parsestr(self._decode()))
try:
value = Parser().parsestr(self._decode())
except TypeError as exc:
raise CBORDecodeValueError("error decoding MIME message") from exc

return self.set_shareable(value)

def decode_uuid(self) -> UUID:
# Semantic tag 37
from uuid import UUID

return self.set_shareable(UUID(bytes=self._decode()))
try:
value = UUID(bytes=self._decode())
except (TypeError, ValueError) as exc:
raise CBORDecodeValueError("error decoding UUID value") from exc

return self.set_shareable(value)

def decode_stringref_namespace(self) -> Any:
# Semantic tag 256
Expand Down
9 changes: 9 additions & 0 deletions source/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,8 @@ CBORDecoder_decode_regexp(CBORDecoderObject *self)
if (pattern) {
ret = PyObject_CallFunctionObjArgs(_CBOR2_re_compile, pattern, NULL);
Py_DECREF(pattern);
if (!ret && PyErr_GivenExceptionMatches(PyErr_Occurred(), _CBOR2_re_error))
raise_from(_CBOR2_CBORDecodeValueError, "error decoding regular expression");
}
set_shareable(self, ret);
return ret;
Expand All @@ -1692,6 +1694,8 @@ CBORDecoder_decode_mime(CBORDecoderObject *self)
ret = PyObject_CallMethodObjArgs(parser,
_CBOR2_str_parsestr, value, NULL);
Py_DECREF(parser);
if (!ret && PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_TypeError))
raise_from(_CBOR2_CBORDecodeValueError, "error decoding MIME message");
}
Py_DECREF(value);
}
Expand All @@ -1713,6 +1717,11 @@ CBORDecoder_decode_uuid(CBORDecoderObject *self)
if (bytes) {
ret = PyObject_CallFunctionObjArgs(_CBOR2_UUID, Py_None, bytes, NULL);
Py_DECREF(bytes);
if (!ret && (
PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_TypeError)
|| PyErr_GivenExceptionMatches(PyErr_Occurred(), PyExc_ValueError)
))
raise_from(_CBOR2_CBORDecodeValueError, "error decoding UUID value");
}
set_shareable(self, ret);
return ret;
Expand Down
21 changes: 19 additions & 2 deletions source/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -480,27 +480,43 @@ _CBOR2_init_UUID(void)
int
_CBOR2_init_re_compile(void)
{
PyObject *re;
PyObject *re = NULL;

// import re
// datestr_re = re.compile("long-date-time-regex...")
re = PyImport_ImportModule("re");
if (!re)
goto error;

_CBOR2_re_compile = PyObject_GetAttr(re, _CBOR2_str_compile);
Py_DECREF(re);
if (!_CBOR2_re_compile)
goto error;

_CBOR2_re_error = PyObject_GetAttrString(re, "error");
if (!_CBOR2_re_error) {
Py_DECREF(_CBOR2_re_compile);
_CBOR2_re_compile = NULL;
goto error;
}

_CBOR2_datetimestr_re = PyObject_CallFunctionObjArgs(
_CBOR2_re_compile, _CBOR2_str_datetimestr_re, NULL);
if (!_CBOR2_datetimestr_re)
goto error;

_CBOR2_datestr_re = PyObject_CallFunctionObjArgs(
_CBOR2_re_compile, _CBOR2_str_datestr_re, NULL);
if (!_CBOR2_datestr_re)
goto error;

_CBOR2_datestr_re = PyObject_CallFunctionObjArgs(
_CBOR2_re_compile, _CBOR2_str_datestr_re, NULL);
if (!_CBOR2_datestr_re)
goto error;

return 0;
error:
Py_XDECREF(re);
PyErr_SetString(PyExc_ImportError, "unable to import compile from re");
return -1;
}
Expand Down Expand Up @@ -667,6 +683,7 @@ PyObject *_CBOR2_FrozenDict = NULL;
PyObject *_CBOR2_UUID = NULL;
PyObject *_CBOR2_Parser = NULL;
PyObject *_CBOR2_re_compile = NULL;
PyObject *_CBOR2_re_error = NULL;
PyObject *_CBOR2_datetimestr_re = NULL;
PyObject *_CBOR2_datestr_re = NULL;
PyObject *_CBOR2_ip_address = NULL;
Expand Down
3 changes: 2 additions & 1 deletion source/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ extern PyObject *_CBOR2_FrozenDict;
extern PyObject *_CBOR2_UUID;
extern PyObject *_CBOR2_Parser;
extern PyObject *_CBOR2_re_compile;
extern PyObject *_CBOR2_re_error;
extern PyObject *_CBOR2_datetimestr_re;
extern PyObject *_CBOR2_datestr_re;
extern PyObject *_CBOR2_ip_address;
Expand All @@ -110,7 +111,7 @@ int _CBOR2_init_Fraction(void);
int _CBOR2_init_FrozenDict(void);
int _CBOR2_init_UUID(void);
int _CBOR2_init_Parser(void);
int _CBOR2_init_re_compile(void); // also handles datetimestr_re & datestr_re
int _CBOR2_init_re_compile(void); // also handles datetimestr_re & datestr_re & re_error
int _CBOR2_init_ip_address(void);
int _CBOR2_init_thread_locals(void);

Expand Down
30 changes: 30 additions & 0 deletions tests/test_decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,15 @@ def test_regex(impl):
assert decoded == expr


def test_regex_unbalanced_parentheses(impl):
with pytest.raises(
impl.CBORDecodeValueError, match="error decoding regular expression"
) as exc:
impl.loads(unhexlify("d8236c68656c6c6f2028776f726c64"))

assert isinstance(exc.value.__cause__, re.error)


def test_mime(impl):
decoded = impl.loads(
unhexlify(
Expand All @@ -540,11 +549,32 @@ def test_mime(impl):
assert decoded.get_payload() == "Hello =A4uro"


def test_mime_invalid_type(impl):
with pytest.raises(impl.CBORDecodeValueError, match="error decoding MIME message") as exc:
impl.loads(unhexlify("d82401"))

assert isinstance(exc.value.__cause__, TypeError)


def test_uuid(impl):
decoded = impl.loads(unhexlify("d825505eaffac8b51e480581277fdcc7842faf"))
assert decoded == UUID(hex="5eaffac8b51e480581277fdcc7842faf")


def test_uuid_invalid_length(impl):
with pytest.raises(impl.CBORDecodeValueError, match="error decoding UUID value") as exc:
impl.loads(unhexlify("d8254f5eaffac8b51e480581277fdcc7842f"))

assert isinstance(exc.value.__cause__, ValueError)


def test_uuid_invalid_type(impl):
with pytest.raises(impl.CBORDecodeValueError, match="error decoding UUID value") as exc:
impl.loads(unhexlify("d82501"))

assert isinstance(exc.value.__cause__, TypeError)


@pytest.mark.parametrize(
"payload, expected",
[
Expand Down

0 comments on commit 98bdde3

Please sign in to comment.