feat(audit-tonality): P89/P76/P91 — Co-Pilot statt Roboter-Anwalt
CI / branch-name (push) Has been skipped
CI / detect-changes (push) Successful in 11s
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 14s
CI / loc-budget (push) Failing after 15s
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) Failing after 48s
CI / iace-gt-coverage (push) Successful in 25s
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

User-Feedback in einer Session: "Wir erzeugen nur Panik. Egal was da steht,
es dauert Wochen. Wir sind Tool an der Seite von CMO/GF/CIO, nicht Gegner."
Memory: feedback_breakpilot_tonalitaet.md (gilt fuer ALLE Module + Marketing).

P89  Critical-Findings-Block ENTFERNT/UMGEBAUT — keine Panik-Rot-Box mehr.
     - Statt "🚨 SOFORTMASSNAHMEN ERFORDERLICH" -> "Zusammenfassung fuer
       die Geschaeftsfuehrung", blauer dezenter Block
     - Statt "VERSTOSSE" -> "Themen zur Besprechung mit DSB, Marketing
       und Entwicklung"
     - Statt "Bussgeldrahmen 4% Weltumsatz" als Erstes -> realistische
       Einordnung (0,1-1%) in dezenter Schluss-Notiz mit Konfidenz-Hinweis
     - "Sofortmassnahme" -> "Empfehlung"
     - "Themen 1, 2, 3..." statt "HIGH"-Badges (P87-Vorbereitung)
     - Explizite Zeitschaetzung "4-8 Wochen (DSB -> Agentur -> Dev -> Freigabe)"

P76  Mercedes-Sekundaer-Buttons (Datenschutzerklaerung + Impressum klein
     unter den 3 Haupt-Buttons) erkennen. Walker scant jetzt label-basiert
     ALLE klickbaren Elemente im Shadow-DOM (wb7-link, wb7-link-secondary,
     wb7-button-text, span[onclick], small a, [role=button], etc.).
     Vermeidet Mercedes-Impressum-False-Positive der Phase 1.

P91  VVT-Tabellen-Renderer in neuer Co-Pilot-Tonalitaet. Statt
     "Verstoss-Liste mit Bussgeldpotenzial" -> Wahrscheinlichkeits-Aussage:
     "Bei Anbieter-Reduktion + Wechsel zu europaeischen Alternativen ist
     Reduktion des Tracking-Footprints + Lizenz-Einsparung wahrscheinlich.
     Fundierte Bewertung erfordert DSB-Abstimmung."

BMW-Bug B1-B4 (P90) bewusst nicht in diesem Commit: BMW-Lauf hat ePaaS
4x captured im consent-tester, aber Backend bekommt 0 cmp_payloads.
Wiring-Bug zwischen consent-tester /dsi-discovery und Backend
_fetch_text — eigene Diagnose-Session noetig (siehe Task P90).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-21 11:24:57 +02:00
parent f534b52817
commit 7938e377b6
3 changed files with 101 additions and 49 deletions
@@ -184,45 +184,66 @@ def build_critical_findings_html(
scorecard: dict | None,
results: list,
) -> str:
"""Render der Critical-Findings-Box. Leerer String wenn keine Issues."""
"""Render der Audit-Zusammenfassung fuer die Geschaeftsfuehrung.
P89: Co-Pilot-Tonalitaet statt Panik-Rot.
- Sachlich blau statt alarmistisch rot
- "Themen die besprochen werden sollten" statt "VERSTOESSE"
- Realistische Zeitschaetzung (4-8 Wochen)
- Buessgeld-Risiko in separater, dezenter Section ganz unten
- Konfidenz-Hinweis "False-Positives moeglich"
"""
issues = _detect_critical_issues(banner_result, scorecard, results)
if not issues:
return ""
items = []
for i in issues:
for idx, i in enumerate(issues, 1):
# P87-Vorbereitung: keine HIGH-Badges mehr — wir nummerieren stattdessen
items.append(
f'<div style="margin-bottom:10px;padding:10px 12px;'
f'background:rgba(255,255,255,0.06);border-radius:4px;'
f'border-left:3px solid #fca5a5">'
f'<div style="font-size:13px;font-weight:700;color:#fff;'
f'<div style="margin-bottom:10px;padding:10px 14px;'
f'background:#fff;border-radius:6px;'
f'border-left:3px solid #2563eb">'
f'<div style="font-size:13px;font-weight:600;color:#1e293b;'
f'margin-bottom:4px">'
f'<span style="display:inline-block;background:#dc2626;color:#fff;'
f'padding:1px 6px;border-radius:3px;font-size:9px;'
f'margin-right:6px">{i["severity"]}</span>{i["title"]}</div>'
f'<div style="font-size:11px;color:#fecaca;margin-top:4px">'
f'<strong>Sofortmassnahme:</strong> {i["action"]}</div>'
f'<div style="font-size:10px;color:#fca5a5;margin-top:4px;'
f'font-style:italic">Rechtsgrundlage: {i.get("source","")}'
+ (f' &middot; Praezedenz: {i["bussgeld"]}'
if i.get("bussgeld") else "") +
f'</div></div>'
f'<span style="display:inline-block;background:#dbeafe;color:#1e40af;'
f'padding:1px 8px;border-radius:10px;font-size:10px;'
f'margin-right:8px;font-weight:600">Thema {idx}</span>'
f'{i["title"]}</div>'
f'<div style="font-size:11px;color:#475569;margin-top:6px">'
f'<strong>Empfehlung:</strong> {i["action"]}</div>'
f'<div style="font-size:10px;color:#94a3b8;margin-top:4px;'
f'font-style:italic">Hintergrund: {i.get("source","")}</div>'
f'</div>'
)
n = len(issues)
plural = "Themen" if n != 1 else "Thema"
return (
'<div style="font-family:-apple-system,BlinkMacSystemFont,sans-serif;'
'max-width:700px;margin:0 auto 18px;padding:18px 22px;'
'background:#7f1d1d;border-radius:10px;color:white">'
'<div style="font-size:12px;color:#fecaca;text-transform:uppercase;'
'letter-spacing:1.5px;margin-bottom:6px;font-weight:700">'
'🚨 Sofortmassnahmen erforderlich</div>'
f'<h2 style="margin:0 0 10px;font-size:18px;color:white">'
f'{n} kritische Compliance-Risiken mit Bussgeldpotenzial</h2>'
'<p style="margin:0 0 12px;font-size:12px;color:#fecaca">'
'Die folgenden Verstoesse sind durch Tool-Analyse belegt und '
'erfordern Sofortmassnahmen. Bussgeldrahmen nach Art. 83 DSGVO: '
'<strong>bis 4% des weltweiten Jahresumsatzes</strong>.</p>'
'background:#f0f9ff;border:1px solid #bfdbfe;border-radius:10px">'
'<div style="font-size:11px;color:#1e40af;text-transform:uppercase;'
'letter-spacing:1.2px;margin-bottom:4px;font-weight:600">'
'Zusammenfassung fuer die Geschaeftsfuehrung</div>'
f'<h2 style="margin:0 0 8px;font-size:18px;color:#1e293b">'
f'{n} {plural} zur Besprechung mit DSB, Marketing und Entwicklung</h2>'
'<p style="margin:0 0 14px;font-size:12px;color:#475569;line-height:1.5">'
'Wir haben Datenschutzerklaerung, Cookie-Banner, Impressum und '
'eingebundene Anbieter technisch analysiert. Die folgenden Punkte '
'sollten in den naechsten Wochen geklaert werden &mdash; typische '
'Umsetzungsdauer 4-8 Wochen (DSB-Review &rarr; Marketing-Agentur '
'&rarr; Entwicklung &rarr; Freigabe). Detaillierte technische '
'Analyse mit weiteren Findings finden Sie unten.</p>'
+ "".join(items) +
'<div style="margin-top:14px;padding:10px 12px;background:#f1f5f9;'
'border-radius:6px;font-size:10px;color:#64748b;line-height:1.5">'
'<strong style="color:#475569">Hinweis:</strong> Automatisierte '
'Audits enthalten False-Positives. Wo unsicher, bitte mit DSB pruefen '
'oder uns Feedback geben &mdash; wir lernen daraus. '
'Rechtliche Risiken (Bussgeld-Rahmen Art. 83 DSGVO bis 4&nbsp;% des '
'weltweiten Jahresumsatzes, realistisch 0,1-1&nbsp;% bei Erstverstoss '
'nach CNIL/LfDI-Massstab) werden weiter unten pro Finding eingeordnet.'
'</div>'
'</div>'
)
@@ -161,14 +161,26 @@ def build_vvt_table_html(vendors: list[dict]) -> str:
'max-width:760px;margin:0 auto 16px;padding:12px 16px;'
'background:#fafafa;border:1px solid #e5e7eb;border-radius:8px">',
'<h3 style="margin:0 0 4px;font-size:14px;color:#334155">'
'VVT-Vorschlag: Verarbeitungstaetigkeiten und Empfaenger aus der '
'Cookie-Richtlinie</h3>',
f'<p style="margin:0 0 10px;font-size:11px;color:#6b7280">{summary}. '
'Gruppiert nach Empfaengerkategorie (Art. 30(1)(d) DSGVO). Innerhalb '
'jeder Gruppe nach Compliance-Score sortiert. Bei eigenen '
'Verarbeitungen (INTERNAL/GROUP) werden Opt-Out und Privacy-Link '
'Vorschlag fuer das Verarbeitungsverzeichnis (Art. 30 DSGVO)</h3>',
# P91: Co-Pilot-Tonalitaet — Wahrscheinlichkeit statt Garantie,
# Empfehlung statt "Verstoss-Liste".
f'<p style="margin:0 0 8px;font-size:11px;color:#6b7280;line-height:1.5">'
f'Wir haben <strong>{n_total} Verarbeitungen</strong> aus dem '
f'Cookie-Banner abgeleitet, mit unserer globalen Anbieter-Bibliothek '
f'abgeglichen und nach Empfaengerkategorie (Art. 30(1)(d) DSGVO) '
f'gruppiert. Bei einer Reduktion der eingebundenen Anbieter, dem '
f'Wechsel zu europaeischen Alternativen und konsequenter Pruefung '
f'der tatsaechlich benoetigten Cookies ist eine Reduktion des '
f'Tracking-Footprints sowie Lizenz-Einsparungen wahrscheinlich. '
f'Eine fundierte Bewertung erfordert die Abstimmung mit dem '
f'Datenschutzbeauftragten.</p>'
f'<p style="margin:0 0 10px;font-size:11px;color:#6b7280">'
f'{summary}. Innerhalb jeder Gruppe nach Verbesserungspotenzial '
f'sortiert. Bei eigenen Verarbeitungen (INTERNAL/GROUP) sind '
f'Opt-Out und Privacy-Link '
'NICHT als Pflicht gewertet &mdash; der Widerruf erfolgt ueber das '
'Cookie-Banner, Privacy ist in der Haupt-DSI dokumentiert.</p>',
'nicht erforderlich (Widerruf ueber Banner, Privacy in der '
'Haupt-Datenschutzerklaerung dokumentiert).</p>',
pattern_notice,
]
+35 -16
View File
@@ -30,6 +30,38 @@ SHADOW_BANNER_WALKER_JS = """() => {
for (const k of LEGAL_KW.dse) if (t.includes(k)) return 'dse';
return null;
}
// P76: EXTENDED scan ANY clickable element by label, not just
// <a href> or named web-components. Mercedes uses small secondary
// buttons below the main 3 actions: "Datenschutzerklaerung" + "Impressum"
// as <wb7-link>/<button>/<small><a> generic label-based scan catches them.
function collectLegalLinksFromRoot(rootEl, acc) {
if (!rootEl || !rootEl.querySelectorAll) return;
// Generic scan: ALLE klickbaren/Link-aussehenden Elemente
const cands = rootEl.querySelectorAll(
'a, button, [role="link"], [role="button"], ' +
'wb7-link, wb7-button, wb7-button-text, wb7-link-secondary, ' +
'span[onclick], small a, small button, ' +
'[class*="link" i], [class*="button" i]'
);
const seen = new Set();
for (const c of cands) {
const label = (c.textContent || '').trim();
if (!label || label.length > 60) continue;
const which = isLegalLabel(label);
if (!which) continue;
const key = which + '|' + label.toLowerCase();
if (seen.has(key)) continue;
seen.add(key);
const href = (c.getAttribute('href') ||
c.getAttribute('data-href') ||
c.getAttribute('data-uri') ||
c.getAttribute('data-url') || '').toLowerCase();
acc.links.push({
href: href || ('#label-' + which),
text: label.toLowerCase(),
});
}
}
function walk(root, acc) {
if (!root) return;
const all = root.querySelectorAll ? root.querySelectorAll('*') : [];
@@ -45,6 +77,9 @@ SHADOW_BANNER_WALKER_JS = """() => {
if (el.shadowRoot) {
const txt = (el.shadowRoot.textContent || '').trim();
if (txt) acc.text += ' ' + txt;
// P76: full label-based scan of shadow content
collectLegalLinksFromRoot(el.shadowRoot, acc);
// Legacy: plain <a href> for backward compatibility
const links = el.shadowRoot.querySelectorAll('a[href]');
for (const a of links) {
acc.links.push({
@@ -52,22 +87,6 @@ SHADOW_BANNER_WALKER_JS = """() => {
text: (a.textContent || '').trim().toLowerCase(),
});
}
const cands = el.shadowRoot.querySelectorAll(
'wb7-link, wb7-button, [role="link"], button, span, a'
);
for (const c of cands) {
const label = (c.textContent || '').trim();
const which = isLegalLabel(label);
if (which) {
const href = (c.getAttribute('href') ||
c.getAttribute('data-href') ||
c.getAttribute('data-uri') || '').toLowerCase();
acc.links.push({
href: href || ('#shadow-' + which),
text: label.toLowerCase(),
});
}
}
}
}
}