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
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:
@@ -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' · 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 — typische '
|
||||
'Umsetzungsdauer 4-8 Wochen (DSB-Review → Marketing-Agentur '
|
||||
'→ Entwicklung → 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 — wir lernen daraus. '
|
||||
'Rechtliche Risiken (Bussgeld-Rahmen Art. 83 DSGVO bis 4 % des '
|
||||
'weltweiten Jahresumsatzes, realistisch 0,1-1 % 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 — 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,
|
||||
]
|
||||
|
||||
|
||||
@@ -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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user