Fix .gitignore swallowing references/findings-format.md

The findings-*.md rule (for generated timestamped reports) also matched the
findings-format.md reference doc, so it was never tracked. Scope the rule to
digit-prefixed names (findings-[0-9]*) and add the missing reference.
This commit is contained in:
Travis Herbranson 2026-05-23 06:50:37 -04:00
parent 31c67b9573
commit 249055fe99
2 changed files with 91 additions and 4 deletions

10
.gitignore vendored
View File

@ -10,10 +10,12 @@ __pycache__/
venv/ venv/
env/ env/
# Generated audit reports (timestamped runs are not source) # Generated audit reports (timestamped runs are not source).
findings-*.md # Scoped to digit-prefixed names so the references/findings-format.md
findings-*.json # doc is NOT swallowed.
findings-*.txt findings-[0-9]*.md
findings-[0-9]*.json
findings-[0-9]*.txt
# Environment / secrets # Environment / secrets
.env .env

View File

@ -0,0 +1,85 @@
# Findings output format
Three renderers, one finding model. Pick the format for the consumer:
- `text` — terminal triage. Summary line, full `INVENTORY` (every repo, clean
ones included), then findings grouped by category.
- `md` — phase-mapped markdown. Each finding is a `- [ ]` checkbox under the
remediation-plan phase heading it belongs to, so a run pastes straight into
the project plan as actionable items.
- `json` — machine-readable for week-over-week diffing and automation.
## Finding model
Every finding is one record:
| Field | Meaning |
|-------|---------|
| `domain` | `homelab` / `pbs` / `docker` (the Obsidian vault is out of scope by default — see check-catalog.md) |
| `repo` | Directory name within the domain |
| `path` | Absolute path |
| `category` | `SECURITY` / `AUDITABILITY` / `CLEANUP` / `GIT_STATE` / `DOCS` |
| `severity` | `high` / `medium` / `low` / `info` |
| `check` | Stable check id (e.g. `no-git`, `env-world-readable:.env`) |
| `detail` | Human-readable description of what was found |
| `hint` | Suggested remediation (informational — the skill does not apply it) |
| `key` | `domain/repo:check` — stable identifier for dedup/diffing (JSON only) |
The `key` is deliberately stable across runs: a finding that persists keeps the
same key, so diffing two JSON runs cleanly separates *new drift* (keys that
appeared) from *remediated items* (keys that disappeared).
## Category → phase mapping
This is how findings line up with the repo-hygiene-remediation plan, so the
recurring review feeds the same backlog structure the original audit used:
| Category | Plan phase |
|----------|-----------|
| `SECURITY` | Phase 2 — Security remediation |
| `AUDITABILITY` | Phase 3 — Unblock un-auditable repos |
| `CLEANUP` | Phase 4 — Cross-repo cleanups |
| `GIT_STATE` | Git state (review before commit/merge) |
| `DOCS` | Docs (audit follow-up) |
## JSON shape
```json
{
"generated": "2026-05-22T23:05:00",
"host": "herbys-dev",
"repos_scanned": 39,
"summary": {
"by_severity": {"high": 11, "medium": 19, "low": 23, "info": 9},
"by_category": {"SECURITY": 5, "AUDITABILITY": 9, "CLEANUP": 8,
"GIT_STATE": 27, "DOCS": 13}
},
"inventory": [
{"domain": "docker", "repo": "authelia", "path": "...",
"is_git": false, "owner": "root", "git_owner": "", "group": "root",
"branch": "", "finding_count": 2}
],
"findings": [
{"domain": "docker", "repo": "authelia", "path": "...",
"category": "AUDITABILITY", "severity": "high", "check": "no-git",
"detail": "directory holds real content but has no .git",
"hint": "git init + initial commit ...", "key": "docker/authelia:no-git"}
]
}
```
## Diffing two runs
```bash
python scripts/audit.py --format json -o this-week.json
# ... a week later ...
python scripts/audit.py --format json -o next-week.json
python3 - <<'PY'
import json
a = {f["key"] for f in json.load(open("this-week.json"))["findings"]}
b = {f["key"] for f in json.load(open("next-week.json"))["findings"]}
print("NEW drift :", sorted(b - a))
print("REMEDIATED :", sorted(a - b))
PY
```