feat(audit): P8 — MC-Severity raus, Email nur harte Findings, MC-Audit als Checkliste
CI / detect-changes (push) Successful in 10s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 17s
CI / loc-budget (push) Failing after 17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m48s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 40s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / detect-changes (push) Successful in 10s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 17s
CI / loc-budget (push) Failing after 17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 2m48s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 40s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Email-Hardening (mc_scorecard.top_fails):
Neue _is_hard_finding-Heuristik filtert konditionale MCs ohne
Negativ-Beleg aus den Top-Auffaelligkeiten. matched_text leer + Label
enthaelt "falls/sofern/wenn/soweit/ggf." -> raus, landet nur noch im
MC-Audit als "selbst pruefen". DATA-2066-A05 (kostenfreie Abschaltung
Standortdaten) ist das prototypische Beispiel.
MC-Audit-Frontend (audit/[checkId]/page.tsx):
Severity-Spalte (CRITICAL/HIGH/MEDIUM/LOW) entfernt — der MC-Audit
ist eine Checkliste, keine Severity-Drohung. Stattdessen:
- Spalte "Prioritaet" mit 3-Tier aus regulation-Mapping:
Gesetz (DSGVO/ePrivacy/TDDDG/...) / Behoerden-Leitlinie
(EDPB/DSK/EuGH/...) / Best-Practice (ISO/NIST/BSI)
- 3-Status: erfuellt (✓) / nicht erfuellt (✗) / selbst pruefen (?)
/ nicht anwendbar (—). rowReviewStatus() leitet "selbst pruefen"
aus matched_text-leer + konditionalem Label ab.
- Filter umgebaut auf 5 Stati statt 4
- Default-Filter "Nicht erfuellt" (vorher "Nur Fail")
Bonus: f.payload.risk_label TS-Cast im FindingsTab clean gemacht
(unknown -> string).
Effekt:
- Email an die GF zeigt nur noch echte Belege ("DSB fehlt",
"Gebuehr fuer Widerruf")
- MC-Audit ist eine sachliche Pruefliste fuer den Compliance-Officer
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -121,11 +121,37 @@ def _dedup_key(label: str) -> str:
|
||||
return label
|
||||
|
||||
|
||||
_CONDITIONAL_MARKERS = ("falls ", "sofern ", "wenn ", "soweit ",
|
||||
"bei bedarf", "ggf.", "gegebenenfalls")
|
||||
|
||||
|
||||
def _is_hard_finding(r: dict) -> bool:
|
||||
"""Echtes Finding = wir haben einen positiven Treffer im Text der den
|
||||
Verstoss belegt. Stille im Text reicht NICHT — das wandert ins MC-Audit
|
||||
als "selbst pruefen", nicht ins Email als HIGH-Drohung.
|
||||
|
||||
Heuristik:
|
||||
- matched_text nicht leer = textuelle Evidenz vorhanden → hart
|
||||
- konditionales Label ("falls / sofern / wenn") UND matched_text leer
|
||||
→ weich (Pre-Condition nicht belegt) → raus aus Top-Fails
|
||||
- sonst: hart (klassische Pflichtangaben-Lücke wie "DSB fehlt")
|
||||
"""
|
||||
mt = (r.get("matched_text") or "").strip()
|
||||
if mt:
|
||||
return True
|
||||
label_low = (r.get("label") or "").lower()
|
||||
if any(m in label_low for m in _CONDITIONAL_MARKERS):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def top_fails(check_results: list[dict], n: int = 10) -> list[dict]:
|
||||
"""Return top-N failing MCs sorted by severity then label.
|
||||
|
||||
Skipped + passed MCs are excluded. INFO severity is excluded by
|
||||
default since those are guidance, not findings.
|
||||
default since those are guidance, not findings. Konditionale MCs
|
||||
ohne Negativ-Beleg (P8) werden ebenfalls ausgesteuert — sie
|
||||
erscheinen nur noch im MC-Audit als "selbst pruefen".
|
||||
|
||||
Near-duplicates (multiple MCs that all complain about "einfache
|
||||
Sprache" / "Einwilligungsaufforderung" / ...) are collapsed to ONE
|
||||
@@ -136,6 +162,7 @@ def top_fails(check_results: list[dict], n: int = 10) -> list[dict]:
|
||||
r for r in (check_results or [])
|
||||
if not r.get("passed") and not r.get("skipped")
|
||||
and (r.get("severity") or "").upper() != "INFO"
|
||||
and _is_hard_finding(r)
|
||||
]
|
||||
fails.sort(key=lambda r: (
|
||||
_SEV_RANK.get((r.get("severity") or "MEDIUM").upper(), 5),
|
||||
|
||||
Reference in New Issue
Block a user