Files
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

108 lines
4.5 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
# t-gpg-password.sh — backup encryption passphrase handling (D3):
# - passphrase fed to gpg on fd 3 (here-string), NEVER as argv token,
# - plaintext archive removed after successful encryption; only .gpg remains,
# - encryption failure: plaintext removed, rc != 0, nothing left behind,
# - verification failure: corrupt .gpg removed, rc != 0,
# - end-to-end success path. gpg/sudo stubbed; tar real; stdin piped (no tty).
run_test() {
require_cmd timeout "gpg password" || return 0
command -v tar >/dev/null 2>&1 || { skip_case "gpg password" "tar not available"; return 0; }
local sandbox stubs work data
sandbox="$(mksandbox gpg-password)"
stubs="$sandbox/stubs"
work="$sandbox/work"
data="$sandbox/data/My Data"
mkdir -p "$stubs" "$work" "$data"
printf 'important file content\n' > "$data/notes.txt"
local gpg_log="$sandbox/gpg.log"
: > "$gpg_log"
printf 'canned\n' > "$sandbox/canned.txt"
tar -czf "$sandbox/fake.tar.gz" -C "$sandbox" canned.txt || \
{ skip_case "gpg password" "cannot create canned tar fixture"; return 0; }
# stub gpg: log every argv token (one per line), simulate failures via
# STUB_GPG_FAIL, decrypt phase emits canned tar.gz stream
cat > "$stubs/gpg" <<STUB
#!/usr/bin/env bash
printf '%s\n' "\$@" >> "$gpg_log"
input=""
for a in "\$@"; do
case "\$a" in
-*) ;;
*) input="\$a" ;;
esac
done
case "\$*" in
*--symmetric*)
if [ "\${STUB_GPG_FAIL:-}" = "encrypt" ]; then exit 1; fi
cp "\$input" "\$input.gpg"
;;
*--decrypt*)
if [ "\${STUB_GPG_FAIL:-}" = "verify" ]; then exit 1; fi
cat "$sandbox/fake.tar.gz"
;;
*) ;;
esac
STUB
printf '#!/usr/bin/env bash\nexec "$@"\n' > "$stubs/sudo"
chmod +x "$stubs/gpg" "$stubs/sudo"
local backup="$ROOT/bin/pos-system-backup"
local common_env=(PATH="$stubs:/usr/bin:/bin" NOTIFY_PLATFORM=""
BACKUP_USB_ROOT="$sandbox/usbroot")
# 1. success path
( cd "$work" && printf 's3cr3t-pass\ns3cr3t-pass\n' \
| timeout 30 env "${common_env[@]}" "$backup" "$data" >"$sandbox/run1.out" 2>&1 )
local rc1=$?
check_rc "backup success exits 0" 0 "$rc1"
check_file_exists "encrypted artifact created (.tar.gz.gpg)" "$work"/*.tar.gz.gpg
check_eq "plaintext archive removed after encryption" 0 \
"$(find "$work" -maxdepth 1 -name '*.tar.gz' | wc -l)"
check_contains "success logs completion" "Backup completed" "$(cat "$sandbox/run1.out")"
check_contains "gpg invoked with --passphrase-fd" "--passphrase-fd" "$(cat "$gpg_log")"
check_eq "gpg passphrase-fd used twice (encrypt+decrypt)" 2 \
"$(count_token --passphrase-fd "$(cat "$gpg_log")")"
# the security property: no bare --passphrase argv token (check token-exact,
# because --passphrase-fd legitimately CONTAINS the substring)
local bare
bare="$(grep -c '^--passphrase$' "$gpg_log" || true)"
check_eq "never a bare --passphrase argv token" 0 "$bare"
check_not_contains "secret never appears in gpg argv" "s3cr3t-pass" "$(cat "$gpg_log")"
# 2. encryption failure → plaintext removed, rc != 0, nothing left
rm -rf "$work"; mkdir -p "$work"
: > "$gpg_log"
( cd "$work" && printf 's3cr3t-pass\ns3cr3t-pass\n' \
| timeout 30 env "${common_env[@]}" STUB_GPG_FAIL=encrypt "$backup" "$data" >"$sandbox/run2.out" 2>&1 )
local rc2=$?
check_contains "encrypt-failure reports cleanup" "plaintext archive removed" "$(cat "$sandbox/run2.out")"
if [ "$rc2" -ne 0 ]; then
printf ' PASS encryption failure exits nonzero\n'
else
printf ' FAIL encryption failure exited 0\n'
fi
check_eq "nothing left behind after encrypt failure" 0 \
"$(find "$work" -maxdepth 1 \( -name '*.tar.gz' -o -name '*.tar.gz.gpg' \) | wc -l)"
# 3. verification failure → corrupt .gpg removed, rc != 0, nothing left
rm -rf "$work"; mkdir -p "$work"
: > "$gpg_log"
( cd "$work" && printf 's3cr3t-pass\ns3cr3t-pass\n' \
| timeout 30 env "${common_env[@]}" STUB_GPG_FAIL=verify "$backup" "$data" >"$sandbox/run3.out" 2>&1 )
local rc3=$?
check_contains "verify-failure reports cleanup" "corrupt artifact removed" "$(cat "$sandbox/run3.out")"
if [ "$rc3" -ne 0 ]; then
printf ' PASS verification failure exits nonzero\n'
else
printf ' FAIL verification failure exited 0\n'
fi
check_eq "nothing left behind after verify failure" 0 \
"$(find "$work" -maxdepth 1 \( -name '*.tar.gz' -o -name '*.tar.gz.gpg' \) | wc -l)"
}