From 4ba15de19153bb97308996ec85242bdeda358387 Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Thu, 7 Dec 2023 11:22:52 +0100 Subject: [PATCH] gh-74616: Raise ValueError in case of null character in input prompt (GH-1738) If the input prompt to the builtin input function on terminal has any null character, then raise ValueError instead of silently truncating it. Co-authored-by: Serhiy Storchaka --- Lib/test/test_builtin.py | 44 +++++++++++++++---- ...3-12-07-12-00-04.gh-issue-74616.kgTGVb.rst | 2 + Python/bltinmodule.c | 5 +++ 3 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 535856adaea..558715383c8 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -2269,7 +2269,10 @@ class PtyTests(unittest.TestCase): return lines - def check_input_tty(self, prompt, terminal_input, stdio_encoding=None): + def check_input_tty(self, prompt, terminal_input, stdio_encoding=None, *, + expected=None, + stdin_errors='surrogateescape', + stdout_errors='replace'): if not sys.stdin.isatty() or not sys.stdout.isatty(): self.skipTest("stdin and stdout must be ttys") def child(wpipe): @@ -2277,22 +2280,26 @@ class PtyTests(unittest.TestCase): if stdio_encoding: sys.stdin = io.TextIOWrapper(sys.stdin.detach(), encoding=stdio_encoding, - errors='surrogateescape') + errors=stdin_errors) sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding=stdio_encoding, - errors='replace') + errors=stdout_errors) print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe) - print(ascii(input(prompt)), file=wpipe) + try: + print(ascii(input(prompt)), file=wpipe) + except BaseException as e: + print(ascii(f'{e.__class__.__name__}: {e!s}'), file=wpipe) lines = self.run_child(child, terminal_input + b"\r\n") # Check we did exercise the GNU readline path self.assertIn(lines[0], {'tty = True', 'tty = False'}) if lines[0] != 'tty = True': self.skipTest("standard IO in should have been a tty") input_result = eval(lines[1]) # ascii() -> eval() roundtrip - if stdio_encoding: - expected = terminal_input.decode(stdio_encoding, 'surrogateescape') - else: - expected = terminal_input.decode(sys.stdin.encoding) # what else? + if expected is None: + if stdio_encoding: + expected = terminal_input.decode(stdio_encoding, 'surrogateescape') + else: + expected = terminal_input.decode(sys.stdin.encoding) # what else? self.assertEqual(input_result, expected) def test_input_tty(self): @@ -2313,13 +2320,32 @@ class PtyTests(unittest.TestCase): def test_input_tty_non_ascii(self): self.skip_if_readline() # Check stdin/stdout encoding is used when invoking PyOS_Readline() - self.check_input_tty("prompté", b"quux\xe9", "utf-8") + self.check_input_tty("prompté", b"quux\xc3\xa9", "utf-8") def test_input_tty_non_ascii_unicode_errors(self): self.skip_if_readline() # Check stdin/stdout error handler is used when invoking PyOS_Readline() self.check_input_tty("prompté", b"quux\xe9", "ascii") + def test_input_tty_null_in_prompt(self): + self.check_input_tty("prompt\0", b"", + expected='ValueError: input: prompt string cannot contain ' + 'null characters') + + def test_input_tty_nonencodable_prompt(self): + self.skip_if_readline() + self.check_input_tty("prompté", b"quux", "ascii", stdout_errors='strict', + expected="UnicodeEncodeError: 'ascii' codec can't encode " + "character '\\xe9' in position 6: ordinal not in " + "range(128)") + + def test_input_tty_nondecodable_input(self): + self.skip_if_readline() + self.check_input_tty("prompt", b"quux\xe9", "ascii", stdin_errors='strict', + expected="UnicodeDecodeError: 'ascii' codec can't decode " + "byte 0xe9 in position 4: ordinal not in " + "range(128)") + def test_input_no_stdout_fileno(self): # Issue #24402: If stdin is the original terminal but stdout.fileno() # fails, do not use the original stdout file descriptor diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst new file mode 100644 index 00000000000..5c345be9de6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst @@ -0,0 +1,2 @@ +:func:`input` now raises a ValueError when output on the terminal if the +prompt contains embedded null characters instead of silently truncating it. diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c index 7a962513476..960bca01990 100644 --- a/Python/bltinmodule.c +++ b/Python/bltinmodule.c @@ -2262,6 +2262,11 @@ builtin_input_impl(PyObject *module, PyObject *prompt) goto _readline_errors; assert(PyBytes_Check(po)); promptstr = PyBytes_AS_STRING(po); + if ((Py_ssize_t)strlen(promptstr) != PyBytes_GET_SIZE(po)) { + PyErr_SetString(PyExc_ValueError, + "input: prompt string cannot contain null characters"); + goto _readline_errors; + } } else { po = NULL;