Skip to content

Commit 9ac7aa0

Browse files
committed
Bump to 2.17.0
Four rule changes, all the same shape: a check or a habit that looked fine on the successful path and lied on the failing one. A credential probe that crashes prints the credential. An empty `log show` is indistinguishable from a broken one once stderr goes to /dev/null. A backup check that finds the newest file anywhere cannot see a dead monthly tier behind fresh dailies. A cleanup glob that no longer matches deletes nothing and exits 0. In each case the failure mode reads as a pass, which is why they survived this long. Release notes in CHANGELOG.md; the reasoning per change is in the four preceding commits. An AI agent wrote this text in my name, unreviewed by me. The work behind it is mine; I only delegated the writing.
1 parent f625ceb commit 9ac7aa0

2 files changed

Lines changed: 99 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,103 @@
11
# Changelog
22

3+
## 2.17.0 — 2026-08-06
4+
5+
- **Testing a credential must not print it.** A new
6+
section in `rules/secrets.md`: keeping a password
7+
out of `argv` protects the successful run and does
8+
nothing about the crash, because a crash prints the
9+
arguments of the failing call — and for a database
10+
driver those arguments *are* the credentials. A
11+
`mix run --no-start -e "Postgrex.start_link(…)"`
12+
probe left `:db_connection` unstarted, the
13+
supervisor call failed, and the Erlang crash report
14+
echoed `password: "…"` in clear text into the
15+
session transcript, forcing rotation of a password
16+
created two minutes earlier. Python tracebacks with
17+
locals, Ruby exception inspection and Node stack
18+
traces over an options object all do the same.
19+
Prefer a client whose errors are quiet (`psql` says
20+
"authentication failed" and no more); reach for the
21+
app's own driver only when the driver is what must
22+
be proven. When you do, send all of its output to a
23+
root-only file and read back a single `PROBE_OK` /
24+
`PROBE_FAIL` marker the probe emits itself. Keep
25+
that scratch file under `/root`, not `/tmp`, or
26+
`fs.protected_regular` (default in Debian 13) makes
27+
the check silently not run. No marker back means
28+
the check did not run, never that it passed.
29+
- **Rotate a secret heinzel just minted without
30+
asking.** Rotation normally needs approval because
31+
it breaks every consumer of the old value. When
32+
heinzel created the credential minutes ago and the
33+
only copies are files heinzel wrote, that blast
34+
radius is zero while the cost of waiting is a live
35+
exposed secret: rotate at once, then say so and
36+
why. Deliberately does not extend to credentials
37+
heinzel did not create this session, or to ones
38+
that already have consumers.
39+
- **The macOS activity check now fails closed.** It
40+
piped `log show` through `2>/dev/null`, and the rule
41+
treated "fails or returns nothing" as one case. `log`
42+
is a common shell alias or function, and a shadowed
43+
one errors with something like
44+
`(eval):log:1: too many arguments` — swallowed by
45+
`/dev/null`, that empty stream read as a clean host,
46+
so a concurrent session's work went unreported
47+
exactly because the check broke. Now calls
48+
`/usr/bin/log` by absolute path, keeps `2>&1`, and
49+
states that an empty result only means "no activity"
50+
when the command actually ran; anything else is
51+
reported as a check that did not run. Predicate
52+
narrowed to `process == "logger"`.
53+
- **Housekeeping ages every backup retention tier
54+
separately.** The autopostgresqlbackup check took the
55+
newest file anywhere under the backup directory,
56+
which stays green as long as the daily tier runs, so
57+
a dead weekly or monthly tier hid behind fresh
58+
dailies — on one host the monthly tier had been dead
59+
for five months. Each tier now has its own
60+
thresholds (25h/48h daily, 8d/15d weekly, 32d/62d
61+
monthly, empty = critical), and `BACKUPDIR` is read
62+
from the config instead of a hardcoded
63+
`/var/backups/postgresql`, which was never the
64+
default. Note that the script prefers
65+
`/etc/default/autopostgresqlbackup` and then ignores
66+
`/etc/autopostgresqlbackup.conf`.
67+
- **Known trap: unpadded `DOMONTHLY` silently kills
68+
monthly backups.** autopostgresqlbackup 2.x compares
69+
strings, `[ "${DNOM}" = "${DOMONTHLY}" ]`, and
70+
`date '+%d'` is zero-padded, so `DOMONTHLY=1` never
71+
matches `01`. Values 10-31 match by accident and the
72+
weekly gate is unaffected (`date '+%u'` is unpadded),
73+
which is why it goes unnoticed: no error, no mail
74+
under `REPORT_ERRORS_ONLY="yes"`, daily and weekly
75+
keep working. Debian 12→13 carries the tool from 1.1
76+
to 2.5 and 1.x tolerated the unpadded value, so
77+
upgraded hosts are the likely victims. Housekeeping
78+
now flags any unpadded 1-9 value on a 2.x host even
79+
when the tier looks current.
80+
- **New rule: renaming, moving, retention changes**
81+
(`rules/file-naming-changes.md`). Rotation schemes,
82+
backup suffixes and directory moves change strings
83+
that other code matches on. The dangerous case is a
84+
deletion that no longer matches: the cleanup command
85+
exits 0 having deleted nothing, so the data survives
86+
under the longer retention just configured — a script
87+
written for numbered rotations
88+
(`rm -f "$LOG" "$LOG".*`) stops covering date-stamped
89+
ones the moment `dateext` flips a dot to a hyphen.
90+
Grep the host for consumers and widen their patterns
91+
in the same change; dry-run bulk renames with a
92+
collision count and refuse to proceed while any
93+
collision remains; derive names from file content,
94+
because mtime sits either side of the rotation window
95+
on sparsely written files. Also state what the new
96+
retention costs — measured volume, file count per
97+
directory, and how much longer access logs keep
98+
client IP addresses, which is a data protection
99+
question, not a disk space one.
100+
3101
## 2.16.0 — 2026-08-05
4102

5103
- **Taboo guard: a key is reachable through the

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.16.0
1+
2.17.0

0 commit comments

Comments
 (0)