From b56666f3cd2238144b4394fb83fa2621a816d608 Mon Sep 17 00:00:00 2001 From: Andrey Esipov Date: Tue, 19 May 2026 10:24:51 -0500 Subject: [PATCH] fix(gbrain-lib): pin LC_ALL=C in varname validator (macOS locale guard) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In many macOS shells the default locale (e.g. en_US.UTF-8) makes bash glob brackets like `[A-Z]` match lowercase letters too, so the existing `case "$name" in [A-Z_][A-Z0-9_]*)` branch lets names like `lower-case` through validation. The function then trips `printf -v "$varname"` and `export "$varname"` with `not a valid identifier` errors that surface mid-prompt, which is exactly what the validator was supposed to prevent. Pinning `LC_ALL=C` inside the function gives ASCII-only bracket semantics on both macOS and Linux, matching the documented `[A-Z_][A-Z0-9_]*` contract. Declared `local` so it doesn't leak to the calling shell — `gstack-gbrain-lib.sh` is documented as a sourced helper, so a bare assignment would mutate the caller's locale for the rest of the process (silently affecting downstream `sort`, `tr`, locale-aware globs in the same shell, etc.). The existing regression test `test/gbrain-lib-verify.test.ts:'rejects invalid var names'` already covers the macOS repro shape (passes `lower-case` and expects the validator to reject + emit `invalid var name`). On Linux CI the test silently passed because `LC_ALL=C` is the typical default; on macOS dev boxes it fails. Verified: - `bun test test/gbrain-lib-verify.test.ts`: 22 pass, 0 fail (on macOS). - `_gstack_gbrain_validate_varname lower-case; echo $?` → 2. - `_gstack_gbrain_validate_varname FOO_BAR; echo $?` → 0. - Caller's LC_ALL preserved across calls (confirmed via sourced bash). --- bin/gstack-gbrain-lib.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/bin/gstack-gbrain-lib.sh b/bin/gstack-gbrain-lib.sh index 7498e568d5..b89cce2e03 100644 --- a/bin/gstack-gbrain-lib.sh +++ b/bin/gstack-gbrain-lib.sh @@ -27,8 +27,22 @@ # restore), D16 (pooler URL paste hygiene with redacted preview). # _gstack_gbrain_validate_varname — returns 0 if usable, 2 otherwise. +# `local LC_ALL=C` is load-bearing twice over: +# 1. In many macOS shells the default locale (e.g. en_US.UTF-8) makes `case` +# glob brackets like `[A-Z]` match lowercase letters too. Without the +# LC_ALL=C pin, names like `lower-case` pass validation and then trip +# `printf -v "$varname"` and `export "$varname"` with "not a valid +# identifier" errors the caller can't easily distinguish from other +# failures. +# 2. `local` is required because this file is documented as a sourced helper +# (see header), so a bare `LC_ALL=C` would mutate the caller's locale for +# the rest of the process — silently affecting downstream `sort`, `tr`, +# and any locale-aware glob in the same shell. +# Together they give ASCII-only bracket semantics on both macOS and Linux +# (matching the documented `[A-Z_][A-Z0-9_]*` contract) without leaking. _gstack_gbrain_validate_varname() { local name="$1" + local LC_ALL=C case "$name" in [A-Z_][A-Z0-9_]*) return 0 ;; *) return 2 ;;