diff --git a/CHANGELOG.md b/CHANGELOG.md index efeefb4..87f71a7 100644 --- a/CHANGELOG.md +++ b/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 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 ` 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 ` (normal), +`/ssh-set-hciroot ` (empty-path CLEAR), trailing-space clear, no-args +usage, `/ssh `, and `/ssh ` (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 Legacy/qa remote-enumeration fix (Clover). Three confirmed-live properties of diff --git a/MANIFEST b/MANIFEST index 2d75948..eb79cbf 100644 --- a/MANIFEST +++ b/MANIFEST @@ -23,16 +23,16 @@ # scripts/make-manifest.sh and bump VERSION. # Top-level scripts -larry.sh 2e7650eb7a014624bd6956c30ce3a54e0e87d4ccfc73bb0b2ae82d1a31b882e0 +larry.sh 7bbd920f7a29379aadad3e59a298ff416333e9299241db7c7bb8c42cc7750f9d larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831 larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0 install-larry.sh e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1 # Metadata -VERSION 8517de55d0fc1041caab07518dbf7da86dba47c3befe0a6ef84d005872cb799d +VERSION 088faf7f331988e309c37e27272af81cd41fdcf4466022059ffdd3184e3c7d34 MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1 -CHANGELOG.md 0b8f2dba750577f934935dd7d5805c498afa9d516cd37e5b6cda039cb86ec350 +CHANGELOG.md 3db03a7913b3a66310f9cb282eaa6c3554d5ed59ef92eca78ef02257dc97277f # Agent personas (system-prompt overlays) agents/larry.md 11ea905fa7cac6fa7baeb11b2d62af07b15a666ce90cfe36491bcbc555244397 diff --git a/VERSION b/VERSION index 7d87d99..ac7dffa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.8.15 +0.8.16 diff --git a/larry.sh b/larry.sh index 91fb3a7..cb0138a 100755 --- a/larry.sh +++ b/larry.sh @@ -78,7 +78,7 @@ set -o pipefail # ───────────────────────────────────────────────────────────────────────────── # Config # ───────────────────────────────────────────────────────────────────────────── -LARRY_VERSION="0.8.15" +LARRY_VERSION="0.8.16" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" # ───────────────────────────────────────────────────────────────────────────── @@ -6900,8 +6900,13 @@ main_loop() { if [ -z "$rest" ]; then err "usage: /ssh-set-hciroot (empty path clears the pin)"; continue fi - local _sh_alias="${rest%% *}" _sh_path="${rest#"$_sh_alias"}" - _sh_path="${_sh_path# }" + # v0.8.16: set-u-safe split. A single-line `local a=… b="…$a…"` + # 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 err "usage: /ssh-set-hciroot "; continue fi @@ -6922,8 +6927,11 @@ main_loop() { continue ;; /ssh*) local rest; rest=$(_slash_args "/ssh" "$input") if [ -z "$rest" ]; then err "usage: /ssh "; continue; fi - local alias="${rest%% *}" rcmd="${rest#"$alias"}" - rcmd="${rcmd# }" + # v0.8.16: same set-u-safe split as /ssh-set-hciroot above — + # $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 err "usage: /ssh "; continue fi