fix curses.newwin() crash on narrow terminals in TUI dialogs

show_confirm() sized its window purely from message content, with no
upper bound — a long line (the Cryptgeon password URL in the
post-issuance "send email?" prompt) could easily exceed a narrow
terminal's width (e.g. a serial console), making curses.newwin()
raise "curses function returned NULL" and crash the whole app.
show_cert_form() had the same unguarded newwin() call, just with a
fixed 13x62 size instead of content-driven.

Clamp both windows' height/width to the actual screen size, and wrap
newwin() itself in try/except as a last-resort fallback (declined
confirm / cancelled form) in case clamping still isn't enough.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Vlad Doloman
2026-07-08 16:33:38 +03:00
parent 702dc2fe14
commit f7ce56a6d4
2 changed files with 67 additions and 5 deletions

View File

@@ -651,9 +651,15 @@ def show_confirm(stdscr, message: str) -> bool:
"""Y/N modal dialog. Returns True if user presses y/Y."""
sh, sw = stdscr.getmaxyx()
lines = message.splitlines()
h = len(lines) + 6
w = max(max(len(l) for l in lines) + 6, 38)
win = curses.newwin(h, w, (sh - h) // 2, (sw - w) // 2)
# Clamp to the screen: an unbounded width (e.g. a long Cryptgeon URL line)
# makes curses.newwin() raise "curses function returned NULL" outright on
# a narrow terminal (e.g. a serial console) instead of just wrapping badly.
h = min(len(lines) + 6, sh)
w = min(max(max(len(l) for l in lines) + 6, 38), sw)
try:
win = curses.newwin(h, w, max(0, (sh - h) // 2), max(0, (sw - w) // 2))
except curses.error:
return False
win.keypad(True)
draw_box(win, "Confirm")
for i, line in enumerate(lines):
@@ -704,8 +710,14 @@ def show_cert_form(
Tab / Shift-Tab / Up / Down cycle fields and buttons. F5 regenerates password.
Enter or Space activates the focused button. Ctrl-G confirms immediately. Escape cancels."""
sh, sw = stdscr.getmaxyx()
h, w = 13, 62
win = curses.newwin(h, w, (sh - h) // 2, (sw - w) // 2)
# Clamp to the screen so curses.newwin() can't raise "curses function
# returned NULL" outright on a terminal narrower/shorter than the usual
# 80x24 (e.g. a serial console with an unusually small viewport).
h, w = min(13, sh), min(62, sw)
try:
win = curses.newwin(h, w, max(0, (sh - h) // 2), max(0, (sw - w) // 2))
except curses.error:
return CertFormResult(cn=cn, email="", password="", confirmed=False)
win.keypad(True)
fw = w - 6 # field width