# --- Deckung der Zielmenge (#0106, ADR-0026) ---------------------
# Bis 2026-08-21 konnte die Pipeline nur sagen, WAS sie gescannt hat.
# Was sie nie angesehen hatte, war unsichtbar: 27 von 51 laufenden
# Images (52 %) - darunter der Web-Client, beide Traefik-Schichten und
# die Registry selbst. Die Zielliste war handgepflegt.
# 6h, weil eine volle Runde ueber ~65 Images dauert: nach einem
# Ausrollen ist die Luecke voruebergehend echt und kein Fehler.
- alert:CveTargetsMissing
expr:cve_targets_missing > 0
for:6h
labels:
severity:warning
room:security
annotations:
summary:"{{ $value }} laufende Images werden NICHT auf CVEs geprueft — die Schwachstellen-Sicht hat ein Loch, das sie selbst nicht zeigt. Details: https://selendis.axion1337.de/d/cve-overview"
- alert:CveTargetsOrphaned
expr:cve_targets_orphaned > 0
for:6h
labels:
severity:info
room:security
annotations:
summary:"{{ $value }} CVE-Bericht(e) betreffen Images, die nirgends laufen — Befunde, auf die niemand handeln kann (Rauschen, das wie Signal aussieht)"
# ⚠️ DIE WICHTIGSTE DER VIER. Faellt eine Quelle aus, wird die
# Soll-Menge KLEINER - und die Deckung saehe damit BESSER aus, nicht
# schlechter. Ein Ausfall waere ohne diese Regel von Erfolg nicht zu
# unterscheiden. Schwelle: 3x das Herleitungs-Intervall (1h).
- alert:CveTargetSourceStale
expr:cve_target_source_stale > 10800
labels:
severity:warning
room:security
annotations:
summary:"Die Quelle '{{ $labels.quelle }}' der CVE-Zielmenge ist seit {{ $value }}s nicht erneuert worden — die Zielmenge schrumpft still, und die Deckung sieht dadurch BESSER aus statt schlechter"
# Der Fall, den die drei darueber NICHT sehen: alle Quellen antworten
# sauber, aber leer. Dann ist 'missing' 0 (leere Soll-Menge) und
# 'stale' frisch - und es wird nichts mehr gescannt. In
# test_targets.py als "leere Menge ist NICHT dasselbe wie Erfolg".
- alert:CveTargetsNoneDesired
expr:cve_targets_desired_total == 0
for:30m
labels:
severity:critical
room:security
annotations:
summary:"Die CVE-Zielmenge ist LEER — es wird nichts mehr geprueft, obwohl keine Quelle einen Fehler meldet"
# --- Entschieden oder offen (#0051) -------------------------------
# ⚠️ Diese vier bewachen eine ENTSCHEIDUNG, kein Werkzeug. Ein CRITICAL,
# zu dem es nichts zu tun gibt, ist erlaubt - aber nur benannt, begruendet
# und mit Pruefdatum. Ohne diese Regeln waere 'entschieden' ein Wort in
# einer Datei, das mit der Zeit still zu 'vergessen' wird.
- alert:CveCriticalOffen
expr:cve_critical_offen > 0
for:24h
labels:
severity:warning
room:security
annotations:
summary:"{{ $value }} CRITICAL auf laufenden Images ohne Entscheidung — weder behoben noch mit Begruendung und Pruefdatum in cve/entscheidungen.json. Details: https://selendis.axion1337.de/d/cve-overview"
# Der Ablauf ist der Sinn der Sache: eine Entscheidung ohne Verfallsdatum
# ist keine Entscheidung, sondern Wegschauen mit Zusatzschritt.
- alert:CveEntscheidungAbgelaufen
expr:cve_entscheidungen_abgelaufen > 0
for:6h
labels:
severity:warning
room:security
annotations:
summary:"{{ $value }} CVE-Entscheidung(en) haben ihr Pruefdatum ueberschritten — die Begruendung von damals gilt bis zum Gegenbeweis nicht mehr"
- alert:CveEntscheidungOhneBefund
expr:cve_entscheidungen_ohne_befund > 0
for:24h
labels:
severity:info
room:security
annotations:
summary:"{{ $value }} CVE-Entscheidung(en) treffen auf keinen Befund mehr — Altpapier in cve/entscheidungen.json, das kuenftig Befunde decken koennte, die niemand geprueft hat"
# ⚠️ Eine unlesbare Datei laesst ALLES als offen zaehlen (so gebaut). Das
# faellt ueber CveCriticalOffen auf - aber erst nach 24h und ohne den Grund
# zu nennen. Diese Regel nennt ihn sofort.
- alert:CveEntscheidungenUnlesbar
expr:cve_entscheidungen_lesefehler > 0
for:30m
labels:
severity:warning
room:security
annotations:
summary:"cve/entscheidungen.json ist nicht lesbar — bis das behoben ist, zaehlt jeder CRITICAL als unentschieden"
expr:time() - max by (namespace, cronjob) (kube_cronjob_status_last_schedule_time{cronjob=~"(synapse|authentik|wikijs)-backup"} or kube_cronjob_created{cronjob=~"(synapse|authentik|wikijs)-backup"}) > 93600
expr:time() - max by (namespace, cronjob) (kube_cronjob_status_last_schedule_time{cronjob=~"restore-drill.*"} or kube_cronjob_created{cronjob=~"restore-drill.*"}) > 3456000