key-gaps: the key regex was blind to digits, inventing missing secrets #69

Merged
admin_emi merged 2 commits from fix/key-gaps-digit-regex into dev 2026-07-24 22:59:13 +00:00
3 changed files with 17 additions and 3 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Claude Code worktrees. These are other sessions' checkouts living inside this
# repo; `git add -A` will otherwise commit them as embedded git repositories.
.claude/worktrees/

View file

@ -34,8 +34,18 @@ Gather the key names over SSH (never the values), then audit. Hosts/keys per INF
K=~/.ssh/thermograph_agent_ed25519
# sudo: /etc/thermograph.env is root-owned (0640). On prod `agent` can read it
# directly too, but sudo works uniformly on both boxes.
ssh -i $K agent@169.58.46.181 'sudo grep -oE "^[A-Z_]+=" /etc/thermograph.env' > /tmp/prod.keys # prod
ssh -i $K agent@75.119.132.91 'sudo grep -oE "^[A-Z_]+=" /etc/thermograph.env' > /tmp/beta.keys # beta
#
# The character class must allow DIGITS. This was `^[A-Z_]+=` until 2026-07-24,
# which silently skipped every key whose name contains a digit — in this estate
# that is all six *_S3_* keys. Prod audited as 26 keys against 32 real ones, and
# the audit reported the S3 credentials as missing from both hosts. They were
# present the whole time, and the phantom was independently "confirmed" twice by
# people re-running the same pattern. An audit that under-reports is worse than
# no audit: the invented finding sends someone to provision a credential that
# already exists, and it was briefly recorded as a root cause of a real bug.
# The match still stops at the `=`, so no value is ever read.
ssh -i $K agent@169.58.46.181 'sudo grep -oE "^[A-Z][A-Z0-9_]*=" /etc/thermograph.env' > /tmp/prod.keys # prod
ssh -i $K agent@75.119.132.91 'sudo grep -oE "^[A-Z][A-Z0-9_]*=" /etc/thermograph.env' > /tmp/beta.keys # beta
python3 .claude/skills/key-gaps/key_gaps.py prod=/tmp/prod.keys beta=/tmp/beta.keys
```

View file

@ -17,7 +17,7 @@ It reads only KEY NAMES, never values — safe to run anywhere. Sources per envi
* a SOPS-encrypted YAML (deploy/secrets/<env>.yaml) keys are plaintext even when
encrypted, so no age key or decryption is needed; or
* a plain key-list file (one KEY per line, or KEY=... lines) e.g. the output of
`ssh <box> 'grep -oE "^[A-Z_]+=" /etc/thermograph.env'` for a live audit.
`ssh <box> 'grep -oE "^[A-Z][A-Z0-9_]*=" /etc/thermograph.env'` for a live audit.
Usage:
key_gaps.py prod=deploy/secrets/prod.yaml beta=deploy/secrets/beta.yaml