|
1 | 1 | # Changelog |
2 | 2 |
|
| 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 | + |
3 | 101 | ## 2.16.0 — 2026-08-05 |
4 | 102 |
|
5 | 103 | - **Taboo guard: a key is reachable through the |
|
0 commit comments