release: restore ZUPT and harden source-only 5.2.2
This commit is contained in:
parent
74e393ba3e
commit
ff99770bd0
205 changed files with 19627 additions and 13215 deletions
85
tests/test_password_prompt_signal.sh
Executable file
85
tests/test_password_prompt_signal.sh
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2026 Cristian Cezar Moisés
|
||||
# A signal received while a password is read must not leave terminal echo off.
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
bin=${1:-./zupt}
|
||||
if [[ ! -x $bin ]]; then
|
||||
printf 'FAIL: ZUPT binary is not executable: %s\n' "$bin" >&2
|
||||
exit 1
|
||||
fi
|
||||
case $(uname -s) in
|
||||
MINGW*|MSYS*|CYGWIN*)
|
||||
printf '%s\n' \
|
||||
'SKIP: POSIX pseudo-terminal signal restoration test is unavailable on Windows'
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
if ! command -v python3 >/dev/null 2>&1; then
|
||||
printf 'SKIP: password-prompt signal test needs python3 with pty support\n'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
bin=$(cd "$(dirname "$bin")" && pwd -P)/$(basename "$bin")
|
||||
python3 - "$bin" <<'PY'
|
||||
import os
|
||||
import pty
|
||||
import select
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import termios
|
||||
import time
|
||||
import sys
|
||||
|
||||
binary = sys.argv[1]
|
||||
with tempfile.TemporaryDirectory(prefix="zupt-password-signal-") as work:
|
||||
source = os.path.join(work, "input.txt")
|
||||
archive = os.path.join(work, "interrupted.zupt")
|
||||
with open(source, "w", encoding="utf-8") as stream:
|
||||
stream.write("terminal restoration regression\n")
|
||||
|
||||
master, slave = pty.openpty()
|
||||
initial = termios.tcgetattr(slave)
|
||||
process = subprocess.Popen(
|
||||
[binary, "compress", "--password-prompt", archive, source],
|
||||
stdin=slave,
|
||||
stdout=slave,
|
||||
stderr=slave,
|
||||
close_fds=True,
|
||||
)
|
||||
transcript = bytearray()
|
||||
deadline = time.monotonic() + 10
|
||||
try:
|
||||
while b"Password:" not in transcript and time.monotonic() < deadline:
|
||||
readable, _, _ = select.select([master], [], [], 0.1)
|
||||
if readable:
|
||||
transcript.extend(os.read(master, 4096))
|
||||
if process.poll() is not None:
|
||||
break
|
||||
if b"Password:" not in transcript:
|
||||
raise SystemExit("password prompt was not reached")
|
||||
hidden = termios.tcgetattr(slave)
|
||||
if hidden[3] & termios.ECHO:
|
||||
raise SystemExit("terminal echo was not disabled during prompt")
|
||||
|
||||
process.send_signal(signal.SIGINT)
|
||||
process.wait(timeout=10)
|
||||
restored = termios.tcgetattr(slave)
|
||||
if (restored[3] & termios.ECHO) != (initial[3] & termios.ECHO):
|
||||
raise SystemExit("terminal echo state was not restored after SIGINT")
|
||||
if not (restored[3] & termios.ECHO):
|
||||
raise SystemExit("terminal echo is disabled after interrupted prompt")
|
||||
if os.path.exists(archive):
|
||||
raise SystemExit("interrupted password prompt left an archive")
|
||||
finally:
|
||||
if process.poll() is None:
|
||||
process.kill()
|
||||
process.wait()
|
||||
os.close(master)
|
||||
os.close(slave)
|
||||
|
||||
print("password prompt signal restoration: PASS")
|
||||
PY
|
||||
Loading…
Reference in a new issue