feat: Mail-Restrukturierung + B22 Cross-Domain-Doc-Detector
CI / validate-canonical-controls (push) Successful in 11s
CI / loc-budget (push) Successful in 13s
CI / go-lint (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / detect-changes (push) Successful in 7s
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 / build-sha-integrity (push) Failing after 4s
CI / python-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-python-backend (push) Successful in 30s
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 11s
CI / loc-budget (push) Successful in 13s
CI / go-lint (push) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / detect-changes (push) Successful in 7s
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 / build-sha-integrity (push) Failing after 4s
CI / python-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-python-backend (push) Successful in 30s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
User-Feedback BMW v5: "740 Cookies verschwunden auf 31, Übersicht
verloren". Drei Anpassungen:
Mail-Restrukturierung (_executive_summary.py + _compose.py):
- render_executive_summary(): Top-of-mail TL;DR mit
Compliance-Score (gross + farbig), Top-3-Findings nach
Severity, Cookie-Statistik (deklariert/Browser/Drittland),
Severity-Verteilungs-Chips.
- collapsible(): wrapt jeden Block in <details>/<summary>.
Mailpit + alle modernen Mail-Clients rendern das nativ.
- _compose.py: alle 18+ B-Blöcke + per_doc + per_theme +
legacy_html in Akkordeons. NUR Critical-Findings + Sofort-
massnahmen sind immer offen — Reviewer sieht ~15 Zeilen
Übersicht und klappt selektiv auf.
- Cookie-Inventar (742) hat jetzt eigene Sektion ganz oben
(Akkordeon "🍪 Cookie-Inventar"), Vendor-Karten parallel.
B22 Cross-Domain-Legal-Doc-Detector (cross_domain_doc_check.py):
Real-Beispiel User-Feedback: Elli's AGB liegt auf docs.logpay.de
statt elli.eco. Detektor erkennt SLD-Mismatch:
- HIGH bei agb / widerruf (vertragsrelevant)
- MEDIUM bei dse / nutzungsbedingungen
- INFO bei cookie / impressum (Best-Practice)
Norm: DSGVO Art. 28 (AVV-Pflicht für Hosting) + Art. 13 Abs. 1
lit. e (Empfänger) + § 312i BGB (Cool-URLs).
9/9 Tests grün inkl. Elli/LogPay Pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -27,59 +27,85 @@ from ._vendor_cards import (
|
||||
render_info_box_rechtsrahmen,
|
||||
render_vendor_cards,
|
||||
)
|
||||
from ._executive_summary import collapsible, render_executive_summary
|
||||
from ._legacy_wrappers import render_all_legacy
|
||||
from ._style import page_close, page_open
|
||||
|
||||
|
||||
def compose_v2(state: dict) -> str:
|
||||
"""Build the full audit-mail HTML in the V2 layout."""
|
||||
"""Build the full audit-mail HTML in the V2 layout.
|
||||
|
||||
Struktur:
|
||||
1. Header (Site-Name + Datum)
|
||||
2. Executive Summary (Compliance-Score + Top-3 + Cookie-Stats)
|
||||
3. Critical Findings (immer offen, max 5)
|
||||
4. Alle anderen Sektionen als <details>-Akkordeons (kollabiert)
|
||||
5. Caveats + Attachments + Page-Close
|
||||
"""
|
||||
site = state.get("site_name") or "—"
|
||||
parts = [
|
||||
page_open(site),
|
||||
render_header(state),
|
||||
render_info_box_rechtsrahmen(),
|
||||
render_toc(state),
|
||||
render_vendor_cards(
|
||||
state.get("cmp_vendors") or [],
|
||||
state.get("cookie_coherence_findings") or [],
|
||||
),
|
||||
render_executive_summary(state),
|
||||
# IMMER OFFEN: kritische Findings + Sofortmaßnahmen
|
||||
render_critical(state),
|
||||
render_manual_review(state),
|
||||
render_internal_reminders(state),
|
||||
render_sofortmassnahmen(state),
|
||||
render_per_doc(state),
|
||||
render_per_theme(state),
|
||||
# B4 — Cross-Doc Vendor-Consistency (Elli Vertex↔Iadvize pattern)
|
||||
state.get("vendor_consistency_html", ""),
|
||||
# B5 — AI-Act Art. 50 Transparenzpflicht
|
||||
state.get("ai_act_html", ""),
|
||||
# B6/B7/B8/B9/B10 — DPO + Staleness + CMP + MultiEntity + Transfer
|
||||
state.get("extra_findings_html", ""),
|
||||
# B12 Chatbot-Cookie-Klassifikation
|
||||
state.get("chatbot_cookie_html", ""),
|
||||
# B13 Widerrufsbelehrung-Reachability (B2C-Pflicht)
|
||||
state.get("widerruf_reach_html", ""),
|
||||
# B14 Widersprüchliche Speicherdauer im selben Doc
|
||||
state.get("retention_conflict_html", ""),
|
||||
# B15 AI-Act Rechtsgrundlage (LLM-Vendor auf lit. f)
|
||||
state.get("ai_legal_basis_html", ""),
|
||||
# B16 Footer-Label-vs-URL-Slug-Drift (SEO / Bookmarks)
|
||||
state.get("url_slug_drift_html", ""),
|
||||
# B17 Audit-Walk-Video (Beweis-Aufzeichnung)
|
||||
state.get("audit_walk_html", ""),
|
||||
# B18 Impressum-Specialist-Agent (Pattern + LLM)
|
||||
state.get("impressum_agent_html", ""),
|
||||
# B19 Cookie-Coherence-Check (Salesforce-as-essential etc.)
|
||||
state.get("cookie_coherence_html", ""),
|
||||
# B20 Legacy-URL-Discovery + Multi-Version-DSE-Vergleich
|
||||
state.get("multi_version_dse_html", ""),
|
||||
state.get("legacy_url_html", ""),
|
||||
# Browser-Matrix (Stage 1.c)
|
||||
state.get("browser_matrix_html", ""),
|
||||
# All legacy build_*_html() wrapped in V2 sections — preserves
|
||||
# every information block from the old renderer (Exec Summary,
|
||||
# Banner-Screenshot, VVT, Redundancy, Solutions, Diff, etc.)
|
||||
render_all_legacy(state),
|
||||
|
||||
# AKKORDEON-Sektionen (kollabiert, Reviewer öffnet selektiv)
|
||||
collapsible("🍪 Cookie-Inventar (alle deklarierten + im Browser)",
|
||||
state.get("cookie_inventory_html", "")
|
||||
+ _render_per_theme_inventory_only(state)),
|
||||
collapsible("🏷️ Vendor-Übersicht (aggregiert nach Anbieter)",
|
||||
render_vendor_cards(
|
||||
state.get("cmp_vendors") or [],
|
||||
state.get("cookie_coherence_findings") or [],
|
||||
)),
|
||||
collapsible("🍪 Cookie-Kohärenz (Salesforce-Pattern, Pseudo-Zwecke)",
|
||||
state.get("cookie_coherence_html", "")),
|
||||
collapsible("💬 Chatbot-Cookie-Klassifikation",
|
||||
state.get("chatbot_cookie_html", "")),
|
||||
collapsible("📜 Widerrufsbelehrung-Reachability (B2C)",
|
||||
state.get("widerruf_reach_html", "")),
|
||||
collapsible("⏱️ Widersprüchliche Speicherdauer",
|
||||
state.get("retention_conflict_html", "")),
|
||||
collapsible("🤖 AI-Act Rechtsgrundlage (LLM-Vendor)",
|
||||
state.get("ai_legal_basis_html", "")),
|
||||
collapsible("🔗 URL-Slug-Drift (SEO / Bookmarks)",
|
||||
state.get("url_slug_drift_html", "")),
|
||||
collapsible("🎥 Audit-Walk-Video (Beweis-Aufzeichnung)",
|
||||
state.get("audit_walk_html", "")),
|
||||
collapsible("🤖 Impressum-Agent (Pattern + LLM)",
|
||||
state.get("impressum_agent_html", "")),
|
||||
collapsible("📑 Mehrere DSE-Versionen erkannt",
|
||||
state.get("multi_version_dse_html", "")),
|
||||
collapsible("🗂️ Legacy-URL-Inventar",
|
||||
state.get("legacy_url_html", "")),
|
||||
collapsible("🌐 Vertragsdoc auf Fremd-Domain (Cross-Domain)",
|
||||
state.get("cross_domain_doc_html", "")),
|
||||
collapsible("🔍 Cross-Doc Vendor-Konsistenz",
|
||||
state.get("vendor_consistency_html", "")),
|
||||
collapsible("⚖️ AI-Act Art. 50 Transparenzpflicht",
|
||||
state.get("ai_act_html", "")),
|
||||
collapsible("📌 Cross-Doc-Befunde (DPO, Staleness, CMP, Transfer)",
|
||||
state.get("extra_findings_html", "")),
|
||||
collapsible("🌐 Browser-Matrix (per-Browser-Verhalten)",
|
||||
state.get("browser_matrix_html", "")),
|
||||
collapsible("📋 Manuell zu prüfen",
|
||||
render_manual_review(state)),
|
||||
collapsible("🔧 Interne Erinnerungen",
|
||||
render_internal_reminders(state)),
|
||||
collapsible("📄 Per-Dokument-Befunde",
|
||||
render_per_doc(state)),
|
||||
collapsible("🧩 Per-Thema-Übersicht (Sub-Sektionen)",
|
||||
render_per_theme(state)),
|
||||
collapsible("📚 Rechtsrahmen-Info (Art. 13 DSGVO, § 25 TDDDG, …)",
|
||||
render_info_box_rechtsrahmen()),
|
||||
collapsible("📑 Inhaltsverzeichnis (alt)",
|
||||
render_toc(state)),
|
||||
collapsible("🗃️ Vollständige Legacy-Blöcke (Banner-Screenshot, "
|
||||
"VVT, Redundancy, Solutions, Diff)",
|
||||
render_all_legacy(state)),
|
||||
|
||||
render_caveats(state),
|
||||
render_attachments(state),
|
||||
page_close(state.get("check_id", ""),
|
||||
@@ -88,6 +114,17 @@ def compose_v2(state: dict) -> str:
|
||||
return "".join(p for p in parts if p)
|
||||
|
||||
|
||||
def _render_per_theme_inventory_only(state: dict) -> str:
|
||||
"""Extrahiert nur die Cookie-Inventar-Tabelle aus per_theme (die
|
||||
742er-Tabelle). per_theme rendert sonst ALL themes — wir wollen
|
||||
hier nur das Inventory-Theme."""
|
||||
try:
|
||||
from ._blocks import render_theme_cookie_inventory
|
||||
return render_theme_cookie_inventory(state)
|
||||
except Exception:
|
||||
return ""
|
||||
|
||||
|
||||
def is_v2_enabled() -> bool:
|
||||
return os.environ.get("MAIL_RENDER_V2", "false").lower() in (
|
||||
"true", "1", "yes", "on",
|
||||
|
||||
Reference in New Issue
Block a user