ac0ff8e2af
ci / build-test (push) Has been cancelled
- untrack CLAUDE.md (tailnet leak) + stale HANDOFF.md; keep local via .gitignore - add source-available LICENSE (attribution on fork, royalty on commercial use) - add lab/ reproduction kit (fake-cred LIVE-VM/mock rotation POCs) + lab/README - rewrite README to current status (22 drivers, 8 LIVE-VM/14 MOCK-ONLY/0 LIVE-REAL, 247 tests) and carry the credential-handling safety rules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
102 lines
5.7 KiB
Bash
Executable File
102 lines
5.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Live-VM POC for the KeePassXC password-manager adapter (Phase B `passwords` engine),
|
|
# inside the sandbox VM. FAKE logins only, throwaway .kdbx database.
|
|
#
|
|
# Flow proven here:
|
|
# 1. install the REAL keepassxc-cli
|
|
# 2. create a throwaway .kdbx (known master pass) + seed 2 login entries (weak pws)
|
|
# 3. incredigo passwords scan -> lists 2 logins + change links (no secrets)
|
|
# 4. incredigo passwords plan -> MANDATORY verified sealed backup + staged new
|
|
# strong passwords (.age) + secrets-free worklist
|
|
# 5. incredigo passwords commit -> in-place edit of BOTH entries via keepassxc-cli
|
|
# 6. ASSERT: both entry passwords changed, OLD gone; backup .age decrypts to OLD values
|
|
set -euo pipefail
|
|
export PATH=/usr/local/bin:$PATH
|
|
|
|
# Seal passphrase (age backup/stage) and the kdbx master — both env-driven => headless.
|
|
export INCREDIGO_PASSPHRASE='lab-seal-passphrase-001'
|
|
export INCREDIGO_KDBX_PASSPHRASE='db-master-pass-001'
|
|
|
|
DB="$HOME/lab.kdbx"
|
|
OLD1='weakpw-github-0001' # fake starting passwords
|
|
OLD2='weakpw-gitlab-0002'
|
|
WORK="$HOME/kp-work"; rm -rf "$WORK"; mkdir -p "$WORK"
|
|
STAGE="$WORK/stage.age"
|
|
BACKUP="$WORK/backup.age"
|
|
WORKLIST="$WORK/worklist.md"
|
|
AUDIT="$WORK/audit.jsonl"
|
|
|
|
echo "== 0. install keepassxc-cli (real tool) =="
|
|
if ! command -v keepassxc-cli >/dev/null 2>&1; then
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get update -qq
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq keepassxc >/dev/null 2>&1 || \
|
|
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq keepassxc-cli >/dev/null 2>&1
|
|
fi
|
|
keepassxc-cli --version | sed 's/^/ keepassxc-cli /'
|
|
|
|
echo "== 1. throwaway .kdbx + 2 fake login entries =="
|
|
rm -f "$DB"
|
|
# db-create: stdin carries master pass then its confirmation.
|
|
printf '%s\n%s\n' "$INCREDIGO_KDBX_PASSPHRASE" "$INCREDIGO_KDBX_PASSPHRASE" \
|
|
| keepassxc-cli db-create -p "$DB" >/dev/null 2>&1
|
|
# add -p: stdin carries the unlock pass then the new entry password.
|
|
printf '%s\n%s\n' "$INCREDIGO_KDBX_PASSPHRASE" "$OLD1" \
|
|
| keepassxc-cli add --username 'alice@example.com' --url 'https://github.com/' -p "$DB" 'GitHub' >/dev/null 2>&1
|
|
printf '%s\n%s\n' "$INCREDIGO_KDBX_PASSPHRASE" "$OLD2" \
|
|
| keepassxc-cli add --username 'bob@example.com' --url 'https://gitlab.com/' -p "$DB" 'GitLab' >/dev/null 2>&1
|
|
echo " entries in db:"
|
|
printf '%s\n' "$INCREDIGO_KDBX_PASSPHRASE" | keepassxc-cli ls "$DB" 2>/dev/null | sed 's/^/ /'
|
|
|
|
echo "== 2. passwords scan (no secrets, commits nothing) =="
|
|
incredigo passwords --manager keepassxc --kdbx "$DB" scan --audit-log "$AUDIT" 2>&1 | sed 's/^/ /'
|
|
|
|
echo "== 3. passwords plan (verified sealed backup + staged new pws + worklist) =="
|
|
incredigo passwords --manager keepassxc --kdbx "$DB" plan \
|
|
--backup-out "$BACKUP" --stage-out "$STAGE" --worklist-out "$WORKLIST" \
|
|
--audit-log "$AUDIT" 2>&1 | sed 's/^/ /'
|
|
echo " --- worklist (must be secrets-free) ---"
|
|
sed 's/^/ /' "$WORKLIST"
|
|
|
|
echo "== 4. ASSERT staged + backup bundles are NOT plaintext =="
|
|
fail=0
|
|
for blob in "$STAGE" "$BACKUP"; do
|
|
if grep -qaF "$OLD1" "$blob" || grep -qaF "$OLD2" "$blob"; then
|
|
echo " FAIL: plaintext password found in $blob"; fail=1
|
|
else
|
|
echo " OK: $(basename "$blob") carries no plaintext old password"
|
|
fi
|
|
done
|
|
|
|
echo "== 5. passwords commit --verified all (in-place edit of both entries) =="
|
|
incredigo passwords --manager keepassxc --kdbx "$DB" commit \
|
|
--stage-in "$STAGE" --verified all --backup-out "$WORK/backup2.age" \
|
|
--audit-log "$AUDIT" 2>&1 | sed 's/^/ /'
|
|
|
|
echo "== 6. CUTOVER ASSERTIONS (re-export the live db) =="
|
|
CSV=$(printf '%s\n' "$INCREDIGO_KDBX_PASSPHRASE" | keepassxc-cli export -f csv "$DB" 2>/dev/null)
|
|
NEW1=$(printf '%s' "$CSV" | awk -F',' 'NR>1 && /GitHub/ {gsub(/"/,"",$4); print $4; exit}')
|
|
NEW2=$(printf '%s' "$CSV" | awk -F',' 'NR>1 && /GitLab/ {gsub(/"/,"",$4); print $4; exit}')
|
|
echo " GitHub new pw len=${#NEW1} GitLab new pw len=${#NEW2}"
|
|
|
|
[ -n "$NEW1" ] && [ "$NEW1" != "$OLD1" ] && echo " OK: GitHub password changed" || { echo " FAIL: GitHub unchanged"; fail=1; }
|
|
[ -n "$NEW2" ] && [ "$NEW2" != "$OLD2" ] && echo " OK: GitLab password changed" || { echo " FAIL: GitLab unchanged"; fail=1; }
|
|
printf '%s' "$CSV" | grep -qaF "$OLD1" && { echo " FAIL: GitHub OLD pw still present"; fail=1; } || echo " OK: GitHub OLD pw gone"
|
|
printf '%s' "$CSV" | grep -qaF "$OLD2" && { echo " FAIL: GitLab OLD pw still present"; fail=1; } || echo " OK: GitLab OLD pw gone"
|
|
# strong (length 20 default)
|
|
[ "${#NEW1}" -ge 16 ] && echo " OK: GitHub new pw is strong (len ${#NEW1})" || { echo " FAIL: GitHub new pw too short"; fail=1; }
|
|
|
|
echo "== 7. RECOVERABILITY: restore OLD passwords from the sealed backup =="
|
|
# The plan-stage backup.age is itself a staged list whose Secret = the OLD password.
|
|
# Feeding it back through `commit --stage-in` proves the backup is a usable restore
|
|
# artifact: it should re-write both entries back to their original values.
|
|
incredigo passwords --manager keepassxc --kdbx "$DB" commit \
|
|
--stage-in "$BACKUP" --verified all --backup-out "$WORK/backup3.age" \
|
|
--audit-log "$AUDIT" 2>&1 | sed 's/^/ /'
|
|
CSV2=$(printf '%s\n' "$INCREDIGO_KDBX_PASSPHRASE" | keepassxc-cli export -f csv "$DB" 2>/dev/null)
|
|
printf '%s' "$CSV2" | grep -qaF "$OLD1" && echo " OK: GitHub OLD pw restored from backup" || { echo " FAIL: GitHub not restored"; fail=1; }
|
|
printf '%s' "$CSV2" | grep -qaF "$OLD2" && echo " OK: GitLab OLD pw restored from backup" || { echo " FAIL: GitLab not restored"; fail=1; }
|
|
printf '%s' "$CSV2" | grep -qaF "$NEW1" && { echo " FAIL: post-restore still has NEW pw"; fail=1; } || echo " OK: NEW pw gone after restore"
|
|
|
|
echo
|
|
if [ "$fail" = 0 ]; then echo "ALL KEEPASSXC CUTOVER ASSERTIONS PASSED"; else echo "SOME ASSERTIONS FAILED"; exit 1; fi
|