mirror of
https://github.com/python/cpython.git
synced 2025-12-05 00:52:25 +00:00
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 <storchaka@gmail.com>
This commit is contained in:
parent
8660fb7fd7
commit
4ba15de191
3 changed files with 42 additions and 9 deletions
|
|
@ -2269,7 +2269,10 @@ class PtyTests(unittest.TestCase):
|
||||||
|
|
||||||
return lines
|
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():
|
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
||||||
self.skipTest("stdin and stdout must be ttys")
|
self.skipTest("stdin and stdout must be ttys")
|
||||||
def child(wpipe):
|
def child(wpipe):
|
||||||
|
|
@ -2277,18 +2280,22 @@ class PtyTests(unittest.TestCase):
|
||||||
if stdio_encoding:
|
if stdio_encoding:
|
||||||
sys.stdin = io.TextIOWrapper(sys.stdin.detach(),
|
sys.stdin = io.TextIOWrapper(sys.stdin.detach(),
|
||||||
encoding=stdio_encoding,
|
encoding=stdio_encoding,
|
||||||
errors='surrogateescape')
|
errors=stdin_errors)
|
||||||
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
|
sys.stdout = io.TextIOWrapper(sys.stdout.detach(),
|
||||||
encoding=stdio_encoding,
|
encoding=stdio_encoding,
|
||||||
errors='replace')
|
errors=stdout_errors)
|
||||||
print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe)
|
print("tty =", sys.stdin.isatty() and sys.stdout.isatty(), file=wpipe)
|
||||||
|
try:
|
||||||
print(ascii(input(prompt)), file=wpipe)
|
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")
|
lines = self.run_child(child, terminal_input + b"\r\n")
|
||||||
# Check we did exercise the GNU readline path
|
# Check we did exercise the GNU readline path
|
||||||
self.assertIn(lines[0], {'tty = True', 'tty = False'})
|
self.assertIn(lines[0], {'tty = True', 'tty = False'})
|
||||||
if lines[0] != 'tty = True':
|
if lines[0] != 'tty = True':
|
||||||
self.skipTest("standard IO in should have been a tty")
|
self.skipTest("standard IO in should have been a tty")
|
||||||
input_result = eval(lines[1]) # ascii() -> eval() roundtrip
|
input_result = eval(lines[1]) # ascii() -> eval() roundtrip
|
||||||
|
if expected is None:
|
||||||
if stdio_encoding:
|
if stdio_encoding:
|
||||||
expected = terminal_input.decode(stdio_encoding, 'surrogateescape')
|
expected = terminal_input.decode(stdio_encoding, 'surrogateescape')
|
||||||
else:
|
else:
|
||||||
|
|
@ -2313,13 +2320,32 @@ class PtyTests(unittest.TestCase):
|
||||||
def test_input_tty_non_ascii(self):
|
def test_input_tty_non_ascii(self):
|
||||||
self.skip_if_readline()
|
self.skip_if_readline()
|
||||||
# Check stdin/stdout encoding is used when invoking PyOS_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):
|
def test_input_tty_non_ascii_unicode_errors(self):
|
||||||
self.skip_if_readline()
|
self.skip_if_readline()
|
||||||
# Check stdin/stdout error handler is used when invoking PyOS_Readline()
|
# Check stdin/stdout error handler is used when invoking PyOS_Readline()
|
||||||
self.check_input_tty("prompté", b"quux\xe9", "ascii")
|
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):
|
def test_input_no_stdout_fileno(self):
|
||||||
# Issue #24402: If stdin is the original terminal but stdout.fileno()
|
# Issue #24402: If stdin is the original terminal but stdout.fileno()
|
||||||
# fails, do not use the original stdout file descriptor
|
# fails, do not use the original stdout file descriptor
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -2262,6 +2262,11 @@ builtin_input_impl(PyObject *module, PyObject *prompt)
|
||||||
goto _readline_errors;
|
goto _readline_errors;
|
||||||
assert(PyBytes_Check(po));
|
assert(PyBytes_Check(po));
|
||||||
promptstr = PyBytes_AS_STRING(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 {
|
else {
|
||||||
po = NULL;
|
po = NULL;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue