v0.8.26: harden control-byte sanitize across the tool suite + ssh-helper traps
Shared _sanitize_ctl (unconditional, nc-document) and _sanitize_ctl_tty (strips only when stdout is a terminal) now live in cygwin-safe.sh. nc-msgs, nc-parse, and the hl7-* tools route stdout through the tty-gated variant, so a terminal is protected from raw HL7/NetConfig control bytes while pipes and redirects stay byte-exact (the 0x1c framing route_test needs is preserved). Exit codes propagate via PIPESTATUS. ssh-helper _read_hidden installs its restore trap before stty -echo on every path and saves/restores the prior trap. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
9289352454
commit
111be2c744
42
CHANGELOG.md
42
CHANGELOG.md
@ -4,6 +4,48 @@ 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.26 — 2026-05-28
|
||||||
|
|
||||||
|
**★ HARDENING: extend the v0.8.25 control-byte sanitize across the whole tool
|
||||||
|
suite (Vera follow-up).** v0.8.25 fixed the terminal-corruption leak in
|
||||||
|
`lib/nc-document.sh` only. Vera flagged that the OTHER tools dumping
|
||||||
|
NetConfig/`.tcl`/HL7 content to stdout carry the SAME risk and were still
|
||||||
|
unsanitised — most importantly `nc-msgs.sh`, whose raw HL7 contains C0 framing
|
||||||
|
bytes (e.g. `0x1c` block separator) that corrupt a terminal when viewed
|
||||||
|
un-redirected.
|
||||||
|
|
||||||
|
- **Shared sanitiser.** `_sanitize_ctl` is hoisted out of `nc-document.sh` into
|
||||||
|
`lib/cygwin-safe.sh` (already the sourceable shared-primitives lib), so every
|
||||||
|
tool shares ONE definition. `nc-document.sh` now sources it; its behavior is
|
||||||
|
UNCHANGED — it still strips UNCONDITIONALLY (the doc is a human-readable
|
||||||
|
artifact; control bytes are unwanted even when redirected to a `.txt`).
|
||||||
|
|
||||||
|
- **New `_sanitize_ctl_tty` — the data-tool variant.** For the data-producing
|
||||||
|
tools (`nc-msgs.sh`, `nc-parse.sh`, `hl7-field.sh`, `hl7-diff.sh`,
|
||||||
|
`hl7-sanitize.sh`, `hl7-desanitize.sh`) stripping happens ONLY when stdout is
|
||||||
|
an interactive terminal (`[ -t 1 ]`). When the user pipes/redirects (e.g.
|
||||||
|
`nc-msgs … --format raw > input.msgs` feeding route_test, or `| awkcut`), the
|
||||||
|
output passes through RAW and byte-identical — the `0x1c` HL7 framing and
|
||||||
|
other bytes are LOAD-BEARING downstream and must not be silently corrupted.
|
||||||
|
Each tool's output region runs in a brace group piped through the gate, with
|
||||||
|
`${PIPESTATUS[0]}` propagated so subcommand exit codes survive the pipe.
|
||||||
|
(`hl7-schema.sh` is sourced-only — no stdout sink of its own — so it is
|
||||||
|
untouched.)
|
||||||
|
|
||||||
|
- **`ssh-helper.sh` `_read_hidden` trap nits (Vera).** (a) The restore trap was
|
||||||
|
installed only when `stty -g` succeeded, yet `stty -echo` ran regardless — a
|
||||||
|
`^C` in that window left echo off. Now a restore trap is installed on EVERY
|
||||||
|
path BEFORE touching echo (falling back to `stty echo` when the save failed).
|
||||||
|
(b) The trap reset was `trap -` (reset-to-default); it now captures the
|
||||||
|
caller's PRIOR `INT/TERM/HUP` trap with `trap -p` and restores it.
|
||||||
|
|
||||||
|
Proved on the real test integrator (`to_appriss.smatdb`, 11 msgs): `nc-msgs …
|
||||||
|
--format raw` to a file kept `0x1c`/`0x0d` intact (493 bytes), while the same
|
||||||
|
command through a PTY stripped ESC/FS; `cmp` confirmed they differ. `nc-document
|
||||||
|
--name codametrix` to a file still emits 0 control bytes with the em-dash
|
||||||
|
preserved. Portable: `LC_ALL=C tr` octal ranges + POSIX `[ -t 1 ]`; no GNU-only
|
||||||
|
flags. `bash -n` clean on all touched files. MANIFEST regenerated.
|
||||||
|
|
||||||
## v0.8.25 — 2026-05-28
|
## v0.8.25 — 2026-05-28
|
||||||
|
|
||||||
**★ FIX: terminal line-editing corruption after `larry tools …` runs (Bryan,
|
**★ FIX: terminal line-editing corruption after `larry tools …` runs (Bryan,
|
||||||
|
|||||||
24
MANIFEST
24
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 dd9fdba88d20c4472826ef9355a7a0aa3d6bcf1d9d040aff9f07a2bb1351a287
|
larry.sh 7fccca0d10a0a742d66efd21da703d780c8359411995cf69925123575b14321c
|
||||||
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 7a2e873644ed9f1015114f43072f374eb0e1716bb1a97e73daa1337474d3e1fd
|
VERSION 6520a3a0746d8a2969ca4c76db2109929b36882541fcdbe3fb6de1718903d97f
|
||||||
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
||||||
CHANGELOG.md c659506dea3dea5b1cc53ef41f978219d6e9894b4fc4a05df07df660a56f79be
|
CHANGELOG.md 0d7a88d389d6723ee2dd289e5d143d4ada8f232ca0df43c392be9bca856f70b6
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||||
@ -41,7 +41,7 @@ agents/cloverleaf-cheatsheet.md cd62d57e7ca067b42f1db2dc75a48f1474ae4b742a560250
|
|||||||
agents/regress.md bb05ed1439b1e35d6e9799e32d683bfab166472c72115c1f02757e227c74e42f
|
agents/regress.md bb05ed1439b1e35d6e9799e32d683bfab166472c72115c1f02757e227c74e42f
|
||||||
|
|
||||||
# Cygwin/MobaXterm CR-taint defense primitives (sourced by every tool)
|
# Cygwin/MobaXterm CR-taint defense primitives (sourced by every tool)
|
||||||
lib/cygwin-safe.sh efa83387f03e213a2b200b78cf5468dc930d71b4e3dbb98477187057fa8f4857
|
lib/cygwin-safe.sh 6cef02f3e931f4067eed990b01fb9535c9e5b1e91b62ff318f0008732d4ad545
|
||||||
|
|
||||||
# v0.8.4: content-validating fetch (HTML-sign-in-page trap detection + per-
|
# v0.8.4: content-validating fetch (HTML-sign-in-page trap detection + per-
|
||||||
# file-type shape checks) for the installer/auto-updater. Canonical home of the
|
# file-type shape checks) for the installer/auto-updater. Canonical home of the
|
||||||
@ -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 b8442e1e086eed7afebc77e1948c9c688301a8ef4b14f563470396aceccdddd4
|
lib/ssh-helper.sh 18df1f1f1936c930ba0197c0e0b4bd89c027500de99b56067b620ca9144f6e9e
|
||||||
|
|
||||||
# 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
|
||||||
@ -66,10 +66,10 @@ lib/lessons.sh 45ea4fdadb843701cd3e87f6a0011ba4097978661851ebc9098ad22ea219efb1
|
|||||||
lib/journal.sh 11c62a2d47b6b67a2f423fd8b86c454126df18d2dc3e150233bbd08293e39fe7
|
lib/journal.sh 11c62a2d47b6b67a2f423fd8b86c454126df18d2dc3e150233bbd08293e39fe7
|
||||||
|
|
||||||
# HL7 utilities
|
# HL7 utilities
|
||||||
lib/hl7-sanitize.sh 6c7d068e0f8538683074c11cf3350868021e9c0f1823f26bf83afdc285d5dc75
|
lib/hl7-sanitize.sh c0ea35d28c32dcbb1476835a6e58c2ecdbd04f0a479b889675724fc564f4205f
|
||||||
lib/hl7-desanitize.sh d43e29eefde170cdee64b31383d32ccc995773eec9ccad26a18d4cf2270e58f5
|
lib/hl7-desanitize.sh 2e5462a61ab1e8bd3fefb956bace8ca1ae33397a09024cbe766fa55c37a5aad6
|
||||||
lib/hl7-diff.sh 162ad0e2ed2cd0e57f395ed53c4b3aa0d8f094ee08fa648f4724e0bda176f464
|
lib/hl7-diff.sh 66985afb3073340f1c12b0d7b39f41a5d8df68dfebc89c55190d6915f6077e86
|
||||||
lib/hl7-field.sh e70b032b6f3d7056fe77a564dafb1025c0feae4eaf596fb7cf315893442c1d42
|
lib/hl7-field.sh a640f7cbd9521dc96171ee1dbdf909170262101a1d7a433f6f0ce2bea8d42b02
|
||||||
lib/hl7-schema.sh 2ba4057a214867ff4950f10057ee4ffd7149e1a82ba94b07b6857d77bf10d75f
|
lib/hl7-schema.sh 2ba4057a214867ff4950f10057ee4ffd7149e1a82ba94b07b6857d77bf10d75f
|
||||||
|
|
||||||
# v0.8.2: Microsoft Presidio sidecar (optional, opt-in install).
|
# v0.8.2: Microsoft Presidio sidecar (optional, opt-in install).
|
||||||
@ -97,12 +97,12 @@ lib/nc-xlate.sh ea02693c3dff5db271771d4bb2927b23465b07798df2f9912bc2d2b58a134d54
|
|||||||
lib/nc-smat-diff.sh ac003954701ea6b7f4aa1f6941f8536af5b5cdfbb75e306789753d453f06800e
|
lib/nc-smat-diff.sh ac003954701ea6b7f4aa1f6941f8536af5b5cdfbb75e306789753d453f06800e
|
||||||
lib/nc-create-thread.sh 5a9d5407c117183cad831d6b95f0e785b1b806f5ccc67f803c12b3695882b5b7
|
lib/nc-create-thread.sh 5a9d5407c117183cad831d6b95f0e785b1b806f5ccc67f803c12b3695882b5b7
|
||||||
lib/nc-tclgen.sh dc95f523d543192fc7b3ae204107ce67ebb9b7e5184fa0642a1af2e2454d3241
|
lib/nc-tclgen.sh dc95f523d543192fc7b3ae204107ce67ebb9b7e5184fa0642a1af2e2454d3241
|
||||||
lib/nc-parse.sh ab06df8264983a9c490af25bf20e1551a91e68b45a9ec24c6cb0fce1f1b9dd69
|
lib/nc-parse.sh 3419b3f8d0cfdaf767f91551d6e2441d0743d80bd31515ffa61c769db1542c2f
|
||||||
lib/nc-paths.sh 388d2f4560736587a01218cadc1de612cd59e392819d16db2f56f19174c1111b
|
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 20517922d1153ec7827c833987497fb305d087b579911d1b9067d65ae156a19f
|
||||||
lib/nc-document.sh 13aef8f6335ee63966fdd74678c506fb4ed7ed749f750e7daad142258e518f41
|
lib/nc-document.sh 47211e99089c0446d25a1e84545a734894720a1c9ad8f59b920332035e4ea880
|
||||||
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.25"
|
LARRY_VERSION="0.8.26"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -130,3 +130,50 @@ read_clean() {
|
|||||||
printf -v "$_var" '%s' "$_raw"
|
printf -v "$_var" '%s' "$_raw"
|
||||||
return $_rc
|
return $_rc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# CONTROL-BYTE SANITIZER (terminal-corruption defence) — shared since v0.8.26.
|
||||||
|
#
|
||||||
|
# Origin: v0.8.25 added this as a private helper in nc-document.sh after raw
|
||||||
|
# ESC/control bytes in tool output flipped the user's terminal mode and broke
|
||||||
|
# backspace/arrows (recoverable only with `stty sane`/`reset`). v0.8.26 hoists
|
||||||
|
# the one definition here so EVERY tool that dumps NetConfig/.tcl/HL7 content
|
||||||
|
# shares it — most importantly nc-msgs.sh, whose raw HL7 carries 0x1c block
|
||||||
|
# framing and other C0 bytes that wreck a terminal when viewed un-redirected.
|
||||||
|
#
|
||||||
|
# `_sanitize_ctl` filters stdin→stdout, stripping the C0 control bytes that
|
||||||
|
# corrupt a terminal while PRESERVING the three whitespace controls that text
|
||||||
|
# legitimately uses (TAB, LF, CR) and all high bytes (0x80-0xFF, so UTF-8
|
||||||
|
# names/comments and the em-dash survive intact).
|
||||||
|
#
|
||||||
|
# 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; 0x1c FS too)
|
||||||
|
# \177 DEL
|
||||||
|
# LC_ALL=C forces byte-wise operation (AIX `tr` is locale-sensitive otherwise).
|
||||||
|
# Falls back to `cat` if `tr` is somehow unavailable, so it never drops data.
|
||||||
|
_sanitize_ctl() {
|
||||||
|
LC_ALL=C tr -d '\001-\010\013\014\016-\037\177' 2>/dev/null || cat
|
||||||
|
}
|
||||||
|
|
||||||
|
# `_sanitize_ctl_tty` — the DATA-TOOL variant. nc-msgs/nc-parse/hl7-* emit data
|
||||||
|
# that downstream tooling consumes byte-for-byte (e.g. `nc-msgs ... > input.msgs`
|
||||||
|
# feeding route_test, or `| awkcut`). The 0x1c HL7 framing and other control
|
||||||
|
# bytes are LOAD-BEARING on a pipe/redirect — stripping them would silently
|
||||||
|
# corrupt the data. So we only sanitize when stdout is an interactive TERMINAL
|
||||||
|
# (protect the human's tty); on a pipe/file we pass through RAW, byte-identical.
|
||||||
|
#
|
||||||
|
# `[ -t 1 ]` is POSIX (true only when fd 1 is a terminal). Note this is a
|
||||||
|
# FILTER (stdin→stdout); the gate decision is made once at call time. Callers
|
||||||
|
# pipe their whole output region through it and propagate ${PIPESTATUS[0]} so
|
||||||
|
# the upstream producer's exit code is preserved across the pipe.
|
||||||
|
_sanitize_ctl_tty() {
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
_sanitize_ctl
|
||||||
|
else
|
||||||
|
cat
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,19 @@ set -o pipefail
|
|||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
DEFAULT_TABLE="$LARRY_HOME/sanitize/lookup.tsv"
|
DEFAULT_TABLE="$LARRY_HOME/sanitize/lookup.tsv"
|
||||||
|
|
||||||
|
# v0.8.26: shared control-byte sanitizer. Desanitized HL7 can carry C0 control
|
||||||
|
# bytes that corrupt a terminal when viewed un-redirected; strip them ONLY when
|
||||||
|
# stdout is a tty. Piping to `less` (a documented use) is NOT a tty, so the
|
||||||
|
# content passes through raw and less handles the control bytes itself. See
|
||||||
|
# lib/cygwin-safe.sh.
|
||||||
|
_HL7D_LIB_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
if [ -r "$_HL7D_LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$_HL7D_LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl_tty() { cat; } # degrade safe: raw passthrough if lib missing
|
||||||
|
fi
|
||||||
|
|
||||||
die() { printf 'hl7-desanitize: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'hl7-desanitize: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
table="$DEFAULT_TABLE"
|
table="$DEFAULT_TABLE"
|
||||||
@ -41,8 +54,8 @@ done
|
|||||||
[ -f "$table" ] || die "no lookup table at $table (sanitize first?)"
|
[ -f "$table" ] || die "no lookup table at $table (sanitize first?)"
|
||||||
|
|
||||||
if [ -n "$single_token" ]; then
|
if [ -n "$single_token" ]; then
|
||||||
awk -F'\t' -v t="$single_token" 'NR>1 && $1==t {print $3; found=1; exit} END{if (!found) {print "no such token: " t > "/dev/stderr"; exit 2}}' "$table"
|
awk -F'\t' -v t="$single_token" 'NR>1 && $1==t {print $3; found=1; exit} END{if (!found) {print "no such token: " t > "/dev/stderr"; exit 2}}' "$table" | _sanitize_ctl_tty
|
||||||
exit $?
|
exit "${PIPESTATUS[0]}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build sed expression set from lookup table
|
# Build sed expression set from lookup table
|
||||||
@ -80,7 +93,9 @@ NR == FNR {
|
|||||||
'
|
'
|
||||||
|
|
||||||
if [ -n "$input_file" ]; then
|
if [ -n "$input_file" ]; then
|
||||||
awk -F'\t' "$awk_script" "$table" "$input_file"
|
awk -F'\t' "$awk_script" "$table" "$input_file" | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
else
|
else
|
||||||
awk -F'\t' "$awk_script" "$table" /dev/stdin
|
awk -F'\t' "$awk_script" "$table" /dev/stdin | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
fi
|
fi
|
||||||
|
|||||||
@ -28,6 +28,18 @@ set -o pipefail
|
|||||||
NC_SELF="$0"
|
NC_SELF="$0"
|
||||||
LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
||||||
|
|
||||||
|
# v0.8.26: shared control-byte sanitizer. The diff echoes raw HL7 field values,
|
||||||
|
# which can carry C0 control bytes that corrupt a terminal when viewed
|
||||||
|
# un-redirected. The final awk (sole stdout producer) is piped through the
|
||||||
|
# tty-gated sanitizer; on a pipe/redirect it passes through raw. See
|
||||||
|
# lib/cygwin-safe.sh.
|
||||||
|
if [ -r "$LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl_tty() { cat; } # degrade safe: raw passthrough if lib missing
|
||||||
|
fi
|
||||||
|
|
||||||
die() { printf 'hl7-diff: %s\n' "$*" >&2; exit 2; }
|
die() { printf 'hl7-diff: %s\n' "$*" >&2; exit 2; }
|
||||||
|
|
||||||
IGNORE="MSH.7"
|
IGNORE="MSH.7"
|
||||||
@ -89,6 +101,8 @@ trap 'rm -f "$TMP_L" "$TMP_R"' EXIT
|
|||||||
split_messages "$LEFT" "$TMP_L"
|
split_messages "$LEFT" "$TMP_L"
|
||||||
split_messages "$RIGHT" "$TMP_R"
|
split_messages "$RIGHT" "$TMP_R"
|
||||||
|
|
||||||
|
# v0.8.26: pipe the comparison output through the tty-gated sanitizer, then
|
||||||
|
# propagate ${PIPESTATUS[0]} so awk's exit (0 identical, 1 differs) survives.
|
||||||
awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
||||||
-v LFILE="$LEFT" -v RFILE="$RIGHT" '
|
-v LFILE="$LEFT" -v RFILE="$RIGHT" '
|
||||||
function ignored(seg, field, comp, subc, key, key2) {
|
function ignored(seg, field, comp, subc, key, key2) {
|
||||||
@ -245,4 +259,5 @@ awk -v IGNORE="$IGNORE" -v INCLUDE="$INCLUDE" -v FMT="$FORMAT" \
|
|||||||
if (FMT == "text") printf "\n%d total field difference(s)\n", DIFF_COUNT
|
if (FMT == "text") printf "\n%d total field difference(s)\n", DIFF_COUNT
|
||||||
exit (DIFF_COUNT > 0 ? 1 : 0)
|
exit (DIFF_COUNT > 0 ? 1 : 0)
|
||||||
}
|
}
|
||||||
' RS=$'\x1e' "$TMP_L" "$TMP_R"
|
' RS=$'\x1e' "$TMP_L" "$TMP_R" | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|||||||
@ -23,6 +23,21 @@
|
|||||||
# Exit codes: 0 = found (any number of values printed), 2 = bad path, 3 = not found.
|
# Exit codes: 0 = found (any number of values printed), 2 = bad path, 3 = not found.
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
|
# v0.8.26: shared control-byte sanitizer. HL7 field/segment content carries C0
|
||||||
|
# control bytes (e.g. 0x1c block framing) that corrupt a terminal when viewed
|
||||||
|
# un-redirected. _sanitize_ctl_tty strips them ONLY when stdout is a tty; on a
|
||||||
|
# pipe/redirect the bytes pass through raw (load-bearing downstream). The whole
|
||||||
|
# body below runs in a brace group piped through it, and ${PIPESTATUS[0]} is
|
||||||
|
# propagated so every exit code (usage 0, bad-path 2, not-found 3) survives.
|
||||||
|
_HL7F_LIB_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
if [ -r "$_HL7F_LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$_HL7F_LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl_tty() { cat; } # degrade safe: raw passthrough if lib missing
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
usage() { sed -n '2,20p' "$0"; exit 0; }
|
usage() { sed -n '2,20p' "$0"; exit 0; }
|
||||||
|
|
||||||
PATH_SPEC="${1:-}"
|
PATH_SPEC="${1:-}"
|
||||||
@ -209,3 +224,5 @@ if [ -n "$FIELD_VAL" ]; then
|
|||||||
}
|
}
|
||||||
'
|
'
|
||||||
fi
|
fi
|
||||||
|
} | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|||||||
@ -29,10 +29,23 @@
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
NC_SELF="$0"
|
NC_SELF="$0"
|
||||||
|
NC_LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
TABLE_DIR="$LARRY_HOME/sanitize"
|
TABLE_DIR="$LARRY_HOME/sanitize"
|
||||||
DEFAULT_TABLE="$TABLE_DIR/lookup.tsv"
|
DEFAULT_TABLE="$TABLE_DIR/lookup.tsv"
|
||||||
|
|
||||||
|
# v0.8.26: shared control-byte sanitizer. Sanitized HL7 and the show-table dump
|
||||||
|
# echo message/field bytes that can include C0 controls (and 0x1c framing on
|
||||||
|
# raw input) that corrupt a terminal when viewed un-redirected. Strip them ONLY
|
||||||
|
# when stdout is a tty; on a pipe/redirect (feeding analysis tooling) the bytes
|
||||||
|
# pass through raw and byte-identical. See lib/cygwin-safe.sh.
|
||||||
|
if [ -r "$NC_LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$NC_LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl_tty() { cat; } # degrade safe: raw passthrough if lib missing
|
||||||
|
fi
|
||||||
|
|
||||||
die() { printf 'hl7-sanitize: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'hl7-sanitize: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@ -455,6 +468,12 @@ AWK_END
|
|||||||
# Dispatch
|
# Dispatch
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
SUB="${1:-}"
|
SUB="${1:-}"
|
||||||
|
# v0.8.26: route the whole dispatch's stdout through the tty-gated sanitizer in
|
||||||
|
# a brace group, then propagate ${PIPESTATUS[0]} so each subcommand's exit code
|
||||||
|
# survives the pipe. On a pipe/redirect the gate is a no-op (raw passthrough).
|
||||||
|
# clear-table's confirmation read uses /dev/tty directly, so the pipeline
|
||||||
|
# subshell does not interfere with it.
|
||||||
|
{
|
||||||
case "$SUB" in
|
case "$SUB" in
|
||||||
show-rules) shift; cmd_show_rules ;;
|
show-rules) shift; cmd_show_rules ;;
|
||||||
show-table) shift; cmd_show_table "$@" ;;
|
show-table) shift; cmd_show_table "$@" ;;
|
||||||
@ -514,3 +533,5 @@ case "$SUB" in
|
|||||||
do_sanitize "$input_file" "$rules" "$table" "$strict" "$update_table"
|
do_sanitize "$input_file" "$rules" "$table" "$strict" "$update_table"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
} | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|||||||
@ -98,6 +98,20 @@ LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
|||||||
NCP="$LIB_DIR/nc-parse.sh"
|
NCP="$LIB_DIR/nc-parse.sh"
|
||||||
NCPATHS="$LIB_DIR/nc-paths.sh"
|
NCPATHS="$LIB_DIR/nc-paths.sh"
|
||||||
|
|
||||||
|
# v0.8.26: the control-byte sanitizer (added here in v0.8.25) now lives in the
|
||||||
|
# shared lib/cygwin-safe.sh so every content-dumping tool shares one definition.
|
||||||
|
# This tool's behavior is UNCHANGED: out_target() still strips UNCONDITIONALLY
|
||||||
|
# (the doc is a human-readable artifact — even when redirected to a .txt for
|
||||||
|
# OneNote the user does NOT want control bytes), so it calls _sanitize_ctl, not
|
||||||
|
# the tty-gated variant. Fallback below keeps the prior inline definition if the
|
||||||
|
# shared lib is somehow unavailable, so behavior never regresses.
|
||||||
|
if [ -r "$LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl() { LC_ALL=C tr -d '\001-\010\013\014\016-\037\177' 2>/dev/null || cat; }
|
||||||
|
fi
|
||||||
|
|
||||||
die() { printf 'nc-document: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'nc-document: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@ -250,27 +264,20 @@ _locate_thread() {
|
|||||||
# Output sink
|
# Output sink
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
# v0.8.25 — CONTROL-BYTE SANITIZER (terminal-corruption fix).
|
# CONTROL-BYTE SANITIZER — added v0.8.25, hoisted to the shared lib in v0.8.26.
|
||||||
# This tool reads arbitrary NetConfig and .tcl source (author comments, raw proc
|
# 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
|
# 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
|
# 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
|
# (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),
|
# different mode and wrecks line editing (backspace prints ^H, arrows die),
|
||||||
# forcing `stty sane`/`reset` to recover. We neutralise it at the single output
|
# forcing `stty sane`/`reset` to recover.
|
||||||
# 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):
|
# We neutralise it at the single output choke-point UNCONDITIONALLY — even when
|
||||||
# \001-\010 SOH..BS (drops BS ^H, the literal-backspace culprit)
|
# redirected to a .txt for OneNote, this is a human-readable document and the
|
||||||
# [keep \011 TAB, \012 LF]
|
# user does NOT want control bytes. So out_target() calls `_sanitize_ctl` (the
|
||||||
# \013\014 VT, FF
|
# always-strip variant from lib/cygwin-safe.sh), NOT the tty-gated variant the
|
||||||
# [keep \015 CR — legit in MobaXterm/Windows-tainted content]
|
# raw-data tools use. The full strip-set rationale lives with the definition in
|
||||||
# \016-\037 SO..US (drops ESC 0x1B, the mode-flip culprit)
|
# lib/cygwin-safe.sh; behavior here is identical to v0.8.25.
|
||||||
# \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
|
||||||
|
|||||||
@ -44,6 +44,9 @@ if [ -r "$LIB_DIR/cygwin-safe.sh" ]; then
|
|||||||
. "$LIB_DIR/cygwin-safe.sh"
|
. "$LIB_DIR/cygwin-safe.sh"
|
||||||
else
|
else
|
||||||
coerce_int() { local r="${1:-}" d="${2:-0}" c; c=$(printf '%s' "$r" | tr -cd '0-9'); printf '%s' "${c:-$d}"; }
|
coerce_int() { local r="${1:-}" d="${2:-0}" c; c=$(printf '%s' "$r" | tr -cd '0-9'); printf '%s' "${c:-$d}"; }
|
||||||
|
# v0.8.26 fallback: raw passthrough if the shared lib is missing. Never strips
|
||||||
|
# — the gated stripping is a terminal-protection nicety, not a correctness need.
|
||||||
|
_sanitize_ctl_tty() { cat; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
die() { printf 'nc-msgs: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'nc-msgs: %s\n' "$*" >&2; exit 1; }
|
||||||
@ -416,6 +419,13 @@ match_filters() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Emit
|
# Emit
|
||||||
|
# v0.8.26: route ALL stdout from the emit block through the tty-gated sanitizer.
|
||||||
|
# CRITICAL: the `raw` format intentionally emits raw HL7 plus 0x1c framing for
|
||||||
|
# downstream tooling (`nc-msgs ... --format raw > input.msgs` → route_test). On a
|
||||||
|
# pipe/redirect those bytes are load-bearing and MUST pass through untouched, so
|
||||||
|
# the gate sanitizes ONLY when stdout is an interactive tty (protect the human's
|
||||||
|
# terminal). Stderr lines (the `>&2` scan summary) bypass this pipe by design.
|
||||||
|
{
|
||||||
case "$FORMAT" in
|
case "$FORMAT" in
|
||||||
count)
|
count)
|
||||||
# Apply filter if ANY group has entries
|
# Apply filter if ANY group has entries
|
||||||
@ -559,3 +569,7 @@ case "$FORMAT" in
|
|||||||
printf ']\n'
|
printf ']\n'
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
} | _sanitize_ctl_tty
|
||||||
|
# Preserve the emit block's exit status across the gating pipe. The EXIT trap
|
||||||
|
# (rm -rf "$TMP_OUT") still fires on this parent exit.
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|||||||
@ -39,6 +39,19 @@ set -u
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
NC_SELF="$0"
|
NC_SELF="$0"
|
||||||
|
NC_LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
||||||
|
|
||||||
|
# v0.8.26: shared control-byte sanitizer. This tool dumps NetConfig and raw .tcl
|
||||||
|
# proc bodies to stdout; that content can carry C0 control bytes (ESC etc.) that
|
||||||
|
# corrupt a terminal when viewed un-redirected. _sanitize_ctl_tty strips them
|
||||||
|
# ONLY when stdout is a tty — on a pipe/redirect the bytes pass through raw so
|
||||||
|
# downstream tooling sees byte-identical content. See lib/cygwin-safe.sh.
|
||||||
|
if [ -r "$NC_LIB_DIR/cygwin-safe.sh" ]; then
|
||||||
|
# shellcheck disable=SC1090,SC1091
|
||||||
|
. "$NC_LIB_DIR/cygwin-safe.sh"
|
||||||
|
else
|
||||||
|
_sanitize_ctl_tty() { cat; } # degrade safe: raw passthrough if lib missing
|
||||||
|
fi
|
||||||
|
|
||||||
die() { printf 'nc-parse: %s\n' "$*" >&2; exit 1; }
|
die() { printf 'nc-parse: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
@ -499,6 +512,11 @@ cmd_help() { sed -n '2,30p' "$NC_SELF"; }
|
|||||||
# Dispatch
|
# Dispatch
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
SUB="${1:-help}"
|
SUB="${1:-help}"
|
||||||
|
# v0.8.26: route the whole dispatch's stdout through the tty-gated sanitizer in
|
||||||
|
# a brace group, then propagate ${PIPESTATUS[0]} so the subcommand's exit code
|
||||||
|
# (incl. die's exit 1 and not-found 2/3) survives the pipe. On a pipe/redirect
|
||||||
|
# the gate is a no-op (raw passthrough); only an interactive tty gets stripped.
|
||||||
|
{
|
||||||
case "$SUB" in
|
case "$SUB" in
|
||||||
list-protocols) [ $# -ge 2 ] || die "usage: $0 list-protocols <netconfig>"; cmd_list_protocols "$2" ;;
|
list-protocols) [ $# -ge 2 ] || die "usage: $0 list-protocols <netconfig>"; cmd_list_protocols "$2" ;;
|
||||||
list-processes) [ $# -ge 2 ] || die "usage: $0 list-processes <netconfig>"; cmd_list_processes "$2" ;;
|
list-processes) [ $# -ge 2 ] || die "usage: $0 list-processes <netconfig>"; cmd_list_processes "$2" ;;
|
||||||
@ -517,3 +535,5 @@ case "$SUB" in
|
|||||||
help|-h|--help) cmd_help ;;
|
help|-h|--help) cmd_help ;;
|
||||||
*) die "unknown subcommand: $SUB (try '$0 help')" ;;
|
*) die "unknown subcommand: $SUB (try '$0 help')" ;;
|
||||||
esac
|
esac
|
||||||
|
} | _sanitize_ctl_tty
|
||||||
|
exit "${PIPESTATUS[0]}"
|
||||||
|
|||||||
@ -90,12 +90,24 @@ ok() { printf 'ssh-helper: %s\n' "$*"; }
|
|||||||
# Sets the named variable to the line read (no echo). Returns 0 always (empty
|
# Sets the named variable to the line read (no echo). Returns 0 always (empty
|
||||||
# input is the caller's "abort" signal).
|
# input is the caller's "abort" signal).
|
||||||
_read_hidden() { # varname
|
_read_hidden() { # varname
|
||||||
local _rh_var="$1" _rh_val="" _rh_saved=""
|
local _rh_var="$1" _rh_val="" _rh_saved="" _rh_prior_trap=""
|
||||||
if command -v stty >/dev/null 2>&1 && { [ -t 0 ] || [ -e /dev/tty ]; }; then
|
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)
|
_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.
|
# v0.8.26 nit (b): capture the caller's PRIOR INT/TERM/HUP trap so we can
|
||||||
|
# RESTORE it on the way out instead of blindly resetting to default. `trap -p`
|
||||||
|
# prints the re-installable trap commands (empty if none was set). Harmless
|
||||||
|
# today (no caller sets these around the prompt) but correct.
|
||||||
|
_rh_prior_trap=$(trap -p INT TERM HUP 2>/dev/null || true)
|
||||||
|
# v0.8.26 nit (a): install a restore trap BEFORE touching echo, on EVERY
|
||||||
|
# path. If `stty -g` saved a state, restore exactly that; if the save failed
|
||||||
|
# (_rh_saved empty), fall back to `stty echo` so a ^C mid-read can never
|
||||||
|
# leave the terminal with echo off. Previously the trap was only installed
|
||||||
|
# when _rh_saved was non-empty, yet `stty -echo` ran regardless — an
|
||||||
|
# interrupt in that window left the tty corrupted.
|
||||||
if [ -n "$_rh_saved" ]; then
|
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
|
trap 'stty "$_rh_saved" </dev/tty 2>/dev/null || stty "$_rh_saved" 2>/dev/null' INT TERM HUP
|
||||||
|
else
|
||||||
|
trap 'stty echo </dev/tty 2>/dev/null || stty echo 2>/dev/null' INT TERM HUP
|
||||||
fi
|
fi
|
||||||
stty -echo </dev/tty 2>/dev/null || stty -echo 2>/dev/null || true
|
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
|
IFS= read -r _rh_val </dev/tty 2>/dev/null || IFS= read -r _rh_val || true
|
||||||
@ -104,7 +116,12 @@ _read_hidden() { # varname
|
|||||||
else
|
else
|
||||||
stty echo </dev/tty 2>/dev/null || stty echo 2>/dev/null || true
|
stty echo </dev/tty 2>/dev/null || stty echo 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
trap - INT TERM HUP
|
# Restore the caller's prior trap (or clear ours if there was none).
|
||||||
|
if [ -n "$_rh_prior_trap" ]; then
|
||||||
|
eval "$_rh_prior_trap"
|
||||||
|
else
|
||||||
|
trap - INT TERM HUP
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
IFS= read -r _rh_val </dev/tty 2>/dev/null || IFS= read -r _rh_val || true
|
IFS= read -r _rh_val </dev/tty 2>/dev/null || IFS= read -r _rh_val || true
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user