feat(audit): P71 JC-vs-AVV Entscheidungsbaum
CI / detect-changes (push) Successful in 10s
CI / branch-name (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 15s
CI / test-python-backend (push) Successful in 39s
CI / test-python-document-crawler (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 / 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) Has been skipped
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped

jc_avv_decision.py: detect_ambiguous_jc_avv prueft ob DSE-Text sowohl
JC-Signale (gemeinsame Auswertung, Schwesterunternehmen, Konzern...)
als auch AVV-Signale (Auftragsverarbeiter, weisungsgebunden...) enthaelt.
Bei Treffer rendert build_jc_avv_decision_html einen Block mit 4 EDPB-
basierten Leitfragen + jeweiliger Empfehlung.

Quellen: EDPB Guidelines 7/2020, EuGH C-25/17, C-40/17.

In Mail-Render zwischen Solutions-Block und VVT eingehaengt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-05-21 17:31:37 +02:00
parent 6dc427a754
commit c281464071
2 changed files with 127 additions and 0 deletions
@@ -1220,6 +1220,16 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest):
except Exception as e:
logger.warning("P73 MC-Solution-Generator skipped: %s", e)
# P71: JC-vs-AVV Entscheidungsbaum (nur wenn DSE ambig)
jc_decision_html = ""
try:
from compliance.services.jc_avv_decision import (
build_jc_avv_decision_html,
)
jc_decision_html = build_jc_avv_decision_html(doc_texts.get("dse"))
except Exception as e:
logger.warning("P71 jc_avv_decision skipped: %s", e)
# P82: GF-1-Pager ganz oben in der Mail — 5-Bullet-Zusammenfassung
# damit die GF nicht 124k Char lesen muss.
gf_one_pager_html = ""
@@ -1285,6 +1295,7 @@ async def _run_compliance_check(check_id: str, req: ComplianceCheckRequest):
+ scorecard_html + redundancy_html
+ providers_html + banner_deep_html + library_mismatch_html
+ consistency_html + signals_html + solutions_html
+ jc_decision_html
+ vvt_html + report_html
)
@@ -0,0 +1,116 @@
"""
P71 JC-vs-AVV Entscheidungsbaum.
Hilft dem Nutzer zu bestimmen, ob ein bestimmtes Verarbeitungsverhaeltnis
gemeinsame Verantwortlichkeit (Art. 26 DSGVO) oder Auftragsverarbeitung
(Art. 28 DSGVO) ist. EDPB 7/2020 ist die Grundlage.
Wird gerendert als kleiner Block am Ende der Mail, wenn im DSE-Text
Konstrukte vorkommen die ambivalent sind (z.B. 'gemeinsame Auswertung
mit Schwesterunternehmen', 'gemeinsame Plattform-Nutzung'). Liefert
3-4 Leitfragen + jeweilige Empfehlung.
"""
from __future__ import annotations
import logging
logger = logging.getLogger(__name__)
_JC_SIGNALS = (
"schwesterunternehmen", "konzernschwester", "gemeinsame plattform",
"gemeinsame auswertung", "gemeinsame studie", "joint venture",
"konzernweite analyse", "gemeinsame zwecke", "gemeinsame ziele",
"konzernweit", "gemeinsamer kunde", "gemeinsamer datenpool",
)
_AVV_SIGNALS = (
"auftragsverarbeiter", "auftragsverarbeitung", "weisungsgebunden",
"im auftrag von", "im namen des verantwortlichen",
"art. 28 dsgvo", "art 28 dsgvo", "dpa (data processing agreement",
)
_QUESTIONS = [
{
"q": "Bestimmen beide Seiten gemeinsam Zweck UND Mittel der Verarbeitung?",
"yes": "JC (Art. 26)",
"no": "AVV-Indikator",
"explain": "EDPB 7/2020 Rn. 51-65: beidseitige Zweckbestimmung ist "
"das Hauptmerkmal der gemeinsamen Verantwortlichkeit.",
},
{
"q": "Verfolgen die Parteien eigene, getrennte Zwecke (z.B. eigene "
"Kundenbeziehung) oder einen gemeinsamen Zweck?",
"yes": "Wenn getrennt: AVV (oder zwei getrennte Verantwortliche)",
"no": "Wenn gemeinsam: JC (Art. 26)",
"explain": "EuGH C-25/17 Zeugen Jehovas: getrennte Zwecke "
"schliessen JC aus.",
},
{
"q": "Existiert eine schriftliche Weisungs-Hierarchie und Pflicht "
"zur Loeschung am Vertragsende?",
"yes": "AVV (Art. 28 Pflichten erfuellt)",
"no": "Pruefen ob JC vorliegt + Art. 26-Vereinbarung noetig",
"explain": "Art. 28 (3)(g) DSGVO + EDPB 7/2020 Rn. 88.",
},
{
"q": "Haben Betroffene gegenueber beiden Stellen vollstaendige "
"Rechte (Art. 15-22)?",
"yes": "JC — Art. 26 (3) verlangt einheitliche Anlaufstelle",
"no": "AVV — Auftragsverarbeiter weist Rechtsausuebung an "
"Verantwortlichen zurueck",
"explain": "Art. 26 (3) DSGVO macht beide Stellen als gemeinsame "
"Anlaufstelle ansprechbar.",
},
]
def detect_ambiguous_jc_avv(dse_text: str | None) -> bool:
"""Heuristik: liegen sowohl JC- als auch AVV-Signale im DSE? Dann
ist die Konstellation typischerweise unklar und der Entscheidungsbaum
hilft."""
if not dse_text:
return False
t = dse_text.lower()
has_jc = any(s in t for s in _JC_SIGNALS)
has_avv = any(s in t for s in _AVV_SIGNALS)
return has_jc and has_avv
def build_jc_avv_decision_html(dse_text: str | None) -> str:
if not detect_ambiguous_jc_avv(dse_text):
return ""
items = []
for i, q in enumerate(_QUESTIONS, 1):
items.append(
f'<li style="margin-bottom:8px;font-size:11px;line-height:1.5">'
f'<strong>{i}. {q["q"]}</strong><br>'
f'<span style="color:#16a34a">Ja: </span>{q["yes"]} &nbsp;|&nbsp; '
f'<span style="color:#dc2626">Nein: </span>{q["no"]}<br>'
f'<span style="color:#64748b;font-size:10px;font-style:italic">'
f'{q["explain"]}</span>'
f'</li>'
)
return (
'<div style="font-family:-apple-system,BlinkMacSystemFont,sans-serif;'
'max-width:760px;margin:0 auto 16px;padding:12px 16px;'
'background:#f1f5f9;border:1px solid #cbd5e1;border-radius:6px">'
'<div style="font-size:11px;color:#475569;text-transform:uppercase;'
'letter-spacing:1.2px;margin-bottom:4px;font-weight:600">'
'JC vs AVV — Entscheidungshilfe</div>'
'<h3 style="margin:0 0 6px;font-size:14px;color:#1e293b">'
'Im DSE-Text gibt es sowohl gemeinsame-Verantwortlichkeits- als '
'auch Auftragsverarbeitungs-Hinweise</h3>'
'<p style="margin:0 0 10px;font-size:11px;color:#475569;line-height:1.5">'
'Pruefen Sie mit dem DSB die folgenden 4 Leitfragen aus EDPB 7/2020. '
'Das Ergebnis bestimmt ob eine Art. 26-Vereinbarung (JC) oder ein '
'Art. 28-AVV vorliegen muss.'
'</p>'
'<ol style="margin:0 0 0 18px;padding:0">'
+ "".join(items) +
'</ol>'
'<p style="margin:8px 0 0;font-size:10px;color:#94a3b8;'
'font-style:italic">Quelle: EDPB Guidelines 7/2020 (Controller/Processor) '
'+ EuGH C-25/17, C-40/17.</p>'
'</div>'
)