]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-74616: Raise ValueError in case of null character in input prompt (GH-1738)
authorKushal Das <mail@kushaldas.in>
Thu, 7 Dec 2023 10:22:52 +0000 (11:22 +0100)
committerGitHub <noreply@github.com>
Thu, 7 Dec 2023 10:22:52 +0000 (10:22 +0000)
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 <storchaka@gmail.com>
Lib/test/test_builtin.py
Misc/NEWS.d/next/Core and Builtins/2023-12-07-12-00-04.gh-issue-74616.kgTGVb.rst [new file with mode: 0644]
Python/bltinmodule.c

index 535856adaea4d353cebe45758ecea67fb064b3d4..558715383c82ee1665a740e490aa7195594ecfaf 100644 (file)
@@ -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 (file)
index 0000000..5c345be
--- /dev/null
@@ -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.
index 7a9625134761f9d38433d3b73a2f9a48988632ba..960bca01990c8374de0a70fde806cb820f377585 100644 (file)
@@ -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;