Files
breakpilot-compliance/backend-compliance/compliance/api/process_task_routes.py
Benjamin Admin 49ce417428
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 32s
CI/CD / test-python-backend-compliance (push) Successful in 34s
CI/CD / test-python-document-crawler (push) Successful in 23s
CI/CD / test-python-dsms-gateway (push) Successful in 21s
CI/CD / validate-canonical-controls (push) Successful in 11s
CI/CD / Deploy (push) Successful in 2s
feat: add compliance modules 2-5 (dashboard, security templates, process manager, evidence collector)
Module 2: Extended Compliance Dashboard with roadmap, module-status, next-actions, snapshots, score-history
Module 3: 7 German security document templates (IT-Sicherheitskonzept, Datenschutz, Backup, Logging, Incident-Response, Zugriff, Risikomanagement)
Module 4: Compliance Process Manager with CRUD, complete/skip/seed, ~50 seed tasks, 3-tab UI
Module 5: Evidence Collector Extended with automated checks, control-mapping, coverage report, 4-tab UI

Also includes: canonical control library enhancements (verification method, categories, dedup), control generator improvements, RAG client extensions

52 tests pass, frontend builds clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 21:03:04 +01:00

1073 lines
40 KiB
Python

"""
FastAPI routes for Compliance Process Manager — recurring compliance tasks.
Endpoints:
GET /process-tasks — list with filters (status, category, frequency, overdue, etc.)
GET /process-tasks/stats — counts by status, category, due windows
GET /process-tasks/upcoming — tasks due in next N days
POST /process-tasks — create task
GET /process-tasks/{id} — single task
PUT /process-tasks/{id} — update task
DELETE /process-tasks/{id} — delete task
POST /process-tasks/{id}/complete — complete a task (with history + next_due recalc)
POST /process-tasks/{id}/skip — skip a task (with reason + next_due recalc)
GET /process-tasks/{id}/history — task history entries
POST /process-tasks/seed — seed ~50 standard tasks (idempotent)
"""
import json
import logging
from datetime import datetime
from typing import Optional, List, Any, Dict
from fastapi import APIRouter, Depends, HTTPException, Query, Header
from pydantic import BaseModel
from sqlalchemy import text
from sqlalchemy.orm import Session
from classroom_engine.database import get_db
from .tenant_utils import get_tenant_id as _get_tenant_id
from .db_utils import row_to_dict as _row_to_dict
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/process-tasks", tags=["process-tasks"])
# =============================================================================
# Constants
# =============================================================================
VALID_CATEGORIES = {"dsgvo", "nis2", "bsi", "iso27001", "ai_act", "internal"}
VALID_PRIORITIES = {"critical", "high", "medium", "low"}
VALID_FREQUENCIES = {"weekly", "monthly", "quarterly", "semi_annual", "yearly", "once"}
VALID_STATUSES = {"pending", "in_progress", "completed", "overdue", "skipped"}
FREQUENCY_DAYS = {
"weekly": 7,
"monthly": 30,
"quarterly": 90,
"semi_annual": 182,
"yearly": 365,
"once": None,
}
# =============================================================================
# Pydantic Schemas
# =============================================================================
class ProcessTaskCreate(BaseModel):
task_code: str
title: str
description: Optional[str] = None
category: str
priority: str = "medium"
frequency: str = "yearly"
assigned_to: Optional[str] = None
responsible_team: Optional[str] = None
linked_control_ids: Optional[List] = []
linked_module: Optional[str] = None
next_due_date: Optional[str] = None
due_reminder_days: int = 14
notes: Optional[str] = None
tags: Optional[List] = []
class ProcessTaskUpdate(BaseModel):
title: Optional[str] = None
description: Optional[str] = None
category: Optional[str] = None
priority: Optional[str] = None
frequency: Optional[str] = None
assigned_to: Optional[str] = None
responsible_team: Optional[str] = None
linked_control_ids: Optional[List] = None
linked_module: Optional[str] = None
next_due_date: Optional[str] = None
due_reminder_days: Optional[int] = None
status: Optional[str] = None
notes: Optional[str] = None
tags: Optional[List] = None
class ProcessTaskComplete(BaseModel):
completed_by: Optional[str] = None
result: Optional[str] = None
evidence_id: Optional[str] = None
notes: Optional[str] = None
class ProcessTaskSkip(BaseModel):
reason: str
# =============================================================================
# Routes
# =============================================================================
@router.get("")
async def list_tasks(
status: Optional[str] = Query(None),
category: Optional[str] = Query(None),
frequency: Optional[str] = Query(None),
assigned_to: Optional[str] = Query(None),
overdue: Optional[bool] = Query(None),
limit: int = Query(100, ge=1, le=500),
offset: int = Query(0, ge=0),
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
):
"""List process tasks with optional filters."""
where_clauses = ["tenant_id = :tenant_id"]
params: Dict[str, Any] = {"tenant_id": tenant_id, "limit": limit, "offset": offset}
if status:
where_clauses.append("status = :status")
params["status"] = status
if category:
where_clauses.append("category = :category")
params["category"] = category
if frequency:
where_clauses.append("frequency = :frequency")
params["frequency"] = frequency
if assigned_to:
where_clauses.append("assigned_to ILIKE :assigned_to")
params["assigned_to"] = f"%{assigned_to}%"
if overdue:
where_clauses.append("next_due_date < CURRENT_DATE AND status NOT IN ('completed','skipped')")
where_sql = " AND ".join(where_clauses)
total_row = db.execute(
text(f"SELECT COUNT(*) FROM compliance_process_tasks WHERE {where_sql}"),
params,
).fetchone()
total = total_row[0] if total_row else 0
rows = db.execute(
text(f"""
SELECT * FROM compliance_process_tasks
WHERE {where_sql}
ORDER BY
CASE priority
WHEN 'critical' THEN 0
WHEN 'high' THEN 1
WHEN 'medium' THEN 2
ELSE 3
END,
next_due_date ASC NULLS LAST,
created_at DESC
LIMIT :limit OFFSET :offset
"""),
params,
).fetchall()
return {
"tasks": [_row_to_dict(r) for r in rows],
"total": total,
}
@router.get("/stats")
async def get_stats(
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
):
"""Return task counts by status, category, and due windows."""
row = db.execute(text("""
SELECT
COUNT(*) AS total,
COUNT(*) FILTER (WHERE status = 'pending') AS pending,
COUNT(*) FILTER (WHERE status = 'in_progress') AS in_progress,
COUNT(*) FILTER (WHERE status = 'completed') AS completed,
COUNT(*) FILTER (WHERE status = 'overdue') AS overdue,
COUNT(*) FILTER (WHERE status = 'skipped') AS skipped,
COUNT(*) FILTER (WHERE next_due_date < CURRENT_DATE
AND status NOT IN ('completed','skipped')) AS overdue_count,
COUNT(*) FILTER (WHERE next_due_date <= CURRENT_DATE + 7
AND next_due_date >= CURRENT_DATE
AND status NOT IN ('completed','skipped')) AS due_7_days,
COUNT(*) FILTER (WHERE next_due_date <= CURRENT_DATE + 14
AND next_due_date >= CURRENT_DATE
AND status NOT IN ('completed','skipped')) AS due_14_days,
COUNT(*) FILTER (WHERE next_due_date <= CURRENT_DATE + 30
AND next_due_date >= CURRENT_DATE
AND status NOT IN ('completed','skipped')) AS due_30_days
FROM compliance_process_tasks
WHERE tenant_id = :tenant_id
"""), {"tenant_id": tenant_id}).fetchone()
by_status = {}
by_category = {}
if row:
d = dict(row._mapping)
by_status = {
"pending": d.get("pending", 0) or 0,
"in_progress": d.get("in_progress", 0) or 0,
"completed": d.get("completed", 0) or 0,
"overdue": d.get("overdue", 0) or 0,
"skipped": d.get("skipped", 0) or 0,
}
else:
d = {}
# Category counts
cat_rows = db.execute(text("""
SELECT category, COUNT(*) AS cnt
FROM compliance_process_tasks
WHERE tenant_id = :tenant_id
GROUP BY category
"""), {"tenant_id": tenant_id}).fetchall()
by_category = {r._mapping["category"]: r._mapping["cnt"] for r in cat_rows}
return {
"total": (d.get("total", 0) or 0),
"by_status": by_status,
"by_category": by_category,
"overdue_count": (d.get("overdue_count", 0) or 0),
"due_7_days": (d.get("due_7_days", 0) or 0),
"due_14_days": (d.get("due_14_days", 0) or 0),
"due_30_days": (d.get("due_30_days", 0) or 0),
}
@router.get("/upcoming")
async def get_upcoming(
days: int = Query(30, ge=1, le=365),
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
):
"""Tasks due in next N days."""
rows = db.execute(text("""
SELECT * FROM compliance_process_tasks
WHERE tenant_id = :tenant_id
AND next_due_date IS NOT NULL
AND next_due_date <= CURRENT_DATE + :days
AND next_due_date >= CURRENT_DATE
AND status NOT IN ('completed','skipped')
ORDER BY next_due_date ASC
"""), {"tenant_id": tenant_id, "days": days}).fetchall()
return {"tasks": [_row_to_dict(r) for r in rows]}
@router.post("", status_code=201)
async def create_task(
payload: ProcessTaskCreate,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Create a new process task."""
logger.info("create_task user_id=%s tenant_id=%s code=%s", x_user_id, tenant_id, payload.task_code)
if payload.category not in VALID_CATEGORIES:
raise HTTPException(status_code=400, detail=f"Invalid category. Must be one of: {', '.join(sorted(VALID_CATEGORIES))}")
if payload.priority not in VALID_PRIORITIES:
raise HTTPException(status_code=400, detail=f"Invalid priority. Must be one of: {', '.join(sorted(VALID_PRIORITIES))}")
if payload.frequency not in VALID_FREQUENCIES:
raise HTTPException(status_code=400, detail=f"Invalid frequency. Must be one of: {', '.join(sorted(VALID_FREQUENCIES))}")
row = db.execute(text("""
INSERT INTO compliance_process_tasks
(tenant_id, task_code, title, description, category, priority, frequency,
assigned_to, responsible_team, linked_control_ids, linked_module,
next_due_date, due_reminder_days, notes, tags)
VALUES
(:tenant_id, :task_code, :title, :description, :category, :priority, :frequency,
:assigned_to, :responsible_team, CAST(:linked_control_ids AS jsonb), :linked_module,
:next_due_date, :due_reminder_days, :notes, CAST(:tags AS jsonb))
RETURNING *
"""), {
"tenant_id": tenant_id,
"task_code": payload.task_code,
"title": payload.title,
"description": payload.description,
"category": payload.category,
"priority": payload.priority,
"frequency": payload.frequency,
"assigned_to": payload.assigned_to,
"responsible_team": payload.responsible_team,
"linked_control_ids": json.dumps(payload.linked_control_ids or []),
"linked_module": payload.linked_module,
"next_due_date": payload.next_due_date,
"due_reminder_days": payload.due_reminder_days,
"notes": payload.notes,
"tags": json.dumps(payload.tags or []),
}).fetchone()
db.commit()
return _row_to_dict(row)
@router.get("/{task_id}")
async def get_task(
task_id: str,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
):
"""Get single process task."""
row = db.execute(text("""
SELECT * FROM compliance_process_tasks
WHERE id = :id AND tenant_id = :tenant_id
"""), {"id": task_id, "tenant_id": tenant_id}).fetchone()
if not row:
raise HTTPException(status_code=404, detail="Task not found")
return _row_to_dict(row)
@router.put("/{task_id}")
async def update_task(
task_id: str,
payload: ProcessTaskUpdate,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Update a process task."""
logger.info("update_task user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, task_id)
updates: Dict[str, Any] = {"id": task_id, "tenant_id": tenant_id, "updated_at": datetime.utcnow()}
set_clauses = ["updated_at = :updated_at"]
data = payload.model_dump(exclude_unset=True)
# Validate enum fields if provided
if "category" in data and data["category"] not in VALID_CATEGORIES:
raise HTTPException(status_code=400, detail=f"Invalid category. Must be one of: {', '.join(sorted(VALID_CATEGORIES))}")
if "priority" in data and data["priority"] not in VALID_PRIORITIES:
raise HTTPException(status_code=400, detail=f"Invalid priority. Must be one of: {', '.join(sorted(VALID_PRIORITIES))}")
if "frequency" in data and data["frequency"] not in VALID_FREQUENCIES:
raise HTTPException(status_code=400, detail=f"Invalid frequency. Must be one of: {', '.join(sorted(VALID_FREQUENCIES))}")
if "status" in data and data["status"] not in VALID_STATUSES:
raise HTTPException(status_code=400, detail=f"Invalid status. Must be one of: {', '.join(sorted(VALID_STATUSES))}")
for field, value in data.items():
if field in ("linked_control_ids", "tags", "follow_up_actions"):
updates[field] = json.dumps(value or [])
set_clauses.append(f"{field} = CAST(:{field} AS jsonb)")
else:
updates[field] = value
set_clauses.append(f"{field} = :{field}")
if len(set_clauses) == 1:
raise HTTPException(status_code=400, detail="No fields to update")
row = db.execute(text(f"""
UPDATE compliance_process_tasks
SET {', '.join(set_clauses)}
WHERE id = :id AND tenant_id = :tenant_id
RETURNING *
"""), updates).fetchone()
db.commit()
if not row:
raise HTTPException(status_code=404, detail="Task not found")
return _row_to_dict(row)
@router.delete("/{task_id}", status_code=204)
async def delete_task(
task_id: str,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Delete a process task."""
logger.info("delete_task user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, task_id)
result = db.execute(text("""
DELETE FROM compliance_process_tasks
WHERE id = :id AND tenant_id = :tenant_id
"""), {"id": task_id, "tenant_id": tenant_id})
db.commit()
if result.rowcount == 0:
raise HTTPException(status_code=404, detail="Task not found")
@router.post("/{task_id}/complete")
async def complete_task(
task_id: str,
payload: ProcessTaskComplete,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Complete a task: insert history, update task, calculate next due date."""
logger.info("complete_task user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, task_id)
# Fetch current task
task_row = db.execute(text("""
SELECT * FROM compliance_process_tasks
WHERE id = :id AND tenant_id = :tenant_id
"""), {"id": task_id, "tenant_id": tenant_id}).fetchone()
if not task_row:
raise HTTPException(status_code=404, detail="Task not found")
task = dict(task_row._mapping)
# Insert history entry
db.execute(text("""
INSERT INTO compliance_process_task_history
(task_id, completed_by, completed_at, result, evidence_id, notes, status)
VALUES
(:task_id, :completed_by, NOW(), :result, :evidence_id, :notes, 'completed')
"""), {
"task_id": task_id,
"completed_by": payload.completed_by,
"result": payload.result,
"evidence_id": payload.evidence_id,
"notes": payload.notes,
})
# Calculate next due date
freq = task.get("frequency", "yearly")
days_delta = FREQUENCY_DAYS.get(freq)
if days_delta is not None:
# Recurring task — set next due and reset to pending
row = db.execute(text("""
UPDATE compliance_process_tasks
SET last_completed_at = NOW(),
status = 'pending',
completion_date = NOW(),
completion_result = :result,
completion_evidence_id = :evidence_id,
next_due_date = CURRENT_DATE + :days_delta,
updated_at = NOW()
WHERE id = :id AND tenant_id = :tenant_id
RETURNING *
"""), {
"id": task_id,
"tenant_id": tenant_id,
"result": payload.result,
"evidence_id": payload.evidence_id,
"days_delta": days_delta,
}).fetchone()
else:
# One-time task — mark completed, no next due
row = db.execute(text("""
UPDATE compliance_process_tasks
SET last_completed_at = NOW(),
status = 'completed',
completion_date = NOW(),
completion_result = :result,
completion_evidence_id = :evidence_id,
next_due_date = NULL,
updated_at = NOW()
WHERE id = :id AND tenant_id = :tenant_id
RETURNING *
"""), {
"id": task_id,
"tenant_id": tenant_id,
"result": payload.result,
"evidence_id": payload.evidence_id,
}).fetchone()
db.commit()
return _row_to_dict(row)
@router.post("/{task_id}/skip")
async def skip_task(
task_id: str,
payload: ProcessTaskSkip,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Skip a task with reason: insert history, calculate next due date."""
logger.info("skip_task user_id=%s tenant_id=%s id=%s", x_user_id, tenant_id, task_id)
# Fetch current task
task_row = db.execute(text("""
SELECT * FROM compliance_process_tasks
WHERE id = :id AND tenant_id = :tenant_id
"""), {"id": task_id, "tenant_id": tenant_id}).fetchone()
if not task_row:
raise HTTPException(status_code=404, detail="Task not found")
task = dict(task_row._mapping)
# Insert history entry
db.execute(text("""
INSERT INTO compliance_process_task_history
(task_id, completed_by, completed_at, result, notes, status)
VALUES
(:task_id, :completed_by, NOW(), :reason, :reason, 'skipped')
"""), {
"task_id": task_id,
"completed_by": x_user_id,
"reason": payload.reason,
})
# Calculate next due date
freq = task.get("frequency", "yearly")
days_delta = FREQUENCY_DAYS.get(freq)
if days_delta is not None:
row = db.execute(text("""
UPDATE compliance_process_tasks
SET status = 'pending',
next_due_date = CURRENT_DATE + :days_delta,
updated_at = NOW()
WHERE id = :id AND tenant_id = :tenant_id
RETURNING *
"""), {
"id": task_id,
"tenant_id": tenant_id,
"days_delta": days_delta,
}).fetchone()
else:
row = db.execute(text("""
UPDATE compliance_process_tasks
SET status = 'skipped',
next_due_date = NULL,
updated_at = NOW()
WHERE id = :id AND tenant_id = :tenant_id
RETURNING *
"""), {
"id": task_id,
"tenant_id": tenant_id,
}).fetchone()
db.commit()
return _row_to_dict(row)
@router.get("/{task_id}/history")
async def get_task_history(
task_id: str,
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
):
"""Return history entries for a task."""
# Verify task belongs to tenant
task_row = db.execute(text("""
SELECT id FROM compliance_process_tasks
WHERE id = :id AND tenant_id = :tenant_id
"""), {"id": task_id, "tenant_id": tenant_id}).fetchone()
if not task_row:
raise HTTPException(status_code=404, detail="Task not found")
rows = db.execute(text("""
SELECT * FROM compliance_process_task_history
WHERE task_id = :task_id
ORDER BY completed_at DESC
"""), {"task_id": task_id}).fetchall()
return {"history": [_row_to_dict(r) for r in rows]}
@router.post("/seed")
async def seed_tasks(
db: Session = Depends(get_db),
tenant_id: str = Depends(_get_tenant_id),
x_user_id: Optional[str] = Header(None),
):
"""Seed ~50 standard compliance tasks. Idempotent via ON CONFLICT."""
logger.info("seed_tasks user_id=%s tenant_id=%s", x_user_id, tenant_id)
seeds = _get_seed_tasks()
inserted = 0
for s in seeds:
result = db.execute(text("""
INSERT INTO compliance_process_tasks
(tenant_id, task_code, title, description, category, priority, frequency,
linked_module, is_seed, next_due_date)
VALUES
(:tenant_id, :task_code, :title, :description, :category, :priority, :frequency,
:linked_module, TRUE, CURRENT_DATE + :initial_days)
ON CONFLICT (tenant_id, project_id, task_code) DO NOTHING
"""), {
"tenant_id": tenant_id,
"task_code": s["task_code"],
"title": s["title"],
"description": s["description"],
"category": s["category"],
"priority": s["priority"],
"frequency": s["frequency"],
"linked_module": s.get("linked_module"),
"initial_days": FREQUENCY_DAYS.get(s["frequency"], 365) or 365,
})
if result.rowcount > 0:
inserted += 1
db.commit()
return {"seeded": inserted, "total_available": len(seeds)}
# =============================================================================
# Seed Data
# =============================================================================
def _get_seed_tasks() -> List[Dict[str, Any]]:
"""Return ~50 standard compliance tasks for seeding."""
return [
# ─── DSGVO (~15) ─────────────────────────────────────────────
{
"task_code": "DSGVO-VVT-REVIEW",
"title": "VVT-Review und Aktualisierung",
"description": "Jaehrliche Ueberpruefung und Aktualisierung des Verzeichnisses von Verarbeitungstaetigkeiten gemaess Art. 30 DSGVO.",
"category": "dsgvo",
"priority": "high",
"frequency": "yearly",
"linked_module": "vvt",
},
{
"task_code": "DSGVO-TOM-REVIEW",
"title": "TOM-Review und Aktualisierung",
"description": "Jaehrliche Ueberpruefung der technischen und organisatorischen Massnahmen gemaess Art. 32 DSGVO.",
"category": "dsgvo",
"priority": "high",
"frequency": "yearly",
"linked_module": "tom",
},
{
"task_code": "DSGVO-LOESCHFRISTEN",
"title": "Loeschfristen-Pruefung",
"description": "Quartalspruefung aller Loeschfristen und Durchfuehrung faelliger Loeschungen.",
"category": "dsgvo",
"priority": "high",
"frequency": "quarterly",
"linked_module": "loeschfristen",
},
{
"task_code": "DSGVO-DSB-BERICHT",
"title": "DSB-Taetigkeitsbericht",
"description": "Jaehrlicher Taetigkeitsbericht des Datenschutzbeauftragten an die Geschaeftsfuehrung.",
"category": "dsgvo",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "DSGVO-DSFA-UPDATE",
"title": "DSFA-Aktualisierung",
"description": "Jaehrliche Ueberpruefung und Aktualisierung der Datenschutz-Folgenabschaetzungen.",
"category": "dsgvo",
"priority": "high",
"frequency": "yearly",
"linked_module": "dsfa",
},
{
"task_code": "DSGVO-SCHULUNG",
"title": "Datenschutz-Schulung Mitarbeiter",
"description": "Jaehrliche Datenschutz-Schulung fuer alle Mitarbeiter mit Nachweis.",
"category": "dsgvo",
"priority": "high",
"frequency": "yearly",
"linked_module": "training",
},
{
"task_code": "DSGVO-BETROFFENENRECHTE",
"title": "Betroffenenrechte-Prozess testen",
"description": "Quartalstest der Prozesse fuer Auskunft, Berichtigung, Loeschung und Datenportabilitaet.",
"category": "dsgvo",
"priority": "medium",
"frequency": "quarterly",
"linked_module": "dsr",
},
{
"task_code": "DSGVO-AV-VERTRAEGE",
"title": "AV-Vertraege pruefen",
"description": "Jaehrliche Pruefung aller Auftragsverarbeitungsvertraege auf Aktualitaet und Vollstaendigkeit.",
"category": "dsgvo",
"priority": "medium",
"frequency": "yearly",
"linked_module": "vendor-compliance",
},
{
"task_code": "DSGVO-DATENPANNE-TEST",
"title": "Datenpannen-Meldeprozess testen",
"description": "Halbjahrestest des Meldeprozesses fuer Datenschutzverletzungen gemaess Art. 33/34 DSGVO.",
"category": "dsgvo",
"priority": "high",
"frequency": "semi_annual",
"linked_module": "incident-response",
},
{
"task_code": "DSGVO-COOKIE-PRUEFUNG",
"title": "Cookie-Banner und Consent pruefen",
"description": "Quartalspruefung des Cookie-Banners und der Einwilligungsmechanismen.",
"category": "dsgvo",
"priority": "medium",
"frequency": "quarterly",
"linked_module": "consent",
},
{
"task_code": "DSGVO-DSE-REVIEW",
"title": "Datenschutzerklaerung Review",
"description": "Halbjaehrliche Ueberpruefung der Datenschutzerklaerung auf Aktualitaet.",
"category": "dsgvo",
"priority": "medium",
"frequency": "semi_annual",
"linked_module": "document-generator",
},
{
"task_code": "DSGVO-EINWILLIGUNG-REVIEW",
"title": "Einwilligungen Review",
"description": "Quartalspruefung der eingeholten Einwilligungen auf Gueltigkeit und Widerrufbarkeit.",
"category": "dsgvo",
"priority": "medium",
"frequency": "quarterly",
"linked_module": "consent",
},
{
"task_code": "DSGVO-DRITTLAND",
"title": "Drittlandsuebermittlung pruefen",
"description": "Jaehrliche Pruefung aller Datenuebermittlungen in Drittlaender und deren Rechtsgrundlage.",
"category": "dsgvo",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "DSGVO-VERPFLICHTUNG",
"title": "Mitarbeiter-Verpflichtung Datengeheimnis",
"description": "Jaehrliche Pruefung, ob alle Mitarbeiter auf das Datengeheimnis verpflichtet wurden.",
"category": "dsgvo",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "DSGVO-DATENKATEGORIEN",
"title": "Datenkategorien-Review",
"description": "Jaehrliche Ueberpruefung der erfassten Datenkategorien und deren Zuordnung.",
"category": "dsgvo",
"priority": "low",
"frequency": "yearly",
"linked_module": "vvt",
},
# ─── NIS2 (~10) ──────────────────────────────────────────────
{
"task_code": "NIS2-RISIKOBEWERTUNG",
"title": "NIS2 Risikobewertung",
"description": "Jaehrliche umfassende Risikobewertung der Netz- und Informationssicherheit.",
"category": "nis2",
"priority": "critical",
"frequency": "yearly",
"linked_module": "risks",
},
{
"task_code": "NIS2-LIEFERKETTE",
"title": "Lieferketten-Sicherheitspruefung",
"description": "Jaehrliche Ueberpruefung der Sicherheitsmassnahmen bei Zulieferern und Dienstleistern.",
"category": "nis2",
"priority": "high",
"frequency": "yearly",
"linked_module": "vendor-compliance",
},
{
"task_code": "NIS2-INCIDENT-UEBUNG",
"title": "Incident-Response-Uebung",
"description": "Jaehrliche Uebung des Incident-Response-Prozesses mit Dokumentation.",
"category": "nis2",
"priority": "high",
"frequency": "yearly",
"linked_module": "incident-response",
},
{
"task_code": "NIS2-VULNSCAN",
"title": "Vulnerability-Scan",
"description": "Monatlicher automatisierter Vulnerability-Scan aller IT-Systeme.",
"category": "nis2",
"priority": "critical",
"frequency": "monthly",
"linked_module": "security-backlog",
},
{
"task_code": "NIS2-PATCHMGMT",
"title": "Patch-Management-Review",
"description": "Monatliche Pruefung des Patch-Status aller Systeme.",
"category": "nis2",
"priority": "high",
"frequency": "monthly",
"linked_module": "security-backlog",
},
{
"task_code": "NIS2-BCM-TEST",
"title": "Business-Continuity-Test",
"description": "Jaehrlicher Test des Business-Continuity-Plans.",
"category": "nis2",
"priority": "high",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "NIS2-ZUGANGSKONTROLLE",
"title": "Zugangskontrollen-Review",
"description": "Quartalspruefung aller Zugangsberechtigungen und Accounts.",
"category": "nis2",
"priority": "high",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "NIS2-KRYPTOGRAFIE",
"title": "Kryptografie-Review",
"description": "Jaehrliche Ueberpruefung der eingesetzten kryptografischen Verfahren.",
"category": "nis2",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "NIS2-MELDEPFLICHT",
"title": "Meldepflicht-Prozess testen",
"description": "Halbjaehrlicher Test des NIS2-Meldeprozesses (24h/72h Fristen).",
"category": "nis2",
"priority": "high",
"frequency": "semi_annual",
"linked_module": None,
},
{
"task_code": "NIS2-NETZSEGMENT",
"title": "Netzwerksegmentierung-Review",
"description": "Jaehrliche Ueberpruefung der Netzwerksegmentierung und Firewall-Regeln.",
"category": "nis2",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
# ─── BSI (~10) ───────────────────────────────────────────────
{
"task_code": "BSI-GRUNDSCHUTZ",
"title": "IT-Grundschutz-Check",
"description": "Jaehrlicher IT-Grundschutz-Check nach BSI-Standard 200-2.",
"category": "bsi",
"priority": "high",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "BSI-BAUSTEIN",
"title": "Baustein-Review",
"description": "Quartalspruefung der implementierten BSI-Bausteine.",
"category": "bsi",
"priority": "medium",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "BSI-NOTFALLPLAN",
"title": "Notfallplan-Test",
"description": "Jaehrlicher Test des IT-Notfallplans mit Uebungsszenario.",
"category": "bsi",
"priority": "high",
"frequency": "yearly",
"linked_module": "notfallplan",
},
{
"task_code": "BSI-BACKUP-TEST",
"title": "Backup-Restore-Test",
"description": "Quartalstest der Backup-Wiederherstellung fuer kritische Systeme.",
"category": "bsi",
"priority": "high",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "BSI-FIREWALL",
"title": "Firewall-Rule-Review",
"description": "Quartalspruefung aller Firewall-Regeln auf Aktualitaet und Minimalitaet.",
"category": "bsi",
"priority": "high",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "BSI-LOGGING",
"title": "Logging-Review",
"description": "Monatliche Pruefung der Log-Einstellungen und Log-Auswertung.",
"category": "bsi",
"priority": "medium",
"frequency": "monthly",
"linked_module": None,
},
{
"task_code": "BSI-HAERTUNG",
"title": "Haertungs-Check",
"description": "Quartalspruefung der System-Haertung nach BSI-Empfehlungen.",
"category": "bsi",
"priority": "medium",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "BSI-ZERTIFIKATE",
"title": "Zertifikats-Erneuerung pruefen",
"description": "Monatliche Pruefung ablaufender TLS/SSL-Zertifikate.",
"category": "bsi",
"priority": "high",
"frequency": "monthly",
"linked_module": None,
},
{
"task_code": "BSI-RAUMSICHERHEIT",
"title": "Raumsicherheit-Pruefung",
"description": "Jaehrliche Pruefung der physischen Sicherheit der Serverraeume.",
"category": "bsi",
"priority": "low",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "BSI-MEDIEN",
"title": "Medienentsorgung-Kontrolle",
"description": "Quartalskontrolle der ordnungsgemaessen Entsorgung von Datentraegern.",
"category": "bsi",
"priority": "medium",
"frequency": "quarterly",
"linked_module": None,
},
# ─── ISO 27001 (~8) ──────────────────────────────────────────
{
"task_code": "ISO-MGMT-REVIEW",
"title": "Management-Review",
"description": "Jaehrliches Management-Review des ISMS gemaess ISO 27001 Kap. 9.3.",
"category": "iso27001",
"priority": "critical",
"frequency": "yearly",
"linked_module": None,
},
{
"task_code": "ISO-INT-AUDIT",
"title": "Internes ISMS-Audit",
"description": "Jaehrliches internes Audit des ISMS gemaess ISO 27001 Kap. 9.2.",
"category": "iso27001",
"priority": "critical",
"frequency": "yearly",
"linked_module": "audit",
},
{
"task_code": "ISO-KORREKTUR",
"title": "Korrekturmassnahmen-Review",
"description": "Quartalspruefung offener Korrekturmassnahmen aus Audits.",
"category": "iso27001",
"priority": "high",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "ISO-RISK-OWNER",
"title": "Risiko-Owner-Review",
"description": "Quartalspruefung der Risiko-Zuordnungen und Verantwortlichkeiten.",
"category": "iso27001",
"priority": "high",
"frequency": "quarterly",
"linked_module": "risks",
},
{
"task_code": "ISO-KENNZAHLEN",
"title": "Kennzahlen-Erhebung",
"description": "Monatliche Erhebung der ISMS-Kennzahlen und KPIs.",
"category": "iso27001",
"priority": "medium",
"frequency": "monthly",
"linked_module": None,
},
{
"task_code": "ISO-SCOPE",
"title": "ISMS-Scope-Review",
"description": "Jaehrliche Ueberpruefung des ISMS-Geltungsbereichs.",
"category": "iso27001",
"priority": "medium",
"frequency": "yearly",
"linked_module": "compliance-scope",
},
{
"task_code": "ISO-POLICY",
"title": "Policy-Aktualisierung",
"description": "Jaehrliche Ueberpruefung und Aktualisierung aller ISMS-Policies.",
"category": "iso27001",
"priority": "medium",
"frequency": "yearly",
"linked_module": "document-generator",
},
{
"task_code": "ISO-ZERTIFIZIERUNG",
"title": "Zertifizierungs-Vorbereitung",
"description": "Jaehrliche Vorbereitung auf externes Zertifizierungsaudit.",
"category": "iso27001",
"priority": "high",
"frequency": "yearly",
"linked_module": None,
},
# ─── AI Act (~7) ─────────────────────────────────────────────
{
"task_code": "AIACT-INVENTAR",
"title": "KI-Inventar aktualisieren",
"description": "Quartalsaktualisierung des Inventars aller KI-Systeme im Einsatz.",
"category": "ai_act",
"priority": "high",
"frequency": "quarterly",
"linked_module": "ai-act",
},
{
"task_code": "AIACT-RISIKO",
"title": "KI-Risikobewertung",
"description": "Jaehrliche Risikobewertung aller KI-Systeme gemaess EU AI Act.",
"category": "ai_act",
"priority": "critical",
"frequency": "yearly",
"linked_module": "ai-act",
},
{
"task_code": "AIACT-TRANSPARENZ",
"title": "Transparenzpflicht-Check",
"description": "Quartalspruefung der Transparenzpflichten fuer KI-Systeme.",
"category": "ai_act",
"priority": "high",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "AIACT-OVERSIGHT",
"title": "Human-Oversight-Validierung",
"description": "Halbjaehrliche Validierung der menschlichen Aufsicht ueber KI-Systeme.",
"category": "ai_act",
"priority": "high",
"frequency": "semi_annual",
"linked_module": None,
},
{
"task_code": "AIACT-BIAS",
"title": "Bias-Monitoring",
"description": "Quartalspruefung auf Bias und Diskriminierung in KI-Ausgaben.",
"category": "ai_act",
"priority": "medium",
"frequency": "quarterly",
"linked_module": None,
},
{
"task_code": "AIACT-SCHULUNG",
"title": "KI-Schulung Mitarbeiter",
"description": "Jaehrliche Schulung aller Mitarbeiter zu KI-Kompetenz und AI Act.",
"category": "ai_act",
"priority": "medium",
"frequency": "yearly",
"linked_module": "training",
},
{
"task_code": "AIACT-DOKU",
"title": "KI-Dokumentations-Review",
"description": "Jaehrliche Ueberpruefung der technischen Dokumentation aller KI-Systeme.",
"category": "ai_act",
"priority": "medium",
"frequency": "yearly",
"linked_module": None,
},
]