v0.8.25: fix terminal corruption from larry tools (control-byte + tty leaks)
nc-document.sh now sanitizes C0 control bytes (except tab/LF/CR) at the single output sink, so raw ESC sequences embedded in NetConfig/.tcl content can no longer flip the terminal's mode and break line editing when output isn't redirected. ssh-helper.sh password prompts save/restore termios via stty -g + trap so a ^C mid-prompt no longer leaves echo off. UTF-8 preserved; portable. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
88fc104c54
commit
9289352454
35
CHANGELOG.md
35
CHANGELOG.md
@ -4,6 +4,41 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
|
|||||||
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
||||||
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
||||||
|
|
||||||
|
## v0.8.25 — 2026-05-28
|
||||||
|
|
||||||
|
**★ FIX: terminal line-editing corruption after `larry tools …` runs (Bryan,
|
||||||
|
reproduced on his live Cloverleaf box).** After running `larry tools nc-document`
|
||||||
|
(and `tbn`-class searches), his interactive shell's line editing broke —
|
||||||
|
backspace printed a literal `^H` instead of erasing, arrow keys died, and he had
|
||||||
|
to run `stty sane`/`reset` to recover. Two independent causes, both fixed
|
||||||
|
belt-and-suspenders:
|
||||||
|
|
||||||
|
- **Cause 1 — CONTROL-BYTE OUTPUT LEAK (the one Bryan hit).** `lib/nc-document.sh`
|
||||||
|
reads arbitrary NetConfig and `.tcl` proc source — author comments, the
|
||||||
|
`--raw-tcl` verbatim appendix (`cat "$apath"`), and xlate bodies — and emits it
|
||||||
|
to stdout unsanitised. Any raw C0 control byte in that content, **especially ESC
|
||||||
|
(0x1B)** and DECSET mode-switch sequences (`ESC[?1049h`, `ESC[?25l`, etc.), flips
|
||||||
|
the terminal into a different mode when the doc is printed (i.e. NOT redirected),
|
||||||
|
wrecking line editing. **Fix:** a single sanitiser at the one output choke-point
|
||||||
|
(`out_target` → new `_sanitize_ctl`) strips C0 controls except TAB/LF/CR via
|
||||||
|
`LC_ALL=C tr -d '\001-\010\013\014\016-\037\177'`. Applies to BOTH stdout and
|
||||||
|
`--out <file>`. High bytes (0x80–0xFF) are untouched, so UTF-8 names/comments
|
||||||
|
survive. Portable to AIX/Linux/BSD/Cygwin (POSIX `tr` octal ranges; no GNU-only
|
||||||
|
flags). The non-interactive `tools <name>` dispatch in `larry.sh` itself touches
|
||||||
|
NO termios (it `exec`s the lib tool before any trap/mouse-mode is installed),
|
||||||
|
confirming the leak was the tool's own output bytes.
|
||||||
|
|
||||||
|
- **Cause 2 — TTY-MODE LEAK on SIGINT (latent, hardened).** `lib/ssh-helper.sh`
|
||||||
|
read hidden passwords with a bare `stty -echo … read … stty echo`. A Ctrl-C (or
|
||||||
|
EOF/signal) BETWEEN the two `stty` calls left echo permanently OFF — terminal
|
||||||
|
corrupted until `stty sane`/`reset`. **Fix:** new `_read_hidden` saves the full
|
||||||
|
prior termios with `stty -g` and restores it via a `trap … INT TERM HUP` plus an
|
||||||
|
explicit restore on normal return, so every interrupt path restores the tty.
|
||||||
|
Reads from `/dev/tty`. Both hidden-password prompts (`cmd_pass`, `cmd_setup`
|
||||||
|
re-prompt) now route through it.
|
||||||
|
|
||||||
|
`bash -n` clean on all touched files. MANIFEST regenerated.
|
||||||
|
|
||||||
## v0.8.24 — 2026-05-28
|
## v0.8.24 — 2026-05-28
|
||||||
|
|
||||||
**★ PLAIN-TEXT output rewrite of `lib/nc-document.sh` (Bryan's priority — OneNote
|
**★ PLAIN-TEXT output rewrite of `lib/nc-document.sh` (Bryan's priority — OneNote
|
||||||
|
|||||||
10
MANIFEST
10
MANIFEST
@ -23,16 +23,16 @@
|
|||||||
# scripts/make-manifest.sh and bump VERSION.
|
# scripts/make-manifest.sh and bump VERSION.
|
||||||
|
|
||||||
# Top-level scripts
|
# Top-level scripts
|
||||||
larry.sh b3af140dcb8518ea98327b33177bcef8973ca223ad1f46f90ad6dba0e9f7ded7
|
larry.sh dd9fdba88d20c4472826ef9355a7a0aa3d6bcf1d9d040aff9f07a2bb1351a287
|
||||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||||
install-larry.sh fa36e23a39eacbd0d7ecedd3b42131902f816ee7e98241dfc6e28c6e4ba80423
|
install-larry.sh fa36e23a39eacbd0d7ecedd3b42131902f816ee7e98241dfc6e28c6e4ba80423
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
VERSION 05d77ce62e5abe7212c0fa5ca747f16348d012c39016080e99c10f8f7f5e20bf
|
VERSION 7a2e873644ed9f1015114f43072f374eb0e1716bb1a97e73daa1337474d3e1fd
|
||||||
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
||||||
CHANGELOG.md 5432e98a75500cf4cb7d1fa171124d64e693865215d4612f5071294a4f871f6a
|
CHANGELOG.md c659506dea3dea5b1cc53ef41f978219d6e9894b4fc4a05df07df660a56f79be
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||||
@ -52,7 +52,7 @@ lib/fetch-safe.sh abecf0045b9856f63ffa346119443c11de56547344be32bddaed9fbae6b021
|
|||||||
lib/oauth.sh 04a93376f88fe53cc1c86a5dbe577735c60375dadd4f2fda55b921ef3cddf22b
|
lib/oauth.sh 04a93376f88fe53cc1c86a5dbe577735c60375dadd4f2fda55b921ef3cddf22b
|
||||||
|
|
||||||
# Secure SSH with ControlMaster (password hidden from Larry-the-LLM)
|
# Secure SSH with ControlMaster (password hidden from Larry-the-LLM)
|
||||||
lib/ssh-helper.sh bd205aa87bc9e53821cac45888faa9434c1e182bee2bf16d6d838dcb79bfac3e
|
lib/ssh-helper.sh b8442e1e086eed7afebc77e1948c9c688301a8ef4b14f563470396aceccdddd4
|
||||||
|
|
||||||
# v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023). Incremental,
|
# v0.8.6: work-box → Mac headers.log sync (tsk-2026-05-27-023). Incremental,
|
||||||
# offset-tracked push of $LARRY_HOME/log/headers.log to a daemon-watched path
|
# offset-tracked push of $LARRY_HOME/log/headers.log to a daemon-watched path
|
||||||
@ -102,7 +102,7 @@ lib/nc-paths.sh 388d2f4560736587a01218cadc1de612cd59e392819d16db2f56f19174c1111b
|
|||||||
lib/nc-inbound.sh 52d28c5f8d97bdf96f0fc7b5300d35b106b8e1226578f4cda430deb2a8b4a91b
|
lib/nc-inbound.sh 52d28c5f8d97bdf96f0fc7b5300d35b106b8e1226578f4cda430deb2a8b4a91b
|
||||||
lib/nc-make-jump.sh 08a0bc58a299c95c60a59a5202792daf0ada3a8a0be7dc1b4cccc5724f5c9c79
|
lib/nc-make-jump.sh 08a0bc58a299c95c60a59a5202792daf0ada3a8a0be7dc1b4cccc5724f5c9c79
|
||||||
lib/nc-msgs.sh 729e2d6c9159e83fa177fc6b982e48ed8453a9743477cc90afdd3cd4ec7e620c
|
lib/nc-msgs.sh 729e2d6c9159e83fa177fc6b982e48ed8453a9743477cc90afdd3cd4ec7e620c
|
||||||
lib/nc-document.sh 56808d5ba86ca6b355f405bf33db7de62e8894a5582987e9de1eeee77a8ab3a8
|
lib/nc-document.sh 13aef8f6335ee63966fdd74678c506fb4ed7ed749f750e7daad142258e518f41
|
||||||
lib/nc-diff-interface.sh 6b64ec3070a3d75d1d79632c9aeb357177fdbcc77c474aa78e6f6929fda1a324
|
lib/nc-diff-interface.sh 6b64ec3070a3d75d1d79632c9aeb357177fdbcc77c474aa78e6f6929fda1a324
|
||||||
lib/nc-find.sh 8c79e0acad7de56e4e1f12d61e071a4b98c4e2310a1f7fb183697df521215e3f
|
lib/nc-find.sh 8c79e0acad7de56e4e1f12d61e071a4b98c4e2310a1f7fb183697df521215e3f
|
||||||
lib/nc-insert-protocol.sh ad1fa0bafbf4fdfb12bad20f9c22c3eed519f8846774331e26aa9becd6f8898a
|
lib/nc-insert-protocol.sh ad1fa0bafbf4fdfb12bad20f9c22c3eed519f8846774331e26aa9becd6f8898a
|
||||||
|
|||||||
2
larry.sh
2
larry.sh
@ -78,7 +78,7 @@ set -o pipefail
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Config
|
# Config
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
LARRY_VERSION="0.8.24"
|
LARRY_VERSION="0.8.25"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -249,12 +249,34 @@ _locate_thread() {
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Output sink
|
# Output sink
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# v0.8.25 — CONTROL-BYTE SANITIZER (terminal-corruption fix).
|
||||||
|
# This tool reads arbitrary NetConfig and .tcl source (author comments, raw proc
|
||||||
|
# bodies via --raw-tcl, xlate bodies). That content can contain raw C0 control
|
||||||
|
# bytes — most dangerously ESC (0x1B). When the doc is printed to a terminal
|
||||||
|
# (NOT redirected to a file), a stray ESC sequence flips the terminal into a
|
||||||
|
# different mode and wrecks line editing (backspace prints ^H, arrows die),
|
||||||
|
# forcing `stty sane`/`reset` to recover. We neutralise it at the single output
|
||||||
|
# choke-point so EVERY emitted byte is clean, whether bound for stdout or --out.
|
||||||
|
#
|
||||||
|
# Strip set (octal, POSIX `tr` ranges — portable to AIX/Linux/BSD/Cygwin):
|
||||||
|
# \001-\010 SOH..BS (drops BS ^H, the literal-backspace culprit)
|
||||||
|
# [keep \011 TAB, \012 LF]
|
||||||
|
# \013\014 VT, FF
|
||||||
|
# [keep \015 CR — legit in MobaXterm/Windows-tainted content]
|
||||||
|
# \016-\037 SO..US (drops ESC 0x1B, the mode-flip culprit)
|
||||||
|
# \177 DEL
|
||||||
|
# High bytes (0x80-0xFF) are NOT touched, so UTF-8 names/comments survive intact.
|
||||||
|
# LC_ALL=C forces byte-wise operation (AIX `tr` is locale-sensitive otherwise).
|
||||||
|
_sanitize_ctl() {
|
||||||
|
LC_ALL=C tr -d '\001-\010\013\014\016-\037\177' 2>/dev/null || cat
|
||||||
|
}
|
||||||
out_target() {
|
out_target() {
|
||||||
if [ -n "$OUT" ]; then
|
if [ -n "$OUT" ]; then
|
||||||
mkdir -p "$(dirname "$OUT")" 2>/dev/null
|
mkdir -p "$(dirname "$OUT")" 2>/dev/null
|
||||||
cat > "$OUT"
|
_sanitize_ctl > "$OUT"
|
||||||
else
|
else
|
||||||
cat
|
_sanitize_ctl
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -77,6 +77,41 @@ die() { printf 'ssh-helper: %s\n' "$*" >&2; exit 1; }
|
|||||||
warn() { printf 'ssh-helper: warn: %s\n' "$*" >&2; }
|
warn() { printf 'ssh-helper: warn: %s\n' "$*" >&2; }
|
||||||
ok() { printf 'ssh-helper: %s\n' "$*"; }
|
ok() { printf 'ssh-helper: %s\n' "$*"; }
|
||||||
|
|
||||||
|
# v0.8.25 — SAFE HIDDEN-PASSWORD READ (terminal-corruption fix).
|
||||||
|
# Previously the hidden-password prompts did a bare `stty -echo` ... read ...
|
||||||
|
# `stty echo`. If the user hit Ctrl-C (or the read got an EOF/signal) BETWEEN
|
||||||
|
# those two stty calls, echo was never re-enabled and the terminal was left
|
||||||
|
# corrupted (typing invisible) — recoverable only with `stty sane`/`reset`.
|
||||||
|
# This wrapper SAVES the full prior termios state with `stty -g` and restores
|
||||||
|
# it via a trap on EXIT/INT/TERM/HUP, so any interrupt path restores the tty.
|
||||||
|
# Reads from /dev/tty (the real terminal), not stdin. Portable: `stty -g`/`stty <state>`
|
||||||
|
# is POSIX and works on AIX/Linux/BSD/Cygwin; no GNU-only flags.
|
||||||
|
# _read_hidden VARNAME
|
||||||
|
# Sets the named variable to the line read (no echo). Returns 0 always (empty
|
||||||
|
# input is the caller's "abort" signal).
|
||||||
|
_read_hidden() { # varname
|
||||||
|
local _rh_var="$1" _rh_val="" _rh_saved=""
|
||||||
|
if command -v stty >/dev/null 2>&1 && { [ -t 0 ] || [ -e /dev/tty ]; }; then
|
||||||
|
_rh_saved=$(stty -g </dev/tty 2>/dev/null || stty -g 2>/dev/null || true)
|
||||||
|
# Restore on ANY exit from the prompt window — normal return OR ^C/kill/HUP.
|
||||||
|
if [ -n "$_rh_saved" ]; then
|
||||||
|
trap 'stty "$_rh_saved" </dev/tty 2>/dev/null || stty "$_rh_saved" 2>/dev/null; trap - INT TERM HUP' INT TERM HUP
|
||||||
|
fi
|
||||||
|
stty -echo </dev/tty 2>/dev/null || stty -echo 2>/dev/null || true
|
||||||
|
IFS= read -r _rh_val </dev/tty 2>/dev/null || IFS= read -r _rh_val || true
|
||||||
|
if [ -n "$_rh_saved" ]; then
|
||||||
|
stty "$_rh_saved" </dev/tty 2>/dev/null || stty "$_rh_saved" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
stty echo </dev/tty 2>/dev/null || stty echo 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
trap - INT TERM HUP
|
||||||
|
else
|
||||||
|
IFS= read -r _rh_val </dev/tty 2>/dev/null || IFS= read -r _rh_val || true
|
||||||
|
fi
|
||||||
|
# Assign back to the caller's variable name without eval-injection risk.
|
||||||
|
printf -v "$_rh_var" '%s' "$_rh_val"
|
||||||
|
}
|
||||||
|
|
||||||
# v0.7.5: shared CR-safety primitives. pull/push use `wc -c | tr -d ' '` to
|
# v0.7.5: shared CR-safety primitives. pull/push use `wc -c | tr -d ' '` to
|
||||||
# verify byte counts — Cygwin wc.exe can pass through \r and tank the
|
# verify byte counts — Cygwin wc.exe can pass through \r and tank the
|
||||||
# `[ "$got" != "$local_size" ]` comparison.
|
# `[ "$got" != "$local_size" ]` comparison.
|
||||||
@ -296,9 +331,7 @@ cmd_pass() {
|
|||||||
ensure_layout
|
ensure_layout
|
||||||
printf 'Password for %s (input is hidden; press Enter when done): ' "$alias" >&2
|
printf 'Password for %s (input is hidden; press Enter when done): ' "$alias" >&2
|
||||||
local pw=""
|
local pw=""
|
||||||
stty -echo 2>/dev/null
|
_read_hidden pw
|
||||||
IFS= read -r pw </dev/tty || true
|
|
||||||
stty echo 2>/dev/null
|
|
||||||
echo "" >&2
|
echo "" >&2
|
||||||
[ -n "$pw" ] || die "no password entered"
|
[ -n "$pw" ] || die "no password entered"
|
||||||
umask 077
|
umask 077
|
||||||
@ -431,9 +464,7 @@ cmd_setup() {
|
|||||||
printf 'ssh-helper: looks like the stored password is stale (this host rotates ~every 12h).\n' >&2
|
printf 'ssh-helper: looks like the stored password is stale (this host rotates ~every 12h).\n' >&2
|
||||||
printf 'Enter a FRESH password for %s (input hidden; Enter to abort): ' "$alias" >&2
|
printf 'Enter a FRESH password for %s (input hidden; Enter to abort): ' "$alias" >&2
|
||||||
local pw=""
|
local pw=""
|
||||||
stty -echo 2>/dev/null
|
_read_hidden pw
|
||||||
IFS= read -r pw </dev/tty || true
|
|
||||||
stty echo 2>/dev/null
|
|
||||||
echo "" >&2
|
echo "" >&2
|
||||||
if [ -n "$pw" ]; then
|
if [ -n "$pw" ]; then
|
||||||
umask 077
|
umask 077
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user