From 52d83e9fa67b7b4cd246f34ecac9689b8cdf266d Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sun, 31 Jan 2021 18:31:28 +0200 Subject: [PATCH 01/23] Added handling for excess data in binascii.a2b_base64 --- Modules/binascii.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index 1f3248b6049b31..d308a161db3ed3 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -466,10 +466,16 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) */ if (this_ch == BASE64_PAD) { if (quad_pos >= 2 && quad_pos + ++pads >= 4) { - /* A pad sequence means no more input. - ** We've already interpreted the data - ** from the quad at this point. + /* A pad sequence means we should not parse more input. + ** We've already interpreted the data from the quad at this point. + ** An error should raise if there's excess data after the padding. */ + if (i + 1 < ascii_len) { + binascii_state *state = PyModule_GetState(module); + PyErr_SetString(state->Error, "Excess data after padding is not allowed"); + goto error_end; + } + goto done; } continue; @@ -522,6 +528,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) } else { PyErr_SetString(state->Error, "Incorrect padding"); } + error_end: _PyBytesWriter_Dealloc(&writer); return NULL; } From 557bce8b6bad1387e8f6fc886f9e8fa42c3031bb Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Sun, 31 Jan 2021 18:24:55 +0000 Subject: [PATCH 02/23] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20b?= =?UTF-8?q?lurb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst diff --git a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst new file mode 100644 index 00000000000000..0b02f6ceeac898 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst @@ -0,0 +1 @@ +Added handling for excess data in binascii.a2b_base64 \ No newline at end of file From 6ed5193a3d302e1200cf729bf5eb88d195c3cd72 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 13 Mar 2021 14:29:18 +0200 Subject: [PATCH 03/23] Added if-state guard --- Modules/binascii.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index d308a161db3ed3..9a04809ec4305b 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -472,7 +472,9 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) */ if (i + 1 < ascii_len) { binascii_state *state = PyModule_GetState(module); - PyErr_SetString(state->Error, "Excess data after padding is not allowed"); + if (state) { + PyErr_SetString(state->Error, "Excess data after padding is not allowed"); + } goto error_end; } From c7b723f4c776be794d3e7626fad62694434e3bb5 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Wed, 31 Mar 2021 01:10:30 +0300 Subject: [PATCH 04/23] Implemented the strict mode logic Also documented the new argument. --- Modules/binascii.c | 17 +++++++++++++---- Modules/clinic/binascii.c.h | 35 ++++++++++++++++++++++++++++------- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index 9a04809ec4305b..25ffe30eb389a6 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -433,12 +433,14 @@ binascii.a2b_base64 data: ascii_buffer / + * + strict_mode: bool(accept={int}) = False Decode a line of base64 data. [clinic start generated code]*/ static PyObject * -binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) +binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) /*[clinic end generated code: output=0628223f19fd3f9b input=5872acf6e1cac243]*/ { assert(data->len >= 0); @@ -468,12 +470,12 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) if (quad_pos >= 2 && quad_pos + ++pads >= 4) { /* A pad sequence means we should not parse more input. ** We've already interpreted the data from the quad at this point. - ** An error should raise if there's excess data after the padding. + ** in strict mode, an error should raise if there's excess data after the padding. */ - if (i + 1 < ascii_len) { + if (strict_mode && i + 1 < ascii_len) { binascii_state *state = PyModule_GetState(module); if (state) { - PyErr_SetString(state->Error, "Excess data after padding is not allowed"); + PyErr_SetString(state->Error, "Excess data after padding is not allowed when using strict mode"); } goto error_end; } @@ -485,6 +487,13 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data) this_ch = table_a2b_base64[this_ch]; if (this_ch >= 64) { + if (strict_mode) { + binascii_state *state = PyModule_GetState(module); + if (state) { + PyErr_SetString(state->Error, "Only base64 data is allowed when using strict mode"); + } + goto error_end; + } continue; } pads = 0; diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index ae1c4574325c56..18e5ae8590a163 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -87,27 +87,48 @@ binascii_b2a_uu(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj } PyDoc_STRVAR(binascii_a2b_base64__doc__, -"a2b_base64($module, data, /)\n" +"a2b_base64($module, data, /, *, strict_mode=False)\n" "--\n" "\n" -"Decode a line of base64 data."); +"Decode a line of base64 data.\n" +"\n" +" strict_mode\n" +" When set to True, bytes that are not part of the base64 standard are not allowed.\n" +" The same applies to excess data after padding (= / ==)."); #define BINASCII_A2B_BASE64_METHODDEF \ - {"a2b_base64", (PyCFunction)binascii_a2b_base64, METH_O, binascii_a2b_base64__doc__}, + {"a2b_base64", (PyCFunction)(void(*)(void))binascii_a2b_base64, METH_FASTCALL|METH_KEYWORDS, binascii_a2b_base64__doc__}, static PyObject * -binascii_a2b_base64_impl(PyObject *module, Py_buffer *data); +binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode); static PyObject * -binascii_a2b_base64(PyObject *module, PyObject *arg) +binascii_a2b_base64(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; + static const char * const _keywords[] = {"", "strict_mode", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "a2b_base64", 0}; + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; Py_buffer data = {NULL, NULL}; + int strict_mode = 0; - if (!ascii_buffer_converter(arg, &data)) { + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf); + if (!args) { goto exit; } - return_value = binascii_a2b_base64_impl(module, &data); + if (!ascii_buffer_converter(args[0], &data)) { + goto exit; + } + if (!noptargs) { + goto skip_optional_kwonly; + } + strict_mode = _PyLong_AsInt(args[1]); + if (strict_mode == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_kwonly: + return_value = binascii_a2b_base64_impl(module, &data, strict_mode); exit: /* Cleanup for data */ From f6283d9787f763ee925c9a97e0731d345379be40 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Wed, 7 Apr 2021 18:20:36 +0300 Subject: [PATCH 05/23] =?UTF-8?q?Trying=20to=20fix=20the=20"Check=20if=20g?= =?UTF-8?q?enerated=20files=20are=20up=20to=20date"=20failure=20Never=20di?= =?UTF-8?q?d=20it=20before=20=F0=9F=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Modules/binascii.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index 25ffe30eb389a6..5fb30eb61fa200 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -441,7 +441,7 @@ Decode a line of base64 data. static PyObject * binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) -/*[clinic end generated code: output=0628223f19fd3f9b input=5872acf6e1cac243]*/ +/*[clinic end generated code: output=5409557788d4f975 input=5872acf6e1cac243]*/ { assert(data->len >= 0); From cedbb856cb14e13fe5bfc7581fcb49653e071869 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Wed, 7 Apr 2021 21:57:32 +0300 Subject: [PATCH 06/23] Generated function signatures using clinic (This time did it properly) --- Modules/binascii.c | 6 +++++- Modules/clinic/binascii.c.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index 5fb30eb61fa200..fe6f749ba18b31 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -437,11 +437,15 @@ binascii.a2b_base64 strict_mode: bool(accept={int}) = False Decode a line of base64 data. + + strict_mode + When set to True, bytes that are not part of the base64 standard are not allowed. + The same applies to excess data after padding (= / ==). [clinic start generated code]*/ static PyObject * binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) -/*[clinic end generated code: output=5409557788d4f975 input=5872acf6e1cac243]*/ +/*[clinic end generated code: output=5409557788d4f975 input=3a30c4e3528317c6]*/ { assert(data->len >= 0); diff --git a/Modules/clinic/binascii.c.h b/Modules/clinic/binascii.c.h index 18e5ae8590a163..a9240046a59110 100644 --- a/Modules/clinic/binascii.c.h +++ b/Modules/clinic/binascii.c.h @@ -767,4 +767,4 @@ binascii_b2a_qp(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObj return return_value; } -/*[clinic end generated code: output=95a0178f30801b89 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=0f261ee49971f5ca input=a9049054013a1b77]*/ From 69c96d54e4c1a4992f00cbb0a8f895292b158296 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Mon, 12 Apr 2021 18:21:21 +0300 Subject: [PATCH 07/23] Handle data in the middle of the padding in strict mode --- Modules/binascii.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Modules/binascii.c b/Modules/binascii.c index fe6f749ba18b31..c1c7e77b7c289f 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -451,6 +451,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) const unsigned char *ascii_data = data->buf; size_t ascii_len = data->len; + char padding_started = 0; /* Allocate the buffer */ Py_ssize_t bin_len = ((ascii_len+3)/4)*3; /* Upper bound, corrected later */ @@ -471,6 +472,8 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) ** the invalid ones. */ if (this_ch == BASE64_PAD) { + padding_started = 1; + if (quad_pos >= 2 && quad_pos + ++pads >= 4) { /* A pad sequence means we should not parse more input. ** We've already interpreted the data from the quad at this point. @@ -500,6 +503,15 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) } continue; } + + // Characters that are not '=', in the middle of the padding, are not allowed + if (strict_mode && padding_started) { + binascii_state *state = PyModule_GetState(module); + if (state) { + PyErr_SetString(state->Error, "Malformed padding in strict mode"); + } + goto error_end; + } pads = 0; switch (quad_pos) { From 3718ebd1c0ee0a8ec4b38f8884ea7a04290670a4 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Mon, 12 Apr 2021 19:00:01 +0300 Subject: [PATCH 08/23] Added a test for strict mode --- Lib/test/test_binascii.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 4d1bf2cce1f1e3..8e161df86c05b1 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -114,6 +114,38 @@ def addnoise(line): # empty strings. TBD: shouldn't it raise an exception instead ? self.assertEqual(binascii.a2b_base64(self.type2test(fillers)), b'') + def test_base64_strict_mode(self): + # Test base64 with strict mode on + def assertExcessData(data): + with self.assertRaisesRegex(binascii.Error, r'(?i)Excess data'): + binascii.a2b_base64(self.type2test(data), strict_mode=True) + + def assertNonBase64Data(data): + with self.assertRaisesRegex(binascii.Error, r'(?i)Only base64 data'): + binascii.a2b_base64(self.type2test(data), strict_mode=True) + + def assertMalformedPadding(data): + with self.assertRaisesRegex(binascii.Error, r'(?i)Malformed padding'): + binascii.a2b_base64(self.type2test(data), strict_mode=True) + + # Test excess data exceptions + assertExcessData(b'ab==a') + assertExcessData(b'ab===') + assertExcessData(b'ab==:') + assertExcessData(b'abc=a') + assertExcessData(b'abc=:') + assertExcessData(b'ab==\n') + + # Test non-base64 data exceptions + assertNonBase64Data(b'ab:(){:|:&};:==') + assertNonBase64Data(b'a\nb==') + assertNonBase64Data(b'a\x00b==') + + # Test malformed padding + assertMalformedPadding(b'ab=c=') + assertMalformedPadding(b'ab=ab==') + + def test_base64errors(self): # Test base64 with invalid padding def assertIncorrectPadding(data): From d0b60e23dd5edaf2efa7a1b10192783381529dd2 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 11:58:23 +0300 Subject: [PATCH 09/23] Added a test for invalid data as the first character --- Lib/test/test_binascii.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 8e161df86c05b1..cf26f2ee8bfeb7 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -137,6 +137,7 @@ def assertMalformedPadding(data): assertExcessData(b'ab==\n') # Test non-base64 data exceptions + assertNonBase64Data(b'\nab==') assertNonBase64Data(b'ab:(){:|:&};:==') assertNonBase64Data(b'a\nb==') assertNonBase64Data(b'a\x00b==') From afb95dba830961aceda9070d69eb727405fbdd8d Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 13:05:25 +0300 Subject: [PATCH 10/23] Disallowed leading padding in strict mode --- Modules/binascii.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/binascii.c b/Modules/binascii.c index c1c7e77b7c289f..a11dcd16196fdc 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -462,6 +462,10 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) return NULL; unsigned char *bin_data_start = bin_data; + if (strict_mode && ascii_len > 0 && ascii_data[0] == '=') { + goto malformed_padding; + } + int quad_pos = 0; unsigned char leftchar = 0; int pads = 0; @@ -506,6 +510,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) // Characters that are not '=', in the middle of the padding, are not allowed if (strict_mode && padding_started) { + malformed_padding: binascii_state *state = PyModule_GetState(module); if (state) { PyErr_SetString(state->Error, "Malformed padding in strict mode"); From 3c5758b63211d2a8a037db018f0632674b179365 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 13:18:04 +0300 Subject: [PATCH 11/23] Added tests for padding-only input --- Lib/test/test_binascii.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index cf26f2ee8bfeb7..90604037d48bbc 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -143,6 +143,9 @@ def assertMalformedPadding(data): assertNonBase64Data(b'a\x00b==') # Test malformed padding + assertMalformedPadding(b'=') + assertMalformedPadding(b'==') + assertMalformedPadding(b'===') assertMalformedPadding(b'ab=c=') assertMalformedPadding(b'ab=ab==') From 5f8df5b7768e62919db79e772de027be5206b093 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 16:01:43 +0300 Subject: [PATCH 12/23] Added tests to validate the default behavior. Also, made the tests more generic. --- Lib/test/test_binascii.py | 51 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 90604037d48bbc..8e7afb910630a7 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -116,38 +116,41 @@ def addnoise(line): def test_base64_strict_mode(self): # Test base64 with strict mode on - def assertExcessData(data): - with self.assertRaisesRegex(binascii.Error, r'(?i)Excess data'): - binascii.a2b_base64(self.type2test(data), strict_mode=True) + def _assertRegexTemplate(assert_regex: str, data: bytes, non_strict_mode_expected_result: bytes): + with self.assertRaisesRegex(binascii.Error, assert_regex): + binascii.a2b_base64(self.type2test(data), strict_mode=True) + assert binascii.a2b_base64(self.type2test(data), strict_mode=False) == non_strict_mode_expected_result + assert binascii.a2b_base64(self.type2test(data)) == non_strict_mode_expected_result - def assertNonBase64Data(data): - with self.assertRaisesRegex(binascii.Error, r'(?i)Only base64 data'): - binascii.a2b_base64(self.type2test(data), strict_mode=True) + def assertExcessData(data, non_strict_mode_expected_result: bytes): + _assertRegexTemplate(r'(?i)Excess data', data, non_strict_mode_expected_result) - def assertMalformedPadding(data): - with self.assertRaisesRegex(binascii.Error, r'(?i)Malformed padding'): - binascii.a2b_base64(self.type2test(data), strict_mode=True) + def assertNonBase64Data(data, non_strict_mode_expected_result: bytes): + _assertRegexTemplate(r'(?i)Only base64 data', data, non_strict_mode_expected_result) + + def assertMalformedPadding(data, non_strict_mode_expected_result: bytes): + _assertRegexTemplate(r'(?i)Malformed padding', data, non_strict_mode_expected_result) # Test excess data exceptions - assertExcessData(b'ab==a') - assertExcessData(b'ab===') - assertExcessData(b'ab==:') - assertExcessData(b'abc=a') - assertExcessData(b'abc=:') - assertExcessData(b'ab==\n') + assertExcessData(b'ab==a', b'i') + assertExcessData(b'ab===', b'i') + assertExcessData(b'ab==:', b'i') + assertExcessData(b'abc=a', b'i\xb7') + assertExcessData(b'abc=:', b'i\xb7') + assertExcessData(b'ab==\n', b'i') # Test non-base64 data exceptions - assertNonBase64Data(b'\nab==') - assertNonBase64Data(b'ab:(){:|:&};:==') - assertNonBase64Data(b'a\nb==') - assertNonBase64Data(b'a\x00b==') + assertNonBase64Data(b'\nab==', b'i') + assertNonBase64Data(b'ab:(){:|:&};:==', b'i') + assertNonBase64Data(b'a\nb==', b'i') + assertNonBase64Data(b'a\x00b==', b'i') # Test malformed padding - assertMalformedPadding(b'=') - assertMalformedPadding(b'==') - assertMalformedPadding(b'===') - assertMalformedPadding(b'ab=c=') - assertMalformedPadding(b'ab=ab==') + assertMalformedPadding(b'=', b'') + assertMalformedPadding(b'==', b'') + assertMalformedPadding(b'===', b'') + assertMalformedPadding(b'ab=c=', b'i\xb7') + assertMalformedPadding(b'ab=ab==', b'i\xb6\x9b') def test_base64errors(self): From 644dbaf5871237da50aa08444e60a6a9b119dce9 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 16:12:58 +0300 Subject: [PATCH 13/23] Described the changed of this pull request --- .../next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst index 0b02f6ceeac898..5eb7f2b1f407bc 100644 --- a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst +++ b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst @@ -1 +1,3 @@ -Added handling for excess data in binascii.a2b_base64 \ No newline at end of file +Added a new optional `strict_mode` parameter to binascii.a2b_base64. +When `scrict_mode` is set to `True`, the a2b_base64 function will accept only valid base64 content. +More details about what "valid base64 content" is - can be found in the function's documentation. \ No newline at end of file From 464484cd1e51d7db33a8235dbd3e5fe81fbad0fb Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 16:18:19 +0300 Subject: [PATCH 14/23] Modified syntax of RST --- .../next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst index 5eb7f2b1f407bc..19ef510e6a7874 100644 --- a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst +++ b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst @@ -1,3 +1,3 @@ -Added a new optional `strict_mode` parameter to binascii.a2b_base64. -When `scrict_mode` is set to `True`, the a2b_base64 function will accept only valid base64 content. -More details about what "valid base64 content" is - can be found in the function's documentation. \ No newline at end of file +| Added a new optional :code:`strict_mode` parameter to `binascii.a2b_base64`. +| When :code:`scrict_mode` is set to :code:`True`, the `a2b_base64` function will accept only valid base64 content. +| More details about what "valid base64 content" is, can be found in the function's documentation. \ No newline at end of file From e1ccf8aed107746bf52c21f3cf632760dd183ce2 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Sat, 10 Jul 2021 21:52:08 +0300 Subject: [PATCH 15/23] Updated the docs to explain the strict_mode parameter --- Doc/library/binascii.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst index 2c0c1bce5d7f8f..17337fa7f3d7bf 100644 --- a/Doc/library/binascii.rst +++ b/Doc/library/binascii.rst @@ -50,11 +50,23 @@ The :mod:`binascii` module defines the following functions: Added the *backtick* parameter. -.. function:: a2b_base64(string) +.. function:: a2b_base64(string, strict_mode=False) Convert a block of base64 data back to binary and return the binary data. More than one line may be passed at a time. + If `strict_mode` is true, only valid base64 data will be converted. Invalid base64 + data will raise :exc:`binascii.Error`. + + Valid base64: + * Conforms to :rfc:`3548`. + * Contains only characters from the base64 alphabet. + * Contains no excess data after padding (including excess padding, newlines, etc.). + * Does not start with a padding. + + .. versionchanged:: 3.11 + Added the *strict_mode* parameter. + .. function:: b2a_base64(data, *, newline=True) From d6a5cbfbc688711f6f53596e1722865455eb7957 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Fri, 16 Jul 2021 19:10:53 +0300 Subject: [PATCH 16/23] Moved declaration of state to the beginning to prevent multiple declaraions under the same scope. This should fix the build errors. --- Modules/binascii.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index a11dcd16196fdc..f01bb2b2f0aadb 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -451,6 +451,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) const unsigned char *ascii_data = data->buf; size_t ascii_len = data->len; + binascii_state *state = NULL; char padding_started = 0; /* Allocate the buffer */ @@ -463,7 +464,12 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) unsigned char *bin_data_start = bin_data; if (strict_mode && ascii_len > 0 && ascii_data[0] == '=') { - goto malformed_padding; + malformed_padding: + state = PyModule_GetState(module); + if (state) { + PyErr_SetString(state->Error, "Malformed padding in strict mode"); + } + goto error_end; } int quad_pos = 0; @@ -484,7 +490,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) ** in strict mode, an error should raise if there's excess data after the padding. */ if (strict_mode && i + 1 < ascii_len) { - binascii_state *state = PyModule_GetState(module); + state = PyModule_GetState(module); if (state) { PyErr_SetString(state->Error, "Excess data after padding is not allowed when using strict mode"); } @@ -499,7 +505,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) this_ch = table_a2b_base64[this_ch]; if (this_ch >= 64) { if (strict_mode) { - binascii_state *state = PyModule_GetState(module); + state = PyModule_GetState(module); if (state) { PyErr_SetString(state->Error, "Only base64 data is allowed when using strict mode"); } @@ -510,12 +516,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) // Characters that are not '=', in the middle of the padding, are not allowed if (strict_mode && padding_started) { - malformed_padding: - binascii_state *state = PyModule_GetState(module); - if (state) { - PyErr_SetString(state->Error, "Malformed padding in strict mode"); - } - goto error_end; + goto malformed_padding; } pads = 0; @@ -543,7 +544,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) } if (quad_pos != 0) { - binascii_state *state = PyModule_GetState(module); + state = PyModule_GetState(module); if (state == NULL) { /* error already set, from PyModule_GetState */ } else if (quad_pos == 1) { From 08aa26c3435cbc9f599eab7e5d6a87b717294dbd Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Fri, 16 Jul 2021 20:00:39 +0300 Subject: [PATCH 17/23] Corrected the RST syntax for argument in docs (italic) --- Doc/library/binascii.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/binascii.rst b/Doc/library/binascii.rst index 17337fa7f3d7bf..fd5df69e852dc1 100644 --- a/Doc/library/binascii.rst +++ b/Doc/library/binascii.rst @@ -55,7 +55,7 @@ The :mod:`binascii` module defines the following functions: Convert a block of base64 data back to binary and return the binary data. More than one line may be passed at a time. - If `strict_mode` is true, only valid base64 data will be converted. Invalid base64 + If *strict_mode* is true, only valid base64 data will be converted. Invalid base64 data will raise :exc:`binascii.Error`. Valid base64: From 2f1990e1e3bc4fcc024ae1fd982221da813b42e9 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Fri, 16 Jul 2021 20:47:22 +0300 Subject: [PATCH 18/23] Corrected the RST syntax for argument in news (italic) --- .../next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst index 19ef510e6a7874..f96139960df909 100644 --- a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst +++ b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst @@ -1,3 +1,3 @@ -| Added a new optional :code:`strict_mode` parameter to `binascii.a2b_base64`. -| When :code:`scrict_mode` is set to :code:`True`, the `a2b_base64` function will accept only valid base64 content. +| Added a new optional :code:`strict_mode` parameter to *binascii.a2b_base64*. +| When :code:`scrict_mode` is set to :code:`True`, the *a2b_base64* function will accept only valid base64 content. | More details about what "valid base64 content" is, can be found in the function's documentation. \ No newline at end of file From fa959bd54f692807a6268f20403b4f56119bf302 Mon Sep 17 00:00:00 2001 From: Idan Moral Date: Fri, 16 Jul 2021 21:15:35 +0300 Subject: [PATCH 19/23] Removed whitespace that lead to build failure --- Lib/test/test_binascii.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 8e7afb910630a7..27960ac6024d79 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -118,7 +118,7 @@ def test_base64_strict_mode(self): # Test base64 with strict mode on def _assertRegexTemplate(assert_regex: str, data: bytes, non_strict_mode_expected_result: bytes): with self.assertRaisesRegex(binascii.Error, assert_regex): - binascii.a2b_base64(self.type2test(data), strict_mode=True) + binascii.a2b_base64(self.type2test(data), strict_mode=True) assert binascii.a2b_base64(self.type2test(data), strict_mode=False) == non_strict_mode_expected_result assert binascii.a2b_base64(self.type2test(data)) == non_strict_mode_expected_result From a60a8c6ec438763b8403d80fb7130276b649e769 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 18 Jul 2021 16:16:02 -0700 Subject: [PATCH 20/23] use self.assertEqual instead of assert == --- Lib/test/test_binascii.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index 27960ac6024d79..e5d301a71be525 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -119,8 +119,10 @@ def test_base64_strict_mode(self): def _assertRegexTemplate(assert_regex: str, data: bytes, non_strict_mode_expected_result: bytes): with self.assertRaisesRegex(binascii.Error, assert_regex): binascii.a2b_base64(self.type2test(data), strict_mode=True) - assert binascii.a2b_base64(self.type2test(data), strict_mode=False) == non_strict_mode_expected_result - assert binascii.a2b_base64(self.type2test(data)) == non_strict_mode_expected_result + self.assertEqual(binascii.a2b_base64(self.type2test(data), strict_mode=False), + non_strict_mode_expected_result) + self.assertEqual(binascii.a2b_base64(self.type2test(data)), + non_strict_mode_expected_result) def assertExcessData(data, non_strict_mode_expected_result: bytes): _assertRegexTemplate(r'(?i)Excess data', data, non_strict_mode_expected_result) From 03072726fe040ec0057d5d3ac0b995cc58918e35 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 18 Jul 2021 16:35:33 -0700 Subject: [PATCH 21/23] remove leadg `| ` characters in NEWS --- .../next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst index f96139960df909..f49e7a84cc5375 100644 --- a/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst +++ b/Misc/NEWS.d/next/Library/2021-01-31-18-24-54.bpo-43086.2_P-SH.rst @@ -1,3 +1,3 @@ -| Added a new optional :code:`strict_mode` parameter to *binascii.a2b_base64*. -| When :code:`scrict_mode` is set to :code:`True`, the *a2b_base64* function will accept only valid base64 content. -| More details about what "valid base64 content" is, can be found in the function's documentation. \ No newline at end of file +Added a new optional :code:`strict_mode` parameter to *binascii.a2b_base64*. +When :code:`scrict_mode` is set to :code:`True`, the *a2b_base64* function will accept only valid base64 content. +More details about what "valid base64 content" is, can be found in the function's documentation. From d26e1ebc086f50f8bc9f2d1ae85600d8279ca9b1 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 18 Jul 2021 16:40:46 -0700 Subject: [PATCH 22/23] Simplify the error messages. No need to mention "strict mode" within the error message. --- Modules/binascii.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/binascii.c b/Modules/binascii.c index edeaa66f507ae3..50f25b406a924e 100644 --- a/Modules/binascii.c +++ b/Modules/binascii.c @@ -467,7 +467,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) malformed_padding: state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Malformed padding in strict mode"); + PyErr_SetString(state->Error, "Leading padding not allowed"); } goto error_end; } @@ -492,7 +492,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) if (strict_mode && i + 1 < ascii_len) { state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Excess data after padding is not allowed when using strict mode"); + PyErr_SetString(state->Error, "Excess data after padding"); } goto error_end; } @@ -507,7 +507,7 @@ binascii_a2b_base64_impl(PyObject *module, Py_buffer *data, int strict_mode) if (strict_mode) { state = get_binascii_state(module); if (state) { - PyErr_SetString(state->Error, "Only base64 data is allowed when using strict mode"); + PyErr_SetString(state->Error, "Only base64 data is allowed"); } goto error_end; } From 652e7f4e6395947c0e558647a7c46c927f544a7f Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Sun, 18 Jul 2021 16:57:22 -0700 Subject: [PATCH 23/23] update test for error message (Leading vs Malformed) --- Lib/test/test_binascii.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_binascii.py b/Lib/test/test_binascii.py index e5d301a71be525..74438d837852c8 100644 --- a/Lib/test/test_binascii.py +++ b/Lib/test/test_binascii.py @@ -131,7 +131,7 @@ def assertNonBase64Data(data, non_strict_mode_expected_result: bytes): _assertRegexTemplate(r'(?i)Only base64 data', data, non_strict_mode_expected_result) def assertMalformedPadding(data, non_strict_mode_expected_result: bytes): - _assertRegexTemplate(r'(?i)Malformed padding', data, non_strict_mode_expected_result) + _assertRegexTemplate(r'(?i)Leading padding', data, non_strict_mode_expected_result) # Test excess data exceptions assertExcessData(b'ab==a', b'i')