Releases: heyvaldemar/sops-env-git
Release list
v1.1.0
Every content pattern in this hook looks for the shape of a credential in text. A PKCS#12 keystore is a binary blob. Traefik's acme.json holds its ACME account private key as base64 inside JSON. Neither of them looks like anything.
On the machine these rules come from, an acme.json went past a content scan in a commit where the same scan correctly rejected an ordinary DB_PASSWORD= line. The key was in the commit, the scanner had nothing to match, and the name on the file was the only warning that existed.
So acme.json, *.p12, *.pfx, *.jks, *.keystore, *.kdbx, *.key and *.ppk are now refused by name, whatever is inside them.
The list stops at credential containers on purpose. *.db and *.sqlite are secrets on a configuration host and ordinary fixtures in a normal repository, and a rule that fires on both is a rule somebody switches off — which is the history recorded at the top of the hook itself. The README says how to add them to a copy that needs them.
Thirteen more assertions, in both directions: each container shape is refused, and prose that merely names one is not. The rules match an extension rather than a substring, so docs/p12-notes.md and docs/keystore.md still commit. A hook that rejects its own documentation is a hook somebody uninstalls. Thirty-two assertions in all.
Upgrading: copy the hook again — install -m 755 hooks/pre-commit .git/hooks/pre-commit. Nothing else changed.
v1.0.0
Added
- A sync that compares values, not bytes. sops drops blank lines, so a
byte-for-byte round trip reports every file as differing forever, and an
alarm that always fires is one nobody reads.--checkreports drift by
variable name and never prints a value. - Every call passes
--input-type dotenv --output-type dotenv. sops picks
its parser from the extension, does not know.sops, falls back to JSON and
dies on the first comment line. - A pre-commit hook that rejects by filename before reading a byte, then by
a narrow set of credential shapes. A.gitignorewith a negation beside an
exclusion is exactly wheregit add -fslips a plaintext file through, and a
filename cannot be argued with. - Nineteen scenarios for the hook, in both directions. Twelve real
credential shapes it must reject, seven benign ones it must accept —
including asudoersNOPASSWD line and an ordinary https URL, which are the
false positives that got the previous version widened until it caught
nothing. - Eight scenarios for the sync, against real sops and age.
Notes
- Comments are not encrypted, on purpose: it is what makes a diff readable. It
also means a token pasted into one goes to the remote in the clear, so the
hook scans comments too. - Each machine needs its own key. One key across two hosts means compromising
either opens the secrets of both.
Found while writing the tests
- A pattern beginning with a dash was never matched.
grep -E "$p"takes
-----BEGIN ... PRIVATE KEY-----for an option. It needs-e, and without
it the rule was invisible in a green run. - The token length rule was too strict to fire on anything but an exact
length. - GitHub's own push protection refused the first push, because a suite that
tests a secret detector necessarily contains strings shaped exactly like
secrets. The fixtures are now assembled at runtime from pieces: the hook
still receives the complete credential and still has to catch it, and the
repository contains no literal that looks like one. Allowlisting them would
have been one more exception in a project whose argument is that exceptions
are where detectors go to die. - The drift report named no keys on busybox. It parsed
diffoutput, whose
format varies between implementations; it now compares key by key and cannot
print an empty list while looking like it worked.