Commit f1046ca8 authored by Florent Xicluna's avatar Florent Xicluna

Issue #4770: Restrict binascii module to accept only bytes (as specified).

And fix the email package to encode to ASCII instead of ``raw-unicode-escape`` before ASCII-to-binary decoding.
parent 4bf70686
...@@ -18,6 +18,11 @@ use these functions directly but use wrapper modules like :mod:`uu`, ...@@ -18,6 +18,11 @@ use these functions directly but use wrapper modules like :mod:`uu`,
low-level functions written in C for greater speed that are used by the low-level functions written in C for greater speed that are used by the
higher-level modules. higher-level modules.
.. note::
Encoding and decoding functions do not accept Unicode strings. Only bytestring
and bytearray objects can be processed.
The :mod:`binascii` module defines the following functions: The :mod:`binascii` module defines the following functions:
...@@ -54,6 +59,9 @@ The :mod:`binascii` module defines the following functions: ...@@ -54,6 +59,9 @@ The :mod:`binascii` module defines the following functions:
data. More than one line may be passed at a time. If the optional argument data. More than one line may be passed at a time. If the optional argument
*header* is present and true, underscores will be decoded as spaces. *header* is present and true, underscores will be decoded as spaces.
.. versionchanged:: 3.2
accept only bytestring or bytearray object as input.
.. function:: b2a_qp(data, quotetabs=False, istext=True, header=False) .. function:: b2a_qp(data, quotetabs=False, istext=True, header=False)
...@@ -83,6 +91,9 @@ The :mod:`binascii` module defines the following functions: ...@@ -83,6 +91,9 @@ The :mod:`binascii` module defines the following functions:
decompressed data, unless data input data ends in an orphaned repeat indicator, decompressed data, unless data input data ends in an orphaned repeat indicator,
in which case the :exc:`Incomplete` exception is raised. in which case the :exc:`Incomplete` exception is raised.
.. versionchanged:: 3.2
accept only bytestring or bytearray object as input.
.. function:: rlecode_hqx(data) .. function:: rlecode_hqx(data)
...@@ -139,6 +150,9 @@ The :mod:`binascii` module defines the following functions: ...@@ -139,6 +150,9 @@ The :mod:`binascii` module defines the following functions:
of hexadecimal digits (which can be upper or lower case), otherwise a of hexadecimal digits (which can be upper or lower case), otherwise a
:exc:`TypeError` is raised. :exc:`TypeError` is raised.
.. versionchanged:: 3.2
accept only bytestring or bytearray object as input.
.. exception:: Error .. exception:: Error
...@@ -164,4 +178,3 @@ The :mod:`binascii` module defines the following functions: ...@@ -164,4 +178,3 @@ The :mod:`binascii` module defines the following functions:
Module :mod:`quopri` Module :mod:`quopri`
Support for quoted-printable encoding used in MIME email messages. Support for quoted-printable encoding used in MIME email messages.
...@@ -74,12 +74,12 @@ def header_encode(header_bytes, charset='iso-8859-1'): ...@@ -74,12 +74,12 @@ def header_encode(header_bytes, charset='iso-8859-1'):
def body_encode(s, maxlinelen=76, eol=NL): def body_encode(s, maxlinelen=76, eol=NL):
"""Encode a string with base64. r"""Encode a string with base64.
Each line will be wrapped at, at most, maxlinelen characters (defaults to Each line will be wrapped at, at most, maxlinelen characters (defaults to
76 characters). 76 characters).
Each line of encoded text will end with eol, which defaults to "\\n". Set Each line of encoded text will end with eol, which defaults to "\n". Set
this to "\r\n" if you will be using the result of this function directly this to "\r\n" if you will be using the result of this function directly
in an email. in an email.
""" """
......
...@@ -198,17 +198,19 @@ class Message: ...@@ -198,17 +198,19 @@ class Message:
return None return None
cte = self.get('content-transfer-encoding', '').lower() cte = self.get('content-transfer-encoding', '').lower()
if cte == 'quoted-printable': if cte == 'quoted-printable':
if isinstance(payload, str):
payload = payload.encode('ascii')
return utils._qdecode(payload) return utils._qdecode(payload)
elif cte == 'base64': elif cte == 'base64':
try: try:
if isinstance(payload, str): if isinstance(payload, str):
payload = payload.encode('raw-unicode-escape') payload = payload.encode('ascii')
return base64.b64decode(payload) return base64.b64decode(payload)
except binascii.Error: except binascii.Error:
# Incorrect padding # Incorrect padding
pass pass
elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'):
in_file = BytesIO(payload.encode('raw-unicode-escape')) in_file = BytesIO(payload.encode('ascii'))
out_file = BytesIO() out_file = BytesIO()
try: try:
uu.decode(in_file, out_file, quiet=True) uu.decode(in_file, out_file, quiet=True)
......
...@@ -55,7 +55,7 @@ class BinASCIITest(unittest.TestCase): ...@@ -55,7 +55,7 @@ class BinASCIITest(unittest.TestCase):
"{!r} != {!r}".format(fb, fa, res, raw)) "{!r} != {!r}".format(fb, fa, res, raw))
self.assertIsInstance(res, bytes) self.assertIsInstance(res, bytes)
self.assertIsInstance(a, bytes) self.assertIsInstance(a, bytes)
self.assertLess(max(c for c in a), 128) self.assertLess(max(a), 128)
self.assertIsInstance(binascii.crc_hqx(raw, 0), int) self.assertIsInstance(binascii.crc_hqx(raw, 0), int)
self.assertIsInstance(binascii.crc32(raw), int) self.assertIsInstance(binascii.crc32(raw), int)
...@@ -167,7 +167,7 @@ class BinASCIITest(unittest.TestCase): ...@@ -167,7 +167,7 @@ class BinASCIITest(unittest.TestCase):
def test_qp(self): def test_qp(self):
# A test for SF bug 534347 (segfaults without the proper fix) # A test for SF bug 534347 (segfaults without the proper fix)
try: try:
binascii.a2b_qp("", **{1:1}) binascii.a2b_qp(b"", **{1:1})
except TypeError: except TypeError:
pass pass
else: else:
...@@ -179,12 +179,10 @@ class BinASCIITest(unittest.TestCase): ...@@ -179,12 +179,10 @@ class BinASCIITest(unittest.TestCase):
self.assertEqual(binascii.a2b_qp(b"=00\r\n=00"), b"\x00\r\n\x00") self.assertEqual(binascii.a2b_qp(b"=00\r\n=00"), b"\x00\r\n\x00")
self.assertEqual( self.assertEqual(
binascii.b2a_qp(b"\xff\r\n\xff\n\xff"), binascii.b2a_qp(b"\xff\r\n\xff\n\xff"),
b"=FF\r\n=FF\r\n=FF" b"=FF\r\n=FF\r\n=FF")
)
self.assertEqual( self.assertEqual(
binascii.b2a_qp(b"0"*75+b"\xff\r\n\xff\r\n\xff"), binascii.b2a_qp(b"0"*75+b"\xff\r\n\xff\r\n\xff"),
b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF" b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF")
)
self.assertEqual(binascii.b2a_qp(b'\0\n'), b'=00\n') self.assertEqual(binascii.b2a_qp(b'\0\n'), b'=00\n')
self.assertEqual(binascii.b2a_qp(b'\0\n', quotetabs=True), b'=00\n') self.assertEqual(binascii.b2a_qp(b'\0\n', quotetabs=True), b'=00\n')
...@@ -210,13 +208,15 @@ class BinASCIITest(unittest.TestCase): ...@@ -210,13 +208,15 @@ class BinASCIITest(unittest.TestCase):
except Exception as err: except Exception as err:
self.fail("{}({!r}) raises {!r}".format(func, empty, err)) self.fail("{}({!r}) raises {!r}".format(func, empty, err))
def test_no_binary_strings(self): def test_unicode_strings(self):
# b2a_ must not accept strings # Unicode strings are not accepted.
for f in (binascii.b2a_uu, binascii.b2a_base64, for func in all_functions:
binascii.b2a_hqx, binascii.b2a_qp, try:
binascii.hexlify, binascii.rlecode_hqx, self.assertRaises(TypeError, getattr(binascii, func), "test")
binascii.crc_hqx, binascii.crc32): except Exception as err:
self.assertRaises(TypeError, f, "test") self.fail('{}("test") raises {!r}'.format(func, err))
# crc_hqx needs 2 arguments
self.assertRaises(TypeError, binascii.crc_hqx, "test", 0)
class ArrayBinASCIITest(BinASCIITest): class ArrayBinASCIITest(BinASCIITest):
......
...@@ -213,6 +213,7 @@ class StructTest(unittest.TestCase): ...@@ -213,6 +213,7 @@ class StructTest(unittest.TestCase):
expected = '%x' % expected expected = '%x' % expected
if len(expected) & 1: if len(expected) & 1:
expected = "0" + expected expected = "0" + expected
expected = expected.encode('ascii')
expected = unhexlify(expected) expected = unhexlify(expected)
expected = (b"\x00" * (self.bytesize - len(expected)) + expected = (b"\x00" * (self.bytesize - len(expected)) +
expected) expected)
......
...@@ -473,6 +473,10 @@ C-API ...@@ -473,6 +473,10 @@ C-API
Library Library
------- -------
- Issue #4770: Restrict binascii module to accept only bytes (as specified).
And fix the email package to encode to ASCII instead of
``raw-unicode-escape`` before ASCII-to-binary decoding.
- Issue #9384: python -m tkinter will now display a simple demo applet. - Issue #9384: python -m tkinter will now display a simple demo applet.
- The default size of the re module's compiled regular expression cache has - The default size of the re module's compiled regular expression cache has
......
...@@ -546,7 +546,7 @@ binascii_a2b_hqx(PyObject *self, PyObject *args) ...@@ -546,7 +546,7 @@ binascii_a2b_hqx(PyObject *self, PyObject *args)
Py_ssize_t len; Py_ssize_t len;
int done = 0; int done = 0;
if ( !PyArg_ParseTuple(args, "s*:a2b_hqx", &pascii) ) if ( !PyArg_ParseTuple(args, "y*:a2b_hqx", &pascii) )
return NULL; return NULL;
ascii_data = pascii.buf; ascii_data = pascii.buf;
len = pascii.len; len = pascii.len;
...@@ -750,7 +750,7 @@ binascii_rledecode_hqx(PyObject *self, PyObject *args) ...@@ -750,7 +750,7 @@ binascii_rledecode_hqx(PyObject *self, PyObject *args)
PyObject *rv; PyObject *rv;
Py_ssize_t in_len, out_len, out_len_left; Py_ssize_t in_len, out_len, out_len_left;
if ( !PyArg_ParseTuple(args, "s*:rledecode_hqx", &pin) ) if ( !PyArg_ParseTuple(args, "y*:rledecode_hqx", &pin) )
return NULL; return NULL;
in_data = pin.buf; in_data = pin.buf;
in_len = pin.len; in_len = pin.len;
...@@ -1121,7 +1121,7 @@ binascii_unhexlify(PyObject *self, PyObject *args) ...@@ -1121,7 +1121,7 @@ binascii_unhexlify(PyObject *self, PyObject *args)
char* retbuf; char* retbuf;
Py_ssize_t i, j; Py_ssize_t i, j;
if (!PyArg_ParseTuple(args, "s*:a2b_hex", &parg)) if (!PyArg_ParseTuple(args, "y*:a2b_hex", &parg))
return NULL; return NULL;
argbuf = parg.buf; argbuf = parg.buf;
arglen = parg.len; arglen = parg.len;
...@@ -1199,7 +1199,7 @@ binascii_a2b_qp(PyObject *self, PyObject *args, PyObject *kwargs) ...@@ -1199,7 +1199,7 @@ binascii_a2b_qp(PyObject *self, PyObject *args, PyObject *kwargs)
static char *kwlist[] = {"data", "header", NULL}; static char *kwlist[] = {"data", "header", NULL};
int header = 0; int header = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s*|i", kwlist, &pdata, if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*|i", kwlist, &pdata,
&header)) &header))
return NULL; return NULL;
data = pdata.buf; data = pdata.buf;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment