# Regeltests fuer die Deckungs-Alarme (#0106) und die Entscheidungs-Alarme (#0051). # docker run --rm --entrypoint promtool -v "$PWD/prometheus:/p:ro" \ # prom/prometheus:v3.14.0 test rules /p/alerts_test.yml # # ⚠️ ZWECK: Eine Regel, die nur je gruen war, ist eine Vermutung. In diesem # Projekt ist "meldet Erfolg, ist aber blind" die haeufigste Fehlerklasse # (AAR 2026-08-01, Befund 1 und 3). Deshalb steht zu jeder Regel ein Fall, in # dem sie feuern MUSS, und einer, in dem sie schweigen MUSS - sonst laesst sich # eine Regel, die immer feuert, von einer richtigen nicht unterscheiden. # # Erste Regeltests in diesem Stack ueberhaupt; die uebrigen 16 Regeln sind # bewusst nicht mit abgedeckt (Nicht-Ziel von #0106). rule_files: - alerts.yml evaluation_interval: 1m tests: # --- CveTargetsMissing ---------------------------------------------------- - interval: 1m name: "Luecke in der Deckung feuert nach 6h" input_series: - series: 'cve_targets_missing' values: '24+0x480' # 8h lang 24 fehlende Images alert_rule_test: - eval_time: 5h alertname: CveTargetsMissing exp_alerts: [] # noch in der Karenz - eine volle Runde darf dauern - eval_time: 7h alertname: CveTargetsMissing exp_alerts: - exp_labels: severity: warning room: security exp_annotations: summary: "24 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" - interval: 1m name: "Volle Deckung schweigt" input_series: - series: 'cve_targets_missing' values: '0+0x480' alert_rule_test: - eval_time: 7h alertname: CveTargetsMissing exp_alerts: [] # --- CveTargetsOrphaned --------------------------------------------------- - interval: 1m name: "Berichte ohne laufendes Image feuern" input_series: - series: 'cve_targets_orphaned' values: '2+0x480' alert_rule_test: - eval_time: 7h alertname: CveTargetsOrphaned exp_alerts: - exp_labels: severity: info room: security exp_annotations: summary: "2 CVE-Bericht(e) betreffen Images, die nirgends laufen — Befunde, auf die niemand handeln kann (Rauschen, das wie Signal aussieht)" # --- CveTargetSourceStale (die wichtigste) -------------------------------- - interval: 1m name: "Eine eingefrorene Quelle faellt auf" input_series: # Die Registry-Quelle altert, die anderen beiden bleiben frisch. - series: 'cve_target_source_stale{quelle="registry"}' values: '0+60x400' # waechst um 60s je Minute -> nach 180 min > 10800 - series: 'cve_target_source_stale{quelle="cluster"}' values: '30+0x400' - series: 'cve_target_source_stale{quelle="operating"}' values: '30+0x400' alert_rule_test: - eval_time: 2h alertname: CveTargetSourceStale exp_alerts: [] # 7200s < 10800s - noch in Ordnung - eval_time: 5h alertname: CveTargetSourceStale exp_alerts: - exp_labels: severity: warning room: security quelle: registry # ⚠️ NUR die eingefrorene, nicht alle drei exp_annotations: summary: "Die Quelle 'registry' der CVE-Zielmenge ist seit 18000s nicht erneuert worden — die Zielmenge schrumpft still, und die Deckung sieht dadurch BESSER aus statt schlechter" # --- CveTargetsNoneDesired ------------------------------------------------ # Der Fall, den die drei anderen NICHT sehen: alle Quellen antworten sauber, # aber leer. missing = 0, stale frisch - und es wird nichts mehr geprueft. - interval: 1m name: "Leere Zielmenge trotz gesunder Quellen" input_series: - series: 'cve_targets_desired_total' values: '0+0x120' - series: 'cve_targets_missing' values: '0+0x120' - series: 'cve_target_source_stale{quelle="cluster"}' values: '5+0x120' alert_rule_test: - eval_time: 20m alertname: CveTargetsNoneDesired exp_alerts: [] # Karenz 30m - eval_time: 40m alertname: CveTargetsNoneDesired exp_alerts: - exp_labels: severity: critical room: security exp_annotations: summary: "Die CVE-Zielmenge ist LEER — es wird nichts mehr geprueft, obwohl keine Quelle einen Fehler meldet" # Gegenprobe im selben Lauf: die anderen drei schweigen hier, obwohl die # Lage schlecht ist. Genau deshalb braucht es die vierte Regel. - eval_time: 40m alertname: CveTargetsMissing exp_alerts: [] - eval_time: 40m alertname: CveTargetSourceStale exp_alerts: [] # --- Gegenprobe: gesunder Zustand loest KEINEN der vier Alarme aus -------- - interval: 1m name: "Alles in Ordnung bleibt still" input_series: - series: 'cve_targets_missing' values: '0+0x480' - series: 'cve_targets_orphaned' values: '0+0x480' - series: 'cve_targets_desired_total' values: '65+0x480' - series: 'cve_target_source_stale{quelle="cluster"}' values: '120+0x480' - series: 'cve_target_source_stale{quelle="operating"}' values: '120+0x480' - series: 'cve_target_source_stale{quelle="registry"}' values: '900+0x480' alert_rule_test: - eval_time: 7h alertname: CveTargetsMissing exp_alerts: [] - eval_time: 7h alertname: CveTargetsOrphaned exp_alerts: [] - eval_time: 7h alertname: CveTargetsNoneDesired exp_alerts: [] - eval_time: 7h alertname: CveTargetSourceStale exp_alerts: [] # --- CveCriticalOffen (#0051) --------------------------------------------- - interval: 1m name: "Ein CRITICAL ohne Entscheidung faellt nach 24h auf" input_series: - series: 'cve_critical_offen' values: '3+0x1800' alert_rule_test: - eval_time: 20h alertname: CveCriticalOffen exp_alerts: [] # Karenz: ein frisch entdeckter Befund darf # einen Arbeitstag lang unentschieden sein - eval_time: 26h alertname: CveCriticalOffen exp_alerts: - exp_labels: severity: warning room: security exp_annotations: summary: "3 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" # --- CveEntscheidungAbgelaufen -------------------------------------------- # ⚠️ Der eigentliche Zweck der Entscheidungsdatei. Ohne diese Regel wuerde # aus "wir haben uns das angesehen und es ist vertretbar" nach ein paar # Monaten still "das steht halt so in der Datei". - interval: 1m name: "Ein ueberschrittenes Pruefdatum meldet sich" input_series: - series: 'cve_entscheidungen_abgelaufen' values: '0+0x60 1+0x600' # erst alles gueltig, dann laeuft eine ab alert_rule_test: - eval_time: 1h alertname: CveEntscheidungAbgelaufen exp_alerts: [] # noch nichts abgelaufen - eval_time: 8h alertname: CveEntscheidungAbgelaufen exp_alerts: - exp_labels: severity: warning room: security exp_annotations: summary: "1 CVE-Entscheidung(en) haben ihr Pruefdatum ueberschritten — die Begruendung von damals gilt bis zum Gegenbeweis nicht mehr" # --- CveEntscheidungenUnlesbar -------------------------------------------- - interval: 1m name: "Eine kaputte Entscheidungsdatei nennt sich selbst" input_series: - series: 'cve_entscheidungen_lesefehler' values: '1+0x120' # Gleichzeitig zaehlt alles als offen - so ist der Exporter gebaut. - series: 'cve_critical_offen' values: '63+0x120' alert_rule_test: - eval_time: 15m alertname: CveEntscheidungenUnlesbar exp_alerts: [] # Karenz 30m - eval_time: 45m alertname: CveEntscheidungenUnlesbar exp_alerts: - exp_labels: severity: warning room: security exp_annotations: summary: "cve/entscheidungen.json ist nicht lesbar — bis das behoben ist, zaehlt jeder CRITICAL als unentschieden" # ⚠️ Gegenprobe zum Zusammenspiel: CveCriticalOffen schweigt hier noch # (Karenz 24h). Ohne die Regel darueber bliebe der wahre Grund also # einen ganzen Tag lang unsichtbar. - eval_time: 45m alertname: CveCriticalOffen exp_alerts: [] # --- Gegenprobe: entschiedener Bestand bleibt still ------------------------ - interval: 1m name: "Alles entschieden und gueltig bleibt still" input_series: - series: 'cve_critical_offen' values: '0+0x1800' - series: 'cve_critical_entschieden' values: '63+0x1800' - series: 'cve_entscheidungen_abgelaufen' values: '0+0x1800' - series: 'cve_entscheidungen_ohne_befund' values: '0+0x1800' - series: 'cve_entscheidungen_lesefehler' values: '0+0x1800' alert_rule_test: - eval_time: 26h alertname: CveCriticalOffen exp_alerts: [] - eval_time: 26h alertname: CveEntscheidungAbgelaufen exp_alerts: [] - eval_time: 26h alertname: CveEntscheidungOhneBefund exp_alerts: [] - eval_time: 26h alertname: CveEntscheidungenUnlesbar exp_alerts: []