v0.8.16: hotfix set -u unbound-variable abort in /ssh-set-hciroot + /ssh REPL slash handlers
Single-line `local a=… b="…$a…"` referenced the 1st var before it was bound within the SAME `local` statement, aborting under set -u on Cygwin/MobaXterm (and modern bash). Split larry.sh:6903 (/ssh-set-hciroot) and the same latent pattern at larry.sh:6925 (/ssh) into set-u-safe declare-then-assign form. Codebase-wide bug-class audit (larry.sh + all lib/*.sh + scripts): zero remaining instances. Closed the v0.8.15 gate gap by driving the ACTUAL REPL slash-dispatch handler bodies under set -u + BASH_COMPAT=3.2 (not just the ssh-helper subcommand): /ssh-set-hciroot normal + empty-path-clear, /ssh, and usage paths all pass; old code aborts under the same harness. No-traffic-bypass line unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
fc667e2451
commit
0e6495223a
47
CHANGELOG.md
47
CHANGELOG.md
@ -4,6 +4,53 @@ 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.16 — 2026-05-28
|
||||||
|
|
||||||
|
Hotfix (Clover): `set -u` unbound-variable abort in the v0.8.15
|
||||||
|
`/ssh-set-hciroot` REPL slash command, reported live on Bryan's MobaXterm /
|
||||||
|
Cygwin bash (`larry.sh: line 6903: _sh_alias: unbound variable`).
|
||||||
|
|
||||||
|
**Root cause.** `larry.sh:6903` was a single-line `local` declaration in which
|
||||||
|
the 2nd variable's initializer referenced the 1st variable assigned in the SAME
|
||||||
|
`local` statement:
|
||||||
|
`local _sh_alias="${rest%% *}" _sh_path="${rest#"$_sh_alias"}"`.
|
||||||
|
Under `set -u`, `$_sh_alias` is treated as unbound at expansion time within its
|
||||||
|
own `local` statement, aborting the command. This is NOT Cygwin/bash-3.2
|
||||||
|
specific — it reproduces identically on bash 5.x. The same latent pattern lived
|
||||||
|
at `larry.sh:6925` (the `/ssh <alias> <cmd>` handler:
|
||||||
|
`local alias="${rest%% *}" rcmd="${rest#"$alias"}"`), not yet triggered only
|
||||||
|
because that path hadn't been run.
|
||||||
|
|
||||||
|
**Fix.** Both split into set-u-safe form — declare the locals first, assign the
|
||||||
|
first, THEN reference it on a later line. Correct on every bash version.
|
||||||
|
|
||||||
|
1. `/ssh-set-hciroot` handler (was `larry.sh:6903`) — safe-split; preserves the
|
||||||
|
empty-path "clear the pin" semantics.
|
||||||
|
2. `/ssh` handler (was `larry.sh:6925`) — same safe-split.
|
||||||
|
|
||||||
|
**Codebase-wide bug-class audit.** Every `.sh` (larry.sh + all `lib/*.sh` +
|
||||||
|
top-level scripts) was scanned for any single-statement `local`/`declare`/
|
||||||
|
`typeset`/`readonly`/`export` where one variable's initializer references
|
||||||
|
another variable assigned in the SAME statement. After the two fixes above,
|
||||||
|
**zero remaining instances**. All superficially-similar lines elsewhere are
|
||||||
|
`;`-separated sequential statements (e.g. `_x="${rest%% *}"; rest="${rest#"$_x"}"`)
|
||||||
|
where the referenced variable is already bound — these are the CORRECT idiom and
|
||||||
|
are safe under `set -u`.
|
||||||
|
|
||||||
|
**Gate-gap closed.** The v0.8.15 gate exercised the `ssh-helper.sh set-hciroot`
|
||||||
|
SUBCOMMAND but never the larry.sh `/ssh-set-hciroot` REPL slash dispatch — where
|
||||||
|
the bug actually was. The v0.8.16 gate drives the ACTUAL slash-command handler
|
||||||
|
bodies (extracted verbatim from the fixed source) through a `case` dispatcher
|
||||||
|
under `set -u` with `BASH_COMPAT=3.2`, non-interactively, with no self-update /
|
||||||
|
API / bootstrap. Verified `/ssh-set-hciroot <alias> <path>` (normal),
|
||||||
|
`/ssh-set-hciroot <alias>` (empty-path CLEAR), trailing-space clear, no-args
|
||||||
|
usage, `/ssh <alias> <cmd>`, and `/ssh <alias>` (usage) all execute WITHOUT the
|
||||||
|
unbound-var abort. The same harness run against the OLD v0.8.15 line aborts with
|
||||||
|
`_sh_alias: unbound variable` (rc=1), confirming the gate now catches this
|
||||||
|
regression class. The no-traffic-bypass security line is unchanged.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## v0.8.15 — 2026-05-28
|
## v0.8.15 — 2026-05-28
|
||||||
|
|
||||||
Legacy/qa remote-enumeration fix (Clover). Three confirmed-live properties of
|
Legacy/qa remote-enumeration fix (Clover). Three confirmed-live properties of
|
||||||
|
|||||||
6
MANIFEST
6
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 2e7650eb7a014624bd6956c30ce3a54e0e87d4ccfc73bb0b2ae82d1a31b882e0
|
larry.sh 7bbd920f7a29379aadad3e59a298ff416333e9299241db7c7bb8c42cc7750f9d
|
||||||
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 e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1
|
install-larry.sh e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
VERSION 8517de55d0fc1041caab07518dbf7da86dba47c3befe0a6ef84d005872cb799d
|
VERSION 088faf7f331988e309c37e27272af81cd41fdcf4466022059ffdd3184e3c7d34
|
||||||
MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1
|
MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1
|
||||||
CHANGELOG.md 0b8f2dba750577f934935dd7d5805c498afa9d516cd37e5b6cda039cb86ec350
|
CHANGELOG.md 3db03a7913b3a66310f9cb282eaa6c3554d5ed59ef92eca78ef02257dc97277f
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 11ea905fa7cac6fa7baeb11b2d62af07b15a666ce90cfe36491bcbc555244397
|
agents/larry.md 11ea905fa7cac6fa7baeb11b2d62af07b15a666ce90cfe36491bcbc555244397
|
||||||
|
|||||||
18
larry.sh
18
larry.sh
@ -78,7 +78,7 @@ set -o pipefail
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Config
|
# Config
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
LARRY_VERSION="0.8.15"
|
LARRY_VERSION="0.8.16"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
@ -6900,8 +6900,13 @@ main_loop() {
|
|||||||
if [ -z "$rest" ]; then
|
if [ -z "$rest" ]; then
|
||||||
err "usage: /ssh-set-hciroot <alias> <path> (empty path clears the pin)"; continue
|
err "usage: /ssh-set-hciroot <alias> <path> (empty path clears the pin)"; continue
|
||||||
fi
|
fi
|
||||||
local _sh_alias="${rest%% *}" _sh_path="${rest#"$_sh_alias"}"
|
# v0.8.16: set-u-safe split. A single-line `local a=… b="…$a…"`
|
||||||
_sh_path="${_sh_path# }"
|
# references $_sh_alias before it is bound within the SAME `local`
|
||||||
|
# statement, which aborts under `set -u` on bash 3.2/Cygwin
|
||||||
|
# (MobaXterm) AND modern bash. Declare first, assign sequentially.
|
||||||
|
local _sh_alias _sh_path
|
||||||
|
_sh_alias="${rest%% *}"
|
||||||
|
_sh_path="${rest#"$_sh_alias"}"; _sh_path="${_sh_path# }"
|
||||||
if [ -z "$_sh_alias" ]; then
|
if [ -z "$_sh_alias" ]; then
|
||||||
err "usage: /ssh-set-hciroot <alias> <path>"; continue
|
err "usage: /ssh-set-hciroot <alias> <path>"; continue
|
||||||
fi
|
fi
|
||||||
@ -6922,8 +6927,11 @@ main_loop() {
|
|||||||
continue ;;
|
continue ;;
|
||||||
/ssh*) local rest; rest=$(_slash_args "/ssh" "$input")
|
/ssh*) local rest; rest=$(_slash_args "/ssh" "$input")
|
||||||
if [ -z "$rest" ]; then err "usage: /ssh <alias> <command>"; continue; fi
|
if [ -z "$rest" ]; then err "usage: /ssh <alias> <command>"; continue; fi
|
||||||
local alias="${rest%% *}" rcmd="${rest#"$alias"}"
|
# v0.8.16: same set-u-safe split as /ssh-set-hciroot above —
|
||||||
rcmd="${rcmd# }"
|
# $alias was referenced before binding in a single-line `local`.
|
||||||
|
local alias rcmd
|
||||||
|
alias="${rest%% *}"
|
||||||
|
rcmd="${rest#"$alias"}"; rcmd="${rcmd# }"
|
||||||
if [ -z "$alias" ] || [ -z "$rcmd" ]; then
|
if [ -z "$alias" ] || [ -z "$rcmd" ]; then
|
||||||
err "usage: /ssh <alias> <command>"; continue
|
err "usage: /ssh <alias> <command>"; continue
|
||||||
fi
|
fi
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user