fix: stabilization pass — fail-closed auth, ai flag validation, lint/config/security hardening, regression tests
gates / consistency-and-conventions (push) Successful in 26s

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.
This commit is contained in:
Your Name
2026-09-06 07:25:44 -04:00
parent 528b16676e
commit d817c37652
69 changed files with 5161 additions and 406 deletions
+28
View File
@@ -25,6 +25,9 @@ attaches the latest pos command output or captured output (tail, max 4096 chars)
to the question and notes on stderr which source was attached, its age, and a
staleness warning once it is older than an hour (`ask` only; stdout stays pure
answer). Use `capture` to save output from any command for `--last`.
`--trust` and `--no-command-execution` control how (if at all) agent-detected
command blocks run — see [Command execution posture](#command-execution-posture)
below.
Backward compatibility: `pos ai gemini`, `pos ai openrouter`, and
`pos ai llamacpp` still work as shorthand for `pos ai --provider gemini`,
@@ -245,6 +248,31 @@ truncated). To disable: `unset __POS_CAPTURE_ACTIVE`.
- On a non-2xx response the API's `error.message` is shown and the exit code is
non-zero — so scripts can rely on `ask` failing loudly.
## Command execution posture
When a model's answer contains a ```sh/shell fenced code block, `ask`/`chat`
offer to **run** it as a shell command on your machine. Because that code is
untrusted, AI-generated external authority, the default is to **deny**:
- On an interactive terminal, `ask`/`chat` print the detected command and prompt
`Run this command? [y/N]` — you must type **`y`** (or `Y`) to execute it.
Enter or any other key **declines**: the command is added to your shell
history (press ↑ to recall and edit it) but never run.
- Without an interactive controlling tty (pipes, scripts, cron, the Telegram /
Matrix bridges) commands are **never** executed — the code block is neither
printed nor run.
`--trust` auto-executes detected commands **without** the confirmation prompt —
but only on an interactive terminal. It is meant for trusted alias wrappers;
do not pass it unless you fully trust the agent's output. It has no effect in a
non-tty/chat-bridge context, which never executes commands anyway.
`--no-command-execution` disables execution entirely: the detected command is
neither printed nor run and no confirmation prompt appears. It is the
structural guard used by the chat bridges so that a future refactor cannot
accidentally auto-execute model output. When both `--trust` and
`--no-command-execution` are given, the last one on the command line wins.
## Troubleshooting
- `ask` errors "No Gemini API key — run 'pos config ai'" → the key isn't set
+24 -8
View File
@@ -24,14 +24,23 @@ health digests, backup alerts, firewall changes — and can be used directly.
```bash
pos config telegram
# edit TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID (masked input), then test:
# edit TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID and TELEGRAM_OWNER_ID (masked input), then test:
pos communication telegram sender test
# config lives in ~/.config/linux_post_install/telegram.env (chmod 600)
```
The bot token comes from @BotFather, the chat ID from @userinfobot (or by
starting a chat and reading it). `pos config telegram` shows the current values
(token masked).
The bot token comes from @BotFather; the chat ID **and your numeric user ID
(`TELEGRAM_OWNER_ID`)** from @userinfobot (or by starting a chat and reading
it). `pos config telegram` shows the current values (token masked). The
listener only executes commands when both the chat id and your owner id match
— with `TELEGRAM_OWNER_ID` unset it runs but ignores every command
(fail-closed, see below).
> **Token in the process list:** the Telegram Bot API embeds the bot token in
> every API URL (`https://api.telegram.org/bot<TOKEN>/…`), so while a send or
> poll runs the token is visible in `ps` output. This is inherent to the Bot
> API — keep the token scoped to a single-purpose bot and revoke it in
> BotFather if it ever leaks.
### Send
@@ -92,8 +101,13 @@ pos communication telegram listener --disable # remove it
falls back to the bash command. Telegram only registers lowercase `[a-z0-9_]`
names (132 chars); `/Status` or `/my-cmd` are skipped from the menu but still
work when typed. An empty map clears the menu.
- **Owner-only:** the bot only reacts to `TELEGRAM_CHAT_ID` (your own chat);
others are ignored. `/help` lists mapped commands; unknown → "Unknown command".
- **Owner-only (chat + account):** the bot executes only messages sent to
`TELEGRAM_CHAT_ID` (your chat) **BY** `TELEGRAM_OWNER_ID` (your account) —
both must match, so a forwarded message or an impersonator can't trigger
commands. With `TELEGRAM_OWNER_ID` unset the daemon runs but ignores every
command (fail-closed; `systemctl --user status pos-telegram-listener.service`
shows the message-time log lines). `/help` lists mapped commands; unknown →
"Unknown command".
- **Runs as you:** mapped commands execute as your user with a 60s timeout,
stdout + stderr are replied to the chat (truncated ~3800 chars; empty → `OK`).
`sudo` inside a command needs a NOPASSWD rule.
@@ -220,8 +234,10 @@ pos communication matrix listener --disable # remove it
```
- **Self-messaging:** the listener reacts to messages **from your own user id**
(`MATRIX_USER_ID`) — in practice that means a second device (or another
account) sending the commands. If `MATRIX_ROOM_ID` is set it only watches
that room, otherwise every room you've joined. `/` and `!` both work
account) sending the commands. It only watches the room set by
`MATRIX_ROOM_ID`; with `MATRIX_ROOM_ID` unset the daemon runs but refuses to
execute any command (fail-closed), so a bot account that has joined many
rooms can't be tricked into running commands. `/` and `!` both work
(`!status` = `/status`). `/help` lists mapped commands; unknown → "Unknown
command".
- **Runs as you:** mapped commands execute as your user with a 60s timeout,