Files
Linux_post_install/AgentsReport/builder/2026-09-06_netprobe-fix.md
T
Your Name d817c37652
gates / consistency-and-conventions (push) Successful in 26s
fix: stabilization pass — fail-closed auth, ai flag validation, lint/config/security hardening, regression tests
17-point code-level audit executed via Explorer->Architect->Builder->Tester->Reviewer;
Reviewer accepted (APPROVE_WITH_NOTES; 3 block-list items resolved):

- security: telegram sender-owner AND-gate + TELEGRAM_OWNER_ID, matrix
  MATRIX_ROOM_ID fail-closed, gpg --passphrase-fd 3 (no argv secret),
  /dev/tcp positional-arg form (checkport/smb-client/share-lib/NET_PROBE),
  eval deny-by-default + --no-command-execution carried by both chat bridges,
  tty-gated --trust; config/{telegram,matrix}.env reference templates
- ai: all ExecStart flags validated against installed llama.cpp
  (requested->error, default->omit+warn, CONFIG_REQUESTED_FLAGS); single-file
  hf download failure rc=1 + no .hf-meta; LLAMACPP_HOST coherent;
  POS_SUBCMDS + metadata gaps closed
- tooling: lint-conventions Bash-native rewrite (~24-30x faster, rules and
  output byte-identical, :num restored); pos system uninstall covers all 12
  libs + scale-tail + flags dir + systemd user units (|| true) + plugin
  markers; anchored .bash_completion/.bashrc removal replaces sed -i '/pos/d'
- config: canonical load_env_file in lib/config-ui.sh (CRLF strip, env-wins,
  XDG, LOADED_ENV_KEYS); 9 tools migrated; entertainment-lib collapsed to
  wrappers; docker-compose deliberately unmigrated (source semantics)
- tests: first committed regression suite — tests/run-tests.sh zero-dep
  runner + make test; 12 files / 179 checks / 0 skip / ~52s; hard skip
  contract; systemd-analyze verify on generated unit PASS

Verified: make gen idempotent; make check green; make lint 0 FAIL, 0 WARN;
make test green; bash -n clean; git diff --check clean. Audit deliverables +
agent reports + AGENT_TODO Done entry included.
2026-09-06 07:25:44 -04:00

4.3 KiB

Builder Report — 2026-09-06: NET_PROBE unbound-variable fix

TL;DR

  • Status: IMPLEMENTED (one-line scope, verified)
  • Root cause: NET_PROBE="${NET_PROBE:-timeout 3 bash -c 'exec 3<>/dev/tcp/$1/$2' _ 8.8.8.8 53}" — inside the double-quoted assignment the OUTER shell expanded $1/$2 at assignment time. Under set -u with no positional args this is an unbound-variable crash on every pos network download run (unless NET_PROBE is already exported).
  • Fix: escape the positional markers so only the inner bash -c sees them — \$1/\$2 in the default string. Env-var override contract (NET_PROBE = full command string) unchanged.
  • File changed (only): bin/pos-network-download — line 31 (NET_PROBE default). Nothing else.
  • Verification: bash -n OK; bare --help and non-network info path reach the assignment with NET_PROBE UNSET → no unbound crash; NET_PROBE='true' override returns 0; static inspection confirms outer shell does not expand $1/$2; gates green (make gen idempotent, make check OK, make lint 0 FAIL, 0 WARN).
  • Not committed (per brief).

Step 1: Confirm bug & scope

bin/pos-network-download line 31 holds the double-quoted default; net_up() (line 379) runs bash -c "$NET_PROBE", which relies on the inner bash receiving the trailing positional args _ 8.8.8.8 53 ($0=_, $1=8.8.8.8, $2=53). Reproduced: set -u; NET_PROBE="${NET_PROBE:-...$1/$2...}"/bin/bash: line 1: $1: unbound variable. Confirmed the pre-existing HEAD default was '</dev/tcp/8.8.8.8/53>' (no $1/$2) — a prior security change to the probe introduced the regression. [DONE]

Step 2: Apply fix (escaped literal default)

Changed only line 31: NET_PROBE="${NET_PROBE:-timeout 3 bash -c 'exec 3<>/dev/tcp/\$1/\$2' _ 8.8.8.8 53}" The \$ escapes keep the outer assignment from expanding $1/$2; the string stored is the literal .../dev/tcp/$1/$2..., so the inner bash -c receives the proper positional args. net_up() unchanged — it already passes the whole command string to the inner bash. Env override contract preserved and documented in usage() ("NET_PROBE override defaults (test seams)"). [DONE]

Probes (report output)

  1. set -u; bash -n bin/pos-network-download → OK (no error).
  2. Bare help, NET_PROBE UNSET (stubbed aria2c/jq/curl, isolated HOME/CONFIG_DIR): --help exits 0, no unbound in output → assignment reached without crash. Non-network path, NET_PROBE UNSET: info (no gid) → graceful ERROR: info: gid required, no unbound-variable crash (assignment ran at source time before dispatch).
  3. NET_PROBE='true' env overridenet_up() returns 0 (override still honored).
  4. Static default string inspection → default stored as timeout 3 bash -c 'exec 3<>/dev/tcp/$1/$2' _ 8.8.8.8 53 (literal $1/$2, NOT expanded by outer shell). Simulated probe with echo inner sees $1 $2 → inner bash prints 8.8.8.8 53, proving positional probe still correct.
  5. --dry-run/usage unaffected — neither references NET_PROBE; usage() unchanged by this fix. [DONE]

Step 3: Repo gates

  • bash -n bin/pos-network-download → OK.
  • make gen → ran; re-ran: idempotent (byte-identical diff before/after), i.e. no new output. Net-probe is an env var, not a # POS: header, so it feeds nothing.
  • make check (scripts/check-sync.sh) → OK.
  • make lint (scripts/lint-conventions.sh) → 0 FAIL, 0 WARN. [DONE]

Scope compliance

  • Approved scope: bin/pos-network-download ONLY (NET_PROBE lines + net_up).
  • Change made: exactly the NET_PROBE default string (line 31). net_up confirmed correct, no change required.
  • Out-of-scope changes: none made by Builder. (Working tree contains pre-existing unstaged changes from prior agents — not authored here.)
  • Not committed (per brief).

Remaining risks / follow-up

  • None for this fix. The env-var override string is operator-controlled; passing an invalid command there is the operator's responsibility (unchanged behavior).

Orchestrator — task is complete, targeted verification and all gates pass; no cross-track coordination needed.

Changes made by Builder

  • bin/pos-network-download: escaped $1/$2 to \$1/\$2 in the NET_PROBE default so the inner bash -c (not the outer shell) performs the probe-host positional expansion.