08671adfdf
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / detect-changes (push) Successful in 12s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / test-python-backend (push) Successful in 43s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 18s
CI / loc-budget (push) Failing after 19s
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) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
P82 — gf_one_pager.py: kompakte 5-Bullet-Kurzfassung ganz oben in der Mail. Score (gross + Farbe), Delta-zu-Vorlauf, Top-Findings nach HIGH/MEDIUM sortiert mit zustaendiger Rolle (DSB / Marketing / IT / Legal / Web-Team) und Klassifizierungsbits aus dem Wizard. Sachlicher Ton — keine 4%-Drohung, '4-8 Wochen' als realistischer Zeitrahmen. Eingehaengt vor Critical-Findings-Block in Mail-Composition und Replay-Pipeline. P87 — finding_confidence.py: 13 Regex-Regeln liefern (confidence_pct, reason) pro Finding-Label. Direkt im DOM beobachtbar = 95-98%, Library-Mismatch = 82%, Textmuster-Match auf Pflichtangaben = 75-88%. Im 1-Pager als kleines '(NN% Konfidenz)'-Tag mit Reason-Tooltip hinter jedem Finding gerendert. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
"""
|
|
P87 — Konfidenz-Score pro Finding.
|
|
|
|
Nicht jedes HIGH-Finding ist gleich sicher. "Kein Reject-Button im Banner"
|
|
ist faktisch direkt beobachtbar (Confidence ~98%). "DSE enthaelt keinen
|
|
DSB-Kontakt" ist ein Textmuster-Match und kann False-Positive sein
|
|
(Confidence ~70%). "Cookie X als essential deklariert, Library sagt
|
|
marketing" haengt von Library-Qualitaet ab (Confidence ~80%).
|
|
|
|
Liefert pro Finding-Label ein (confidence_pct, reason) Paar. Wird im
|
|
Mail-Render als kleine graue Klammer hinter dem Severity-Pill angezeigt:
|
|
"HOCH (95% Konfidenz: Direkt im DOM beobachtet)".
|
|
|
|
Keine ML — nur regelbasiert. Eine zentrale Stelle damit alle Render-
|
|
Stellen einheitlich klassifizieren.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
|
|
# (regex, confidence_pct, reason)
|
|
# Reihenfolge wichtig: spezifischere Patterns zuerst.
|
|
_RULES: list[tuple[re.Pattern, int, str]] = [
|
|
# 1) Direkt im DOM / im Cookie-Jar beobachtet — sehr hohe Sicherheit
|
|
(re.compile(r"reject[- ]?button.*(fehlt|nicht.*vorhanden)", re.I), 98,
|
|
"Direkt im Banner-DOM ueberprueft"),
|
|
(re.compile(r"(anpassen|einstellungen|customize).*button.*fehlt", re.I), 95,
|
|
"Initial-Banner-DOM ueberprueft"),
|
|
(re.compile(r"cookie.*vor.*einwilligung.*gesetzt", re.I), 96,
|
|
"Cookie-Jar vor Akzeptieren beobachtet"),
|
|
(re.compile(r"(tracking|marketing).*ohne.*einwilligung", re.I), 92,
|
|
"Network-Calls vor Akzeptieren beobachtet"),
|
|
|
|
# 2) Library-Mismatches — abhaengig von Library-Qualitaet
|
|
(re.compile(r"deklariert als.*library.*sagt", re.I), 82,
|
|
"Vergleich mit ~2.300-Cookie-Library + Open-Cookie-DB"),
|
|
(re.compile(r"library.*marketing", re.I), 82,
|
|
"Cookie-Library-Klassifikation"),
|
|
|
|
# 3) Pflichtangaben-Checks (Impressum/AGB/DSE) — Textmuster, MEDIUM-Sicherheit
|
|
(re.compile(r"impressum.*(fehlt|unvollstaendig)", re.I), 88,
|
|
"Pattern-Match auf Impressums-Pflichtfelder (§ 5 TMG)"),
|
|
(re.compile(r"dsb.*(fehlt|nicht.*genannt)", re.I), 75,
|
|
"Textmuster-Suche; DSB kann ueber Impressum referenziert sein"),
|
|
(re.compile(r"drittland.*(fehlt|nicht.*genannt|ohne.*hinweis)", re.I), 80,
|
|
"Pattern-Match auf typische Drittland-Klauseln"),
|
|
(re.compile(r"widerruf.*(fehlt|unvollstaendig)", re.I), 85,
|
|
"Pattern-Match auf Widerrufsbelehrungs-Pflichtfelder"),
|
|
|
|
# 4) Anti-Auditing-Detection — heuristisch
|
|
(re.compile(r"anti[- ]?audit", re.I), 70,
|
|
"Skript-Domain-Heuristik; manuelle Pruefung empfohlen"),
|
|
|
|
# 5) Generische Konsistenz-Findings (DSE vs. Banner vs. Cookie-Liste)
|
|
(re.compile(r"banner.*nennt.*\d+.*cmp.*\d+", re.I), 90,
|
|
"Quantitativer Vergleich zwischen Banner-Text und CMP-Payload"),
|
|
|
|
# 6) Klassifikations- / Kontext-Findings (Wizard-getrieben)
|
|
(re.compile(r"(branchen|scope).*passt.*nicht", re.I), 88,
|
|
"Wizard-Klassifikation + MC-scope_doc_type"),
|
|
]
|
|
|
|
_DEFAULT_CONFIDENCE = 78
|
|
_DEFAULT_REASON = (
|
|
"Standard-Regelpruefung; Bestaetigung mit DSB / interner Doku empfohlen"
|
|
)
|
|
|
|
|
|
def score_finding(label: str) -> tuple[int, str]:
|
|
"""Returns (confidence_pct, reason) for a finding label."""
|
|
if not label:
|
|
return _DEFAULT_CONFIDENCE, _DEFAULT_REASON
|
|
for pat, conf, reason in _RULES:
|
|
if pat.search(label):
|
|
return conf, reason
|
|
return _DEFAULT_CONFIDENCE, _DEFAULT_REASON
|
|
|
|
|
|
def confidence_pill_html(label: str) -> str:
|
|
"""Returns an inline HTML snippet '(NN% Konfidenz: ...)' or empty."""
|
|
conf, reason = score_finding(label)
|
|
return (
|
|
f' <span style="color:#94a3b8;font-size:10px" title="{reason}">'
|
|
f'({conf}% Konfidenz)</span>'
|
|
)
|