fix: pre-landing review fixes (heal ordering, key untrack, content-aware gitignore)

Red-team + specialist findings from /ship pre-landing review:

- CRITICAL: ensure_store_protections ran BEFORE `git pull --ff-only` in
  commit_and_push_secrets; a pull bringing history without .gitignore
  left the post-pull window unprotected and `git add -A` committed AND
  pushed key.txt (reproduced end-to-end). Heal now runs after the pull,
  immediately before staging.
- CRITICAL: .gitignore can't untrack an already-tracked key.txt. Heal
  now defensively `git rm --cached`s a tracked key with a history-scrub
  warning.
- Content-aware heal: a present .gitignore missing the key.txt line is
  rewritten, not skipped (security specialist).
- Init guard now renders the real sanitized clone URL from
  .secrets-store when available, mirroring EGB-282's check_initialized.
- Tests: positive heal-message assertions, non-empty key guard in the
  init test, 4 new regression tests (126 total).
This commit is contained in:
Brian Majewski 2026-06-05 10:14:46 -07:00
parent 5865c40d77
commit 2a7afc34dd
3 changed files with 82 additions and 7 deletions

27
secrets
View file

@ -724,10 +724,20 @@ EOF
# pre-commit hook (hooks aren't cloned), and a half-initialized store may
# lack .gitignore — without it, `git add -A` would commit key.txt.
ensure_store_protections() {
if [ ! -f "$SECRETS_DIR/.gitignore" ]; then
# Content-aware: a present-but-corrupted .gitignore missing the key.txt
# line is just as dangerous as a missing one.
if [ ! -f "$SECRETS_DIR/.gitignore" ] || ! grep -qx 'key.txt' "$SECRETS_DIR/.gitignore"; then
write_store_gitignore
info "Restored store .gitignore"
fi
# .gitignore can't untrack an already-tracked key (legacy damage, or a
# past window where .gitignore was missing). Remove it from the index so
# the next commit drops it from the tip.
if git -C "$SECRETS_DIR" ls-files --error-unmatch key.txt >/dev/null 2>&1; then
git -C "$SECRETS_DIR" rm --cached --quiet key.txt
echo "WARNING: key.txt was tracked in the store repo — untracked it now." >&2
echo "It may still exist in git history; consider 'secrets rekey' and scrubbing history." >&2
fi
if [ ! -x "$SECRETS_DIR/.git/hooks/pre-commit" ]; then
mkdir -p "$SECRETS_DIR/.git/hooks"
install_hook
@ -750,10 +760,14 @@ cmd_init() {
# should clone their existing secrets repo, not init a fresh one.
# Catch it BEFORE git init so we don't leave a half-initialized store.
if [ -f "$KEY_FILE" ]; then
# Render a runnable clone command when .secrets-store carried a remote
# URL (already sanitized by resolve_store), mirroring check_initialized.
local clone_src="<your-secrets-remote>"
[ -n "${_REMOTE_URL:-}" ] && clone_src="$_REMOTE_URL"
die "Found an existing key at $KEY_FILE but no repo at $SECRETS_DIR.
If this is a second machine, don't run 'secrets init' — clone your existing secrets repo instead:
git clone <your-secrets-remote> $SECRETS_DIR
git clone $clone_src $SECRETS_DIR
Your key file has been left untouched."
fi
@ -814,16 +828,17 @@ push_dir_to_project() {
commit_and_push_secrets() {
local message="$1"
# Must run before `git add -A`: a store missing its .gitignore would
# otherwise stage and push key.txt.
ensure_store_protections
if git -C "$SECRETS_DIR" remote get-url origin >/dev/null 2>&1; then
if ! git -C "$SECRETS_DIR" pull --ff-only 2>/dev/null; then
die "Fast-forward pull failed. Run 'secrets pull' first, then retry push."
fi
fi
# Must run AFTER the pull and immediately before `git add -A`: the pull
# can remove or alter .gitignore (remote history that lacks it), and a
# store missing the key.txt line would stage and push the private key.
ensure_store_protections
git -C "$SECRETS_DIR" add -A
if git -C "$SECRETS_DIR" diff --cached --quiet 2>/dev/null; then
info "No changes to push (secrets unchanged)"