v0.5.0
Security-focused release covering 25 issues from a self-run audit, plus a redesign of the core verification/enforcement model that most of them shared a root cause with, plus ArgoCD/GitOps documentation for the gpg key backend.
⚠️ Upgrade notes / behavior changes
verifynow requires the key to be available. It authenticates every candidate ciphertext instead of trusting a magic-byte prefix, so it needs to actually decrypt. If your CI ranverifywithout key access before, it will now fail with exit code 2 instead of a false "OK" — this is intentional (see #1, #2).- Ambient
CI=1no longer bypasses hooks. Only the explicitSECRETIZE_SKIP_HOOKS=1does now. If your CI relied on the ambientCIvar to skip encryption/push-protection, setSECRETIZE_SKIP_HOOKS=1explicitly (#21). - kubectl-secret ciphertext is now bound to the full object identity (
apiVersion/kind/metadata.name/namespace), not just the file and key. Values encrypted with prior versions were sealed under the old, narrower AAD and will need to be re-encrypted withencrypt-valueunder this version (#23). kubectl secret encrypt-valuereads the plaintext from stdin by default now (echo -n VALUE | kubectl secret encrypt-value -f FILE -k KEY). Scripts passing it as a positional argument need--allow-argv(#5).k8s_secret_pathsmanifests are now enforced byverify/pre-commit. A stringData value that isn't ciphertext and isn't explicitly allowlisted via the newk8s_plaintext_keysconfig field will now be flagged — add it to the allowlist if it's intentionally plaintext (#15).
Verification & enforcement redesign
The core design change: verify/hooks used to check encryption status via a magic-byte prefix, only at HEAD, using working-tree config — a checkpoint with several independent ways around it. verify is now revision-pinned and authenticated; pre-push walks every commit in the pushed range via git's ref-update protocol, not just HEAD.
- #1 —
verifyaccepted fakeRENC-prefixed plaintext as encrypted - #2 —
verifyused mutable index/worktree config, could miss plaintext at HEAD - #3 —
pre-pushverified only HEAD, letting earlier leaked commits reach remotes - #4 — a committed raw
file-backend key wasn't detected byverify - #6 — git path parsing wasn't NUL-safe, letting quoted filenames bypass protection
- #15 —
k8s_secret_pathswere ignored by hooks andverify - #16 — a global config could silently disable repo-local encryption policy via
exclude - #20 — root-anchored patterns (
/secrets/**) validated but failed open - #21 — ambient
CIdisabled both encryption and push-protection hooks
Path, symlink, and key-lifecycle safety
- #7 —
key_sourceand explicit paths could escape the repository root - #8 — GPG recipients weren't validated as full fingerprints
- #9 —
unlockwrote plaintext secrets with world-readable0644mode - #10 —
removeusersaved the recipient removal before key rotation succeeded - #11 — global GPG recipients could be silently included in the committed
key.gpg - #12 —
addusercould grant a new GPG recipient even when the config save failed - #18 — matched symlinks could capture local files into encrypted commits
- #19 — a failed
rotate-keyscould leave an unignored raw staging key
kubectl-secret per-value crypto
- #5 —
encrypt-valueexposed plaintext through argv - #23 — Kubernetes value ciphertext wasn't bound to the Secret identity
- #24 — YAML aliases could copy decrypted
stringDatainto other fields - #25 — manifest-state verification gaps (undetected plaintext, argv leak, ArgoCD
selfHealrace) — all three sub-items fixed
Release/supply-chain hardening
- #13 — ArgoCD CMP docs now carry ArgoCD's own warning about manifest-generation secret injection
- #14 — release binaries are now checksummed (
.sha256assets below); the ArgoCD install snippet verifies beforechmod +x - #17 — the release workflow no longer interpolates the pushed tag name directly into shell (was shell-injectable)
- #22 — the ArgoCD CMP key-copy step no longer follows a repo-controlled symlink
Docs
New "Recommended: use the gpg backend instead of file" section for the ArgoCD integration — granting/revoking ArgoCD's access becomes the same adduser/removeuser commands used for teammates, and no raw key material ever needs to be copied into the repo checkout.
Every fix has a regression test. Full commit history: v0.4.1...v0.5.0