Guard unguarded array expansion in statusline-command.sh (latent, ships to users) #96
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found during PR #95's review round, in a file that PR does not touch. Filed separately rather than piggybacked — that PR already carries one unrelated fix and the review flagged undisclosed scope as a problem.
The defect
providers/claude-code/statusline-command.sh:partsis seeded empty and every append is guarded by a condition that can be false, so"${parts[@]}"at:96can expand an empty array.Why it is latent, not live
The file sets no
set -u(grep -n '^set ' providers/claude-code/statusline-command.sh→ no output), so an empty array expands to nothing and the loop simply does not run. Output is an empty string, which is correct behaviour.It becomes a hard failure the moment anyone adds
set -u— which is the house style in this repo's other scripts.Why it matters
install.sh:16deploys this file to every user machine:Fix
Guard the expansion with the idiom already used throughout the repo:
or the short-circuit form used in the cleanup traps:
Unblocks
tests/test-vale-wrap.sh's derived bash-3.2 scan currently excludesproviders/**specifically because of this hazard, with the exclusion and its reason recorded in-file. Fixing this allows adding aprovidersglob so deployed provider scripts get the same bash-3.2 coverage asscripts/,tests/andplugins/*/.apm/.Verification note: an earlier review pass misread
:85as seeding the array non-empty. It does not — the appends at:86+ are all conditional. Confirmed by reading the file directly.Fixed in
49d21bconfeat/90-execute-apm-conversion, folded into PR #95 (disclosed there, since this widens that PR intoproviders/— an area it did not previously touch).What landed
The join loop now uses the repo idiom:
and
providers/**/*.shis intest-vale-wrap.sh's bash-3.2 scan — the exclusion this issue's "Unblocks" section describes is gone. 44 files now scanned.Floor for the new glob is 1, not "current count minus slack". The glob holds exactly one file, so any slack at all means a floor of 0, which passes vacuously on a renamed or deleted directory — precisely the silent-zero failure the per-glob floors exist to catch.
Correction to the issue's framing
That is one of two conditions, and the issue names only the first. bash 4.4 stopped treating an empty-array expansion as unbound. So the pre-fix code is safe if either no
set -uis enabled or the shell is bash 4.4+. On bash 3.2 — macOS's system bash, and the reason this scan exists — addingset -uaborts.The practical consequence is worth recording, because it will mislead the next person who tries to reproduce this: the hazard cannot be demonstrated at runtime on a modern dev box. Measured here on bash 5.2:
No abort. It looks like a false positive. It is not — it is a shell-version artifact. There is no bash 3.2 binary in this environment to demonstrate it with, which is exactly why the enforcement is a static scan rather than a runtime test, and why the
providersfloor must never be dropped to zero. Both facts are now recorded in the two files themselves so the glob is not later deleted as redundant.Verification
bash -unow exits 0 either way."${parts[@]}"producesFAIL: bash-4-only construct(s) found in 44 scanned script(s): statusline-command.sh:105.providersglob at a wrong path producesFAIL: … derived 0 file(s), under its floor of 1.shellcheck --severity=warningclean; full suite 16/16; all 14 pre-push hooks pass.The verification note in the issue was correct —
:85does seed the array empty and every append is conditional.