docs: slices 2 to 4 for #0106, and the handover checklist

The four rules, the derived target set and the deletion of orphaned reports are
built and checked as far as they can be without the host. What remains is a
deploy, and the first three items of its checklist are numbers already known by
hand: 24 missing, 2 orphaned, coverage near 0.52. If those disagree the
derivation is wrong.

Slice 4 records the more useful failure. Its test first rebuilt the loop instead
of loading it, which would have stayed green while the shipped file was broken.
Loading the real function exposed within a single run that the script set its
paths unconditionally and ignored the environment.
This commit is contained in:
Thore Cimbal
2026-08-21 12:00:00 +00:00
parent 2f2c21d38c
commit c4e440dc1d
2 changed files with 100 additions and 0 deletions
@@ -509,3 +509,93 @@ damit beim Umschalten in Slice 3 kein Neustart-Rennen entsteht.
Zahl. `cve_targets_missing` muss **24** ergeben, `cve_targets_orphaned` **2**,
`cve_target_coverage_ratio` **≈ 0,52** — die Werte der Handmessung. Weicht sie
ab, ist die Herleitung falsch.
### Slice 2 — durchgeführt, Commit `b989987` · Status: **DONE**
**Abweichung von Gate 3: vier Regeln statt drei.** Die vierte
(`CveTargetsNoneDesired`) deckt einen Fall ab, den die anderen drei **nicht
sehen**: Alle Quellen antworten sauber, aber leer. Dann ist `missing` 0 (leere
Soll-Menge), die Frische-Stempel sind aktuell — und es wird nichts mehr
gescannt. `test_targets.py` führt genau diesen Fall bereits als *„leere Menge
ist NICHT dasselbe wie Erfolg"*; die Regel gehört dazu.
**Erste Regeltests in diesem Stack überhaupt** (`prometheus/alerts_test.yml`).
Je Regel ein Fall, in dem sie feuern **muss**, und einer, in dem sie schweigen
**muss** — sonst ließe sich eine Regel, die immer feuert, von einer richtigen
nicht unterscheiden. `promtool check rules`: 20 Regeln, `promtool test rules`:
SUCCESS.
**Zwei Sabotagen belegen, dass die Tests beißen:**
- Schwelle von `CveTargetSourceStale` auf `999999999` → der erwartete Alarm
bleibt aus, Test rot.
- Karenz `for: 6h` bei `CveTargetsMissing` entfernt → Alarm feuert bei 5 h, wo
der Test Stille verlangt, Test rot.
Die Karenz ist damit nachweislich tragend und keine Zierde: Eine volle Runde
über ~65 Images dauert, nach einem Ausrollen ist die Lücke vorübergehend echt.
**Dashboard:** Deckung und ungeprüfte Images in die zwei freien Plätze der
obersten Reihe, Quellen-Frische als Balken **unten** angehängt — kein
bestehendes Panel verschiebt sich. Geprüft: JSON lesbar, IDs eindeutig, keine
doppelt belegte Position.
### Slice 3 — durchgeführt, Commit `386b65a` · Status: **DONE**
`images.txt` ist **gelöscht** — Kriterium 1 erfüllt. Der Scanner liest
`/targets/targets.txt`.
Ohne brauchbare Liste tut die Schleife nichts und versucht es nach **einer
Minute** erneut statt nach 24 Stunden: Nach einem Ausrollen schreibt der
Exporter die Datei erst beim nächsten Scrape. Belegt mit gestelltem Trivy: ohne
Liste ausgesetzt, leere Liste ausgesetzt, mit Liste genau die zwei Einträge
gescannt und die Kopfzeile ignoriert.
### Slice 4 — durchgeführt, Commit `07875ba` · Status: **DONE**
Berichte ohne Ziel werden entfernt — **nur** gegen eine erfolgreich gelesene
Liste. Der Fall vom 2026-08-20 (`threadnet-web:v0.3.0` meldet HIGH-CVEs, obwohl
das Image nirgends läuft) ist als eigener Testfall geführt.
⚠️ **Der Test hat sich erst selbst als wertlos entlarvt.** Die erste Fassung
**baute** die Schleifenlogik nach, statt sie zu prüfen — und wäre grün
geblieben, während die ausgelieferte Datei kaputt ist. Nach dem Umbau auf
*Laden* der echten `runde()` (Ladewache `SCAN_LOOP_NUR_LADEN`) fiel binnen eines
Laufs auf, dass `scan-loop.sh` seine Pfade **fest** setzte und die Umgebung
ignorierte. Ein nachgebauter Test prüft den Nachbau.
⚠️ **Zwei eigene Fehler, hier behoben:**
1. Der Treiber lautete `runde || sleep A && sleep B`. Das gruppiert
links-assoziativ zu `(runde || sleep A) && sleep B` — bei fehlender Liste
hätte er die Wartezeit **und danach das volle Tagesintervall** geschlafen,
also genau das, was die kurze Wartezeit verhindern soll.
2. Pfade fest verdrahtet, obwohl der Exporter sie längst aus der Umgebung nimmt.
**Gegenprobe:** Wache entfernt → „Berichte unberührt" fällt von **2 auf 0**, der
gesamte Bestand wäre gelöscht. Der Test fängt es.
### Übergabe — was der Ausrollende prüfen muss
Vor der Übergabe belegt (ohne Host): `py_compile` sauber · **21** Python-
Zusicherungen grün · **13** Shell-Zusicherungen grün · `sh -n` beide ·
`docker compose config` grün · `promtool check rules` 20 Regeln ·
`promtool test rules` SUCCESS.
⚠️ **`docker compose up -d` genügt nicht** (AAR 2026-08-01, Befund 2):
`--force-recreate` für `cve-exporter` und `cve-scan`, und die Konfiguration
**im Container** prüfen, nicht auf der Platte.
| # | Prüfung | Erwartet |
|---|---|---|
| 1 | `cve_targets_missing` **vor** der ersten neuen Runde | **24** — die Handmessung |
| 2 | `cve_targets_orphaned` vorher | **2** |
| 3 | `cve_target_coverage_ratio` vorher | **≈ 0,52** |
| 4 | `docker exec cve-scan cat /targets/targets.txt \| wc -l` | ~65 |
| 5 | Nach einer vollen Runde: `missing` / `orphaned` | **0** / **0** |
| 6 | `threadnet-web:v0.6.0` hat einen Bericht | ja |
| 7 | `threadnet-web:v0.3.0` hat **keinen** mehr | ja |
| 8 | Zahl der Meldungen im Security-Raum in der ersten Runde | **gemessen notieren** (erwartet ≤ 65, gestern waren es 12) |
| 9 | Dauer der ersten vollen Runde | **gemessen notieren** |
| 10 | `cve_target_source_stale` je Quelle | < 3600 |
**Weicht 13 ab, ist die Herleitung falsch — nicht die Handmessung überholt.**
Dann nicht weiterfahren, sondern den Unterschied ausmessen.
@@ -23,6 +23,9 @@ related:
| slice | commit | status | note |
|---|---|---|---|
| 4 | 07875ba | DONE | Berichte ausgefallener Ziele entfernt; Test laedt die echte runde() statt sie nachzubauen. |
| 3 | 386b65a | DONE | Scanner liest die abgeleitete Menge, images.txt geloescht (Kriterium 1). |
| 2 | b989987 | DONE | Vier Regeln statt drei; erste Regeltests im Stack, zwei Sabotagen belegen sie. |
| 1 | 27770b6 | DONE_WITH_CONCERNS | Herleitung + Metriken, nur beobachtend; Abnahme (24/2/0,52) erst nach dem Ausrollen messbar. |
## Ladder
@@ -73,3 +76,10 @@ von `v0.8.0` statt `v0.10.0`) — der Test korrigierte meinen Denkfehler, nicht
den Code. Und der erste Compose-Eingriff ging von falscher Einruckung aus; die
`assert`-Zeile davor hat verhindert, dass eine halb angewandte Aenderung
geschrieben wurde. Beide Male war die Kontrolle billiger als der Schaden.
**Der Test von Slice 4 war zuerst wertlos, und das war lehrreich.** Er baute die
Schleifenlogik NACH, statt sie zu laden - und waere gruen geblieben, waehrend
die ausgelieferte Datei kaputt ist. Nach dem Umbau auf Laden der echten
`runde()` fiel binnen eines Laufs auf, dass `scan-loop.sh` seine Pfade fest
setzte und die Umgebung ignorierte. Wer eine Nachbildung prueft, prueft die
Nachbildung.