feat: Document Templates V2 — DSFA, TOM, VVT, AVV, Verpflichtung, Art.13/14

Erweiterte Compliance-Vorlagen fuer den Document Generator:
- DSFA V2: Schwellwertanalyse (9 WP248-Kriterien), SDM-basierte TOM,
  strukturierte Risikobewertung, KI-Modul (AI Act), Art.36-Pruefung
- TOM V2: 7 SDM-Gewaehrleistungsziele, Sektor-Erweiterungen,
  NIS2/ISO27001/AI Act Varianten
- VVT V2: 6 Branchen-Muster (IT/SaaS, Gesundheit, Handel, Handwerk,
  Bildung, Beratung) + allgemeine Art.30-Vorlage
- AVV V2: Vollstaendiger Art.28-Vertrag mit TOM-Anlage
- Verpflichtungserklaerung: Mitarbeiter-Vertraulichkeit
- Art.13/14 Informationspflichten-Muster

Enthalt SQL-Migrations (compliance_legal_templates), Python-Generatoren
und Qdrant-Cleanup-Skript. Feature-Branch fuer spaetere Integration
in breakpilot-compliance.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-10 11:39:39 +02:00
parent 441d5740bd
commit fc71117bf2
10 changed files with 3126 additions and 0 deletions

View File

@@ -0,0 +1,455 @@
"""DSFA template generator V2 — creates DSFA skeleton from company profile.
Enhanced with:
- Schwellwertanalyse (9 WP248 criteria)
- Bundesland-specific Muss-Listen references
- SDM-based TOM structure (7 Gewaehrleistungsziele)
- Structured risk assessment (ISO 29134 methodology)
- AI Act module (Section 8)
- Art. 36 consultation assessment
"""
from typing import Optional
# -- WP248 Kriterien --------------------------------------------------------
WP248_CRITERIA = [
{"id": "K1", "label": "Bewertung oder Scoring (einschl. Profiling und Prognose)",
"ctx_keys": ["has_profiling", "has_scoring"]},
{"id": "K2", "label": "Automatisierte Entscheidungsfindung mit Rechtswirkung",
"ctx_keys": ["has_automated_decisions"]},
{"id": "K3", "label": "Systematische Ueberwachung von Personen",
"ctx_keys": ["has_surveillance", "has_employee_monitoring", "has_video_surveillance"]},
{"id": "K4", "label": "Verarbeitung sensibler Daten (Art. 9/10 DS-GVO)",
"ctx_keys": ["processes_health_data", "processes_biometric_data", "processes_criminal_data"]},
{"id": "K5", "label": "Datenverarbeitung in grossem Umfang",
"ctx_keys": ["large_scale_processing"]},
{"id": "K6", "label": "Verknuepfung oder Zusammenfuehrung von Datenbestaenden",
"ctx_keys": ["data_matching", "data_combining"]},
{"id": "K7", "label": "Daten zu schutzbeduerftigen Betroffenen",
"ctx_keys": ["processes_minors_data", "processes_employee_data", "processes_patient_data"]},
{"id": "K8", "label": "Innovative Nutzung neuer technologischer Loesungen",
"ctx_keys": ["uses_ai", "uses_biometrics", "uses_iot"]},
{"id": "K9", "label": "Verarbeitung hindert Betroffene an Rechtsausuebung",
"ctx_keys": ["blocks_service_access", "blocks_contract"]},
]
# -- Bundesland -> Aufsichtsbehoerde Mapping --------------------------------
BUNDESLAND_AUFSICHT = {
"baden-wuerttemberg": ("LfDI Baden-Wuerttemberg", "DSK Muss-Liste + BW-spezifische Liste (Art. 35 Abs. 4)"),
"bayern": ("BayLDA (nicht-oeffentlicher Bereich)", "BayLDA Muss-Liste (17.10.2018) + Fallbeispiel ISO 29134"),
"berlin": ("BlnBDI", "BlnBDI Muss-Liste nicht-oeffentlich / oeffentlich"),
"brandenburg": ("LDA Brandenburg", "LDA BB Muss-Liste allgemein / oeffentlich"),
"bremen": ("LfDI Bremen", "LfDI HB Muss-Liste"),
"hamburg": ("HmbBfDI", "HmbBfDI Muss-Liste nicht-oeffentlich / oeffentlich"),
"hessen": ("HBDI", "DSK Muss-Liste (HBDI uebernimmt DSK-Liste)"),
"mecklenburg-vorpommern": ("LfDI M-V", "LfDI M-V Muss-Liste"),
"niedersachsen": ("LfD Niedersachsen", "LfD NI Muss-Liste + Pruefschema"),
"nordrhein-westfalen": ("LDI NRW", "LDI NRW Muss-Liste nicht-oeffentlich / oeffentlich"),
"rheinland-pfalz": ("LfDI RLP", "LfDI RLP Muss-Liste allgemein / oeffentlich"),
"saarland": ("UDS Saarland", "DSK Muss-Liste (UDS uebernimmt DSK-Liste)"),
"sachsen": ("SDB Sachsen", "SDB Sachsen Muss-Liste"),
"sachsen-anhalt": ("LfD Sachsen-Anhalt", "LfD SA Muss-Liste allgemein / oeffentlich"),
"schleswig-holstein": ("ULD Schleswig-Holstein", "ULD Muss-Liste + Planspiel-DSFA"),
"thueringen": ("TLfDI", "TLfDI Muss-Liste (04.07.2018)"),
"bund": ("BfDI", "BfDI Muss-Liste / DSFA-Hinweise"),
}
# -- SDM Gewaehrleistungsziele -----------------------------------------------
SDM_GOALS = [
{
"id": "verfuegbarkeit",
"label": "Verfuegbarkeit",
"description": "Personenbezogene Daten stehen zeitgerecht zur Verfuegung und koennen ordnungsgemaess verarbeitet werden.",
"default_measures": [
"Redundante Datenhaltung und regelmaessige Backups",
"Disaster-Recovery-Plan mit definierten RTO/RPO-Werten",
"USV und Notstromversorgung fuer kritische Systeme",
],
},
{
"id": "integritaet",
"label": "Integritaet",
"description": "Personenbezogene Daten bleiben waehrend der Verarbeitung unversehrt, vollstaendig und aktuell.",
"default_measures": [
"Pruefsummen und digitale Signaturen fuer Datenuebertragungen",
"Eingabevalidierung und Plausibilitaetspruefungen",
"Versionierung und Change-Management-Verfahren",
],
},
{
"id": "vertraulichkeit",
"label": "Vertraulichkeit",
"description": "Nur befugte Personen koennen personenbezogene Daten zur Kenntnis nehmen.",
"default_measures": [
"Verschluesselung: TLS 1.3 im Transit, AES-256 at Rest",
"Rollenbasiertes Zugriffskonzept (RBAC) mit Least-Privilege-Prinzip",
"Multi-Faktor-Authentifizierung fuer administrative Zugaenge",
],
},
{
"id": "nichtverkettung",
"label": "Nichtverkettung",
"description": "Personenbezogene Daten werden nur fuer den Zweck verarbeitet, zu dem sie erhoben wurden.",
"default_measures": [
"Technische Zweckbindung durch Mandantentrennung",
"Pseudonymisierung wo fachlich moeglich",
"Getrennte Datenbanken / Schemata je Verarbeitungszweck",
],
},
{
"id": "transparenz",
"label": "Transparenz",
"description": "Betroffene, der Verantwortliche und die Aufsichtsbehoerde koennen die Verarbeitung nachvollziehen.",
"default_measures": [
"Vollstaendiges Audit-Log aller Datenzugriffe und -aenderungen",
"Verzeichnis der Verarbeitungstaetigkeiten (Art. 30 DS-GVO)",
"Informationspflichten gemaess Art. 13/14 DS-GVO umgesetzt",
],
},
{
"id": "intervenierbarkeit",
"label": "Intervenierbarkeit",
"description": "Betroffenenrechte (Auskunft, Berichtigung, Loeschung, Widerspruch) koennen wirksam ausgeuebt werden.",
"default_measures": [
"Self-Service-Portal oder dokumentierter Prozess fuer Betroffenenanfragen",
"Technische Loeschfaehigkeit mit Nachweis (Loeschprotokoll)",
"Datenexport in maschinenlesbarem Format (Art. 20 DS-GVO)",
],
},
{
"id": "datenminimierung",
"label": "Datenminimierung",
"description": "Die Verarbeitung beschraenkt sich auf das erforderliche Mass.",
"default_measures": [
"Regelmaessige Pruefung der Erforderlichkeit erhobener Datenfelder",
"Automatisierte Loeschung nach Ablauf der Aufbewahrungsfrist",
"Anonymisierung / Aggregation fuer statistische Zwecke",
],
},
]
def generate_dsfa_draft(ctx: dict) -> dict:
"""Generate a DSFA draft document from template context.
Args:
ctx: Flat dict from company-profile/template-context endpoint.
Returns:
Dict with DSFA fields ready for creation via POST /dsfa.
"""
company = ctx.get("company_name", "Unbekannt")
dpo = ctx.get("dpo_name", "")
dpo_email = ctx.get("dpo_email", "")
federal_state = ctx.get("federal_state", "").lower().replace(" ", "-")
# --- Section 0: Schwellwertanalyse ---
schwellwert = _generate_schwellwertanalyse(ctx)
# --- Section 1: Verarbeitungsbeschreibung ---
section_1 = _generate_section_1(ctx, company, dpo, dpo_email)
# --- Section 2: Notwendigkeit ---
section_2 = _generate_section_2(ctx)
# --- Section 3: Risikobewertung ---
section_3 = _generate_risk_assessment(ctx)
# --- Section 4: Stakeholder-Konsultation ---
section_4 = _generate_section_4(ctx)
# --- Section 5: TOM nach SDM ---
section_5 = _generate_sdm_tom_section(ctx)
# --- Section 6: DSB-Stellungnahme ---
section_6 = _generate_section_6(ctx, dpo)
# --- Section 7: Ergebnis ---
section_7 = _generate_section_7(ctx)
# --- Section 8: KI-Modul ---
ai_systems = ctx.get("ai_systems", [])
involves_ai = len(ai_systems) > 0
section_8 = _generate_ai_module(ctx) if involves_ai else None
sections = {
"section_0": {"title": "Schwellwertanalyse", "content": schwellwert["content"]},
"section_1": {"title": "Allgemeine Informationen und Verarbeitungsbeschreibung", "content": section_1},
"section_2": {"title": "Notwendigkeit und Verhaeltnismaessigkeit", "content": section_2},
"section_3": {"title": "Risikobewertung", "content": section_3},
"section_4": {"title": "Konsultation der Betroffenen", "content": section_4},
"section_5": {"title": "Technische und organisatorische Massnahmen (SDM)", "content": section_5},
"section_6": {"title": "Stellungnahme des DSB", "content": section_6},
"section_7": {"title": "Ergebnis und Ueberprufungsplan", "content": section_7},
}
if section_8:
sections["section_8"] = {"title": "KI-spezifisches Modul (EU AI Act)", "content": section_8}
# Assess Art. 36 consultation requirement
art36_required = _assess_art36_consultation(ctx, schwellwert)
return {
"title": f"DSFA — {company}",
"description": f"Datenschutz-Folgenabschaetzung fuer {company}",
"status": "draft",
"risk_level": "high" if involves_ai or schwellwert["criteria_met"] >= 3 else "medium",
"involves_ai": involves_ai,
"dpo_name": dpo,
"federal_state": ctx.get("federal_state", ""),
"sections": sections,
"wp248_criteria_met": schwellwert["criteria_details"],
"art35_abs3_triggered": schwellwert["art35_abs3"],
"threshold_analysis": {
"criteria_met_count": schwellwert["criteria_met"],
"dsfa_required": schwellwert["dsfa_required"],
"muss_liste_ref": schwellwert.get("muss_liste_ref", ""),
},
"consultation_requirement": {
"art36_required": art36_required,
"reason": "Restrisiko bleibt nach Massnahmen hoch" if art36_required else "Restrisiko akzeptabel",
},
"processing_systems": [s.get("name", "") for s in ctx.get("processing_systems", [])],
"ai_systems_summary": [
{"name": s.get("name"), "risk": s.get("risk_category", "unknown")}
for s in ai_systems
],
}
# -- Internal helpers --------------------------------------------------------
def _generate_schwellwertanalyse(ctx: dict) -> dict:
"""Evaluate 9 WP248 criteria against company profile."""
criteria_details = []
criteria_met = 0
for criterion in WP248_CRITERIA:
met = any(ctx.get(key) for key in criterion["ctx_keys"])
criteria_details.append({
"id": criterion["id"],
"label": criterion["label"],
"met": met,
})
if met:
criteria_met += 1
# Art. 35 Abs. 3 specific triggers
art35_abs3 = []
if ctx.get("has_profiling") and ctx.get("has_automated_decisions"):
art35_abs3.append("Art. 35 Abs. 3 lit. a: Profiling mit Rechtswirkung")
if any(ctx.get(k) for k in ["processes_health_data", "processes_biometric_data", "processes_criminal_data"]):
if ctx.get("large_scale_processing"):
art35_abs3.append("Art. 35 Abs. 3 lit. b: Umfangreiche Verarbeitung besonderer Kategorien")
if ctx.get("has_surveillance"):
art35_abs3.append("Art. 35 Abs. 3 lit. c: Systematische Ueberwachung oeffentlicher Bereiche")
dsfa_required = criteria_met >= 2 or len(art35_abs3) > 0
# Bundesland reference
federal_state = ctx.get("federal_state", "").lower().replace(" ", "-")
aufsicht_info = BUNDESLAND_AUFSICHT.get(federal_state, ("Nicht zugeordnet", "DSK Muss-Liste (allgemein)"))
met_labels = [c["label"] for c in criteria_details if c["met"]]
content_lines = [
f"**Anzahl erfuellter WP248-Kriterien:** {criteria_met} von 9\n",
f"**Erfuellte Kriterien:** {', '.join(met_labels) if met_labels else 'Keine'}\n",
]
if art35_abs3:
content_lines.append(f"**Art. 35 Abs. 3 DS-GVO direkt ausgeloest:** {'; '.join(art35_abs3)}\n")
content_lines.append(
f"\n**Ergebnis:** DSFA ist {'**erforderlich**' if dsfa_required else '**nicht erforderlich**'}."
)
if dsfa_required and criteria_met < 2:
content_lines.append(" (Ausgeloest durch Art. 35 Abs. 3 DS-GVO)")
return {
"content": "\n".join(content_lines),
"criteria_met": criteria_met,
"criteria_details": criteria_details,
"art35_abs3": art35_abs3,
"dsfa_required": dsfa_required,
"muss_liste_ref": aufsicht_info[1],
}
def _generate_section_1(ctx: dict, company: str, dpo: str, dpo_email: str) -> str:
federal_state = ctx.get("federal_state", "")
aufsicht = BUNDESLAND_AUFSICHT.get(
federal_state.lower().replace(" ", "-"), ("Nicht zugeordnet",)
)[0]
lines = [
f"**Verantwortlicher:** {company}",
f"**Datenschutzbeauftragter:** {dpo}" + (f" ({dpo_email})" if dpo_email else ""),
f"**Zustaendige Aufsichtsbehoerde:** {aufsicht}",
]
systems = ctx.get("processing_systems", [])
if systems:
lines.append("\n**Eingesetzte Verarbeitungssysteme:**")
for s in systems:
hosting = s.get("hosting", "")
lines.append(f"- {s.get('name', 'N/A')}" + (f" ({hosting})" if hosting else ""))
return "\n".join(lines)
def _generate_section_2(ctx: dict) -> str:
lines = [
"### Notwendigkeit\n",
"Die Verarbeitung ist zur Erreichung des beschriebenen Zwecks erforderlich. ",
"Alternative, weniger eingriffsintensive Massnahmen wurden geprueft.\n",
"### Datenminimierung\n",
"Die verarbeiteten Datenkategorien beschraenken sich auf das fuer den ",
"Verarbeitungszweck erforderliche Minimum (Art. 5 Abs. 1 lit. c DS-GVO).\n",
]
return "".join(lines)
def _generate_risk_assessment(ctx: dict) -> str:
lines = ["## Risikoanalyse\n"]
# Standard risks
risks = [
("Unbefugter Zugriff auf personenbezogene Daten", "mittel", "hoch", "hoch"),
("Datenverlust durch technischen Ausfall", "niedrig", "hoch", "mittel"),
("Fehlerhafte Verarbeitung / Datenqualitaet", "niedrig", "mittel", "niedrig"),
("Zweckentfremdung erhobener Daten", "niedrig", "hoch", "mittel"),
]
if ctx.get("has_ai_systems") or ctx.get("uses_ai"):
risks.append(("Diskriminierung durch algorithmische Entscheidungen", "mittel", "hoch", "hoch"))
risks.append(("Mangelnde Erklaerbarkeit von KI-Entscheidungen", "mittel", "mittel", "mittel"))
if ctx.get("processes_health_data"):
risks.append(("Offenlegung von Gesundheitsdaten", "niedrig", "gross", "hoch"))
if any(ctx.get(k) for k in ["third_country_transfer", "processes_in_third_country"]):
risks.append(("Zugriff durch Behoerden in Drittlaendern", "mittel", "hoch", "hoch"))
lines.append("| Risiko | Eintrittswahrscheinlichkeit | Schwere | Gesamt |")
lines.append("|--------|----------------------------|---------|--------|")
for risk_name, likelihood, severity, overall in risks:
lines.append(f"| {risk_name} | {likelihood} | {severity} | **{overall}** |")
lines.append("")
high_risks = sum(1 for _, _, _, o in risks if o == "hoch")
if high_risks > 0:
lines.append(f"\n**{high_risks} Risiken mit Stufe 'hoch' identifiziert.** "
"Massnahmen gemaess Abschnitt 5 reduzieren das Restrisiko.")
return "\n".join(lines)
def _generate_section_4(ctx: dict) -> str:
lines = []
if ctx.get("has_works_council"):
lines.append("Der Betriebsrat wurde informiert und angehoert.")
lines.append(
"Eine Konsultation der Betroffenen gemaess Art. 35 Abs. 9 DS-GVO "
"wird empfohlen, soweit verhaeltnismaessig und praktikabel."
)
return "\n".join(lines)
def _generate_sdm_tom_section(ctx: dict) -> str:
"""Generate TOM section structured by 7 SDM Gewaehrleistungsziele."""
lines = []
for goal in SDM_GOALS:
lines.append(f"**{goal['label']}** — {goal['description']}\n")
lines.append("| Massnahme | Typ | Status |")
lines.append("|-----------|-----|--------|")
for measure in goal["default_measures"]:
mtype = "technisch" if any(
kw in measure.lower()
for kw in ["verschluesselung", "backup", "redundanz", "tls", "aes", "rbac", "mfa",
"pruefsumm", "validierung", "loeschfaehigkeit", "export", "automatisiert"]
) else "organisatorisch"
lines.append(f"| {measure} | {mtype} | geplant |")
lines.append("")
return "\n".join(lines)
def _generate_section_6(ctx: dict, dpo: str) -> str:
if dpo:
return (
f"Der Datenschutzbeauftragte ({dpo}) wurde konsultiert. "
"Die Stellungnahme liegt bei bzw. wird nachgereicht."
)
return (
"Ein Datenschutzbeauftragter wurde noch nicht benannt. "
"Sofern eine Benennungspflicht besteht (Art. 37 DS-GVO), "
"ist dies vor Abschluss der DSFA nachzuholen."
)
def _generate_section_7(ctx: dict) -> str:
review_months = ctx.get("review_cycle_months", 12)
lines = [
"### Ergebnis\n",
"Die DSFA wurde gemaess Art. 35 DS-GVO durchgefuehrt. Die identifizierten Risiken ",
"wurden bewertet und durch geeignete Massnahmen auf ein akzeptables Niveau reduziert.\n",
"### Ueberprufungsplan\n",
f"- **Regelmaessige Ueberprufung:** alle {review_months} Monate\n",
"- **Trigger fuer ausserplanmaessige Ueberprufung:**\n",
" - Wesentliche Aenderung der Verarbeitungstaetigkeit\n",
" - Neue oder geaenderte Rechtsgrundlage\n",
" - Sicherheitsvorfall mit Bezug zur Verarbeitung\n",
" - Aenderung der eingesetzten Technologie oder Auftragsverarbeiter\n",
" - Neue Erkenntnisse zu Risiken oder Bedrohungen\n",
]
return "".join(lines)
def _generate_ai_module(ctx: dict) -> str:
"""Generate Section 8 for AI systems (EU AI Act)."""
lines = ["### Eingesetzte KI-Systeme\n"]
ai_systems = ctx.get("ai_systems", [])
if ai_systems:
lines.append("| System | Zweck | Risikokategorie | Human Oversight |")
lines.append("|--------|-------|-----------------|-----------------|")
for s in ai_systems:
risk = s.get("risk_category", "unbekannt")
oversight = "Ja" if s.get("has_human_oversight") else "Nein"
lines.append(f"| {s.get('name', 'N/A')} | {s.get('purpose', 'N/A')} | {risk} | {oversight} |")
lines.append("")
if ctx.get("subject_to_ai_act"):
lines.append(
"**Hinweis:** Das Unternehmen unterliegt dem EU AI Act (Verordnung (EU) 2024/1689). "
"Fuer Hochrisiko-KI-Systeme ist eine grundrechtliche Folgenabschaetzung "
"gemaess Art. 27 KI-VO durchzufuehren.\n"
)
high_risk = [s for s in ai_systems if s.get("risk_category") in ("high", "hoch")]
if high_risk:
lines.append("### Hochrisiko-KI-Systeme — Zusatzanforderungen\n")
lines.append("Fuer die folgenden Systeme gelten die Anforderungen aus Kapitel III KI-VO:\n")
for s in high_risk:
lines.append(f"- **{s.get('name', 'N/A')}**: Risikomanagement (Art. 9), "
f"Daten-Governance (Art. 10), Transparenz (Art. 13), "
f"Human Oversight (Art. 14)\n")
return "\n".join(lines)
def _assess_art36_consultation(ctx: dict, schwellwert: dict) -> bool:
"""Determine if Art. 36 DSGVO consultation with supervisory authority is required.
Art. 36 requires prior consultation when the DSFA indicates that the processing
would result in a HIGH residual risk despite mitigation measures.
"""
if schwellwert["criteria_met"] >= 4:
return True
if len(schwellwert.get("art35_abs3", [])) >= 2:
return True
ai_systems = ctx.get("ai_systems", [])
high_risk_ai = [s for s in ai_systems if s.get("risk_category") in ("high", "hoch", "unacceptable")]
if len(high_risk_ai) >= 2:
return True
return False

View File

@@ -0,0 +1,285 @@
"""TOM template generator V2 — SDM-structured TOM catalog.
Replaces the flat 17-measure list with a hierarchical structure based on
the 7 SDM Gewaehrleistungsziele (Standard-Datenschutzmodell V3.1a).
"""
# -- SDM-structured TOM catalog ---------------------------------------------
SDM_TOM_CATALOG = {
"verfuegbarkeit": {
"label": "Verfuegbarkeit",
"sdm_baustein": "SDM-B11 (Aufbewahren)",
"measures": [
{"name": "Redundante Datenhaltung", "description": "RAID, Replikation, Geo-Redundanz", "type": "technical"},
{"name": "Backup-Strategie", "description": "Taeglich inkrementell, woechentlich voll, verschluesselt", "type": "technical"},
{"name": "Disaster-Recovery-Plan", "description": "Dokumentierte RTO/RPO-Werte, jaehrliche Tests", "type": "organizational"},
{"name": "USV / Notstromversorgung", "description": "Unterbrechungsfreie Stromversorgung fuer kritische Systeme", "type": "technical"},
],
},
"integritaet": {
"label": "Integritaet",
"sdm_baustein": "SDM-B61 (Berichtigen)",
"measures": [
{"name": "Pruefsummen und Signaturen", "description": "Digitale Signaturen fuer Datenuebertragungen", "type": "technical"},
{"name": "Eingabevalidierung", "description": "Plausibilitaetspruefungen auf allen Eingabeschnittstellen", "type": "technical"},
{"name": "Change Management", "description": "Dokumentierte Aenderungsverfahren mit Freigabeprozess", "type": "organizational"},
{"name": "Versionierung", "description": "Versionierung von Datensaetzen und Konfigurationen", "type": "technical"},
],
},
"vertraulichkeit": {
"label": "Vertraulichkeit",
"sdm_baustein": "SDM-B51 (Zugriffe regeln)",
"measures": [
{"name": "Verschluesselung im Transit", "description": "TLS 1.3 fuer alle Verbindungen", "type": "technical"},
{"name": "Verschluesselung at Rest", "description": "AES-256 fuer gespeicherte Daten", "type": "technical"},
{"name": "Zugriffskonzept (RBAC)", "description": "Rollenbasiert, Least-Privilege-Prinzip, regelmaessige Reviews", "type": "technical"},
{"name": "Multi-Faktor-Authentifizierung", "description": "MFA fuer alle administrativen Zugaenge", "type": "technical"},
{"name": "Physische Zutrittskontrolle", "description": "Schluessel, Kartenleser, Besucherprotokoll", "type": "technical"},
{"name": "Vertraulichkeitsverpflichtung", "description": "Schriftliche Verpflichtung aller Mitarbeitenden", "type": "organizational"},
],
},
"nichtverkettung": {
"label": "Nichtverkettung",
"sdm_baustein": "SDM-B50 (Trennen)",
"measures": [
{"name": "Mandantentrennung", "description": "Logische Datentrennung nach Mandanten/Zweck", "type": "technical"},
{"name": "Pseudonymisierung", "description": "Wo fachlich moeglich, Einsatz von Pseudonymen", "type": "technical"},
{"name": "Zweckbindungspruefung", "description": "Pruefung bei jeder neuen Datennutzung", "type": "organizational"},
],
},
"transparenz": {
"label": "Transparenz",
"sdm_baustein": "SDM-B42 (Dokumentieren), SDM-B43 (Protokollieren)",
"measures": [
{"name": "Verarbeitungsverzeichnis", "description": "Art. 30 DS-GVO konformes VVT", "type": "organizational"},
{"name": "Audit-Logging", "description": "Vollstaendige Protokollierung aller Datenzugriffe", "type": "technical"},
{"name": "Informationspflichten", "description": "Art. 13/14 DS-GVO Datenschutzerklaerung", "type": "organizational"},
{"name": "Datenpannen-Prozess", "description": "Dokumentierter Meldeprozess Art. 33/34 DS-GVO", "type": "organizational"},
],
},
"intervenierbarkeit": {
"label": "Intervenierbarkeit",
"sdm_baustein": "SDM-B60 (Loeschen), SDM-B61 (Berichtigen), SDM-B62 (Einschraenken)",
"measures": [
{"name": "Betroffenenanfragen-Prozess", "description": "Auskunft, Loeschung, Berichtigung, Widerspruch", "type": "organizational"},
{"name": "Technische Loeschfaehigkeit", "description": "Loeschung mit Nachweis (Loeschprotokoll)", "type": "technical"},
{"name": "Datenportabilitaet", "description": "Export in maschinenlesbarem Format (Art. 20)", "type": "technical"},
{"name": "Sperrfunktion", "description": "Einschraenkung der Verarbeitung moeglich", "type": "technical"},
],
},
"datenminimierung": {
"label": "Datenminimierung",
"sdm_baustein": "SDM-B41 (Planen und Spezifizieren)",
"measures": [
{"name": "Erforderlichkeitspruefung", "description": "Regelmaessige Pruefung der erhobenen Datenfelder", "type": "organizational"},
{"name": "Automatisierte Loeschung", "description": "Fristgerechte Loeschung nach Aufbewahrungsfrist", "type": "technical"},
{"name": "Anonymisierung", "description": "Anonymisierung/Aggregation fuer Statistik", "type": "technical"},
{"name": "Privacy by Design", "description": "Datenschutz ab Entwurfsphase neuer Verarbeitungen", "type": "organizational"},
],
},
}
# -- Sector-specific extensions ----------------------------------------------
SECTOR_TOMS = {
"it_saas": {
"label": "IT / SaaS",
"measures": [
{"name": "Container-Isolation", "description": "Workload-Isolation zwischen Mandanten (Kubernetes Namespaces)", "type": "technical", "sdm_goal": "nichtverkettung"},
{"name": "API-Security", "description": "Rate Limiting, OAuth 2.0, API-Key-Rotation", "type": "technical", "sdm_goal": "vertraulichkeit"},
{"name": "DevSecOps Pipeline", "description": "SAST/DAST in CI/CD, Dependency Scanning", "type": "technical", "sdm_goal": "integritaet"},
{"name": "Secrets Management", "description": "Vault/KMS fuer Credentials, keine Hardcoded Secrets", "type": "technical", "sdm_goal": "vertraulichkeit"},
],
},
"gesundheitswesen": {
"label": "Gesundheitswesen",
"measures": [
{"name": "Patientenakten-Verschluesselung", "description": "Ende-zu-Ende-Verschluesselung fuer Gesundheitsdaten (Art. 9)", "type": "technical", "sdm_goal": "vertraulichkeit"},
{"name": "Notfallzugriff", "description": "Break-the-Glass-Verfahren fuer medizinische Notfaelle", "type": "organizational", "sdm_goal": "verfuegbarkeit"},
{"name": "Forschungsdaten-Anonymisierung", "description": "Vollstaendige Anonymisierung vor Forschungsnutzung", "type": "technical", "sdm_goal": "datenminimierung"},
],
},
"finanzdienstleistungen": {
"label": "Finanzdienstleistungen",
"measures": [
{"name": "Transaktions-Monitoring", "description": "Echtzeit-Ueberwachung auf Unregelmaessigkeiten (GwG)", "type": "technical", "sdm_goal": "integritaet"},
{"name": "Aufbewahrungspflichten", "description": "10 Jahre Aufbewahrung gemaess AO/HGB, danach Loeschung", "type": "organizational", "sdm_goal": "datenminimierung"},
{"name": "PCI-DSS Compliance", "description": "Payment Card Industry Standards fuer Kartendaten", "type": "technical", "sdm_goal": "vertraulichkeit"},
],
},
"handel": {
"label": "Handel / E-Commerce",
"measures": [
{"name": "Cookie-Consent-Management", "description": "TDDDG-konformes Einwilligungsmanagement", "type": "technical", "sdm_goal": "transparenz"},
{"name": "Gastzugang-Option", "description": "Bestellung ohne Pflicht-Kundenkonto (Datenminimierung)", "type": "organizational", "sdm_goal": "datenminimierung"},
{"name": "Zahlungsdaten-Tokenisierung", "description": "Keine direkte Speicherung von Zahlungsdaten", "type": "technical", "sdm_goal": "vertraulichkeit"},
],
},
"handwerk": {
"label": "Handwerk",
"measures": [
{"name": "Mobile-Device-Management", "description": "Absicherung mobiler Endgeraete auf Baustellen", "type": "technical", "sdm_goal": "vertraulichkeit"},
{"name": "Papierakten-Sicherung", "description": "Verschlossene Schraenke fuer physische Kundenakten", "type": "technical", "sdm_goal": "vertraulichkeit"},
],
},
}
# -- NIS2 / ISO 27001 / AI Act extensions -----------------------------------
NIS2_TOMS = [
{"name": "Incident-Response-Plan", "description": "NIS2-konformer Vorfallreaktionsplan (72h Meldepflicht an BSI)", "type": "organizational", "sdm_goal": "verfuegbarkeit"},
{"name": "Supply-Chain-Security", "description": "Bewertung der Lieferkettensicherheit (BSIG 2025)", "type": "organizational", "sdm_goal": "integritaet"},
{"name": "Vulnerability Management", "description": "Regelmaessige Schwachstellenscans, Patch-Management", "type": "technical", "sdm_goal": "integritaet"},
{"name": "Security Awareness", "description": "Pflicht-Schulungen Cybersicherheit fuer Geschaeftsleitung", "type": "organizational", "sdm_goal": "vertraulichkeit"},
]
ISO27001_TOMS = [
{"name": "ISMS Risikomanagement", "description": "ISO 27001 Anhang A — Informationssicherheits-Risikobewertung", "type": "organizational", "sdm_goal": "verfuegbarkeit"},
{"name": "Dokumentenlenkung", "description": "Versionierte Sicherheitsrichtlinien und -verfahren", "type": "organizational", "sdm_goal": "transparenz"},
{"name": "Management Review", "description": "Jaehrliche Ueberprufung des ISMS durch Geschaeftsleitung", "type": "organizational", "sdm_goal": "transparenz"},
]
AI_ACT_TOMS = [
{"name": "KI-Risikoklassifizierung", "description": "Bewertung aller KI-Systeme nach EU AI Act Risikokategorien", "type": "organizational", "sdm_goal": "transparenz"},
{"name": "Human Oversight", "description": "Menschliche Aufsicht fuer Hochrisiko-KI-Systeme (Art. 14 KI-VO)", "type": "organizational", "sdm_goal": "intervenierbarkeit"},
{"name": "KI-Transparenz", "description": "Transparenzpflichten bei KI-Einsatz gegenueber Betroffenen (Art. 13 KI-VO)", "type": "organizational", "sdm_goal": "transparenz"},
{"name": "KI-Bias-Monitoring", "description": "Ueberwachung auf diskriminierende Ergebnisse", "type": "technical", "sdm_goal": "integritaet"},
]
def generate_tom_drafts(ctx: dict) -> list[dict]:
"""Generate TOM measure drafts structured by SDM Gewaehrleistungsziele.
Args:
ctx: Flat dict from company-profile/template-context.
Returns:
List of TOM measure dicts with SDM goal assignment.
"""
measures = []
control_counter = 0
# Base SDM measures
for goal_key, goal_data in SDM_TOM_CATALOG.items():
for m in goal_data["measures"]:
control_counter += 1
measures.append(_build_measure(
counter=control_counter,
measure=m,
sdm_goal=goal_key,
sdm_baustein=goal_data["sdm_baustein"],
category=goal_data["label"],
ctx=ctx,
))
# Regulatory extensions
if ctx.get("subject_to_nis2"):
for m in NIS2_TOMS:
control_counter += 1
measures.append(_build_measure(
counter=control_counter,
measure=m,
sdm_goal=m["sdm_goal"],
sdm_baustein="NIS2 / BSIG 2025",
category="Cybersicherheit (NIS2)",
ctx=ctx,
))
if ctx.get("subject_to_iso27001"):
for m in ISO27001_TOMS:
control_counter += 1
measures.append(_build_measure(
counter=control_counter,
measure=m,
sdm_goal=m["sdm_goal"],
sdm_baustein="ISO 27001 Anhang A",
category="ISMS (ISO 27001)",
ctx=ctx,
))
if ctx.get("subject_to_ai_act") or ctx.get("has_ai_systems"):
for m in AI_ACT_TOMS:
control_counter += 1
measures.append(_build_measure(
counter=control_counter,
measure=m,
sdm_goal=m["sdm_goal"],
sdm_baustein="EU AI Act (2024/1689)",
category="KI-Compliance",
ctx=ctx,
))
# Sector-specific extensions
sector = _detect_sector(ctx)
if sector and sector in SECTOR_TOMS:
sector_data = SECTOR_TOMS[sector]
for m in sector_data["measures"]:
control_counter += 1
measures.append(_build_measure(
counter=control_counter,
measure=m,
sdm_goal=m.get("sdm_goal", "vertraulichkeit"),
sdm_baustein=f"Sektor: {sector_data['label']}",
category=f"Sektor ({sector_data['label']})",
ctx=ctx,
))
return measures
def sdm_coverage_summary(measures: list[dict]) -> dict:
"""Return coverage matrix: SDM goal -> measure count."""
summary = {}
for goal_key in SDM_TOM_CATALOG:
count = sum(1 for m in measures if m.get("sdm_goal") == goal_key)
summary[goal_key] = {
"label": SDM_TOM_CATALOG[goal_key]["label"],
"count": count,
}
return summary
# -- Internal helpers --------------------------------------------------------
def _build_measure(counter: int, measure: dict, sdm_goal: str,
sdm_baustein: str, category: str, ctx: dict) -> dict:
return {
"control_id": f"TOM-SDM-{counter:03d}",
"name": measure["name"],
"description": measure["description"],
"category": category,
"type": measure.get("type", "organizational"),
"sdm_goal": sdm_goal,
"sdm_baustein_ref": sdm_baustein,
"implementation_status": "not_implemented",
"effectiveness_rating": "not_assessed",
"responsible_department": "IT-Sicherheit",
"priority": _assess_priority(measure, ctx),
"review_frequency": f"{ctx.get('review_cycle_months', 12)} Monate",
}
def _assess_priority(measure: dict, ctx: dict) -> str:
name_lower = measure.get("name", "").lower()
if any(kw in name_lower for kw in ["verschluesselung", "mfa", "incident", "ki-risiko"]):
return "high"
if any(kw in name_lower for kw in ["backup", "zugriff", "logging", "loeschung"]):
return "high"
return "medium"
def _detect_sector(ctx: dict) -> str | None:
"""Map company industry to sector key."""
industry = (ctx.get("industry") or "").lower()
mapping = {
"technologie": "it_saas", "it": "it_saas", "saas": "it_saas", "software": "it_saas",
"gesundheit": "gesundheitswesen", "pharma": "gesundheitswesen", "medizin": "gesundheitswesen",
"finanz": "finanzdienstleistungen", "bank": "finanzdienstleistungen", "versicherung": "finanzdienstleistungen",
"handel": "handel", "e-commerce": "handel", "einzelhandel": "handel", "shop": "handel",
"handwerk": "handwerk", "bau": "handwerk", "kfz": "handwerk",
}
for keyword, sector in mapping.items():
if keyword in industry:
return sector
return None

View File

@@ -0,0 +1,393 @@
"""VVT template generator V2 — sector-specific VVT activity drafts.
Generates Art. 30 DS-GVO compliant VVT entries with sector-specific
standard processing activities inspired by BayLDA patterns.
"""
from typing import Optional
# -- Sector activity catalogs ------------------------------------------------
SECTOR_ACTIVITIES = {
"it_saas": [
{
"name": "SaaS-Plattformbetrieb",
"purposes": ["Bereitstellung und Betrieb der SaaS-Plattform"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO (Vertragserfullung)"],
"data_subject_categories": ["Kunden", "Endnutzer"],
"personal_data_categories": ["Stammdaten", "Nutzungsdaten", "Inhaltsdaten", "Logdaten"],
"recipient_categories": ["Hosting-Anbieter (AVV)", "Support-Dienstleister (AVV)"],
"retention_period": "90 Tage nach Vertragsende + gesetzl. Aufbewahrung",
"tom_description": "Mandantentrennung, Verschluesselung, RBAC",
"dpia_required": True,
},
{
"name": "Kundenverwaltung / CRM",
"purposes": ["Verwaltung von Kundenbeziehungen, Vertragsmanagement"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Kunden", "Ansprechpartner", "Interessenten"],
"personal_data_categories": ["Kontaktdaten", "Vertragsdaten", "Kommunikationshistorie"],
"recipient_categories": ["CRM-Anbieter (AVV)"],
"retention_period": "3 Jahre nach letztem Kontakt, 10 Jahre Rechnungsdaten",
"tom_description": "Zugriffsbeschraenkung Vertrieb/Support, Protokollierung",
},
{
"name": "E-Mail-Marketing / Newsletter",
"purposes": ["Versand von Produkt-Updates und Marketing-Newsletter"],
"legal_bases": ["Art. 6 Abs. 1 lit. a DS-GVO (Einwilligung)", "UWG §7"],
"data_subject_categories": ["Newsletter-Abonnenten"],
"personal_data_categories": ["E-Mail-Adresse", "Name", "Oeffnungs-/Klickverhalten"],
"recipient_categories": ["E-Mail-Dienstleister (AVV)"],
"retention_period": "Unverzueglich nach Widerruf",
"tom_description": "Double-Opt-In, einfache Abmeldefunktion",
},
{
"name": "Webanalyse",
"purposes": ["Analyse der Website-Nutzung zur Verbesserung"],
"legal_bases": ["Art. 6 Abs. 1 lit. a DS-GVO (Einwilligung via Cookie-Banner)"],
"data_subject_categories": ["Website-Besucher"],
"personal_data_categories": ["IP-Adresse (anonymisiert)", "Seitenaufrufe", "Geraeteinformationen"],
"recipient_categories": ["Analyse-Anbieter (AVV)"],
"retention_period": "14 Monate",
"tom_description": "IP-Anonymisierung, Cookie-Consent (TDDDG §25)",
},
{
"name": "Bewerbermanagement",
"purposes": ["Bearbeitung von Bewerbungen"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO i.V.m. §26 BDSG"],
"data_subject_categories": ["Bewerber"],
"personal_data_categories": ["Kontaktdaten", "Lebenslauf", "Qualifikationen"],
"recipient_categories": ["Fachabteilung"],
"retention_period": "6 Monate nach Verfahrensabschluss (AGG)",
"tom_description": "Zugriffsschutz Bewerbungsportal, verschluesselte Uebertragung",
},
{
"name": "Mitarbeiterverwaltung / HR",
"purposes": ["Personalverwaltung, Lohnabrechnung, Arbeitszeiterfassung"],
"legal_bases": ["Art. 6 Abs. 1 lit. b/c DS-GVO i.V.m. §26 BDSG"],
"data_subject_categories": ["Beschaeftigte"],
"personal_data_categories": ["Stammdaten", "Vertragsdaten", "Bankverbindung", "Arbeitszeiten"],
"recipient_categories": ["Lohnbuero (AVV)", "Finanzamt", "Sozialversicherungstraeger"],
"retention_period": "10 Jahre nach Austritt",
"tom_description": "Besonderer Zugriffsschutz (nur HR), verschluesselte Speicherung",
},
{
"name": "Support-Ticketing",
"purposes": ["Bearbeitung von Kundenanfragen und Stoerungsmeldungen"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Kunden", "Endnutzer"],
"personal_data_categories": ["Kontaktdaten", "Ticket-Inhalt", "Systemlogs"],
"recipient_categories": ["Support-Tool-Anbieter (AVV)"],
"retention_period": "2 Jahre nach Ticket-Schliessung",
"tom_description": "Rollenbasierter Zugriff, Pseudonymisierung in Reports",
},
{
"name": "Logging und Monitoring",
"purposes": ["Sicherheitsueberwachung, Fehleranalyse"],
"legal_bases": ["Art. 6 Abs. 1 lit. f DS-GVO (berechtigtes Interesse: IT-Sicherheit)"],
"data_subject_categories": ["Plattform-Nutzer", "Administratoren"],
"personal_data_categories": ["IP-Adressen", "Zugriffszeitpunkte", "Fehlerprotokolle"],
"recipient_categories": ["Log-Management-Anbieter (AVV)"],
"retention_period": "30 Tage Anwendungslogs, 90 Tage Sicherheitslogs",
"tom_description": "Zugriffsschutz Logdaten, automatische Rotation",
},
],
"gesundheitswesen": [
{
"name": "Patientenverwaltung",
"purposes": ["Patientenakte, Behandlungsdokumentation"],
"legal_bases": ["Art. 9 Abs. 2 lit. h DS-GVO i.V.m. §630f BGB"],
"data_subject_categories": ["Patienten"],
"personal_data_categories": ["Stammdaten", "Versicherung", "Diagnosen", "Befunde (Art. 9)"],
"recipient_categories": ["PVS-Anbieter (AVV)", "Labor (AVV)", "ueberweisende Aerzte"],
"retention_period": "10 Jahre nach letzter Behandlung (§630f BGB)",
"tom_description": "Verschluesselung Patientenakte, Notfallzugriff",
"dpia_required": True,
},
{
"name": "Abrechnung (KV/PKV)",
"purposes": ["Abrechnung aerztlicher Leistungen"],
"legal_bases": ["Art. 6 Abs. 1 lit. c DS-GVO", "Art. 9 Abs. 2 lit. h"],
"data_subject_categories": ["Patienten"],
"personal_data_categories": ["Stammdaten", "Versicherung", "Diagnosen (ICD)", "Leistungsziffern"],
"recipient_categories": ["KV", "PKV", "Abrechnungsstelle (AVV)"],
"retention_period": "10 Jahre (AO)",
"tom_description": "Verschluesselte Uebermittlung (KV-Connect/KIM)",
},
],
"handel": [
{
"name": "Bestellabwicklung",
"purposes": ["Bestellannahme, Versand, Rechnungsstellung"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Kunden (Besteller)"],
"personal_data_categories": ["Kontaktdaten", "Lieferadresse", "Bestelldaten", "Rechnungsdaten"],
"recipient_categories": ["Versanddienstleister", "Zahlungsanbieter (AVV)"],
"retention_period": "10 Jahre Rechnungen, 3 Jahre Bestelldaten",
"tom_description": "Verschluesselte Uebertragung, Zugriffsschutz",
},
{
"name": "Kundenkonto",
"purposes": ["Bereitstellung Kundenkonto (optional)"],
"legal_bases": ["Art. 6 Abs. 1 lit. a/b DS-GVO"],
"data_subject_categories": ["Registrierte Kunden"],
"personal_data_categories": ["Stammdaten", "Passwort (gehasht)", "Bestellhistorie"],
"recipient_categories": ["Shop-Plattform (AVV)"],
"retention_period": "Sofort nach Kontoloesch-Anfrage, Rechnungen 10 Jahre",
"tom_description": "MFA-Option, bcrypt Passwortspeicherung, Gastzugang-Alternative",
},
{
"name": "Zahlungsabwicklung",
"purposes": ["Abwicklung von Zahlungsvorgaengen"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Zahlende Kunden"],
"personal_data_categories": ["Zahlungsart", "Transaktionsdaten"],
"recipient_categories": ["Payment-Service-Provider"],
"retention_period": "10 Jahre (AO)",
"tom_description": "PCI-DSS, Tokenisierung, keine direkte Kartenspeicherung",
},
],
"handwerk": [
{
"name": "Kundenauftraege und Angebotserstellung",
"purposes": ["Angebotserstellung, Auftragsabwicklung, Rechnungsstellung"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Kunden (Privat/Gewerbe)"],
"personal_data_categories": ["Kontaktdaten", "Objektadresse", "Auftrag", "Rechnungsdaten"],
"recipient_categories": ["Steuerberater", "ggf. Subunternehmer"],
"retention_period": "10 Jahre Rechnungen, 5 Jahre Gewaehrleistung",
"tom_description": "Zugriffskontrolle Auftragssystem",
},
{
"name": "Baustellendokumentation",
"purposes": ["Dokumentation Baufortschritt, Maengelprotokoll"],
"legal_bases": ["Art. 6 Abs. 1 lit. b/f DS-GVO"],
"data_subject_categories": ["Kunden", "Mitarbeitende"],
"personal_data_categories": ["Fotos", "Protokolle", "Abnahmedokumente"],
"recipient_categories": ["Auftraggeber", "Architekten"],
"retention_period": "5 Jahre nach Abnahme",
"tom_description": "Projektordner mit Zugriffsbeschraenkung",
},
],
"bildung": [
{
"name": "Schueler-/Studierendenverwaltung",
"purposes": ["Verwaltung von Schueler-/Studierendendaten"],
"legal_bases": ["Art. 6 Abs. 1 lit. c/e DS-GVO i.V.m. Schulgesetz"],
"data_subject_categories": ["Schueler/Studierende (ggf. Minderjaehrige)", "Erziehungsberechtigte"],
"personal_data_categories": ["Stammdaten", "Kontaktdaten Erziehungsberechtigte"],
"recipient_categories": ["Schulverwaltungssoftware (AVV)", "Schulbehoerde"],
"retention_period": "Gemaess Schulgesetz (i.d.R. 5 Jahre nach Abgang)",
"tom_description": "Besonderer Zugriffsschutz, Einwilligung Erziehungsberechtigte",
"dpia_required": True,
},
{
"name": "Notenverarbeitung",
"purposes": ["Leistungsbewertung, Zeugniserstellung"],
"legal_bases": ["Art. 6 Abs. 1 lit. c/e DS-GVO i.V.m. Schulgesetz"],
"data_subject_categories": ["Schueler/Studierende"],
"personal_data_categories": ["Noten", "Leistungsbewertungen", "Pruefungsergebnisse"],
"recipient_categories": ["Lehrkraefte", "Schulleitung"],
"retention_period": "Zeugniskopien 50 Jahre, Einzelnoten 2 Jahre",
"tom_description": "Zugriffsbeschraenkung auf Fachlehrkraft, verschluesselt",
},
],
"beratung": [
{
"name": "Mandantenverwaltung",
"purposes": ["Verwaltung von Mandantenbeziehungen"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Mandanten", "Ansprechpartner"],
"personal_data_categories": ["Kontaktdaten", "Vertragsdaten", "Korrespondenz"],
"recipient_categories": ["Kanzleisoftware (AVV)", "Steuerberater"],
"retention_period": "10 Jahre Rechnungen, 5 Jahre Handakten",
"tom_description": "Mandantengeheimnis, Need-to-know-Prinzip",
},
{
"name": "Projektmanagement",
"purposes": ["Planung und Steuerung von Beratungsprojekten"],
"legal_bases": ["Art. 6 Abs. 1 lit. b/f DS-GVO"],
"data_subject_categories": ["Projektbeteiligte"],
"personal_data_categories": ["Projektdaten", "Aufgaben", "Zeiterfassung"],
"recipient_categories": ["PM-Tool (AVV)", "Mandant"],
"retention_period": "2 Jahre nach Projektabschluss",
"tom_description": "Projektspezifische Zugriffsrechte, Mandantentrennung",
},
{
"name": "Zeiterfassung und Abrechnung",
"purposes": ["Stundenerfassung, Abrechnung gegenueber Mandanten"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO"],
"data_subject_categories": ["Berater/Mitarbeitende", "Mandanten"],
"personal_data_categories": ["Arbeitszeiten", "Taetigkeitsbeschreibungen", "Stundensaetze"],
"recipient_categories": ["Abrechnungssystem (AVV)", "Buchhaltung"],
"retention_period": "10 Jahre (AO)",
"tom_description": "Zugriff nur eigene Zeiten + Projektleitung",
},
],
}
# Industry -> Sector mapping
INDUSTRY_SECTOR_MAP = {
"technologie": "it_saas", "it": "it_saas", "saas": "it_saas", "software": "it_saas",
"it dienstleistungen": "it_saas",
"gesundheit": "gesundheitswesen", "pharma": "gesundheitswesen",
"e-commerce": "handel", "handel": "handel", "einzelhandel": "handel",
"handwerk": "handwerk", "bau": "handwerk", "kfz": "handwerk",
"bildung": "bildung", "schule": "bildung", "hochschule": "bildung",
"beratung": "beratung", "consulting": "beratung", "kanzlei": "beratung",
"recht": "beratung",
}
def generate_vvt_drafts(ctx: dict) -> list[dict]:
"""Generate VVT activity drafts, sector-specific if possible.
Args:
ctx: Flat dict from company-profile/template-context.
Returns:
List of VVT activity dicts ready for creation.
"""
company = ctx.get("company_name", "Unbekannt")
dpo = ctx.get("dpo_name", "")
sector = _detect_sector(ctx)
# Use sector-specific activities if available, else generate from systems
if sector and sector in SECTOR_ACTIVITIES:
activities = _generate_sector_vvt(ctx, sector, company, dpo)
else:
activities = _generate_system_vvt(ctx, company, dpo)
# Always add standard HR activity if not already present
has_hr = any("mitarbeiter" in a.get("name", "").lower() or "hr" in a.get("name", "").lower()
for a in activities)
if not has_hr and len(activities) > 0:
activities.append(_build_hr_activity(len(activities) + 1, company, dpo))
return activities
def _detect_sector(ctx: dict) -> Optional[str]:
industry = (ctx.get("industry") or "").lower().strip()
for keyword, sector in INDUSTRY_SECTOR_MAP.items():
if keyword in industry:
return sector
return None
def _generate_sector_vvt(ctx: dict, sector: str, company: str, dpo: str) -> list[dict]:
activities = []
sector_data = SECTOR_ACTIVITIES[sector]
for i, template in enumerate(sector_data, 1):
activity = {
"vvt_id": f"VVT-{sector.upper()[:3]}-{i:03d}",
"name": template["name"],
"description": f"Automatisch generierter VVT-Eintrag: {template['name']}",
"purposes": template["purposes"],
"legal_bases": template["legal_bases"],
"data_subject_categories": template["data_subject_categories"],
"personal_data_categories": template["personal_data_categories"],
"recipient_categories": template["recipient_categories"],
"third_country_transfers": _assess_third_country(ctx),
"retention_period": {"default": template["retention_period"]},
"tom_description": template["tom_description"],
"business_function": _infer_business_function(template["name"]),
"systems": [],
"protection_level": "HIGH" if template.get("dpia_required") else "MEDIUM",
"dpia_required": template.get("dpia_required", False),
"status": "DRAFT",
"responsible": dpo or company,
"source_sector": sector,
}
activities.append(activity)
return activities
def _generate_system_vvt(ctx: dict, company: str, dpo: str) -> list[dict]:
"""Fallback: generate VVT per processing system (original approach)."""
systems = ctx.get("processing_systems", [])
activities = []
for i, system in enumerate(systems, 1):
name = system.get("name", f"System {i}")
vendor = system.get("vendor", "")
hosting = system.get("hosting", "on-premise")
categories = system.get("personal_data_categories", [])
activity = {
"vvt_id": f"VVT-SYS-{i:03d}",
"name": f"Verarbeitung in {name}",
"description": f"VVT-Eintrag fuer System '{name}'"
+ (f" (Anbieter: {vendor})" if vendor else ""),
"purposes": [f"Datenverarbeitung via {name}"],
"legal_bases": ["Art. 6 Abs. 1 lit. b DS-GVO (Vertragserfullung)"],
"data_subject_categories": [],
"personal_data_categories": categories,
"recipient_categories": [vendor] if vendor else [],
"third_country_transfers": _assess_third_country_hosting(hosting),
"retention_period": {"default": "Gemaess Loeschfristenkatalog"},
"tom_description": f"Siehe TOM-Katalog fuer {name}",
"business_function": "IT",
"systems": [name],
"deployment_model": hosting,
"protection_level": "HIGH" if len(categories) > 3 else "MEDIUM",
"dpia_required": len(categories) > 3,
"status": "DRAFT",
"responsible": dpo or company,
}
activities.append(activity)
return activities
def _build_hr_activity(index: int, company: str, dpo: str) -> dict:
return {
"vvt_id": f"VVT-STD-{index:03d}",
"name": "Mitarbeiterverwaltung / HR",
"description": "Standard-Verarbeitungstaetigkeit Personalverwaltung",
"purposes": ["Personalverwaltung, Lohnabrechnung, Arbeitszeiterfassung"],
"legal_bases": ["Art. 6 Abs. 1 lit. b/c DS-GVO i.V.m. §26 BDSG"],
"data_subject_categories": ["Beschaeftigte"],
"personal_data_categories": ["Stammdaten", "Vertragsdaten", "Bankverbindung", "Arbeitszeiten"],
"recipient_categories": ["Lohnbuero (AVV)", "Finanzamt", "Sozialversicherungstraeger"],
"third_country_transfers": [],
"retention_period": {"default": "10 Jahre nach Austritt"},
"tom_description": "Besonderer Zugriffsschutz (nur HR), verschluesselte Speicherung",
"business_function": "HR",
"systems": [],
"protection_level": "HIGH",
"dpia_required": False,
"status": "DRAFT",
"responsible": dpo or company,
}
def _assess_third_country(ctx: dict) -> list:
if ctx.get("third_country_transfer"):
return [{"country": "Abhaengig von Dienstleister", "mechanism": "Pruefung erforderlich"}]
return []
def _assess_third_country_hosting(hosting: str) -> list:
if hosting in ("us-cloud", "international"):
return [{"country": "USA", "mechanism": "EU-US Data Privacy Framework"}]
return []
def _infer_business_function(name: str) -> str:
name_lower = name.lower()
if any(kw in name_lower for kw in ["mitarbeiter", "hr", "personal", "bewerbung"]):
return "HR"
if any(kw in name_lower for kw in ["abrechnung", "rechnung", "zahlung", "buchhaltung"]):
return "Finanzen"
if any(kw in name_lower for kw in ["marketing", "newsletter", "webanalyse", "crm", "akquise"]):
return "Marketing/Vertrieb"
if any(kw in name_lower for kw in ["support", "ticket", "kundenservice"]):
return "Support"
if any(kw in name_lower for kw in ["patient", "befund", "labor", "termin"]):
return "Medizin"
if any(kw in name_lower for kw in ["schueler", "noten", "lernplattform"]):
return "Paedagogik"
return "IT"