merge: sync with origin/main, take upstream on conflicts
# Conflicts: # admin-compliance/lib/sdk/types.ts # admin-compliance/lib/sdk/vendor-compliance/types.ts
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,976 @@
|
||||
"""
|
||||
FastAPI routes for the Control Generator Pipeline.
|
||||
|
||||
Endpoints:
|
||||
POST /v1/canonical/generate — Start generation run
|
||||
GET /v1/canonical/generate/status/{job_id} — Job status
|
||||
GET /v1/canonical/generate/jobs — All jobs
|
||||
GET /v1/canonical/generate/review-queue — Controls needing review
|
||||
POST /v1/canonical/generate/review/{control_id} — Complete review
|
||||
GET /v1/canonical/generate/processed-stats — Processing stats per collection
|
||||
GET /v1/canonical/blocked-sources — Blocked sources list
|
||||
POST /v1/canonical/blocked-sources/cleanup — Start cleanup workflow
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from typing import Optional, List
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query
|
||||
from pydantic import BaseModel
|
||||
from sqlalchemy import text
|
||||
|
||||
from database import SessionLocal
|
||||
from compliance.services.control_generator import (
|
||||
ControlGeneratorPipeline,
|
||||
GeneratorConfig,
|
||||
ALL_COLLECTIONS,
|
||||
VALID_CATEGORIES,
|
||||
VALID_DOMAINS,
|
||||
_detect_category,
|
||||
_detect_domain,
|
||||
_llm_local,
|
||||
_parse_llm_json,
|
||||
CATEGORY_LIST_STR,
|
||||
)
|
||||
from compliance.services.citation_backfill import CitationBackfill, BackfillResult
|
||||
from compliance.services.rag_client import get_rag_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/v1/canonical", tags=["control-generator"])
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# REQUEST / RESPONSE MODELS
|
||||
# =============================================================================
|
||||
|
||||
class GenerateRequest(BaseModel):
|
||||
domain: Optional[str] = None
|
||||
collections: Optional[List[str]] = None
|
||||
max_controls: int = 50
|
||||
max_chunks: int = 1000 # Default: process max 1000 chunks per job (respects document boundaries)
|
||||
batch_size: int = 5
|
||||
skip_web_search: bool = False
|
||||
dry_run: bool = False
|
||||
|
||||
|
||||
class GenerateResponse(BaseModel):
|
||||
job_id: str
|
||||
status: str
|
||||
message: str
|
||||
total_chunks_scanned: int = 0
|
||||
controls_generated: int = 0
|
||||
controls_verified: int = 0
|
||||
controls_needs_review: int = 0
|
||||
controls_too_close: int = 0
|
||||
controls_duplicates_found: int = 0
|
||||
controls_qa_fixed: int = 0
|
||||
errors: list = []
|
||||
controls: list = []
|
||||
|
||||
|
||||
class ReviewRequest(BaseModel):
|
||||
action: str # "approve", "reject", "needs_rework"
|
||||
release_state: Optional[str] = None # Override release_state
|
||||
notes: Optional[str] = None
|
||||
|
||||
|
||||
class ProcessedStats(BaseModel):
|
||||
collection: str
|
||||
total_chunks_estimated: int
|
||||
processed_chunks: int
|
||||
pending_chunks: int
|
||||
direct_adopted: int
|
||||
llm_reformed: int
|
||||
skipped: int
|
||||
|
||||
|
||||
class BlockedSourceResponse(BaseModel):
|
||||
id: str
|
||||
regulation_code: str
|
||||
document_title: str
|
||||
reason: str
|
||||
deletion_status: str
|
||||
qdrant_collection: Optional[str] = None
|
||||
marked_at: str
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# ENDPOINTS
|
||||
# =============================================================================
|
||||
|
||||
async def _run_pipeline_background(config: GeneratorConfig, job_id: str):
|
||||
"""Run the pipeline in the background. Uses its own DB session."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
config.existing_job_id = job_id
|
||||
pipeline = ControlGeneratorPipeline(db=db, rag_client=get_rag_client())
|
||||
result = await pipeline.run(config)
|
||||
logger.info(
|
||||
"Background generation job %s completed: %d controls from %d chunks",
|
||||
job_id, result.controls_generated, result.total_chunks_scanned,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Background generation job %s failed: %s", job_id, e)
|
||||
# Update job as failed
|
||||
try:
|
||||
db.execute(
|
||||
text("""
|
||||
UPDATE canonical_generation_jobs
|
||||
SET status = 'failed', errors = :errors, completed_at = NOW()
|
||||
WHERE id = CAST(:job_id AS uuid)
|
||||
"""),
|
||||
{"job_id": job_id, "errors": json.dumps([str(e)])},
|
||||
)
|
||||
db.commit()
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/generate", response_model=GenerateResponse)
|
||||
async def start_generation(req: GenerateRequest):
|
||||
"""Start a control generation run (runs in background).
|
||||
|
||||
Returns immediately with job_id. Use GET /generate/status/{job_id} to poll progress.
|
||||
"""
|
||||
config = GeneratorConfig(
|
||||
collections=req.collections,
|
||||
domain=req.domain,
|
||||
batch_size=req.batch_size,
|
||||
max_controls=req.max_controls,
|
||||
max_chunks=req.max_chunks,
|
||||
skip_web_search=req.skip_web_search,
|
||||
dry_run=req.dry_run,
|
||||
)
|
||||
|
||||
if req.dry_run:
|
||||
# Dry run: execute synchronously and return controls
|
||||
db = SessionLocal()
|
||||
try:
|
||||
pipeline = ControlGeneratorPipeline(db=db, rag_client=get_rag_client())
|
||||
result = await pipeline.run(config)
|
||||
return GenerateResponse(
|
||||
job_id=result.job_id,
|
||||
status=result.status,
|
||||
message=f"Dry run: {result.controls_generated} controls from {result.total_chunks_scanned} chunks",
|
||||
total_chunks_scanned=result.total_chunks_scanned,
|
||||
controls_generated=result.controls_generated,
|
||||
controls_verified=result.controls_verified,
|
||||
controls_needs_review=result.controls_needs_review,
|
||||
controls_too_close=result.controls_too_close,
|
||||
controls_duplicates_found=result.controls_duplicates_found,
|
||||
errors=result.errors,
|
||||
controls=result.controls,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error("Dry run failed: %s", e)
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# Create job record first so we can return the ID
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
INSERT INTO canonical_generation_jobs (status, config)
|
||||
VALUES ('running', :config)
|
||||
RETURNING id
|
||||
"""),
|
||||
{"config": json.dumps(config.model_dump())},
|
||||
)
|
||||
db.commit()
|
||||
row = result.fetchone()
|
||||
job_id = str(row[0]) if row else None
|
||||
except Exception as e:
|
||||
logger.error("Failed to create job: %s", e)
|
||||
raise HTTPException(status_code=500, detail=f"Failed to create job: {e}")
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
if not job_id:
|
||||
raise HTTPException(status_code=500, detail="Failed to create job record")
|
||||
|
||||
# Launch pipeline in background
|
||||
asyncio.create_task(_run_pipeline_background(config, job_id))
|
||||
|
||||
return GenerateResponse(
|
||||
job_id=job_id,
|
||||
status="running",
|
||||
message="Generation started in background. Poll /generate/status/{job_id} for progress.",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/generate/status/{job_id}")
|
||||
async def get_job_status(job_id: str):
|
||||
"""Get status of a generation job."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("SELECT * FROM canonical_generation_jobs WHERE id = CAST(:id AS uuid)"),
|
||||
{"id": job_id},
|
||||
)
|
||||
row = result.fetchone()
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="Job not found")
|
||||
|
||||
cols = result.keys()
|
||||
job = dict(zip(cols, row))
|
||||
# Serialize datetime fields
|
||||
for key in ("started_at", "completed_at", "created_at"):
|
||||
if job.get(key):
|
||||
job[key] = str(job[key])
|
||||
job["id"] = str(job["id"])
|
||||
return job
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("/generate/jobs")
|
||||
async def list_jobs(
|
||||
limit: int = Query(20, ge=1, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""List all generation jobs."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
SELECT id, status, total_chunks_scanned, controls_generated,
|
||||
controls_verified, controls_needs_review, controls_too_close,
|
||||
controls_duplicates_found, created_at, completed_at
|
||||
FROM canonical_generation_jobs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT :limit OFFSET :offset
|
||||
"""),
|
||||
{"limit": limit, "offset": offset},
|
||||
)
|
||||
jobs = []
|
||||
cols = result.keys()
|
||||
for row in result:
|
||||
job = dict(zip(cols, row))
|
||||
job["id"] = str(job["id"])
|
||||
for key in ("created_at", "completed_at"):
|
||||
if job.get(key):
|
||||
job[key] = str(job[key])
|
||||
jobs.append(job)
|
||||
return {"jobs": jobs, "total": len(jobs)}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("/generate/review-queue")
|
||||
async def get_review_queue(
|
||||
release_state: str = Query("needs_review", regex="^(needs_review|too_close|duplicate)$"),
|
||||
limit: int = Query(50, ge=1, le=200),
|
||||
):
|
||||
"""Get controls that need manual review."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
SELECT c.id, c.control_id, c.title, c.objective, c.severity,
|
||||
c.release_state, c.license_rule, c.customer_visible,
|
||||
c.generation_metadata, c.open_anchors, c.tags,
|
||||
c.created_at
|
||||
FROM canonical_controls c
|
||||
WHERE c.release_state = :state
|
||||
ORDER BY c.created_at DESC
|
||||
LIMIT :limit
|
||||
"""),
|
||||
{"state": release_state, "limit": limit},
|
||||
)
|
||||
controls = []
|
||||
cols = result.keys()
|
||||
for row in result:
|
||||
ctrl = dict(zip(cols, row))
|
||||
ctrl["id"] = str(ctrl["id"])
|
||||
ctrl["created_at"] = str(ctrl["created_at"])
|
||||
# Parse JSON fields
|
||||
for jf in ("generation_metadata", "open_anchors", "tags"):
|
||||
if isinstance(ctrl.get(jf), str):
|
||||
try:
|
||||
ctrl[jf] = json.loads(ctrl[jf])
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
controls.append(ctrl)
|
||||
return {"controls": controls, "total": len(controls)}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/generate/review/{control_id}")
|
||||
async def review_control(control_id: str, req: ReviewRequest):
|
||||
"""Complete review of a generated control."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Validate control exists and is in reviewable state
|
||||
result = db.execute(
|
||||
text("SELECT id, release_state FROM canonical_controls WHERE control_id = :cid"),
|
||||
{"cid": control_id},
|
||||
)
|
||||
row = result.fetchone()
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="Control not found")
|
||||
|
||||
current_state = row[1]
|
||||
if current_state not in ("needs_review", "too_close", "duplicate"):
|
||||
raise HTTPException(status_code=400, detail=f"Control is in state '{current_state}', not reviewable")
|
||||
|
||||
# Determine new state
|
||||
if req.action == "approve":
|
||||
new_state = req.release_state or "draft"
|
||||
elif req.action == "reject":
|
||||
new_state = "deprecated"
|
||||
elif req.action == "needs_rework":
|
||||
new_state = "needs_review"
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"Unknown action: {req.action}")
|
||||
|
||||
if new_state not in ("draft", "review", "approved", "deprecated", "needs_review", "too_close", "duplicate"):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid release_state: {new_state}")
|
||||
|
||||
db.execute(
|
||||
text("""
|
||||
UPDATE canonical_controls
|
||||
SET release_state = :state, updated_at = NOW()
|
||||
WHERE control_id = :cid
|
||||
"""),
|
||||
{"state": new_state, "cid": control_id},
|
||||
)
|
||||
db.commit()
|
||||
|
||||
return {"control_id": control_id, "release_state": new_state, "action": req.action}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
class BulkReviewRequest(BaseModel):
|
||||
release_state: str # Filter: which controls to bulk-review
|
||||
action: str # "approve" or "reject"
|
||||
new_state: Optional[str] = None # Override target state
|
||||
|
||||
|
||||
@router.post("/generate/bulk-review")
|
||||
async def bulk_review(req: BulkReviewRequest):
|
||||
"""Bulk review all controls matching a release_state filter.
|
||||
|
||||
Example: reject all needs_review → sets them to deprecated.
|
||||
"""
|
||||
if req.release_state not in ("needs_review", "too_close", "duplicate"):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid filter state: {req.release_state}")
|
||||
|
||||
if req.action == "approve":
|
||||
target = req.new_state or "draft"
|
||||
elif req.action == "reject":
|
||||
target = "deprecated"
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"Unknown action: {req.action}")
|
||||
|
||||
if target not in ("draft", "review", "approved", "deprecated", "needs_review"):
|
||||
raise HTTPException(status_code=400, detail=f"Invalid target state: {target}")
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
UPDATE canonical_controls
|
||||
SET release_state = :target, updated_at = NOW()
|
||||
WHERE release_state = :source
|
||||
RETURNING control_id
|
||||
"""),
|
||||
{"source": req.release_state, "target": target},
|
||||
)
|
||||
affected = [row[0] for row in result]
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"action": req.action,
|
||||
"source_state": req.release_state,
|
||||
"target_state": target,
|
||||
"affected_count": len(affected),
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
class QAReclassifyRequest(BaseModel):
|
||||
limit: int = 100 # How many controls to reclassify per run
|
||||
dry_run: bool = True # Preview only by default
|
||||
filter_category: Optional[str] = None # Only reclassify controls of this category
|
||||
filter_domain_prefix: Optional[str] = None # Only reclassify controls with this prefix
|
||||
|
||||
|
||||
@router.post("/generate/qa-reclassify")
|
||||
async def qa_reclassify(req: QAReclassifyRequest):
|
||||
"""Run QA reclassification on existing controls using local LLM.
|
||||
|
||||
Finds controls where keyword-detection disagrees with current category/domain,
|
||||
then uses Ollama to determine the correct classification.
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Load controls to check
|
||||
where_clauses = ["release_state NOT IN ('deprecated')"]
|
||||
params = {"limit": req.limit}
|
||||
if req.filter_category:
|
||||
where_clauses.append("category = :cat")
|
||||
params["cat"] = req.filter_category
|
||||
if req.filter_domain_prefix:
|
||||
where_clauses.append("control_id LIKE :prefix")
|
||||
params["prefix"] = f"{req.filter_domain_prefix}-%"
|
||||
|
||||
where_sql = " AND ".join(where_clauses)
|
||||
rows = db.execute(
|
||||
text(f"""
|
||||
SELECT id, control_id, title, objective, category,
|
||||
COALESCE(requirements::text, '[]') as requirements,
|
||||
COALESCE(source_original_text, '') as source_text
|
||||
FROM canonical_controls
|
||||
WHERE {where_sql}
|
||||
ORDER BY created_at DESC
|
||||
LIMIT :limit
|
||||
"""),
|
||||
params,
|
||||
).fetchall()
|
||||
|
||||
results = {"checked": 0, "mismatches": 0, "fixes": [], "errors": []}
|
||||
|
||||
for row in rows:
|
||||
results["checked"] += 1
|
||||
control_id = row[1]
|
||||
title = row[2]
|
||||
objective = row[3] or ""
|
||||
current_category = row[4]
|
||||
source_text = row[6] or objective
|
||||
|
||||
# Keyword detection on source text
|
||||
kw_category = _detect_category(source_text) or _detect_category(objective)
|
||||
kw_domain = _detect_domain(source_text)
|
||||
current_prefix = control_id.split("-")[0] if "-" in control_id else ""
|
||||
|
||||
# Skip if keyword detection agrees with current classification
|
||||
if kw_category == current_category and kw_domain == current_prefix:
|
||||
continue
|
||||
|
||||
results["mismatches"] += 1
|
||||
|
||||
# Ask Ollama to arbitrate
|
||||
try:
|
||||
reqs_text = ""
|
||||
try:
|
||||
reqs = json.loads(row[5])
|
||||
if isinstance(reqs, list):
|
||||
reqs_text = ", ".join(str(r) for r in reqs[:3])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
prompt = f"""Pruefe dieses Compliance-Control auf korrekte Klassifizierung.
|
||||
|
||||
Titel: {title[:100]}
|
||||
Ziel: {objective[:200]}
|
||||
Anforderungen: {reqs_text[:200]}
|
||||
|
||||
Aktuelle Zuordnung: domain={current_prefix}, category={current_category}
|
||||
Keyword-Erkennung: domain={kw_domain}, category={kw_category}
|
||||
|
||||
Welche Zuordnung ist korrekt? Antworte NUR als JSON:
|
||||
{{"domain": "KUERZEL", "category": "kategorie_name", "reason": "kurze Begruendung"}}
|
||||
|
||||
Domains: AUTH=Authentifizierung, CRYP=Kryptographie, NET=Netzwerk, DATA=Datenschutz, LOG=Logging, ACC=Zugriffskontrolle, SEC=IT-Sicherheit, INC=Vorfallmanagement, AI=KI, COMP=Compliance, GOV=Behoerden, LAB=Arbeitsrecht, FIN=Finanzregulierung, TRD=Gewerbe, ENV=Umwelt, HLT=Gesundheit
|
||||
Kategorien: {CATEGORY_LIST_STR}"""
|
||||
|
||||
raw = await _llm_local(prompt)
|
||||
data = _parse_llm_json(raw)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
qa_domain = data.get("domain", "").upper()
|
||||
qa_category = data.get("category", "")
|
||||
reason = data.get("reason", "")
|
||||
|
||||
fix_entry = {
|
||||
"control_id": control_id,
|
||||
"title": title[:80],
|
||||
"old_category": current_category,
|
||||
"old_domain": current_prefix,
|
||||
"new_category": qa_category if qa_category in VALID_CATEGORIES else current_category,
|
||||
"new_domain": qa_domain if qa_domain in VALID_DOMAINS else current_prefix,
|
||||
"reason": reason,
|
||||
}
|
||||
|
||||
category_changed = qa_category in VALID_CATEGORIES and qa_category != current_category
|
||||
|
||||
if category_changed and not req.dry_run:
|
||||
db.execute(
|
||||
text("""
|
||||
UPDATE canonical_controls
|
||||
SET category = :category, updated_at = NOW()
|
||||
WHERE id = :id
|
||||
"""),
|
||||
{"id": row[0], "category": qa_category},
|
||||
)
|
||||
fix_entry["applied"] = True
|
||||
else:
|
||||
fix_entry["applied"] = False
|
||||
|
||||
results["fixes"].append(fix_entry)
|
||||
|
||||
except Exception as e:
|
||||
results["errors"].append({"control_id": control_id, "error": str(e)})
|
||||
|
||||
if not req.dry_run:
|
||||
db.commit()
|
||||
|
||||
return results
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.get("/generate/processed-stats")
|
||||
async def get_processed_stats():
|
||||
"""Get processing statistics per collection."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
SELECT
|
||||
collection,
|
||||
COUNT(*) as processed_chunks,
|
||||
COUNT(*) FILTER (WHERE processing_path = 'structured') as direct_adopted,
|
||||
COUNT(*) FILTER (WHERE processing_path = 'llm_reform') as llm_reformed,
|
||||
COUNT(*) FILTER (WHERE processing_path = 'skipped') as skipped
|
||||
FROM canonical_processed_chunks
|
||||
GROUP BY collection
|
||||
ORDER BY collection
|
||||
""")
|
||||
)
|
||||
stats = []
|
||||
cols = result.keys()
|
||||
for row in result:
|
||||
stat = dict(zip(cols, row))
|
||||
stat["total_chunks_estimated"] = 0 # Would need Qdrant API to get total
|
||||
stat["pending_chunks"] = 0
|
||||
stats.append(stat)
|
||||
return {"stats": stats}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# BLOCKED SOURCES
|
||||
# =============================================================================
|
||||
|
||||
@router.get("/blocked-sources")
|
||||
async def list_blocked_sources():
|
||||
"""List all blocked (Rule 3) sources."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
SELECT id, regulation_code, document_title, reason,
|
||||
deletion_status, qdrant_collection, marked_at
|
||||
FROM canonical_blocked_sources
|
||||
ORDER BY marked_at DESC
|
||||
""")
|
||||
)
|
||||
sources = []
|
||||
cols = result.keys()
|
||||
for row in result:
|
||||
src = dict(zip(cols, row))
|
||||
src["id"] = str(src["id"])
|
||||
src["marked_at"] = str(src["marked_at"])
|
||||
sources.append(src)
|
||||
return {"sources": sources}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/blocked-sources/cleanup")
|
||||
async def start_cleanup():
|
||||
"""Start cleanup workflow for blocked sources.
|
||||
|
||||
This marks all pending blocked sources for deletion.
|
||||
Actual RAG chunk deletion and file removal is a separate manual step.
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
result = db.execute(
|
||||
text("""
|
||||
UPDATE canonical_blocked_sources
|
||||
SET deletion_status = 'marked_for_deletion'
|
||||
WHERE deletion_status = 'pending'
|
||||
RETURNING regulation_code
|
||||
""")
|
||||
)
|
||||
marked = [row[0] for row in result]
|
||||
db.commit()
|
||||
|
||||
return {
|
||||
"status": "marked_for_deletion",
|
||||
"marked_count": len(marked),
|
||||
"regulation_codes": marked,
|
||||
"message": "Sources marked for deletion. Run manual cleanup to remove RAG chunks and files.",
|
||||
}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CUSTOMER VIEW FILTER
|
||||
# =============================================================================
|
||||
|
||||
@router.get("/controls-customer")
|
||||
async def get_controls_customer_view(
|
||||
severity: Optional[str] = Query(None),
|
||||
domain: Optional[str] = Query(None),
|
||||
):
|
||||
"""Get controls filtered for customer visibility.
|
||||
|
||||
Rule 3 controls have source_citation and source_original_text hidden.
|
||||
generation_metadata is NEVER shown to customers.
|
||||
"""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
query = """
|
||||
SELECT c.id, c.control_id, c.title, c.objective, c.rationale,
|
||||
c.scope, c.requirements, c.test_procedure, c.evidence,
|
||||
c.severity, c.risk_score, c.implementation_effort,
|
||||
c.open_anchors, c.release_state, c.tags,
|
||||
c.license_rule, c.customer_visible,
|
||||
c.source_original_text, c.source_citation,
|
||||
c.created_at, c.updated_at
|
||||
FROM canonical_controls c
|
||||
WHERE c.release_state IN ('draft', 'approved')
|
||||
"""
|
||||
params: dict = {}
|
||||
|
||||
if severity:
|
||||
query += " AND c.severity = :severity"
|
||||
params["severity"] = severity
|
||||
if domain:
|
||||
query += " AND c.control_id LIKE :domain"
|
||||
params["domain"] = f"{domain.upper()}-%"
|
||||
|
||||
query += " ORDER BY c.control_id"
|
||||
|
||||
result = db.execute(text(query), params)
|
||||
controls = []
|
||||
cols = result.keys()
|
||||
for row in result:
|
||||
ctrl = dict(zip(cols, row))
|
||||
ctrl["id"] = str(ctrl["id"])
|
||||
for key in ("created_at", "updated_at"):
|
||||
if ctrl.get(key):
|
||||
ctrl[key] = str(ctrl[key])
|
||||
|
||||
# Parse JSON fields
|
||||
for jf in ("scope", "requirements", "test_procedure", "evidence",
|
||||
"open_anchors", "tags", "source_citation"):
|
||||
if isinstance(ctrl.get(jf), str):
|
||||
try:
|
||||
ctrl[jf] = json.loads(ctrl[jf])
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
# Customer visibility rules:
|
||||
# - NEVER show generation_metadata
|
||||
# - Rule 3: NEVER show source_citation or source_original_text
|
||||
ctrl.pop("generation_metadata", None)
|
||||
if not ctrl.get("customer_visible", True):
|
||||
ctrl["source_citation"] = None
|
||||
ctrl["source_original_text"] = None
|
||||
|
||||
controls.append(ctrl)
|
||||
|
||||
return {"controls": controls, "total": len(controls)}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# CITATION BACKFILL
|
||||
# =============================================================================
|
||||
|
||||
class BackfillRequest(BaseModel):
|
||||
dry_run: bool = True # Default to dry_run for safety
|
||||
limit: int = 0 # 0 = all controls
|
||||
|
||||
|
||||
class BackfillResponse(BaseModel):
|
||||
status: str
|
||||
total_controls: int = 0
|
||||
matched_hash: int = 0
|
||||
matched_regex: int = 0
|
||||
matched_llm: int = 0
|
||||
unmatched: int = 0
|
||||
updated: int = 0
|
||||
errors: list = []
|
||||
|
||||
|
||||
_backfill_status: dict = {}
|
||||
|
||||
|
||||
async def _run_backfill_background(dry_run: bool, limit: int, backfill_id: str):
|
||||
"""Run backfill in background with own DB session."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
backfill = CitationBackfill(db=db, rag_client=get_rag_client())
|
||||
result = await backfill.run(dry_run=dry_run, limit=limit)
|
||||
_backfill_status[backfill_id] = {
|
||||
"status": "completed",
|
||||
"total_controls": result.total_controls,
|
||||
"matched_hash": result.matched_hash,
|
||||
"matched_regex": result.matched_regex,
|
||||
"matched_llm": result.matched_llm,
|
||||
"unmatched": result.unmatched,
|
||||
"updated": result.updated,
|
||||
"errors": result.errors[:50],
|
||||
}
|
||||
logger.info("Backfill %s completed: %d updated", backfill_id, result.updated)
|
||||
except Exception as e:
|
||||
logger.error("Backfill %s failed: %s", backfill_id, e)
|
||||
_backfill_status[backfill_id] = {"status": "failed", "errors": [str(e)]}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/generate/backfill-citations", response_model=BackfillResponse)
|
||||
async def start_backfill(req: BackfillRequest):
|
||||
"""Backfill article/paragraph into existing control source_citations.
|
||||
|
||||
Uses 3-tier matching: hash lookup → regex parse → Ollama LLM.
|
||||
Default is dry_run=True (preview only, no DB changes).
|
||||
"""
|
||||
import uuid
|
||||
backfill_id = str(uuid.uuid4())[:8]
|
||||
_backfill_status[backfill_id] = {"status": "running"}
|
||||
|
||||
# Always run in background (RAG index build takes minutes)
|
||||
asyncio.create_task(_run_backfill_background(req.dry_run, req.limit, backfill_id))
|
||||
return BackfillResponse(
|
||||
status=f"running (id={backfill_id})",
|
||||
)
|
||||
|
||||
|
||||
@router.get("/generate/backfill-status/{backfill_id}")
|
||||
async def get_backfill_status(backfill_id: str):
|
||||
"""Get status of a backfill job."""
|
||||
status = _backfill_status.get(backfill_id)
|
||||
if not status:
|
||||
raise HTTPException(status_code=404, detail="Backfill job not found")
|
||||
return status
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# DOMAIN + TARGET AUDIENCE BACKFILL
|
||||
# =============================================================================
|
||||
|
||||
class DomainBackfillRequest(BaseModel):
|
||||
dry_run: bool = True
|
||||
job_id: Optional[str] = None # Only backfill controls from this job
|
||||
limit: int = 0 # 0 = all
|
||||
|
||||
_domain_backfill_status: dict = {}
|
||||
|
||||
|
||||
async def _run_domain_backfill(req: DomainBackfillRequest, backfill_id: str):
|
||||
"""Backfill domain, category, and target_audience for existing controls using Anthropic."""
|
||||
import os
|
||||
import httpx
|
||||
|
||||
ANTHROPIC_API_KEY = os.getenv("ANTHROPIC_API_KEY", "")
|
||||
ANTHROPIC_MODEL = os.getenv("CONTROL_GEN_ANTHROPIC_MODEL", "claude-sonnet-4-6")
|
||||
|
||||
if not ANTHROPIC_API_KEY:
|
||||
_domain_backfill_status[backfill_id] = {
|
||||
"status": "failed", "error": "ANTHROPIC_API_KEY not set"
|
||||
}
|
||||
return
|
||||
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Find controls needing backfill
|
||||
where_clauses = ["(target_audience IS NULL OR target_audience = '[]' OR target_audience = 'null')"]
|
||||
params: dict = {}
|
||||
if req.job_id:
|
||||
where_clauses.append("generation_metadata->>'job_id' = :job_id")
|
||||
params["job_id"] = req.job_id
|
||||
|
||||
query = f"""
|
||||
SELECT id, control_id, title, objective, category, source_original_text, tags
|
||||
FROM canonical_controls
|
||||
WHERE {' AND '.join(where_clauses)}
|
||||
ORDER BY control_id
|
||||
"""
|
||||
if req.limit > 0:
|
||||
query += f" LIMIT {req.limit}"
|
||||
|
||||
result = db.execute(text(query), params)
|
||||
controls = [dict(zip(result.keys(), row)) for row in result]
|
||||
|
||||
total = len(controls)
|
||||
updated = 0
|
||||
errors = []
|
||||
|
||||
_domain_backfill_status[backfill_id] = {
|
||||
"status": "running", "total": total, "updated": 0, "errors": []
|
||||
}
|
||||
|
||||
# Process in batches of 10
|
||||
BATCH_SIZE = 10
|
||||
for batch_start in range(0, total, BATCH_SIZE):
|
||||
batch = controls[batch_start:batch_start + BATCH_SIZE]
|
||||
|
||||
entries = []
|
||||
for idx, ctrl in enumerate(batch):
|
||||
text_for_analysis = ctrl.get("objective") or ctrl.get("title") or ""
|
||||
original = ctrl.get("source_original_text") or ""
|
||||
if original:
|
||||
text_for_analysis += f"\n\nQuelltext-Auszug: {original[:500]}"
|
||||
entries.append(
|
||||
f"--- CONTROL {idx + 1}: {ctrl['control_id']} ---\n"
|
||||
f"Titel: {ctrl.get('title', '')}\n"
|
||||
f"Objective: {text_for_analysis[:800]}\n"
|
||||
f"Tags: {json.dumps(ctrl.get('tags', []))}"
|
||||
)
|
||||
|
||||
prompt = f"""Analysiere die folgenden {len(batch)} Controls und bestimme fuer jedes:
|
||||
1. domain: Das Fachgebiet (AUTH, CRYP, NET, DATA, LOG, ACC, SEC, INC, AI, COMP, GOV, LAB, FIN, TRD, ENV, HLT)
|
||||
2. category: Die Kategorie (encryption, authentication, network, data_protection, logging, incident, continuity, compliance, supply_chain, physical, personnel, application, system, risk, governance, hardware, identity, public_administration, labor_law, finance, trade_regulation, environmental, health)
|
||||
3. target_audience: Liste der Zielgruppen (moegliche Werte: "unternehmen", "behoerden", "entwickler", "datenschutzbeauftragte", "geschaeftsfuehrung", "it-abteilung", "rechtsabteilung", "compliance-officer", "personalwesen", "einkauf", "produktion", "vertrieb", "gesundheitswesen", "finanzwesen", "oeffentlicher_dienst")
|
||||
|
||||
Antworte mit einem JSON-Array mit {len(batch)} Objekten. Jedes Objekt hat:
|
||||
- control_index: 1-basierter Index
|
||||
- domain: Fachgebiet-Kuerzel
|
||||
- category: Kategorie
|
||||
- target_audience: Liste der Zielgruppen
|
||||
|
||||
{"".join(entries)}"""
|
||||
|
||||
try:
|
||||
headers = {
|
||||
"x-api-key": ANTHROPIC_API_KEY,
|
||||
"anthropic-version": "2023-06-01",
|
||||
"content-type": "application/json",
|
||||
}
|
||||
payload = {
|
||||
"model": ANTHROPIC_MODEL,
|
||||
"max_tokens": 4096,
|
||||
"system": "Du bist ein Compliance-Experte. Klassifiziere Controls nach Fachgebiet und Zielgruppe. Antworte NUR mit validem JSON.",
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
}
|
||||
|
||||
async with httpx.AsyncClient(timeout=60.0) as client:
|
||||
resp = await client.post(
|
||||
"https://api.anthropic.com/v1/messages",
|
||||
headers=headers,
|
||||
json=payload,
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
errors.append(f"Anthropic API {resp.status_code} at batch {batch_start}")
|
||||
continue
|
||||
|
||||
raw = resp.json().get("content", [{}])[0].get("text", "")
|
||||
|
||||
# Parse response
|
||||
import re
|
||||
bracket_match = re.search(r"\[.*\]", raw, re.DOTALL)
|
||||
if not bracket_match:
|
||||
errors.append(f"No JSON array in response at batch {batch_start}")
|
||||
continue
|
||||
|
||||
results_list = json.loads(bracket_match.group(0))
|
||||
|
||||
for item in results_list:
|
||||
idx = item.get("control_index", 0) - 1
|
||||
if idx < 0 or idx >= len(batch):
|
||||
continue
|
||||
ctrl = batch[idx]
|
||||
ctrl_id = str(ctrl["id"])
|
||||
|
||||
new_domain = item.get("domain", "")
|
||||
new_category = item.get("category", "")
|
||||
new_audience = item.get("target_audience", [])
|
||||
|
||||
if not isinstance(new_audience, list):
|
||||
new_audience = []
|
||||
|
||||
# Build new control_id from domain if domain changed
|
||||
old_prefix = ctrl["control_id"].split("-")[0] if ctrl["control_id"] else ""
|
||||
new_prefix = new_domain.upper()[:4] if new_domain else old_prefix
|
||||
|
||||
if not req.dry_run:
|
||||
update_parts = []
|
||||
update_params: dict = {"ctrl_id": ctrl_id}
|
||||
|
||||
if new_category:
|
||||
update_parts.append("category = :category")
|
||||
update_params["category"] = new_category
|
||||
|
||||
if new_audience:
|
||||
update_parts.append("target_audience = :target_audience")
|
||||
update_params["target_audience"] = json.dumps(new_audience)
|
||||
|
||||
# Note: We do NOT rename control_ids here — that would
|
||||
# break references and cause unique constraint violations.
|
||||
|
||||
if update_parts:
|
||||
update_parts.append("updated_at = NOW()")
|
||||
db.execute(
|
||||
text(f"UPDATE canonical_controls SET {', '.join(update_parts)} WHERE id = CAST(:ctrl_id AS uuid)"),
|
||||
update_params,
|
||||
)
|
||||
updated += 1
|
||||
|
||||
if not req.dry_run:
|
||||
db.commit()
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Batch {batch_start}: {str(e)}")
|
||||
db.rollback()
|
||||
|
||||
_domain_backfill_status[backfill_id] = {
|
||||
"status": "running", "total": total, "updated": updated,
|
||||
"progress": f"{min(batch_start + BATCH_SIZE, total)}/{total}",
|
||||
"errors": errors[-10:],
|
||||
}
|
||||
|
||||
_domain_backfill_status[backfill_id] = {
|
||||
"status": "completed", "total": total, "updated": updated,
|
||||
"errors": errors[-50:],
|
||||
}
|
||||
logger.info("Domain backfill %s completed: %d/%d updated", backfill_id, updated, total)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Domain backfill %s failed: %s", backfill_id, e)
|
||||
_domain_backfill_status[backfill_id] = {"status": "failed", "error": str(e)}
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@router.post("/generate/backfill-domain")
|
||||
async def start_domain_backfill(req: DomainBackfillRequest):
|
||||
"""Backfill domain, category, and target_audience for controls using Anthropic API.
|
||||
|
||||
Finds controls where target_audience is NULL and enriches them.
|
||||
Default is dry_run=True (preview only).
|
||||
"""
|
||||
import uuid
|
||||
backfill_id = str(uuid.uuid4())[:8]
|
||||
_domain_backfill_status[backfill_id] = {"status": "starting"}
|
||||
asyncio.create_task(_run_domain_backfill(req, backfill_id))
|
||||
return {"status": "running", "backfill_id": backfill_id,
|
||||
"message": f"Domain backfill started. Poll /generate/backfill-status/{backfill_id}"}
|
||||
|
||||
|
||||
@router.get("/generate/domain-backfill-status/{backfill_id}")
|
||||
async def get_domain_backfill_status(backfill_id: str):
|
||||
"""Get status of a domain backfill job."""
|
||||
status = _domain_backfill_status.get(backfill_id)
|
||||
if not status:
|
||||
raise HTTPException(status_code=404, detail="Domain backfill job not found")
|
||||
return status
|
||||
@@ -1,15 +1,15 @@
|
||||
# CI/CD Pipeline
|
||||
|
||||
Übersicht über den Deployment-Prozess für Breakpilot.
|
||||
Uebersicht ueber den Deployment-Prozess fuer BreakPilot Compliance.
|
||||
|
||||
## Übersicht
|
||||
## Uebersicht
|
||||
|
||||
| Komponente | Build-Tool | Deployment |
|
||||
|------------|------------|------------|
|
||||
| Frontend (Next.js) | Docker | Mac Mini |
|
||||
| Backend (FastAPI) | Docker | Mac Mini |
|
||||
| Go Services | Docker (Multi-stage) | Mac Mini |
|
||||
| Documentation | MkDocs | Docker (Nginx) |
|
||||
| Frontend (Next.js) | Docker | Coolify (automatisch) |
|
||||
| Backend (FastAPI) | Docker | Coolify (automatisch) |
|
||||
| Go Services | Docker (Multi-stage) | Coolify (automatisch) |
|
||||
| Documentation | MkDocs | Docker (Nginx, lokal) |
|
||||
|
||||
## Deployment-Architektur
|
||||
|
||||
@@ -17,386 +17,129 @@
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Entwickler-MacBook │
|
||||
│ │
|
||||
│ breakpilot-pwa/ │
|
||||
│ ├── studio-v2/ (Next.js Frontend) │
|
||||
│ ├── admin-v2/ (Next.js Admin) │
|
||||
│ ├── backend/ (Python FastAPI) │
|
||||
│ ├── consent-service/ (Go Service) │
|
||||
│ ├── klausur-service/ (Python FastAPI) │
|
||||
│ ├── voice-service/ (Python FastAPI) │
|
||||
│ ├── ai-compliance-sdk/ (Go Service) │
|
||||
│ └── docs-src/ (MkDocs) │
|
||||
│ breakpilot-compliance/ │
|
||||
│ ├── admin-compliance/ (Next.js Dashboard) │
|
||||
│ ├── backend-compliance/ (Python FastAPI) │
|
||||
│ ├── ai-compliance-sdk/ (Go/Gin) │
|
||||
│ ├── developer-portal/ (Next.js) │
|
||||
│ └── docs-src/ (MkDocs) │
|
||||
│ │
|
||||
│ $ ./sync-and-deploy.sh │
|
||||
│ git push origin main && git push gitea main │
|
||||
└───────────────────────────────┬─────────────────────────────────┘
|
||||
│
|
||||
│ rsync + SSH
|
||||
│ git push
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Mac Mini Server │
|
||||
│ Gitea (gitea.meghsakha.com) │
|
||||
│ │
|
||||
│ Docker Compose │
|
||||
│ ├── website (Port 3000) │
|
||||
│ ├── studio-v2 (Port 3001) │
|
||||
│ ├── admin-v2 (Port 3002) │
|
||||
│ ├── backend (Port 8000) │
|
||||
│ ├── consent-service (Port 8081) │
|
||||
│ ├── klausur-service (Port 8086) │
|
||||
│ ├── voice-service (Port 8082) │
|
||||
│ ├── ai-compliance-sdk (Port 8090) │
|
||||
│ ├── docs (Port 8009) │
|
||||
│ ├── postgres │
|
||||
│ ├── valkey (Redis) │
|
||||
│ ├── qdrant (extern: qdrant-dev.breakpilot.ai) │
|
||||
│ └── object-storage (extern: nbg1.your-objectstorage.com) │
|
||||
│ Gitea Actions CI: │
|
||||
│ ├── test-go-ai-compliance │
|
||||
│ ├── test-python-backend-compliance │
|
||||
│ ├── test-python-document-crawler │
|
||||
│ ├── test-python-dsms-gateway │
|
||||
│ └── validate-canonical-controls │
|
||||
│ │
|
||||
│ Coolify Webhook → Build + Deploy (automatisch) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
│ auto-deploy
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Production (Coolify) │
|
||||
│ │
|
||||
│ ├── admin-dev.breakpilot.ai (Admin Compliance) │
|
||||
│ ├── api-dev.breakpilot.ai (Backend API) │
|
||||
│ ├── sdk-dev.breakpilot.ai (AI SDK) │
|
||||
│ └── developers-dev.breakpilot.ai (Developer Portal) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Sync & Deploy Workflow
|
||||
## Workflow
|
||||
|
||||
### 1. Dateien synchronisieren
|
||||
### 1. Code entwickeln und committen
|
||||
|
||||
```bash
|
||||
# Sync aller relevanten Verzeichnisse zum Mac Mini
|
||||
rsync -avz --delete \
|
||||
--exclude 'node_modules' \
|
||||
--exclude '.next' \
|
||||
--exclude '.git' \
|
||||
--exclude '__pycache__' \
|
||||
--exclude 'venv' \
|
||||
--exclude '.pytest_cache' \
|
||||
/Users/benjaminadmin/Projekte/breakpilot-pwa/ \
|
||||
macmini:/Users/benjaminadmin/Projekte/breakpilot-pwa/
|
||||
# Code auf MacBook bearbeiten
|
||||
# Committen und zu beiden Remotes pushen:
|
||||
git push origin main && git push gitea main
|
||||
```
|
||||
|
||||
### 2. Container bauen
|
||||
### 2. Automatische Tests (Gitea Actions)
|
||||
|
||||
Push auf gitea triggert automatisch die CI-Pipeline:
|
||||
|
||||
- **Go Tests:** `ai-compliance-sdk` Unit Tests
|
||||
- **Python Tests:** `backend-compliance`, `document-crawler`, `dsms-gateway`
|
||||
- **Validierung:** Canonical Controls JSON-Validierung
|
||||
- **Lint:** Go, Python, Node.js (nur bei PRs)
|
||||
|
||||
### 3. Automatisches Deployment (Coolify)
|
||||
|
||||
Nach erfolgreichem Push baut Coolify automatisch alle Services und deployt sie.
|
||||
|
||||
**WICHTIG:** Niemals manuell in Coolify auf "Redeploy" klicken!
|
||||
|
||||
### 4. Health Checks
|
||||
|
||||
```bash
|
||||
# Einzelnen Service bauen
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
build --no-cache <service-name>"
|
||||
|
||||
# Beispiele:
|
||||
# studio-v2, admin-v2, website, backend, klausur-service, docs
|
||||
# Production Health pruefen
|
||||
curl -sf https://api-dev.breakpilot.ai/health
|
||||
curl -sf https://sdk-dev.breakpilot.ai/health
|
||||
```
|
||||
|
||||
### 3. Container deployen
|
||||
## CI Pipeline-Konfiguration
|
||||
|
||||
```bash
|
||||
# Container neu starten
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d <service-name>"
|
||||
**Datei:** `.gitea/workflows/ci.yaml`
|
||||
|
||||
```yaml
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main, develop]
|
||||
|
||||
jobs:
|
||||
test-go-ai-compliance: # Go Unit Tests
|
||||
test-python-backend: # Python Unit Tests
|
||||
test-python-document-crawler:
|
||||
test-python-dsms-gateway:
|
||||
validate-canonical-controls: # JSON Validierung
|
||||
go-lint: # Nur bei PRs
|
||||
python-lint: # Nur bei PRs
|
||||
nodejs-lint: # Nur bei PRs
|
||||
```
|
||||
|
||||
### 4. Logs prüfen
|
||||
## Lokale Entwicklung (Mac Mini)
|
||||
|
||||
Fuer lokale Tests ohne Coolify:
|
||||
|
||||
```bash
|
||||
# Container-Logs anzeigen
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
logs -f <service-name>"
|
||||
```
|
||||
|
||||
## Service-spezifische Deployments
|
||||
|
||||
### Next.js Frontend (studio-v2, admin-v2, website)
|
||||
|
||||
```bash
|
||||
# 1. Sync
|
||||
rsync -avz --delete \
|
||||
--exclude 'node_modules' --exclude '.next' --exclude '.git' \
|
||||
/Users/benjaminadmin/Projekte/breakpilot-pwa/studio-v2/ \
|
||||
macmini:/Users/benjaminadmin/Projekte/breakpilot-pwa/studio-v2/
|
||||
|
||||
# 2. Build & Deploy
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
build --no-cache studio-v2 && \
|
||||
/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d studio-v2"
|
||||
```
|
||||
|
||||
### Python Services (backend, klausur-service, voice-service)
|
||||
|
||||
```bash
|
||||
# Build mit requirements.txt
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
build klausur-service && \
|
||||
/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d klausur-service"
|
||||
```
|
||||
|
||||
### Go Services (consent-service, ai-compliance-sdk)
|
||||
|
||||
```bash
|
||||
# Multi-stage Build (Go → Alpine)
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
build --no-cache consent-service && \
|
||||
/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d consent-service"
|
||||
```
|
||||
|
||||
### MkDocs Dokumentation
|
||||
|
||||
```bash
|
||||
# Build & Deploy
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
build --no-cache docs && \
|
||||
/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d docs"
|
||||
|
||||
# Verfügbar unter: http://macmini:8009
|
||||
```
|
||||
|
||||
## Health Checks
|
||||
|
||||
### Service-Status prüfen
|
||||
|
||||
```bash
|
||||
# Alle Container-Status
|
||||
ssh macmini "docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'"
|
||||
|
||||
# Health-Endpoints prüfen
|
||||
curl -s http://macmini:8000/health
|
||||
curl -s http://macmini:8081/health
|
||||
curl -s http://macmini:8086/health
|
||||
curl -s http://macmini:8090/health
|
||||
```
|
||||
|
||||
### Logs analysieren
|
||||
|
||||
```bash
|
||||
# Letzte 100 Zeilen
|
||||
ssh macmini "docker logs --tail 100 breakpilot-pwa-backend-1"
|
||||
|
||||
# Live-Logs folgen
|
||||
ssh macmini "docker logs -f breakpilot-pwa-backend-1"
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
### Container auf vorherige Version zurücksetzen
|
||||
|
||||
```bash
|
||||
# 1. Aktuelles Image taggen
|
||||
ssh macmini "docker tag breakpilot-pwa-backend:latest breakpilot-pwa-backend:backup"
|
||||
|
||||
# 2. Altes Image deployen
|
||||
ssh macmini "/usr/local/bin/docker compose \
|
||||
-f /Users/benjaminadmin/Projekte/breakpilot-pwa/docker-compose.yml \
|
||||
up -d backend"
|
||||
|
||||
# 3. Bei Problemen: Backup wiederherstellen
|
||||
ssh macmini "docker tag breakpilot-pwa-backend:backup breakpilot-pwa-backend:latest"
|
||||
# Auf Mac Mini pullen und bauen
|
||||
ssh macmini "git -C ~/Projekte/breakpilot-compliance pull --no-rebase origin main"
|
||||
ssh macmini "/usr/local/bin/docker compose -f ~/Projekte/breakpilot-compliance/docker-compose.yml build --no-cache <service>"
|
||||
ssh macmini "/usr/local/bin/docker compose -f ~/Projekte/breakpilot-compliance/docker-compose.yml up -d <service>"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container startet nicht
|
||||
### CI-Status pruefen
|
||||
|
||||
```bash
|
||||
# 1. Logs prüfen
|
||||
ssh macmini "docker logs breakpilot-pwa-<service>-1"
|
||||
|
||||
# 2. Container manuell starten für Debug-Output
|
||||
ssh macmini "docker compose -f .../docker-compose.yml run --rm <service>"
|
||||
|
||||
# 3. In Container einloggen
|
||||
ssh macmini "docker exec -it breakpilot-pwa-<service>-1 /bin/sh"
|
||||
# Im Browser:
|
||||
# https://gitea.meghsakha.com/Benjamin_Boenisch/breakpilot-compliance/actions
|
||||
```
|
||||
|
||||
### Port bereits belegt
|
||||
### Container-Logs (lokal)
|
||||
|
||||
```bash
|
||||
# Port-Belegung prüfen
|
||||
ssh macmini "lsof -i :8000"
|
||||
|
||||
# Container mit dem Port finden
|
||||
ssh macmini "docker ps --filter publish=8000"
|
||||
ssh macmini "/usr/local/bin/docker logs -f bp-compliance-<service>"
|
||||
```
|
||||
|
||||
### Build-Fehler
|
||||
|
||||
```bash
|
||||
# Cache komplett leeren
|
||||
ssh macmini "docker builder prune -a"
|
||||
|
||||
# Ohne Cache bauen
|
||||
ssh macmini "docker compose build --no-cache <service>"
|
||||
```
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Resource-Nutzung
|
||||
|
||||
```bash
|
||||
# CPU/Memory aller Container
|
||||
ssh macmini "docker stats --no-stream"
|
||||
|
||||
# Disk-Nutzung
|
||||
ssh macmini "docker system df"
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Ungenutzte Images/Container entfernen
|
||||
ssh macmini "docker system prune -a --volumes"
|
||||
|
||||
# Nur dangling Images
|
||||
ssh macmini "docker image prune"
|
||||
```
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
Umgebungsvariablen werden über `.env` Dateien und docker-compose.yml verwaltet:
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
backend:
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://...
|
||||
- REDIS_URL=redis://valkey:6379
|
||||
- SECRET_KEY=${SECRET_KEY}
|
||||
```
|
||||
|
||||
**Wichtig**: Sensible Werte niemals in Git committen. Stattdessen:
|
||||
- `.env` Datei auf dem Server pflegen
|
||||
- Secrets über HashiCorp Vault (siehe unten)
|
||||
|
||||
## Woodpecker CI - Automatisierte OAuth Integration
|
||||
|
||||
### Überblick
|
||||
|
||||
Die OAuth-Integration zwischen Woodpecker CI und Gitea ist **vollständig automatisiert**. Credentials werden in HashiCorp Vault gespeichert und bei Bedarf automatisch regeneriert.
|
||||
|
||||
!!! info "Warum automatisiert?"
|
||||
Diese Automatisierung ist eine DevSecOps Best Practice:
|
||||
|
||||
- **Infrastructure-as-Code**: Alles ist reproduzierbar
|
||||
- **Disaster Recovery**: Verlorene Credentials können automatisch regeneriert werden
|
||||
- **Security**: Secrets werden zentral in Vault verwaltet
|
||||
- **Onboarding**: Neue Entwickler müssen nichts manuell konfigurieren
|
||||
|
||||
### Architektur
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Mac Mini Server │
|
||||
│ │
|
||||
│ ┌───────────────┐ OAuth 2.0 ┌───────────────┐ │
|
||||
│ │ Gitea │ ←─────────────────────────→│ Woodpecker │ │
|
||||
│ │ (Port 3003) │ Client ID + Secret │ (Port 8090) │ │
|
||||
│ └───────────────┘ └───────────────┘ │
|
||||
│ │ │ │
|
||||
│ │ OAuth App │ Env Vars│
|
||||
│ │ (DB: oauth2_application) │ │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ HashiCorp Vault (Port 8200) │ │
|
||||
│ │ │ │
|
||||
│ │ secret/cicd/woodpecker: │ │
|
||||
│ │ - gitea_client_id │ │
|
||||
│ │ - gitea_client_secret │ │
|
||||
│ │ │ │
|
||||
│ │ secret/cicd/api-tokens: │ │
|
||||
│ │ - gitea_token (für API-Zugriff) │ │
|
||||
│ │ - woodpecker_token (für Pipeline-Trigger) │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Credentials-Speicherorte
|
||||
|
||||
| Ort | Pfad | Inhalt |
|
||||
|-----|------|--------|
|
||||
| **HashiCorp Vault** | `secret/cicd/woodpecker` | Client ID + Secret (Quelle der Wahrheit) |
|
||||
| **.env Datei** | `WOODPECKER_GITEA_CLIENT/SECRET` | Für Docker Compose (aus Vault geladen) |
|
||||
| **Gitea PostgreSQL** | `oauth2_application` Tabelle | OAuth App Registration (gehashtes Secret) |
|
||||
|
||||
### Troubleshooting: OAuth Fehler
|
||||
|
||||
Falls der Fehler "Client ID not registered" oder "user does not exist [uid: 0]" auftritt:
|
||||
|
||||
```bash
|
||||
# Option 1: Automatisches Regenerieren (empfohlen)
|
||||
./scripts/sync-woodpecker-credentials.sh --regenerate
|
||||
|
||||
# Option 2: Manuelles Vorgehen
|
||||
# 1. Credentials aus Vault laden
|
||||
vault kv get secret/cicd/woodpecker
|
||||
|
||||
# 2. .env aktualisieren
|
||||
WOODPECKER_GITEA_CLIENT=<client_id>
|
||||
WOODPECKER_GITEA_SECRET=<client_secret>
|
||||
|
||||
# 3. Zu Mac Mini synchronisieren
|
||||
rsync .env macmini:~/Projekte/breakpilot-pwa/
|
||||
|
||||
# 4. Woodpecker neu starten
|
||||
ssh macmini "cd ~/Projekte/breakpilot-pwa && \
|
||||
docker compose up -d --force-recreate woodpecker-server"
|
||||
```
|
||||
|
||||
### Das Sync-Script
|
||||
|
||||
Das Script `scripts/sync-woodpecker-credentials.sh` automatisiert den gesamten Prozess:
|
||||
|
||||
```bash
|
||||
# Credentials aus Vault laden und .env aktualisieren
|
||||
./scripts/sync-woodpecker-credentials.sh
|
||||
|
||||
# Neue Credentials generieren (OAuth App in Gitea + Vault + .env)
|
||||
./scripts/sync-woodpecker-credentials.sh --regenerate
|
||||
```
|
||||
|
||||
Was das Script macht:
|
||||
|
||||
1. **Liest** die aktuellen Credentials aus Vault
|
||||
2. **Aktualisiert** die .env Datei automatisch
|
||||
3. **Bei `--regenerate`**:
|
||||
- Löscht alte OAuth Apps in Gitea
|
||||
- Erstellt neue OAuth App mit neuem Client ID/Secret
|
||||
- Speichert Credentials in Vault
|
||||
- Aktualisiert .env
|
||||
|
||||
### Vault-Zugriff
|
||||
|
||||
```bash
|
||||
# Vault Token (Development)
|
||||
export VAULT_TOKEN=breakpilot-dev-token
|
||||
|
||||
# Credentials lesen
|
||||
docker exec -e VAULT_TOKEN=$VAULT_TOKEN breakpilot-pwa-vault \
|
||||
vault kv get secret/cicd/woodpecker
|
||||
|
||||
# Credentials setzen
|
||||
docker exec -e VAULT_TOKEN=$VAULT_TOKEN breakpilot-pwa-vault \
|
||||
vault kv put secret/cicd/woodpecker \
|
||||
gitea_client_id="..." \
|
||||
gitea_client_secret="..."
|
||||
```
|
||||
|
||||
### Services neustarten nach Credentials-Änderung
|
||||
|
||||
```bash
|
||||
# Wichtig: --force-recreate um neue Env Vars zu laden
|
||||
cd /Users/benjaminadmin/Projekte/breakpilot-pwa
|
||||
docker compose up -d --force-recreate woodpecker-server
|
||||
|
||||
# Logs prüfen
|
||||
docker logs breakpilot-pwa-woodpecker-server --tail 50
|
||||
# Lokalen Build-Cache leeren
|
||||
ssh macmini "/usr/local/bin/docker builder prune -a"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,395 @@
|
||||
# QA: Control Quality Pipeline
|
||||
|
||||
## Übersicht
|
||||
|
||||
Die Control Quality Pipeline prüft und verbessert die Canonical Controls der Compliance-Bibliothek. Sie nutzt **PDF-basierte Verifizierung** als Ground Truth — jeder Control-Originaltext wird direkt im Quelldokument (PDF) lokalisiert.
|
||||
|
||||
Alle Scripts liegen in **`scripts/qa/`**. Starten auf dem Mac Mini via Runner-Script:
|
||||
|
||||
```bash
|
||||
# Job starten (laedt .env automatisch, PID-Lock, unbuffered output)
|
||||
ssh macmini "bash ~/Projekte/breakpilot-compliance/scripts/qa/run_job.sh <script.py> [args...]"
|
||||
|
||||
# Status aller Jobs
|
||||
ssh macmini "bash ~/Projekte/breakpilot-compliance/scripts/qa/run_job.sh --status"
|
||||
|
||||
# Log ansehen
|
||||
ssh macmini "bash ~/Projekte/breakpilot-compliance/scripts/qa/run_job.sh --log <script.py>"
|
||||
|
||||
# Job stoppen
|
||||
ssh macmini "bash ~/Projekte/breakpilot-compliance/scripts/qa/run_job.sh --kill <script.py>"
|
||||
```
|
||||
|
||||
## Architektur
|
||||
|
||||
```
|
||||
Original-PDFs (~/rag-ingestion/pdfs/)
|
||||
↓
|
||||
PDF Text-Extraktion (PyMuPDF)
|
||||
↓
|
||||
Artikel-Index aufbauen
|
||||
(Erwägungsgründe → Artikel → Anhänge)
|
||||
↓
|
||||
source_original_text im PDF suchen
|
||||
(Substring-Match, normalisiert)
|
||||
↓
|
||||
Artikel/§/Section zuordnen + article_type setzen
|
||||
↓
|
||||
Duplikat-Erkennung
|
||||
(Preamble vs. Artikel: Artikel hat Vorrang)
|
||||
```
|
||||
|
||||
## PDF-basierte Artikelzuordnung
|
||||
|
||||
### Konzept
|
||||
|
||||
Jeder Control hat ein Feld `source_original_text` — der Chunk-Text aus dem Quelldokument. Statt über Qdrant-Hashes wird dieser Text **direkt im Original-PDF gesucht**. Die Position im PDF bestimmt den Artikel.
|
||||
|
||||
### Dokumenttypen
|
||||
|
||||
| Typ | Struktur | Beispiel |
|
||||
|-----|----------|---------|
|
||||
| EU-Verordnung | Erwägungsgründe → Artikel → Anhänge | DSGVO, KI-VO, CRA |
|
||||
| Deutsches Gesetz | §-Paragraphen | BDSG, GewO, HGB |
|
||||
| NIST | Control Families (AC-1, SC-7) | SP 800-53, CSF 2.0 |
|
||||
| OWASP | Kategorien (A01:2021, V1.1) | Top 10, ASVS, MASVS |
|
||||
| EDPB/ENISA | Nummerierte Abschnitte | Leitlinien, Guidelines |
|
||||
|
||||
### Article Types
|
||||
|
||||
| article_type | Bedeutung | Beispiel |
|
||||
|---|---|---|
|
||||
| `article` | Gesetzesartikel / Paragraph | Artikel 25 DSGVO |
|
||||
| `preamble` | Erwägungsgrund | Erwägungsgrund (78) |
|
||||
| `annex` | Anhang | Anhang III |
|
||||
| `control` | NIST Control Family | AC-6, SA-7 |
|
||||
| `section` | Nummerierter Abschnitt | Section 2.1 |
|
||||
| `category` | OWASP Kategorie | A01:2021 |
|
||||
| `requirement` | OWASP Requirement | V1.1, MASVS-STORAGE-1 |
|
||||
|
||||
### Ergebnisse (Stand 2026-03-20)
|
||||
|
||||
| Metrik | Wert |
|
||||
|---|---|
|
||||
| Controls mit source_original_text | 5.751 (86%) |
|
||||
| Im PDF lokalisiert | **5.063 (88%)** |
|
||||
| Nicht gefunden | 649 |
|
||||
| Kein PDF vorhanden | 29 |
|
||||
| Recital_suspect markiert | 648 |
|
||||
| 100% Match-Rate | 20+ Regulations (inkl. DSGVO, KI-VO, NIS2, NIST 800-53, Blue Guide) |
|
||||
|
||||
**Verlauf:** v1 (4.110, 52%) → v2 (6.091, 77%) → v3 (6.259, 79%) → v4 +Blue Guide EN (6.803, 86%) → v5 nach Cleanup (5.063/5.741, 88%)
|
||||
|
||||
### Nicht-matchende Controls
|
||||
|
||||
| Ursache | Controls | Status |
|
||||
|---|---|---|
|
||||
| ~~Blue Guide EN vs. DE PDF~~ | ~~562~~ | ✅ Gelöst — EN-PDF beschafft, 544/544 gematcht |
|
||||
| ~~OWASP Top 10 multilingual~~ | ~~324~~ | ✅ Als duplicate markiert — Übersetzungen ohne Mehrwert |
|
||||
| CRA Encoding | ~76 | PDF-Ligaturen/Sonderzeichen-Differenzen |
|
||||
| CISA Secure by Design | ~113 | Falsches PDF (ENISA statt CISA) |
|
||||
| OWASP ASVS | ~173 | PDF-Matching-Problem (meist EN) |
|
||||
|
||||
## Brute-Force-Suche
|
||||
|
||||
Für Controls mit unbekannter Quelle: Text gegen **alle ~100 PDFs** suchen. Findet:
|
||||
- Korrekte Quelldokument-Zuordnung
|
||||
- Falsche Source-Zuordnungen (44 entdeckt)
|
||||
|
||||
## Erwägungsgrund-Controls (Wettbewerbsvorteil)
|
||||
|
||||
Controls aus Erwägungsgründen (`article_type = preamble`) sind **kein Nachteil**. Sie decken Aspekte ab, die reine Artikel-basierte Compliance-Tools übersehen.
|
||||
|
||||
**Duplikat-Regel:** Wenn ein Preamble-Control das gleiche Thema wie ein Artikel-Control behandelt (Jaccard-Ähnlichkeit ≥ 0.40), hat der **Artikel Vorrang**. Das Preamble-Control wird als `duplicate` markiert.
|
||||
|
||||
### Ergebnis der Preamble-Dedup
|
||||
|
||||
| Metrik | Wert |
|
||||
|---|---|
|
||||
| Preamble-Controls geprüft | 838 |
|
||||
| Als Duplikat markiert | 190 |
|
||||
| **Unique Preamble-Controls** | **648** |
|
||||
|
||||
## Pipeline-Versionen
|
||||
|
||||
| Version | Controls | Mit Originaltext | Beschreibung |
|
||||
|---|---|---|---|
|
||||
| v1 | 5.332 | 4.137 (78%) | Automatisch + manuell erstellt |
|
||||
| v2 | 2.258 | 2.258 (100%) | Automatisch aus Chunks |
|
||||
| v3 | 1.570 | 1.548 (99%) | Neueste Pipeline |
|
||||
|
||||
Die 1.195 v1-Controls **ohne** Originaltext sind manuell erstellt (`strategy=ungrouped`) und haben keine Chunk-Referenz.
|
||||
|
||||
## OWASP Cleanup (2026-03-20)
|
||||
|
||||
- **324 OWASP Top 10 multilingual Controls** → `duplicate` markiert (ZH, AR, ID, FR, ES, PT — Übersetzungen derselben 10 Kategorien)
|
||||
- **47 Controls** mit falscher Quellenzuordnung korrigiert (z.B. als "OWASP Top 10" getaggt, aber tatsächlich aus ASVS/SAMM/API/MASVS)
|
||||
- **~200 OWASP ASVS/SAMM/MASVS EN Controls** behalten — unique Content aus GitHub/Website, nicht im PDF auffindbar
|
||||
|
||||
## NIST OSCAL Import (2026-03-20)
|
||||
|
||||
**776 neue Controls** aus NIST SP 800-53 Rev 5 OSCAL (Public Domain, maschinenlesbar):
|
||||
|
||||
- Quelle: `usnistgov/oscal-content` (JSON Catalog)
|
||||
- Vor allem **Control Enhancements** (z.B. AC-2(3), SC-7(8)) — die atomaren Unteranforderungen
|
||||
- Jeder Control enthält: Statement + Guidance + Assessment-Methoden + Cross-References + Parameters
|
||||
- `pipeline_version = 4`, `generation_strategy = 'oscal_import'`
|
||||
- Kein Pass 0a/0b nötig — Controls sind **bereits atomar**
|
||||
|
||||
| Metrik | Vorher | Nachher |
|
||||
|---|---|---|
|
||||
| SP 800-53 Controls (aktiv) | 1.107 | **1.883** |
|
||||
| OSCAL-Abdeckung | 238/1.014 (23%) | **1.014/1.014 (100%)** |
|
||||
|
||||
## Phase 5: RAG-Deduplizierung + Normalisierung (2026-03-20)
|
||||
|
||||
### Durchgeführte Schritte
|
||||
|
||||
| Schritt | Beschreibung | Controls |
|
||||
|---|---|---|
|
||||
| 5.1 | OSCAL Controls: `source_regulation` in generation_metadata gesetzt | 776 |
|
||||
| 5.2 | v3 Controls ohne Source → `needs_review` mit `missing_source` Flag | 20 |
|
||||
| 5.3 | Leerer Source-Name korrigiert (AT TKG) | 1 |
|
||||
| 5.4 | OWASP regulation_code Fehlzuordnungen korrigiert | 47 |
|
||||
| 5.5 | **duplicate/too_close Controls hart gelöscht** | **3.301** |
|
||||
| 5.6 | Processed Chunks bereinigt (gelöschte Control-IDs entfernt) | 2.520 |
|
||||
|
||||
### Ergebnis
|
||||
|
||||
- **Vorher:** 9.936 Controls (6.635 aktiv, 2.998 duplicate, 303 too_close)
|
||||
- **Nachher:** 6.635 Controls, **alle aktiv** (0 duplicate/too_close)
|
||||
- Alle regulation_codes haben jetzt einheitliche Source-Namen
|
||||
- OWASP-Controls sind korrekt ihren Quellen zugeordnet
|
||||
|
||||
## DB-Status (Stand 2026-03-20, nach Phase 7.4)
|
||||
|
||||
| release_state | Count |
|
||||
|---|---|
|
||||
| draft | ~6.030 |
|
||||
| needs_review | 838 |
|
||||
| **Gesamt** | **6.868** |
|
||||
|
||||
## Scripts (`scripts/qa/`)
|
||||
|
||||
### Kern-QA (PDF-Matching)
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `pdf_qa_all.py` | **Haupt-QA**: Controls gegen PDFs matchen, Artikel-Index aufbauen. Enthaelt `SOURCE_FILE_MAP`, alle Index-Builder (EU, DE, NIST, OWASP, generic). 526 Zeilen. |
|
||||
| `pdf_qa_inventory.py` | Inventar: Welche Regulations haben Controls, wie viele, welche PDFs existieren |
|
||||
| `apply_pdf_qa_results.py` | Ergebnisse aus `pdf_qa_all.py` in DB schreiben (`article_type`, `recital_suspect`) |
|
||||
| `pdf_article_lookup_poc.py` | POC: Control-Text in PDF lokalisieren, Headings von Cross-Refs unterscheiden |
|
||||
|
||||
### Lueckenanalyse + Control-Generierung
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `gap_analysis.py` | **Phase 7.3**: Artikel im PDF vs. Controls in DB vergleichen, Luecken identifizieren |
|
||||
| `phase74_generate_gap_controls.py` | **Phase 7.4**: Neue Controls fuer Luecken via Anthropic API generieren. `pipeline_version=5`. 624 Zeilen. |
|
||||
| `benchmark_llm_controls.py` | LLM-Vergleich: gpt-oss-120b vs. Claude Sonnet fuer Control-Generierung |
|
||||
| `test_pass0a.py` | **Pass 0a Test**: Obligation Extraction + 3-Tier-Klassifizierung (Pflicht/Empfehlung/Kann). Standalone, speichert JSON. |
|
||||
|
||||
### Deduplizierung + Normalisierung
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `preamble_dedup.py` | Preamble vs. Artikel Duplikat-Erkennung (Jaccard >= 0.40) |
|
||||
| `qa_dedup_controls.py` | Jaccard-basierte Titel-Deduplizierung |
|
||||
| `qa_apply_and_dedup.py` | Ergebnisse anwenden + Duplikate in einem Schritt markieren |
|
||||
| `qa_normalize_sources.py` | Source-Namen normalisieren (kanonische Namen) |
|
||||
| `phase5_normalize_and_cleanup.py` | **Phase 5**: Normalisierung + 3.301 Duplikate hart loeschen |
|
||||
| `qa_delete_gpsr_dupe.py` | GPSR-Duplikate loeschen |
|
||||
| `delete_gpsr_prod.py` | GPSR-Duplikate aus Production-Qdrant entfernen |
|
||||
|
||||
### Quellen-spezifische Scripts
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `blue_guide_en_match.py` | Blue Guide EN-PDF matchen (544/544 Erfolg) |
|
||||
| `owasp_cleanup.py` | OWASP multilingual Cleanup (324 Duplikate) + Source-Fix (47 korrigiert) |
|
||||
| `owasp_github_match.py` | OWASP ASVS/SAMM/MASVS gegen GitHub-Markdown matchen |
|
||||
| `oscal_import.py` | NIST OSCAL Import (776 Controls aus JSON Catalog) |
|
||||
| `oscal_analysis.py` | NIST OSCAL Analyse: Abdeckung, fehlende Controls |
|
||||
|
||||
### Diagnose + Utilities
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `db_status.py` | DB-Status: release_state Counts, pipeline_version, source Verteilung |
|
||||
| `debug_low_match.py` | Debugging: Warum matchen Blue Guide / OWASP / CISA schlecht? |
|
||||
| `qa_article_map_all_chunks.py` | Alle Chunks Artikel-Nummern zuordnen (Bulk) |
|
||||
| `backfill_job_66228863.py` | Einmaliger Backfill-Job |
|
||||
| `sync_controls_to_prod.py` | Controls von Dev nach Production synchronisieren |
|
||||
|
||||
### Runner
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `run_job.sh` | **Job-Runner**: Laedt `.env`, PID-Lock, Monitoring (`--status`, `--log`, `--kill`) |
|
||||
|
||||
## Phase 7: PDF-Validierung + Enrichment (2026-03-20)
|
||||
|
||||
### 7.1 + 7.2: Controls gegen PDFs validiert + Ergebnisse angewendet ✅
|
||||
|
||||
- 5.063 Controls erfolgreich im Original-PDF lokalisiert (88%)
|
||||
- `article_type` fuer alle gematchten Controls gesetzt
|
||||
- 648 Preamble-Controls als `recital_suspect` in `generation_metadata` markiert
|
||||
- 332 Controls nicht matchbar (OWASP ASVS 132, CISA 72, ENISA 38, OWASP SAMM 31, CRA 28)
|
||||
|
||||
### 7.3: Lueckenanalyse ✅
|
||||
|
||||
**494 Artikel-Luecken** in 15 Quellen identifiziert. Geschaetzt ~300 davon actionable.
|
||||
|
||||
| Source | Luecken | Coverage | Bemerkung |
|
||||
|---|---:|---:|---|
|
||||
| AML-Verordnung | 91 | 5% | Kaum ingestiert |
|
||||
| MiCA | 71 | 52% | Grosse Verordnung |
|
||||
| NIST SP 800-53 | 59 | 83% | Meist Section-Header, nur SA-15 fehlt |
|
||||
| OWASP ASVS 4.0 | 47 | 35% | Requirement-Gruppen fehlen |
|
||||
| Batterieverordnung | 41 | 58% | |
|
||||
| DSGVO | 35 | 65% | Einige Governance/Aufsicht-Artikel |
|
||||
| ENISA ICS/SCADA | 34 | 31% | |
|
||||
| ENISA Supply Chain | 26 | 7% | |
|
||||
| CRA | 23 | 68% | |
|
||||
| NIS2 | 16 | 65% | |
|
||||
| KI-Verordnung | 15 | 87% | Fast komplett |
|
||||
| Maschinenverordnung | 5 | 91% | Fast komplett |
|
||||
|
||||
### 7.4: Neue Controls fuer Luecken generieren ✅ (2026-03-20)
|
||||
|
||||
Script: `phase74_generate_gap_controls.py --resume`
|
||||
|
||||
- **494 Artikel-Luecken** in 15 Quellen → Anthropic Claude Sonnet 4.6
|
||||
- `pipeline_version = 5`, `generation_strategy = 'phase74_gap_fill'`
|
||||
- Direkt PDF-Text als Input (nicht RAG-Chunks)
|
||||
- Starten via: `run_job.sh phase74_generate_gap_controls.py --resume`
|
||||
|
||||
**Ergebnis:**
|
||||
|
||||
| Source | Luecken | Generiert |
|
||||
|---|---:|---:|
|
||||
| AML-Verordnung | 91 | 97 |
|
||||
| MiCA | 71 | 68 |
|
||||
| NIST SP 800-53 | 59 | 19 |
|
||||
| KI-Verordnung | 15 | 15 |
|
||||
| OWASP ASVS 4.0 | 47 | 11 |
|
||||
| Batterieverordnung | 41 | 9 |
|
||||
| DSGVO | 35 | 4 |
|
||||
| OWASP Top 10 | 12 | 3 |
|
||||
| NIS2 | 16 | 3 |
|
||||
| CRA | 23 | 3 |
|
||||
| OECD KI-Empfehlung | 4 | 1 |
|
||||
| **Gesamt** | **494** | **233** |
|
||||
|
||||
Nicht generiert: 75 zu kurzer Text, 29 NIST-Intros, 11 Parse-Errors, 162 ID-Konflikte (COMP-1000 etc.).
|
||||
API-Kosten: ~$7,55 (109 min Laufzeit).
|
||||
|
||||
## Pass 0a: Obligation Extraction — 3-Tier-Klassifizierung
|
||||
|
||||
### Konzept
|
||||
|
||||
Pass 0a zerlegt Rich Controls (~6.000) in **atomare Obligations** per LLM (Claude Sonnet 4.6).
|
||||
Jede Obligation wird durch den **Quality Gate** klassifiziert — nicht gefiltert:
|
||||
|
||||
| obligation_type | Signal | Beispiel |
|
||||
|---|---|---|
|
||||
| **pflicht** | müssen, muss, ist zu, hat zu, shall, must, required | "Der Betreiber muss alle Daten verschluesseln" |
|
||||
| **empfehlung** | soll, sollen, should, sicherstellen, gewaehrleisten, dokumentieren | "Der Betreiber soll regelmaessige Audits durchfuehren" |
|
||||
| **kann** | kann, koennen, darf, duerfen, may, optional | "Der Betreiber kann zusaetzliche Massnahmen ergreifen" |
|
||||
|
||||
**Wichtig:** Nichts wird mehr rejected wegen fehlendem normativem Signal. Obligations ohne Signal werden als `empfehlung` klassifiziert. Rejected werden nur noch: Evidence-Only, zu kurz (<20 Zeichen), fehlender Parent-Link.
|
||||
|
||||
### Warum auch Empfehlungen behalten?
|
||||
|
||||
Empfehlungen helfen Firmen, ihre Systeme sicherer zu machen — ueber das Pflichtprogramm hinaus. Im Frontend erhalten Kunden einen Marker, der klar anzeigt:
|
||||
|
||||
- **Pflicht** = gesetzlich/regulatorisch vorgeschrieben
|
||||
- **Empfehlung** = Best Practice, freiwillig, aber wertvoll
|
||||
- **Kann** = optional, weitergehende Massnahme
|
||||
|
||||
### Quality Gate — Kritische Flags
|
||||
|
||||
| Flag | Kritisch? | Beschreibung |
|
||||
|---|---|---|
|
||||
| `has_normative_signal` | Nein | Informativer Check, kein Ablehnungsgrund |
|
||||
| `obligation_type` | — | Klassifizierung (pflicht/empfehlung/kann) |
|
||||
| `not_evidence_only` | **Ja** | Kein reiner Nachweis-Eintrag |
|
||||
| `min_length` | **Ja** | Mindestens 20 Zeichen |
|
||||
| `has_parent_link` | **Ja** | Verbindung zum Parent-Control |
|
||||
| `single_action` | Nein | Nur ein Hauptverb (heuristisch) |
|
||||
| `not_rationale` | Nein | Keine reine Begruendung |
|
||||
|
||||
### Normative Signal Detection — Regex-Tiers
|
||||
|
||||
```
|
||||
Tier 1 (Pflicht): muessen, muss, ist/sind/hat/haben zu + Infinitiv,
|
||||
Compound-Verben (festzustellen, vorzunehmen),
|
||||
Gerundivum (mitzuteilen, bereitzustellen),
|
||||
shall, must, required
|
||||
|
||||
Tier 2 (Empfehlung): soll, sollen, sollte, sollten,
|
||||
gewaehrleisten, sicherstellen,
|
||||
should, ensure, recommend,
|
||||
dokumentieren, implementieren, ueberpruefen
|
||||
|
||||
Tier 3 (Kann): kann, koennen, darf, duerfen, may, optional
|
||||
```
|
||||
|
||||
### Testergebnisse (3 Iterationen, 2026-03-20)
|
||||
|
||||
| Run | Controls | Obligations | Validated | Rejected | Kosten |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| 1 (v0 Regex) | 10 | ~100 | 68% | 32% | $0,28 |
|
||||
| 2 (v1 Regex) | 50 | ~530 | 78% | 22% | $1,43 |
|
||||
| 3 (v2 Regex) | 50 | ~530 | 86% | 14% | $1,44 |
|
||||
| 4 (3-Tier) | 60 | — | — | — | — |
|
||||
|
||||
Run 4 laeuft mit dem neuen Klassifizierer — statt PASS/REJECT wird jetzt PFLICHT/EMPFEHLUNG/KANN ausgegeben.
|
||||
|
||||
### Scripts
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `test_pass0a.py` | **Test-Script**: Standalone (kein SQLAlchemy), psycopg2 + Anthropic API. Speichert Ergebnisse als JSON. |
|
||||
|
||||
```bash
|
||||
# Test mit 10 Controls
|
||||
run_job.sh test_pass0a.py --limit 10
|
||||
|
||||
# Test mit bestimmter Quelle
|
||||
run_job.sh test_pass0a.py --limit 20 --source "DSGVO"
|
||||
|
||||
# Ergebnisse: /tmp/pass0a_results_<N>controls.json
|
||||
```
|
||||
|
||||
### Backend-Code
|
||||
|
||||
- **Klassifizierung:** `backend-compliance/compliance/services/decomposition_pass.py`
|
||||
- `classify_obligation_type()` — 3-Tier-Klassifizierung
|
||||
- `quality_gate()` — gibt `obligation_type` in Flags zurueck
|
||||
- `passes_quality_gate()` — `has_normative_signal` nicht mehr kritisch
|
||||
- `ObligationCandidate.obligation_type` — neues Feld
|
||||
|
||||
### Hochrechnung (basierend auf 50-Control-Runs)
|
||||
|
||||
| Metrik | Wert |
|
||||
|---|---|
|
||||
| Kosten pro Control | ~$0,029 |
|
||||
| Kosten fuer ~6.000 Controls | **~$172** |
|
||||
| Laufzeit (geschaetzt) | ~25h |
|
||||
| Obligations pro Control | ~10,5 |
|
||||
|
||||
---
|
||||
|
||||
## Naechste Schritte
|
||||
|
||||
1. ~~**Phase 5 Cleanup** → 3.301 Duplikate geloescht, Source normalisiert~~ ✅
|
||||
2. ~~**Phase 6 Pipeline-Haertung** → Source aus REGULATION_LICENSE_MAP~~ ✅
|
||||
3. ~~**Phase 7.1-7.3** → PDF-Validierung + Enrichment + Lueckenanalyse~~ ✅
|
||||
4. ~~**Phase 7.4** → 233 neue Controls fuer Luecken generiert ($7,55)~~ ✅
|
||||
5. **Pass 0a** → Obligation Extraction mit 3-Tier-Klassifizierung (Tests laufen, ~$172)
|
||||
6. **Pass 0b** → Atomic Control Composition aus validierten Obligations
|
||||
7. **Pass 1-5** → Multi-Layer Migration (Code + 500 Tests bereits vorhanden)
|
||||
8. **Phase 8** → Qdrant Re-Ingestion (Runtime-Betrieb, ZULETZT)
|
||||
9. **needs_review Triage** — 838 Controls klassifizieren
|
||||
10. **Frontend** — `obligation_type` (Pflicht/Empfehlung/Kann) + `article_type` anzeigen
|
||||
@@ -0,0 +1,206 @@
|
||||
# RAG Pipeline Benchmark & Optimierungen
|
||||
|
||||
Stand: 2026-03-21. Vergleich unserer Implementierung mit State of the Art. Priorisierte Empfehlungen nach Impact/Effort.
|
||||
|
||||
---
|
||||
|
||||
## Aktuelle Pipeline (Ist-Zustand)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Dokumente] -->|Document Crawler| B[Chunks 512/50]
|
||||
B -->|bge-m3| C[Qdrant Dense]
|
||||
C -->|Cosine Search| D[Control Generator v2]
|
||||
D -->|LLM| E[Rich Controls 6.373]
|
||||
E -->|Pass 0a| F[Obligations]
|
||||
F -->|Pass 0b| G[Atomare Controls]
|
||||
G -->|4-Stage Dedup| H[Master Controls ~18K]
|
||||
```
|
||||
|
||||
| Komponente | Implementierung | SOTA-Bewertung |
|
||||
|-----------|----------------|----------------|
|
||||
| **Chunking** | Rekursiv, 512 Zeichen, 50 Overlap | Zu klein fuer Rechtstexte |
|
||||
| **Embedding** | bge-m3 (1024-dim, Ollama) | Gut, aber nur Dense genutzt |
|
||||
| **Vector DB** | Qdrant mit Payload-Filtering | Hybrid Search nicht aktiviert |
|
||||
| **Retrieval** | Pure Dense Cosine Similarity | Kein Re-Ranking, kein BM25 |
|
||||
| **Extraktion** | 3-Tier (Exact → Embedding → LLM) | Solide Architektur |
|
||||
| **Dedup** | 4-Stage (Pattern → Action → Object → Embedding) | Ueberdurchschnittlich |
|
||||
| **QA** | 5-Metrik Similarity + PDF-QA Matching | Gut, RAGAS fehlt |
|
||||
|
||||
---
|
||||
|
||||
## Tier 1: Quick Wins (Tage, nicht Wochen)
|
||||
|
||||
### 1. Chunk-Groesse erhoehen: 512 → 1024, Overlap 50 → 128
|
||||
|
||||
**Problem:** NAACL 2025 Vectara-Studie zeigt: fuer analytische/juristische Queries sind 512-1024 Token optimal. Unsere 512-Zeichen-Chunks (= ~128 Token) sind deutlich zu klein.
|
||||
|
||||
**Unsere Lessons Learned:** "Chunks werden mitten im Absatz abgeschnitten. Artikel- und Paragraphennummern fehlen."
|
||||
|
||||
**Aenderung:** Config-Parameter in `ingest-phase-h.sh` anpassen.
|
||||
|
||||
| Metrik | Vorher | Nachher |
|
||||
|--------|--------|---------|
|
||||
| Chunk Size | 512 chars (~128 Token) | 1024 chars (~256 Token) |
|
||||
| Overlap | 50 chars (10%) | 128 chars (12.5%) |
|
||||
|
||||
**Impact:** HOCH | **Effort:** NIEDRIG
|
||||
|
||||
### 2. Ollama JSON-Mode fuer Obligation Extraction
|
||||
|
||||
**Problem:** `_parse_json` in `decomposition_pass.py` hat Regex-Fallback — das zeigt, dass LLM-Output nicht zuverlaessig JSON ist.
|
||||
|
||||
**Aenderung:** `format: "json"` in Ollama-API-Calls setzen.
|
||||
|
||||
**Impact:** MITTEL | **Effort:** NIEDRIG (1 Parameter)
|
||||
|
||||
### 3. Chain-of-Thought Prompting fuer Pass 0a/0b
|
||||
|
||||
**Problem:** LegalGPT-Framework zeigt: explizite Reasoning-Chains ("Erst Addressat identifizieren, dann Aktion, dann normative Staerke") verbessern Extraktionsqualitaet signifikant.
|
||||
|
||||
**Impact:** MITTEL | **Effort:** NIEDRIG (Prompt Engineering)
|
||||
|
||||
---
|
||||
|
||||
## Tier 2: High Impact, Medium Effort (1-2 Wochen)
|
||||
|
||||
### 4. Hybrid Search (Dense + Sparse) via Qdrant
|
||||
|
||||
**Problem:** Reine Dense-Suche. Juristische Queries enthalten spezifische Begriffe ("DSGVO Art. 35", "Abs. 3"), die BM25/Sparse besser findet.
|
||||
|
||||
**Loesungsansatz:** BGE-M3 generiert bereits Sparse Vectors — wir verwerfen sie aktuell!
|
||||
|
||||
```
|
||||
Qdrant Query API:
|
||||
- Dense: bge-m3 Cosine (wie bisher)
|
||||
- Sparse: bge-m3 Sparse Vectors (neu)
|
||||
- Fusion: Reciprocal Rank Fusion (RRF)
|
||||
```
|
||||
|
||||
**Benchmarks (Anthropic):** 49% weniger fehlgeschlagene Retrievals mit Contextual Retrieval, 67% mit Re-Ranking.
|
||||
|
||||
**Impact:** SEHR HOCH | **Effort:** MITTEL
|
||||
|
||||
### 5. Cross-Encoder Re-Ranking
|
||||
|
||||
**Problem:** Top-5 Ergebnisse direkt an LLM — keine Qualitaetspruefung der Retrieval-Ergebnisse.
|
||||
|
||||
**Loesungsansatz:** BGE Reranker v2 (MIT-Lizenz) auf Top-20 Ergebnisse, dann Top-5 an LLM.
|
||||
|
||||
| Re-Ranker | Lizenz | Empfehlung |
|
||||
|-----------|--------|------------|
|
||||
| BGE Reranker v2 | MIT | Empfohlen |
|
||||
| Jina Reranker v2 | Apache-2.0 | Alternative |
|
||||
| ColBERT v2 | MIT | Spaeter |
|
||||
|
||||
**Impact:** HOCH | **Effort:** MITTEL
|
||||
|
||||
### 6. Cross-Regulation Dedup Pass
|
||||
|
||||
**Problem:** Dedup filtert immer nach `pattern_id` — Controls aus DSGVO Art. 25 und NIS2 Art. 21 (beide Security-by-Design) werden nie verglichen.
|
||||
|
||||
**Loesungsansatz:** Zweiter Qdrant-Search ohne `pattern_id`-Filter nach dem normalen Dedup-Pass.
|
||||
|
||||
**Impact:** HOCH | **Effort:** MITTEL
|
||||
|
||||
### 7. Automatische Regressionstests (Golden Set)
|
||||
|
||||
**Problem:** Keine systematische Qualitaetsmessung nach Pipeline-Aenderungen.
|
||||
|
||||
**Loesungsansatz:** 20-Chunk Golden Set → Control-Generation → Output-Stabilitaet pruefen.
|
||||
|
||||
**Impact:** HOCH | **Effort:** NIEDRIG
|
||||
|
||||
---
|
||||
|
||||
## Tier 3: Strategische Investitionen (Wochen bis Monate)
|
||||
|
||||
### 8. Artikel-Boundary Chunking
|
||||
|
||||
Eigener Splitter fuer EU-Verordnungen und deutsche Gesetze: Split an "Art.", "Artikel", "Paragraph"-Grenzen statt nach Zeichenzahl.
|
||||
|
||||
### 9. RAGAS Evaluation Pipeline
|
||||
|
||||
[RAGAS](https://docs.ragas.io/) mit Golden Dataset (50-100 manuell verifizierte Control-to-Source Mappings). Metriken: Faithfulness, Answer Relevancy, Context Precision, Context Recall.
|
||||
|
||||
### 10. BGE-M3 Fine-Tuning
|
||||
|
||||
Fine-Tuning auf Compliance-Corpus (~6.373 Control-Titel/Objective-Paare). Research zeigt +10-30% Domain-Retrieval-Verbesserung.
|
||||
|
||||
### 11. LLM-as-Judge
|
||||
|
||||
Claude Sonnet bewertet jeden generierten Control auf Faithfulness zum Quelltext (~$0.01/Control).
|
||||
|
||||
### 12. Active Learning aus Review-Queue
|
||||
|
||||
Menschliche Entscheidungen der Dedup Review-Queue nutzen, um Schwellenwerte ueber die Zeit zu optimieren.
|
||||
|
||||
---
|
||||
|
||||
## Nicht empfohlen (niedriger ROI oder Konflikte)
|
||||
|
||||
| Ansatz | Grund |
|
||||
|--------|-------|
|
||||
| Jina v3 Embeddings | **CC-BY-NC-4.0** — verletzt Open Source Policy |
|
||||
| Voyage-law-2 | API-only, proprietaer — kein Self-Hosting |
|
||||
| Semantic Chunking | Benchmarks zeigen keinen Vorteil gegenueber Recursive fuer strukturierte Dokumente |
|
||||
| HyDE als Primaerstrategie | Latenz (+43-60%) + Halluzinationsrisiko |
|
||||
| Knowledge Graph RAG | Massiver Aufwand, unklarer Gewinn bei strukturiertem Rechtskorpus |
|
||||
|
||||
---
|
||||
|
||||
## Embedding-Modell Vergleich
|
||||
|
||||
| Modell | MTEB Score | Multilingual | Kontext | Lizenz | Bewertung |
|
||||
|--------|-----------|-------------|---------|--------|-----------|
|
||||
| **BGE-M3** (aktuell) | 63.0 | 100+ Sprachen | 8192 Token | MIT | Gut, Dense+Sparse+ColBERT |
|
||||
| Jina v3 | 65.5 | 89 Sprachen | 8192 Token | CC-BY-NC | Nicht nutzbar (Lizenz!) |
|
||||
| E5-Mistral-7B | ~65 | Gut | 4096 Token | MIT | Gross, hoher RAM |
|
||||
| Voyage-law-2 | Best Legal | EN Legal | 16K Token | Proprietaer | Nicht nutzbar (API-only) |
|
||||
|
||||
**Fazit:** BGE-M3 bleibt die beste Wahl fuer unseren Stack. Sparse-Vectors aktivieren und Fine-Tuning bringen mehr als ein Modellwechsel.
|
||||
|
||||
---
|
||||
|
||||
## Test-Coverage Analyse
|
||||
|
||||
### Pipeline-Module (567 Tests)
|
||||
|
||||
| Modul | Tests | Bewertung | Fehlende Tests |
|
||||
|-------|-------|-----------|----------------|
|
||||
| Control Generator | 110 | Exzellent | 10-15 Edge Cases |
|
||||
| Obligation Extractor | 107 | Exzellent | 8-10 Edge Cases |
|
||||
| Decomposition Pass | 90 | Exzellent | 5-8 Edge Cases |
|
||||
| Pattern Matcher | 72 | Gut | 10-15 Edge Cases |
|
||||
| Control Dedup | 56 | Exzellent | 5-8 Edge Cases |
|
||||
| Control Composer | 54 | Gut | 8-10 Edge Cases |
|
||||
| Pipeline Adapter | 36 | Gut | 10-15 Edge Cases |
|
||||
| Citation Backfill | 20 | Moderat | 5-8 Edge Cases |
|
||||
| License Gate | 12 | Minimal | 5-8 Edge Cases |
|
||||
| RAG Client | 10 | Minimal | 5-8 Edge Cases |
|
||||
|
||||
### Kritische Luecken (fehlende Tests)
|
||||
|
||||
| Service | Datei | Prioritaet |
|
||||
|---------|-------|------------|
|
||||
| AI Compliance Assistant | `ai_compliance_assistant.py` | HOCH (25-30 Tests noetig) |
|
||||
| PDF Extractor | `pdf_extractor.py` | HOCH (20-25 Tests noetig) |
|
||||
| LLM Provider | `llm_provider.py` | HOCH (15-20 Tests noetig) |
|
||||
| Similarity Detector | `similarity_detector.py` | MITTEL (20-25 Tests noetig) |
|
||||
| Anchor Finder | `anchor_finder.py` | MITTEL |
|
||||
|
||||
### Test-Infrastruktur
|
||||
|
||||
**Fehlend:** Shared `conftest.py` mit gemeinsamen Fixtures (LLM-Mock, DB-Mock, Embedding-Mock). Aktuell sind Fixtures in jedem Test-File dupliziert.
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- [NAACL 2025 Vectara Chunking Study](https://blog.premai.io/rag-chunking-strategies-the-2026-benchmark-guide/)
|
||||
- [Anthropic Contextual Retrieval](https://www.anthropic.com/news/contextual-retrieval)
|
||||
- [Qdrant Hybrid Search Query API](https://qdrant.tech/articles/hybrid-search/)
|
||||
- [Structure-Aware Chunking for Legal (ACL 2025)](https://aclanthology.org/2025.justnlp-main.19/)
|
||||
- [RAGAS Evaluation Framework](https://docs.ragas.io/)
|
||||
- [BGE Reranker v2 (MIT)](https://huggingface.co/BAAI/bge-reranker-v2-m3)
|
||||
- [LegalGPT / CALLM Framework](https://www.emergentmind.com/topics/compliance-alignment-llm-callm)
|
||||
@@ -0,0 +1,223 @@
|
||||
# RAG Pipeline: Lessons Learned & Hardening
|
||||
|
||||
## Übersicht
|
||||
|
||||
Dieses Dokument beschreibt die Erkenntnisse aus dem Aufbau der RAG-Pipeline und die daraus abgeleiteten Maßnahmen zur Härtung. Es dient als Referenz für zukünftige Ingestion-Runs und Pipeline-Erweiterungen.
|
||||
|
||||
## Architektur: Wann brauchen wir RAG vs. Direct PDF?
|
||||
|
||||
### RAG ist nötig für:
|
||||
|
||||
| Use Case | Warum RAG? |
|
||||
|---|---|
|
||||
| **Compliance Advisor (Chat)** | Semantische Suche über 38+ Dokumente in Echtzeit |
|
||||
| **Cross-Regulation Mapping** | "Zeige alle Anforderungen zu Verschlüsselung" über alle Quellen |
|
||||
| **Customer Scope-Filtering** | Nur Chunks aus relevanten Regulations für den Kunden |
|
||||
| **Inkrementelle Updates** | Neues Dokument → nur neue Chunks verarbeiten |
|
||||
|
||||
### RAG ist NICHT nötig für:
|
||||
|
||||
| Use Case | Besser: Direct PDF |
|
||||
|---|---|
|
||||
| **Control-Generierung (Batch)** | PDF → PyMuPDF → Strukturparser → Artikel-Index → API |
|
||||
| **PDF-QA/Verifizierung** | Substring-Match direkt im PDF (schneller, exakter) |
|
||||
| **Artikel/§-Extraktion** | Regex-basierte Extraktion aus PDF-Text |
|
||||
|
||||
### Hybrid-Ansatz (Empfehlung)
|
||||
|
||||
```
|
||||
Control-Generierung: PDF → Strukturparser → Artikel-Index → Anthropic API
|
||||
(KEIN RAG nötig, direkt aus PDF)
|
||||
|
||||
Runtime-Betrieb: Qdrant-RAG für semantische Suche, Chat, Scope-Analyse
|
||||
(RAG mit angereicherten Chunks + Struktur-Metadaten)
|
||||
```
|
||||
|
||||
## Fehler und Root Causes
|
||||
|
||||
### 1. Doppelte Ingestion = Doppelte Controls
|
||||
|
||||
**Problem:** Gleiche PDFs unter verschiedenen Namen ingestiert (z.B. "Maschinenverordnung" und "Verordnung (EU) 2023/1230") → unterschiedliche Chunks (anderes Chunking) → anderer Hash → doppelt verarbeitet → doppelte Controls.
|
||||
|
||||
**Root Cause:**
|
||||
- `regulation_name` aus Chunk-Metadaten statt aus kanonischer Quelle
|
||||
- UNIQUE-Constraint nur `(chunk_hash, collection, document_version)` — nicht global
|
||||
- Kein Check ob `regulation_code` bereits in einer Collection existiert
|
||||
|
||||
**Fix (implementiert):**
|
||||
- `REGULATION_LICENSE_MAP` enthält jetzt kanonische `name`-Werte die den DB-Einträgen entsprechen
|
||||
- `source_citation.source` wird aus `REGULATION_LICENSE_MAP.name` genommen, NICHT aus `chunk.regulation_name`
|
||||
- Phase 5 Cleanup: 3.301 Duplikate hart gelöscht
|
||||
|
||||
**Fix (noch offen):**
|
||||
- Chunk-Hash UNIQUE Constraint global machen: `(chunk_hash, document_version)` statt `(chunk_hash, collection, document_version)`
|
||||
- Vor Ingestion: Check ob `regulation_code` bereits in einer Collection existiert
|
||||
|
||||
### 2. Chunks verlieren Strukturinformation
|
||||
|
||||
**Problem:** Chunks werden mitten im Absatz abgeschnitten. § und Artikelnummern fehlen in den Chunk-Metadaten. Kontext des Kapitels/Abschnitts geht verloren.
|
||||
|
||||
**Root Cause:**
|
||||
- `chunk_strategy=recursive` mit `chunk_size=512, chunk_overlap=50` — zu kleine Chunks
|
||||
- Chunking beachtet keine Dokumentstruktur (Artikel-/Paragraphengrenzen)
|
||||
- Keine Einleitung/Kapitelkontext als Prefix
|
||||
|
||||
**Empfehlung für Re-Ingestion:**
|
||||
- **Strukturiertes Chunking:** Chunks an Artikel-/Paragraphengrenzen schneiden
|
||||
- **Kontext-Prefix:** Kapiteleinleitung und übergeordnete Struktur mitliefern
|
||||
- **Metadaten anreichern:** `article`, `paragraph`, `article_type`, `section_hierarchy`
|
||||
- **Größere Chunks:** Mindestens 1024 Tokens, besser volle Artikel/Paragraphen
|
||||
|
||||
### 3. Cross-Collection-Duplikate
|
||||
|
||||
**Problem:** `nist_csf_2_0` in `bp_compliance_ce` (67 Chunks) UND `bp_compliance_datenschutz` (162 Chunks). EU-Verordnungen sowohl in `bp_compliance_ce` als auch `bp_compliance_gesetze`.
|
||||
|
||||
**Root Cause:** Keine Collection-Zuordnungsregeln. Manuelle Zuweisung bei Ingestion.
|
||||
|
||||
**Fix:** `cleanup-qdrant-duplicates.py` Script bereinigt Cross-Collection-Duplikate.
|
||||
|
||||
**Empfehlung:** Klare Collection-Zuordnungsregeln:
|
||||
- `bp_compliance_ce` = EU-Verordnungen + internationale Standards
|
||||
- `bp_compliance_gesetze` = Deutsche + österreichische Gesetze (NUR nationale Gesetze)
|
||||
- `bp_compliance_datenschutz` = EDPB/WP29 Leitlinien + Privacy Frameworks
|
||||
|
||||
### 4. OWASP Multilingual Controls
|
||||
|
||||
**Problem:** 324 OWASP Top 10 Controls in ZH, AR, ID, FR, ES, PT — Übersetzungen derselben 10 Kategorien. Kein Mehrwert, aber 324 doppelte Controls generiert.
|
||||
|
||||
**Root Cause:** Multilingual PDFs/GitHub-Quellen ohne Spracherkennung ingestiert.
|
||||
|
||||
**Fix:** 324 als `duplicate` markiert und gelöscht.
|
||||
|
||||
**Empfehlung:** Bei Ingestion Spracherkennung + Deduplizierung. Nur DE + EN behalten.
|
||||
|
||||
### 5. Fehlende Artikel/Paragraph-Extraktion
|
||||
|
||||
**Problem:** Chunks haben `article` und `paragraph` oft leer oder falsch. Die LLM-basierte Extraktion bei der Control-Generierung ist unzuverlässig.
|
||||
|
||||
**Root Cause:** Ingestion-Pipeline extrahiert keine Strukturinformation aus dem PDF.
|
||||
|
||||
**Fix (implementiert):** PDF-QA-Pipeline (`pdf_qa_all.py`) matched `source_original_text` gegen Original-PDFs und extrahiert korrekte Artikel/Paragraphen — 86% Match-Rate.
|
||||
|
||||
**Empfehlung:** Bei Re-Ingestion direkt in den Chunk-Metadaten speichern.
|
||||
|
||||
### 6. Job-Tracking nicht persistent
|
||||
|
||||
**Problem:** Generation-Jobs laufen als Background-Tasks. Kein Logging, welche Chunks verarbeitet, Status nur über API abfragbar. Bei API-Timeout oder Restart geht der Fortschritt verloren.
|
||||
|
||||
**Root Cause:** `asyncio.create_task()` hat keinen Recovery-Mechanismus.
|
||||
|
||||
**Fix (teilweise):** `canonical_generation_jobs` Tabelle trackt Jobs. `canonical_processed_chunks` markiert verarbeitete Chunks.
|
||||
|
||||
**Empfehlung:**
|
||||
- Job-Log in DB persistieren (nicht nur stdout)
|
||||
- Fortschritt in `canonical_generation_jobs.progress` als JSONB speichern
|
||||
- Chunk-Level-Status: verarbeitet / übersprungen / Fehler
|
||||
- Recovery-Fähigkeit: Job kann von letztem Checkpoint fortgesetzt werden
|
||||
|
||||
## Empfohlene Metadaten für Re-Ingestion
|
||||
|
||||
### Chunk-Level Metadaten (Qdrant Payload)
|
||||
|
||||
```json
|
||||
{
|
||||
"chunk_text": "...",
|
||||
"regulation_code": "eu_2016_679",
|
||||
"regulation_name_de": "DSGVO (EU) 2016/679",
|
||||
"regulation_name_en": "GDPR (EU) 2016/679",
|
||||
"article": "25",
|
||||
"article_title": "Datenschutz durch Technikgestaltung und datenschutzfreundliche Voreinstellungen",
|
||||
"article_type": "article",
|
||||
"paragraph": "1",
|
||||
"section_hierarchy": ["Kapitel IV", "Abschnitt 2", "Artikel 25"],
|
||||
"chapter_context": "Kapitel IV — Verantwortlicher und Auftragsverarbeiter",
|
||||
"pages": [45, 46],
|
||||
"effective_date": "2018-05-25",
|
||||
"publication_date": "2016-04-27",
|
||||
"document_version": "2016-04-27",
|
||||
"source_language": "de",
|
||||
"source_url": "https://eur-lex.europa.eu/...",
|
||||
"celex": "32016R0679",
|
||||
"license": "EU_LAW",
|
||||
"license_rule": 1,
|
||||
"source_type": "law",
|
||||
"category": "datenschutz",
|
||||
"chunk_position": 42,
|
||||
"total_chunks": 423
|
||||
}
|
||||
```
|
||||
|
||||
### Dokument-Level Metadaten (Corpus Version)
|
||||
|
||||
```json
|
||||
{
|
||||
"regulation_code": "eu_2016_679",
|
||||
"canonical_name_de": "DSGVO (EU) 2016/679",
|
||||
"canonical_name_en": "GDPR (EU) 2016/679",
|
||||
"document_type": "eu_regulation",
|
||||
"effective_date": "2018-05-25",
|
||||
"publication_date": "2016-04-27",
|
||||
"supersedes": null,
|
||||
"superseded_by": null,
|
||||
"source_pdf": "gdpr_regulation_eu_2016_679.pdf",
|
||||
"source_pdf_sha256": "abc123...",
|
||||
"total_articles": 99,
|
||||
"total_recitals": 173,
|
||||
"total_annexes": 0,
|
||||
"ingestion_date": "2026-03-20",
|
||||
"ingestion_version": "v2"
|
||||
}
|
||||
```
|
||||
|
||||
## Pipeline-Härtung Checkliste
|
||||
|
||||
### Vor Ingestion
|
||||
|
||||
- [ ] Prüfen ob `regulation_code` bereits in einer Collection existiert
|
||||
- [ ] PDF-SHA256 gegen bekannte PDFs prüfen (Duplikat-Erkennung)
|
||||
- [ ] `regulation_name` aus `REGULATION_LICENSE_MAP` verwenden, NICHT aus Chunk-Metadaten
|
||||
- [ ] Spracherkennung: Nur DE + EN ingestieren
|
||||
- [ ] Dokument-Metadaten (effective_date, publication_date) recherchieren
|
||||
|
||||
### Während Ingestion
|
||||
|
||||
- [ ] Strukturiertes Chunking an Artikel-/Paragraphengrenzen
|
||||
- [ ] Kontext-Prefix mit Kapiteleinleitung
|
||||
- [ ] Chunk-Metadaten anreichern (article, paragraph, article_type, section_hierarchy)
|
||||
- [ ] Fortschritt in DB loggen
|
||||
|
||||
### Nach Ingestion
|
||||
|
||||
- [ ] Chunk-Count pro `regulation_code` prüfen (Sanity Check)
|
||||
- [ ] PDF-QA gegen Original-PDF laufen lassen
|
||||
- [ ] Cross-Collection-Duplikat-Check
|
||||
- [ ] Corpus-Version in DB eintragen
|
||||
|
||||
### Control-Generierung
|
||||
|
||||
- [ ] `source_citation.source` aus `REGULATION_LICENSE_MAP.name`, NICHT aus Chunk-Metadaten
|
||||
- [ ] Harmonisierung: Threshold 0.85 für Duplikate innerhalb gleicher `regulation_code`
|
||||
- [ ] Cross-Regulation-Harmonisierung bei ähnlichen Themen (z.B. DSGVO Art. 25 ↔ NIS2 Art. 21)
|
||||
- [ ] Job-Fortschritt persistent in DB speichern
|
||||
|
||||
## Workflow: Mac Mini → Production Sync
|
||||
|
||||
```
|
||||
1. Mac Mini: PDF → Qdrant (lokal, http://macmini:6333)
|
||||
2. Mac Mini: Control-Generierung → PostgreSQL (shared, 46.225.100.82:54321)
|
||||
3. QA: PDF-Match, Dedup, Source-Normalisierung
|
||||
4. Qdrant Migration: macmini:6333 → qdrant-dev.breakpilot.ai (scripts/migrate-qdrant.py)
|
||||
5. Deploy: git push gitea → Coolify Build + Deploy
|
||||
```
|
||||
|
||||
**WICHTIG:** PostgreSQL ist SHARED — Änderungen auf Mac Mini sind sofort in Production sichtbar. Qdrant hat getrennte Instanzen (lokal + production) und muss manuell synchronisiert werden.
|
||||
|
||||
## Scripts
|
||||
|
||||
| Script | Beschreibung |
|
||||
|---|---|
|
||||
| `scripts/ingest-phase-h.sh` | Haupt-Ingestion: 38 Dokumente → Qdrant |
|
||||
| `scripts/cleanup-qdrant-duplicates.py` | Qdrant Duplikat-Cleanup (8 Schritte) |
|
||||
| `scripts/migrate-qdrant.py` | Qdrant Migration: lokal → production |
|
||||
| `scripts/qa/phase5_normalize_and_cleanup.py` | DB Normalisierung + Hard Delete |
|
||||
| `scripts/qa/pdf_qa_all.py` | PDF-Match QA |
|
||||
@@ -209,3 +209,49 @@ Wenn du z.B. eine neue `GetUserStats()` Funktion im Go Service hinzufuegst:
|
||||
```
|
||||
3. **Tests ausfuehren**: `go test -v ./internal/services/...`
|
||||
4. **Dokumentation aktualisieren** (siehe [Dokumentation](./documentation.md))
|
||||
|
||||
---
|
||||
|
||||
## Modul-spezifische Tests
|
||||
|
||||
### Canonical Control Generator (98+ Tests)
|
||||
|
||||
Die Control Library hat eine umfangreiche Test-Suite ueber 6 Dateien.
|
||||
Siehe [Canonical Control Library — Tests](../services/sdk-modules/canonical-control-library.md#tests) und [Control Generator Pipeline](../services/sdk-modules/control-generator-pipeline.md) fuer Details.
|
||||
|
||||
```bash
|
||||
# Alle Generator-Tests (98 Tests in 13 Klassen)
|
||||
cd backend-compliance && pytest -v tests/test_control_generator.py
|
||||
|
||||
# Similarity Detector Tests
|
||||
cd backend-compliance && pytest -v compliance/tests/test_similarity_detector.py
|
||||
|
||||
# API Route Tests
|
||||
cd backend-compliance && pytest -v tests/test_canonical_control_routes.py
|
||||
|
||||
# License Gate Tests
|
||||
cd backend-compliance && pytest -v tests/test_license_gate.py
|
||||
|
||||
# CI/CD Validator Tests
|
||||
cd backend-compliance && pytest -v tests/test_validate_controls.py
|
||||
```
|
||||
|
||||
**Wichtig:** Die Generator-Tests nutzen Mocks fuer Anthropic-API und Qdrant — sie laufen ohne externe Abhaengigkeiten.
|
||||
|
||||
**Testklassen in `test_control_generator.py`:**
|
||||
|
||||
| Klasse | Tests | Prueft |
|
||||
|--------|-------|--------|
|
||||
| `TestLicenseMapping` | 13 | Lizenz-Klassifikation (Rule 1/2/3), Case-Insensitivitaet, source_type |
|
||||
| `TestDomainDetection` | 5 | Keyword-basierte Domain-Erkennung (AUTH, CRYP, NET, DATA) |
|
||||
| `TestJsonParsing` | 4 | JSON-Parser fuer LLM-Responses (Markdown-Fencing, Preamble) |
|
||||
| `TestGeneratedControlRules` | 3 | Rule-spezifische Felder (original_text, citation, source_info) |
|
||||
| `TestAnchorFinder` | 2 | RAG-Suche + Web-Framework-Erkennung |
|
||||
| `TestPipelineMocked` | 5 | End-to-End Pipeline mit Mocks (Lizenz, Hash-Dedup, Config) |
|
||||
| `TestParseJsonArray` | 15 | JSON-Array-Parser (Wrapper-Objekte, Bracket-Extraction, Fallbacks) |
|
||||
| `TestBatchSizeConfig` | 5 | Batch-Groesse-Konfiguration + Defaults |
|
||||
| `TestBatchProcessingLoop` | 10 | Batch-Verarbeitung (Rule-Split, Mixed-Rules, Too-Close, Null-Handling) |
|
||||
| `TestRegulationFilter` | 5 | regulation_filter Prefix-Matching, leere regulation_codes |
|
||||
| `TestPipelineVersion` | 5 | pipeline_version=2 in DB-Writes, null-Handling in Structure/Reform |
|
||||
| `TestRecitalDetection` | 10 | Erwaegungsgrund-Erkennung in Quelltexten (Regex, Phrasen, Kombiniert) |
|
||||
| `TestSourceTypeClassification` | 16 | law/guideline/standard/restricted Klassifizierung aller Quellentypen |
|
||||
|
||||
+31
-109
@@ -64,131 +64,39 @@ Module die Compliance-Kunden im SDK sehen und nutzen:
|
||||
| **Document Crawler** | Automatisches Crawling von Rechtstexten | /sdk/document-crawler |
|
||||
| **Advisory Board** | KI-Compliance-Beirat | /sdk/advisory-board |
|
||||
|
||||
## Admin-Module (Plattform-Verwaltung)
|
||||
|
||||
Interne Tools fuer die BreakPilot-Plattformverwaltung:
|
||||
|
||||
| Modul | Beschreibung | Frontend |
|
||||
|-------|--------------|----------|
|
||||
| **Katalogverwaltung** | SDK-Kataloge & Auswahltabellen | /dashboard/catalog-manager |
|
||||
| **Mandantenverwaltung** | B2B-Kundenverwaltung & Mandanten | /dashboard/multi-tenant |
|
||||
| **SSO-Konfiguration** | Single Sign-On & Authentifizierung | /dashboard/sso |
|
||||
| **DSB Portal** | Datenschutzbeauftragter-Arbeitsbereich | /dashboard/dsb-portal |
|
||||
|
||||
---
|
||||
|
||||
## URLs
|
||||
|
||||
### Production (Coolify-deployed)
|
||||
|
||||
| URL | Service | Beschreibung |
|
||||
|-----|---------|--------------|
|
||||
| https://macmini:3007/ | Admin Compliance | Compliance-Dashboard |
|
||||
| https://macmini:3006/ | Developer Portal | API-Dokumentation |
|
||||
| https://macmini:8002/ | Backend API | Compliance REST API |
|
||||
| https://macmini:8093/ | AI SDK API | SDK Backend-API |
|
||||
| https://admin-dev.breakpilot.ai/ | Admin Compliance | Compliance-Dashboard |
|
||||
| https://developers-dev.breakpilot.ai/ | Developer Portal | API-Dokumentation |
|
||||
| https://api-dev.breakpilot.ai/ | Backend API | Compliance REST API |
|
||||
| https://sdk-dev.breakpilot.ai/ | AI SDK API | SDK Backend-API |
|
||||
|
||||
### SDK-Module (Admin Compliance)
|
||||
### Lokal (Mac Mini — nur Dev/Tests)
|
||||
|
||||
| URL | Modul |
|
||||
|-----|-------|
|
||||
| https://macmini:3007/sdk | SDK Uebersicht |
|
||||
| https://macmini:3007/sdk/requirements | Requirements |
|
||||
| https://macmini:3007/sdk/controls | Controls |
|
||||
| https://macmini:3007/sdk/evidence | Evidence |
|
||||
| https://macmini:3007/sdk/risks | Risk Matrix |
|
||||
| https://macmini:3007/sdk/ai-act | AI Act |
|
||||
| https://macmini:3007/sdk/audit-checklist | Audit Checklist |
|
||||
| https://macmini:3007/sdk/audit-report | Audit Report |
|
||||
| https://macmini:3007/sdk/obligations | Obligations v2 |
|
||||
| https://macmini:3007/sdk/iace | IACE (CE-Risikobeurteilung) |
|
||||
| https://macmini:3007/sdk/import | Document Import |
|
||||
| https://macmini:3007/sdk/screening | System Screening |
|
||||
| https://macmini:3007/sdk/rag | RAG/Quellen |
|
||||
| https://macmini:3007/sdk/tom | TOM |
|
||||
| https://macmini:3007/sdk/dsfa | DSFA |
|
||||
| https://macmini:3007/sdk/vvt | VVT |
|
||||
| https://macmini:3007/sdk/loeschfristen | Loeschfristen |
|
||||
| https://macmini:3007/sdk/email-templates | E-Mail-Templates |
|
||||
| https://macmini:3007/sdk/academy | Academy |
|
||||
| https://macmini:3007/sdk/training | Training Engine |
|
||||
| https://macmini:3007/sdk/whistleblower | Whistleblower |
|
||||
| https://macmini:3007/sdk/incidents | Incidents |
|
||||
| https://macmini:3007/sdk/reporting | Reporting |
|
||||
| https://macmini:3007/sdk/vendor-compliance | Vendor Compliance |
|
||||
| https://macmini:3007/sdk/industry-templates | Branchenvorlagen |
|
||||
| https://macmini:3007/sdk/document-crawler | Document Crawler |
|
||||
| https://macmini:3007/sdk/advisory-board | Advisory Board |
|
||||
|
||||
### Admin-Module (Dashboard)
|
||||
|
||||
| URL | Modul |
|
||||
|-----|-------|
|
||||
| https://macmini:3007/dashboard | Dashboard |
|
||||
| https://macmini:3007/dashboard/catalog-manager | Katalogverwaltung |
|
||||
| https://macmini:3007/dashboard/multi-tenant | Mandantenverwaltung |
|
||||
| https://macmini:3007/dashboard/sso | SSO-Konfiguration |
|
||||
| https://macmini:3007/dashboard/dsb-portal | DSB Portal |
|
||||
|
||||
---
|
||||
|
||||
## Abhaengigkeiten zu Core
|
||||
|
||||
Compliance-Services nutzen folgende Core-Infrastruktur:
|
||||
|
||||
| Core Service | Genutzt von | Zweck |
|
||||
|-------------|-------------|-------|
|
||||
| PostgreSQL (46.225.100.82:54321, extern) | Alle | Compliance-Datenbank (Hetzner/meghshakka, TLS) |
|
||||
| Valkey (6379) | Backend, Admin | Session Cache |
|
||||
| Vault (8200) | Alle | Secrets Management |
|
||||
| Qdrant (qdrant-dev.breakpilot.ai) | AI SDK, Document Crawler | Vector-Suche (gehostet, API-Key) |
|
||||
| Hetzner Object Storage | TTS Service, Document Crawler | Datei-Storage (S3-kompatibel) |
|
||||
| Embedding (8087) | AI SDK | Text-Embeddings |
|
||||
| RAG Service (8097) | AI SDK | Retrieval Augmented Generation |
|
||||
| Nginx | Alle | HTTPS Reverse Proxy |
|
||||
|
||||
---
|
||||
|
||||
## Services-Dokumentation
|
||||
|
||||
- [AI Compliance SDK](services/ai-compliance-sdk/index.md)
|
||||
- [Architektur](services/ai-compliance-sdk/ARCHITECTURE.md)
|
||||
- [Developer Guide](services/ai-compliance-sdk/DEVELOPER.md)
|
||||
- [Auditor-Dokumentation](services/ai-compliance-sdk/AUDITOR_DOCUMENTATION.md)
|
||||
- [SBOM](services/ai-compliance-sdk/SBOM.md)
|
||||
- [Document Crawler](services/document-crawler/index.md)
|
||||
- SDK-Module:
|
||||
- [Analyse-Module (Paket 2)](services/sdk-modules/analyse-module.md) — Requirements, Controls, Evidence, Risk Matrix, AI Act, Audit Checklist, Audit Report
|
||||
- [Dokumentations-Module (Paket 3+)](services/sdk-modules/dokumentations-module.md) — VVT, Source Policy, Document Generator, Audit Checklist, Training Engine
|
||||
- [DSFA (Art. 35 DSGVO)](services/sdk-modules/dsfa.md) — vollständig backend-persistent, Migration 024
|
||||
- [Rechtliche Texte (Paket 4)](services/sdk-modules/rechtliche-texte.md) — Einwilligungen, Consent, Cookie Banner, Workflow
|
||||
- [Academy](services/sdk-modules/academy.md)
|
||||
- [Whistleblower](services/sdk-modules/whistleblower.md)
|
||||
- [Incidents](services/sdk-modules/incidents.md)
|
||||
- [Reporting](services/sdk-modules/reporting.md)
|
||||
- [Vendors](services/sdk-modules/vendors.md)
|
||||
- [Industry Templates](services/sdk-modules/industry-templates.md)
|
||||
- [Document Crawler](services/sdk-modules/document-crawler.md)
|
||||
- [Advisory Board](services/sdk-modules/advisory-board.md)
|
||||
- [DSB Portal](services/sdk-modules/dsb-portal.md)
|
||||
|
||||
## Entwicklung
|
||||
|
||||
- [Testing](development/testing.md)
|
||||
- [Dokumentation](development/documentation.md)
|
||||
- [CI/CD Pipeline](development/ci-cd-pipeline.md)
|
||||
| URL | Service |
|
||||
|-----|---------|
|
||||
| https://macmini:3007/ | Admin Compliance |
|
||||
| https://macmini:3006/ | Developer Portal |
|
||||
| https://macmini:8002/ | Backend API |
|
||||
| https://macmini:8093/ | AI SDK API |
|
||||
|
||||
---
|
||||
|
||||
## Deployment
|
||||
|
||||
```bash
|
||||
# Voraussetzung: breakpilot-core muss laufen
|
||||
# Production (Coolify — Standardweg):
|
||||
git push origin main && git push gitea main
|
||||
# Coolify baut und deployt automatisch.
|
||||
|
||||
# Alle Compliance-Services starten
|
||||
# Lokal (Mac Mini — nur Dev/Tests):
|
||||
docker compose -f breakpilot-compliance/docker-compose.yml up -d
|
||||
|
||||
# Einzelnen Service neu bauen
|
||||
docker compose -f breakpilot-compliance/docker-compose.yml build --no-cache <service>
|
||||
docker compose -f breakpilot-compliance/docker-compose.yml up -d <service>
|
||||
```
|
||||
|
||||
---
|
||||
@@ -203,3 +111,17 @@ git push origin main && git push gitea main
|
||||
# origin: http://macmini:3003/pilotadmin/breakpilot-compliance.git
|
||||
# gitea: git@gitea.meghsakha.com:Benjamin_Boenisch/breakpilot-compliance.git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Services-Dokumentation
|
||||
|
||||
- [AI Compliance SDK](services/ai-compliance-sdk/index.md)
|
||||
- [Document Crawler](services/document-crawler/index.md)
|
||||
- SDK-Module: siehe Unterverzeichnisse
|
||||
|
||||
## Entwicklung
|
||||
|
||||
- [Testing](development/testing.md)
|
||||
- [Dokumentation](development/documentation.md)
|
||||
- [CI/CD Pipeline](development/ci-cd-pipeline.md)
|
||||
|
||||
@@ -0,0 +1,813 @@
|
||||
# Anti-Fake-Evidence Architektur
|
||||
|
||||
**Status:** Phase 3 (aktiv seit 2026-03-23)
|
||||
**Prefix:** CP-AFE
|
||||
**Motivation:** Delve-Vorfall (Maerz 2026) — Compliance-Theater verhindern
|
||||
|
||||
---
|
||||
|
||||
## Warum dieses System existiert
|
||||
|
||||
### Das Delve-Problem
|
||||
|
||||
Im Maerz 2026 wurde bekannt, wie die Compliance-Plattform Delve ein Compliance-Audit
|
||||
bestand — ohne dass die Firma tatsaechlich compliant war:
|
||||
|
||||
1. **LLM-generierte Nachweise** wurden 1:1 als Evidence akzeptiert. Ein GPT-generierter
|
||||
Text "Die Organisation hat ein ISMS implementiert" wurde als Nachweis fuer ISO 27001
|
||||
Kontrolle A.5 eingesetzt — ohne dass ein ISMS existierte.
|
||||
|
||||
2. **Controls ohne Evidence** standen auf "pass". Das System erlaubte es, jeden Control-Status
|
||||
manuell auf "pass" zu setzen, ohne dass ein einziger Nachweis hochgeladen wurde.
|
||||
|
||||
3. **100%-Compliance-Dashboards** ohne Validierung. Das Management sah eine gruene 100%-Anzeige
|
||||
und glaubte, das Audit sei bestanden — bis der externe Auditor nach Dokumenten fragte.
|
||||
|
||||
### Haftungsrisiko
|
||||
|
||||
Wenn eine Compliance-Plattform falsche Compliance suggeriert, haftet:
|
||||
|
||||
- **Die Firma** — Aufsichtsbehoerden (z.B. BfDI, BaFin) akzeptieren "die Software hat gesagt
|
||||
wir sind compliant" nicht als Entschuldigung
|
||||
- **Die Geschaeftsfuehrung** — persoenliche Haftung bei DSGVO/NIS2-Verstoessen
|
||||
- **Der Plattform-Anbieter** — wenn die Software den Eindruck erweckt, Compliance sei
|
||||
nachgewiesen, obwohl sie nur Platzhalter anzeigt
|
||||
|
||||
### Die 6 Guardrails
|
||||
|
||||
Die Anti-Fake-Evidence Architektur implementiert 6 Schutzmechanismen:
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
G1["1. Evidence Confidence Levels<br/>(E0-E4)"]
|
||||
G2["2. Truth-Status Lifecycle<br/>(generated → validated)"]
|
||||
G3["3. Control Status Machine<br/>(pass erfordert Evidence ≥ E2)"]
|
||||
G4["4. Four-Eyes-Prinzip<br/>(GOV/PRIV: 2 unabhaengige Reviewer)"]
|
||||
G5["5. LLM Truth Labels<br/>(may_be_used_as_evidence = false)"]
|
||||
G6["6. Verbotene Formulierungen<br/>(keine Claims ohne Nachweis)"]
|
||||
|
||||
G1 --> G3
|
||||
G2 --> G3
|
||||
G4 --> G3
|
||||
G5 --> G1
|
||||
G3 --> G6
|
||||
|
||||
style G1 fill:#fee2e2,stroke:#dc2626
|
||||
style G2 fill:#fef3c7,stroke:#d97706
|
||||
style G3 fill:#dbeafe,stroke:#2563eb
|
||||
style G4 fill:#d1fae5,stroke:#059669
|
||||
style G5 fill:#e0e7ff,stroke:#4f46e5
|
||||
style G6 fill:#fce7f3,stroke:#db2777
|
||||
```
|
||||
|
||||
**Zusammenspiel:** Ein Control kann nur auf "pass" stehen, wenn mindestens ein Evidence
|
||||
mit Confidence >= E2 vorliegt, dessen Truth-Status validiert ist. Bei GOV/PRIV-Controls
|
||||
muessen zwei verschiedene Personen den Evidence reviewt haben (Four-Eyes). LLM-generierte
|
||||
Inhalte koennen nie als Evidence zaehlen.
|
||||
|
||||
---
|
||||
|
||||
## Zusammenspiel mit evidence_type
|
||||
|
||||
Das [evidence_type Feld](evidence-type.md) (code/process/hybrid) bestimmt,
|
||||
**wie** Evidence gesammelt wird. Das Anti-Fake-Evidence System bestimmt,
|
||||
**ob** die gesammelte Evidence valide ist:
|
||||
|
||||
| evidence_type | Typische Evidence-Quelle | Confidence | Automatisierbar? |
|
||||
|---|---|---|---|
|
||||
| `code` | SAST-Report, CI/CD-Pipeline, IaC-Scan | E3 (observed) | Ja — System-beobachtet |
|
||||
| `process` | Policy-Dokument, Schulungsnachweis, Vertrag | E1 (uploaded) → E2 (reviewed) | Nein — Review noetig |
|
||||
| `hybrid` | Code-Scan + Prozess-Doku | E1-E3 gemischt | Teilweise |
|
||||
|
||||
!!! warning "Code Controls sind nicht automatisch valide"
|
||||
Auch ein SAST-Report (E3) muss einen validen Truth-Status haben.
|
||||
Ein veralteter Scan (> 90 Tage) wird als "stale" markiert und reduziert
|
||||
die Evidence Freshness im Dashboard-Score.
|
||||
|
||||
---
|
||||
|
||||
## Evidence Confidence Levels (E0–E4)
|
||||
|
||||
| Level | Bezeichnung | Beschreibung | Beispiel |
|
||||
|-------|-------------|--------------|----------|
|
||||
| **E0** | Generated | LLM-Output, Platzhalter | KI-generierter Nachweis-Entwurf |
|
||||
| **E1** | Uploaded | Manuell hochgeladen, ungeprüft | PDF ohne Reviewer |
|
||||
| **E2** | Reviewed | Intern geprüft, Hash verifiziert | Dokument von Compliance-Beauftragtem bestätigt |
|
||||
| **E3** | Observed | System-beobachtet (CI/CD, API) | Automatischer SAST-Report mit SHA-256 |
|
||||
| **E4** | Auditor-validated | Extern validiert | Wirtschaftsprüfer hat akzeptiert |
|
||||
|
||||
### Auto-Klassifikation
|
||||
|
||||
| Source | Confidence | Truth Status |
|
||||
|--------|-----------|--------------|
|
||||
| `ci_pipeline` | E3 | observed |
|
||||
| `api` (mit Hash) | E3 | observed |
|
||||
| `manual` / `upload` | E1 | uploaded |
|
||||
| `generated` | E0 | generated |
|
||||
|
||||
---
|
||||
|
||||
## Evidence Truth-Status Lifecycle
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> generated : LLM erzeugt
|
||||
[*] --> uploaded : Manuell hochgeladen
|
||||
[*] --> observed : CI/CD Pipeline
|
||||
|
||||
generated --> rejected : Review abgelehnt
|
||||
uploaded --> validated_internal : Intern geprueft
|
||||
uploaded --> rejected : Review abgelehnt
|
||||
observed --> validated_internal : Intern bestaetigt
|
||||
|
||||
validated_internal --> provided_to_auditor : An Auditor uebergeben
|
||||
provided_to_auditor --> accepted_by_auditor : Auditor akzeptiert
|
||||
provided_to_auditor --> rejected : Auditor lehnt ab
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Control Status-Transition State Machine
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
planned --> in_progress : immer erlaubt
|
||||
in_progress --> pass : Evidence >= E2 + truth_status valid
|
||||
in_progress --> partial : min 1 Evidence (beliebig)
|
||||
in_progress --> fail : immer erlaubt
|
||||
pass --> fail : Degradation (immer)
|
||||
partial --> pass : Evidence >= E2 + truth_status valid
|
||||
|
||||
note right of pass
|
||||
Voraussetzung: min 1 Evidence
|
||||
mit confidence >= E2 UND
|
||||
truth_status in (uploaded,
|
||||
observed, validated_internal,
|
||||
accepted_by_auditor)
|
||||
end note
|
||||
```
|
||||
|
||||
### Transition-Regeln
|
||||
|
||||
| Von | Nach | Voraussetzung |
|
||||
|-----|------|---------------|
|
||||
| planned | in_progress | keine |
|
||||
| in_progress | pass | min 1 Evidence mit confidence >= E2, truth_status valide |
|
||||
| in_progress | partial | min 1 Evidence (beliebig) |
|
||||
| in_progress | fail | immer erlaubt |
|
||||
| pass | fail | immer erlaubt (Degradation) |
|
||||
| * | n/a | erfordert `status_justification` |
|
||||
| * | planned | immer erlaubt (Reset) |
|
||||
|
||||
Bei Verstoß: **HTTP 409 Conflict** mit Liste der Violations.
|
||||
|
||||
---
|
||||
|
||||
## LLM Truth-Labels
|
||||
|
||||
Jeder LLM-generierte Inhalt wird mit einem Truth-Label versehen:
|
||||
|
||||
```json
|
||||
{
|
||||
"generation_mode": "draft_assistance",
|
||||
"truth_status": "generated",
|
||||
"may_be_used_as_evidence": false,
|
||||
"generated_by": "system"
|
||||
}
|
||||
```
|
||||
|
||||
### Audit-Trail
|
||||
|
||||
Tabelle `compliance_llm_generation_audit`:
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|--------------|
|
||||
| entity_type | VARCHAR(50) | 'evidence', 'control', 'document' |
|
||||
| entity_id | VARCHAR(36) | FK zur generierten Entitaet |
|
||||
| generation_mode | VARCHAR(100) | 'draft_assistance', 'auto_generation' |
|
||||
| truth_status | ENUM | generated, uploaded, ... |
|
||||
| may_be_used_as_evidence | BOOLEAN | Default: FALSE |
|
||||
| llm_model | VARCHAR(100) | z.B. 'qwen2.5vl:32b' |
|
||||
| llm_provider | VARCHAR(50) | 'ollama', 'anthropic' |
|
||||
| prompt_hash | VARCHAR(64) | SHA-256 des Prompts |
|
||||
|
||||
---
|
||||
|
||||
## Multi-dimensionaler Compliance-Score
|
||||
|
||||
Statt einer einzelnen Prozentzahl zeigt der Score 6 Dimensionen:
|
||||
|
||||
| Dimension | Gewicht | Beschreibung |
|
||||
|-----------|---------|--------------|
|
||||
| requirement_coverage | 20% | % Requirements mit verlinktem Control |
|
||||
| evidence_strength | 25% | Gewichteter Durchschnitt der Evidence-Confidence |
|
||||
| validation_quality | 20% | % Evidence mit truth_status >= validated_internal |
|
||||
| evidence_freshness | 10% | % Evidence nicht expired + reviewed < 90 Tage |
|
||||
| control_effectiveness | 25% | Bestehende Formel (pass + partial*0.5) |
|
||||
| **overall_readiness** | — | Gewichteter Composite der 5 Dimensionen |
|
||||
|
||||
### Hard Blocks
|
||||
|
||||
Zusaetzlich werden **Sperrgründe** angezeigt, die eine Audit-Readiness verhindern:
|
||||
|
||||
- Controls auf 'pass' ohne jegliche Evidence
|
||||
- Controls auf 'pass' mit nur E0/E1-Evidence (keine Validierung)
|
||||
|
||||
---
|
||||
|
||||
## Verbotene Formulierungen
|
||||
|
||||
Der Drafting-Engine Validator prueft auf Formulierungen, die **ohne ausreichenden Nachweis** nicht verwendet werden duerfen:
|
||||
|
||||
| Verboten | Sicher stattdessen |
|
||||
|----------|--------------------|
|
||||
| "ist compliant" | "soll compliant sein" |
|
||||
| "erfuellt vollstaendig" | "soll vollstaendig erfuellt werden" |
|
||||
| "wurde geprueft" | "soll geprueft werden" |
|
||||
| "wurde umgesetzt" | "ist zur Umsetzung vorgesehen" |
|
||||
| "ist auditiert" | "soll auditiert werden" |
|
||||
| "vollstaendig implementiert" | "Implementierung ist vorgesehen" |
|
||||
| "nachweislich konform" | "Konformitaet ist nachzuweisen" |
|
||||
|
||||
**Erlaubt nur wenn:** control_status = pass AND confidence >= E2 AND truth_status in (validated_internal, accepted_by_auditor).
|
||||
|
||||
---
|
||||
|
||||
## API-Aenderungen
|
||||
|
||||
### Neue Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| PATCH | `/evidence/{id}/review` | Evidence reviewen (Confidence upgraden) |
|
||||
| POST | `/llm-audit` | LLM-Generierungs-Audit erstellen |
|
||||
| GET | `/llm-audit` | LLM-Audit-Eintraege auflisten |
|
||||
|
||||
### Erweiterte Responses
|
||||
|
||||
**EvidenceResponse** — 6 neue Felder:
|
||||
|
||||
```json
|
||||
{
|
||||
"confidence_level": "E3",
|
||||
"truth_status": "observed",
|
||||
"generation_mode": null,
|
||||
"may_be_used_as_evidence": true,
|
||||
"reviewed_by": null,
|
||||
"reviewed_at": null
|
||||
}
|
||||
```
|
||||
|
||||
**DashboardResponse** — neues Feld `multi_score`:
|
||||
|
||||
```json
|
||||
{
|
||||
"multi_score": {
|
||||
"requirement_coverage": 85.0,
|
||||
"evidence_strength": 60.0,
|
||||
"validation_quality": 40.0,
|
||||
"evidence_freshness": 90.0,
|
||||
"control_effectiveness": 70.0,
|
||||
"overall_readiness": 65.0,
|
||||
"hard_blocks": ["3 Controls auf 'pass' haben nur E0/E1-Evidence"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**ControlResponse** — neues Feld `status_justification`.
|
||||
|
||||
**ControlUpdate** — neues Feld `status_justification` (Pflicht fuer n/a-Transitions).
|
||||
|
||||
### Status-Transition Fehler (409)
|
||||
|
||||
```json
|
||||
{
|
||||
"detail": {
|
||||
"error": "Status transition not allowed",
|
||||
"current_status": "in_progress",
|
||||
"requested_status": "pass",
|
||||
"violations": [
|
||||
"Transition to 'pass' requires at least 1 evidence with confidence >= E2..."
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration
|
||||
|
||||
### Phase 1
|
||||
**Datei:** `backend-compliance/migrations/076_anti_fake_evidence.sql`
|
||||
|
||||
- Neue ENUM-Typen: `evidence_confidence_level`, `evidence_truth_status`
|
||||
- 6 neue Spalten auf `compliance_evidence`
|
||||
- `in_progress` Wert fuer `controlstatusenum`
|
||||
- `status_justification` auf `compliance_controls`
|
||||
- Neue Tabelle `compliance_llm_generation_audit`
|
||||
- Backfill bestehender Evidence nach Source
|
||||
- Indizes auf neue Spalten
|
||||
|
||||
### Phase 2
|
||||
**Datei:** `backend-compliance/migrations/077_anti_fake_evidence_phase2.sql`
|
||||
|
||||
- Neue Tabelle `compliance_assertions` (Assertion Engine)
|
||||
- 6 neue Spalten auf `compliance_evidence` (Four-Eyes: approval_status, first_reviewer, etc.)
|
||||
- Performance-Index auf `compliance_audit_trail (entity_type, action, performed_at)`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: UI-Badges
|
||||
|
||||
Badges werden auf Evidence-Cards angezeigt und zeigen den Vertrauensstatus auf einen Blick:
|
||||
|
||||
| Badge | Farben | Anzeige |
|
||||
|-------|--------|---------|
|
||||
| **ConfidenceLevelBadge** | E0=rot, E1=gelb, E2=blau, E3=gruen, E4=emerald | Immer |
|
||||
| **TruthStatusBadge** | generated=violet, uploaded=grau, observed=blau, validated=gruen, rejected=rot | Immer |
|
||||
| **GenerationModeBadge** | violet + Sparkles-Icon | Wenn LLM-generiert |
|
||||
| **ApprovalStatusBadge** | pending=gelb, first_approved=blau, approved=gruen, rejected=rot | Nur bei Four-Eyes |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Assertion Engine
|
||||
|
||||
Die Assertion Engine trennt **Behauptungen** von **Fakten** in Compliance-Texten.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Text[Freitext] --> Split[Satz-Splitting]
|
||||
Split --> Classify{Normativ?}
|
||||
Classify -->|Pflicht| A[Assertion pflicht]
|
||||
Classify -->|Empfehlung| B[Assertion empfehlung]
|
||||
Classify -->|Kann| C[Assertion kann]
|
||||
Classify -->|Begruendung| D[Rationale]
|
||||
Classify -->|Evidence-Keywords| E[Fact tentativ]
|
||||
E --> Verify[Manuell verifizieren]
|
||||
Verify --> Fact[Verified Fact]
|
||||
```
|
||||
|
||||
### Assertion-Typen
|
||||
|
||||
| Typ | Bedeutung | Beispiel |
|
||||
|-----|-----------|----------|
|
||||
| **assertion** | Normative Aussage (unbewiesen) | "Die Organisation muss ein ISMS implementieren" |
|
||||
| **fact** | Verifizierte Tatsache | "ISO-Zertifikat Nr. 12345 liegt vor" |
|
||||
| **rationale** | Begruendung | "Dies ist notwendig, weil..." |
|
||||
|
||||
### Normative Tiers
|
||||
|
||||
| Tier | Signal-Woerter |
|
||||
|------|---------------|
|
||||
| **pflicht** | muss, hat sicherzustellen, ist verpflichtet, shall, must, required |
|
||||
| **empfehlung** | soll, sollte, gewaehrleisten, should, ensure |
|
||||
| **kann** | kann, darf, may, optional |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Four-Eyes-Prinzip
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> pending_first : Evidence erstellt (Gov/Priv Domain)
|
||||
pending_first --> first_approved : 1. Reviewer OK
|
||||
first_approved --> approved : 2. Reviewer OK (andere Person!)
|
||||
first_approved --> rejected : 2. Reviewer lehnt ab
|
||||
pending_first --> rejected : 1. Reviewer lehnt ab
|
||||
|
||||
note right of first_approved
|
||||
Zweiter Reviewer MUSS
|
||||
eine andere Person sein
|
||||
als der erste Reviewer
|
||||
end note
|
||||
```
|
||||
|
||||
### Domains mit Four-Eyes-Pflicht
|
||||
|
||||
| Domain | Four-Eyes? | Begruendung |
|
||||
|--------|-----------|-------------|
|
||||
| `gov` | Ja | Governance-Controls sind audit-kritisch |
|
||||
| `priv` | Ja | Datenschutz erfordert unabhaengige Pruefung |
|
||||
| `ops`, `sdlc`, `ai`, ... | Nein | Operationale Controls mit Single-Review |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Audit-Trail-Erweiterung
|
||||
|
||||
Neue Audit-Trail-Eintraege:
|
||||
|
||||
| Entity | Action | Wann |
|
||||
|--------|--------|------|
|
||||
| evidence | create | Bei Evidence-Erstellung |
|
||||
| evidence | review | Bei Confidence/Truth-Status-Aenderung |
|
||||
| evidence | reject | Bei Evidence-Ablehnung |
|
||||
| control | status_change | Bei Control-Status-Aenderung |
|
||||
|
||||
Jeder Eintrag enthaelt `old_value`, `new_value` und einen SHA-256 `checksum`.
|
||||
|
||||
Neuer Query-Endpoint: `GET /audit-trail?entity_type=evidence&entity_id={id}`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Neue API-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| PATCH | `/evidence/{id}/reject` | Evidence ablehnen |
|
||||
| GET | `/audit-trail` | Audit-Trail abfragen (Filter: entity_type, entity_id, action) |
|
||||
| POST | `/assertions` | Assertion manuell erstellen |
|
||||
| GET | `/assertions` | Assertions auflisten (Filter: entity_type, entity_id, assertion_type) |
|
||||
| GET | `/assertions/{id}` | Assertion Detail |
|
||||
| PUT | `/assertions/{id}` | Assertion aktualisieren |
|
||||
| POST | `/assertions/{id}/verify` | Als Fakt markieren |
|
||||
| POST | `/assertions/extract` | Automatische Extraktion aus Freitext |
|
||||
| GET | `/assertions/summary` | Stats (total, facts, rationale, unverified) |
|
||||
|
||||
### Erweiterte EvidenceResponse (Phase 2)
|
||||
|
||||
```json
|
||||
{
|
||||
"approval_status": "first_approved",
|
||||
"first_reviewer": "reviewer1@example.com",
|
||||
"first_reviewed_at": "2026-03-23T14:00:00Z",
|
||||
"second_reviewer": null,
|
||||
"second_reviewed_at": null,
|
||||
"requires_four_eyes": true
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Durchsetzung (UI + Dashboard)
|
||||
|
||||
Phase 3 macht das Anti-Fake-Evidence-System **benutzbar und durchsetzbar** im Frontend.
|
||||
|
||||
### Evidence Review/Reject UI
|
||||
|
||||
Die Evidence-Seite bietet jetzt direkte Buttons fuer Review und Ablehnung:
|
||||
|
||||
- **Reviewen-Button**: Sichtbar wenn `approvalStatus` nicht `approved` oder `rejected`
|
||||
- **Ablehnen-Button**: Sichtbar bei Four-Eyes-Evidence die noch nicht abgeschlossen ist
|
||||
|
||||
**ReviewModal** erlaubt:
|
||||
- Confidence-Level aendern (E0-E4 Dropdown)
|
||||
- Truth-Status aendern (Dropdown)
|
||||
- Reviewer E-Mail angeben
|
||||
- Four-Eyes-Warnung wenn noch ein weiterer Review noetig ist
|
||||
|
||||
**RejectModal** erlaubt:
|
||||
- Ablehnungsgrund als Freitext
|
||||
- Reviewer E-Mail angeben
|
||||
|
||||
Bei Four-Eyes Same-Person-Fehler (HTTP 400) wird eine Fehlermeldung angezeigt.
|
||||
|
||||
### Control Status-Transition Fehlerbehandlung
|
||||
|
||||
Die Controls-Seite zeigt jetzt detaillierte Fehler bei blockierten Status-Transitionen:
|
||||
|
||||
- **Optimistic Update mit Rollback**: UI aktualisiert sofort, rollt bei Fehler zurueck
|
||||
- **TransitionErrorBanner**: Zeigt Violations-Liste bei HTTP 409 Conflict
|
||||
- z.B. "Transition to 'pass' requires at least 1 evidence with confidence >= E2"
|
||||
- **Link zur Evidence-Seite**: "Evidence hinzufuegen" direkt im Fehler-Banner
|
||||
|
||||
### Evidence Audit-Trail Anzeige
|
||||
|
||||
Neuer "Historie"-Button auf jeder Evidence-Card zeigt den vollstaendigen Audit-Trail:
|
||||
|
||||
- Timeline mit Zeitstempel, Aktion und Akteur
|
||||
- Details zu Feldaenderungen (old_value → new_value)
|
||||
- Lazy-Loading: Erst beim Aufklappen wird `GET /audit-trail` abgerufen
|
||||
|
||||
### Evidence Confidence-Filter
|
||||
|
||||
Neue Filter-Pills auf der Evidence-Seite:
|
||||
|
||||
```
|
||||
[Alle] [Gueltig] [Abgelaufen] [Ausstehend] | [E0] [E1] [E2] [E3] [E4]
|
||||
```
|
||||
|
||||
Farbcodierung: E0=rot, E1=gelb, E2=blau, E3=gruen, E4=emerald (passend zu den Badges)
|
||||
|
||||
### Evidence Confidence-Verteilung (Dashboard)
|
||||
|
||||
Neuer Endpoint und Dashboard-Bereich:
|
||||
|
||||
**Endpoint:** `GET /dashboard/evidence-distribution`
|
||||
|
||||
```json
|
||||
{
|
||||
"by_confidence": {"E0": 2, "E1": 5, "E2": 3, "E3": 8, "E4": 1},
|
||||
"four_eyes_pending": 3,
|
||||
"total": 19
|
||||
}
|
||||
```
|
||||
|
||||
**Compliance Hub** zeigt:
|
||||
- Horizontal gestapelter Balken der Confidence-Verteilung (E0 rot → E4 emerald)
|
||||
- Multi-Score Dimensionen als Fortschrittsbalken (5 Dimensionen + Audit-Readiness)
|
||||
- Four-Eyes-Warteschlange (Anzahl pending)
|
||||
- Hard-Blocks-Liste oder "Keine Hard Blocks" Status
|
||||
|
||||
### Assertions-Seite
|
||||
|
||||
Neue Seite unter `/sdk/assertions` mit 3 Tabs:
|
||||
|
||||
| Tab | Inhalt |
|
||||
|-----|--------|
|
||||
| **Uebersicht** | Summary-Stats (Assertions, Facts, Rationale, Unverified) |
|
||||
| **Assertion-Liste** | Filterbarer Tabelle (entity_type, assertion_type) mit AssertionCards |
|
||||
| **Extraktion** | Textfeld + Button → `POST /assertions/extract` |
|
||||
|
||||
**AssertionCard** zeigt:
|
||||
- Normative-Tier als farbiger Badge (Pflicht=rot, Empfehlung=gelb, Kann=blau)
|
||||
- Typ-Badge (Assertion/Fact/Rationale)
|
||||
- "Als Fakt pruefen"-Button → `POST /assertions/{id}/verify`
|
||||
|
||||
### Phase 3: Neue API-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| GET | `/dashboard/evidence-distribution` | Evidence-Verteilung nach Confidence + Four-Eyes-Status |
|
||||
|
||||
---
|
||||
|
||||
## Sicherheitsarchitektur im Detail
|
||||
|
||||
### Warum E0-E4 und nicht einfach "valide/invalide"?
|
||||
|
||||
Ein binaeres System ("valide" vs. "invalide") haette ein fundamentales Problem:
|
||||
Wo zieht man die Grenze? Ein manuell hochgeladenes PDF ist "besser" als ein
|
||||
LLM-Entwurf, aber "schlechter" als ein automatischer SAST-Report.
|
||||
|
||||
Die 5-stufige Skala erlaubt:
|
||||
|
||||
1. **Graduelle Verbesserung**: Ein Kunde kann mit E1-Evidence starten und
|
||||
schrittweise auf E3/E4 aufruesten
|
||||
2. **Risiko-basierte Entscheidungen**: Ein Auditor sieht sofort, welche
|
||||
Controls nur auf schwacher Evidence basieren
|
||||
3. **Automatische Schwellwerte**: Das System blockiert "pass" unterhalb E2,
|
||||
aber zeigt auch E1-Evidence (mit Warnung)
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
E0["E0<br/>Generated<br/>❌ Kein Nachweis"]
|
||||
E1["E1<br/>Uploaded<br/>⚠️ Ungeprüft"]
|
||||
E2["E2<br/>Reviewed<br/>✓ Intern geprüft"]
|
||||
E3["E3<br/>Observed<br/>✓✓ System-beobachtet"]
|
||||
E4["E4<br/>Auditor<br/>✓✓✓ Extern validiert"]
|
||||
|
||||
E0 --> E1 --> E2 --> E3 --> E4
|
||||
|
||||
style E0 fill:#fee2e2,stroke:#dc2626
|
||||
style E1 fill:#fef3c7,stroke:#d97706
|
||||
style E2 fill:#dbeafe,stroke:#2563eb
|
||||
style E3 fill:#d1fae5,stroke:#059669
|
||||
style E4 fill:#d1fae5,stroke:#047857
|
||||
```
|
||||
|
||||
### Confidence-Gewichtung im Score
|
||||
|
||||
| Level | Gewicht | Bedeutung |
|
||||
|---|---|---|
|
||||
| E0 | 0.00 | Zaehlt nicht als Evidence |
|
||||
| E1 | 0.25 | Minimal — ungepruefte Uploads |
|
||||
| E2 | 0.50 | Schwelle fuer "pass"-Status |
|
||||
| E3 | 0.75 | Stark — system-verifiziert |
|
||||
| E4 | 1.00 | Perfekt — extern validiert |
|
||||
|
||||
### Warum Four-Eyes nur fuer GOV und PRIV?
|
||||
|
||||
Das Four-Eyes-Prinzip erhoert den Aufwand erheblich (jedes Evidence braucht
|
||||
zwei verschiedene Reviewer). Deshalb wird es gezielt nur fuer Domains eingesetzt,
|
||||
bei denen Manipulation besonders gefaehrlich ist:
|
||||
|
||||
| Domain | Four-Eyes | Begruendung |
|
||||
|---|---|---|
|
||||
| **GOV** (Governance) | Ja | Governance-Controls definieren das Compliance-Framework selbst. Wenn hier manipuliert wird, ist alles darunter wertlos. |
|
||||
| **PRIV** (Datenschutz) | Ja | DSGVO-Nachweise sind rechtlich bindend. Falsche Nachweise koennen zu Bussgeldern fuehren. |
|
||||
| SEC, AUTH, NET, ... | Nein | Technische Controls sind oft automatisch pruefbar (E3). Der Aufwand von Four-Eyes waere unverhältnismäßig. |
|
||||
|
||||
### Same-Person-Schutz
|
||||
|
||||
```python
|
||||
# Der zweite Reviewer MUSS eine andere Person sein:
|
||||
if review.reviewed_by == evidence.first_reviewer:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="Four-Eyes: second reviewer must be different from first reviewer"
|
||||
)
|
||||
```
|
||||
|
||||
Dies verhindert, dass eine einzelne Person ein Evidence "durchwinkt". Der Schutz
|
||||
ist auf DB-Ebene durchgesetzt — nicht nur im Frontend.
|
||||
|
||||
---
|
||||
|
||||
## Hard Blocks — Audit-Sperren
|
||||
|
||||
Hard Blocks sind **absolute Sperren**, die eine Audit-Readiness verhindern.
|
||||
Sie werden im Dashboard prominent rot angezeigt.
|
||||
|
||||
### Block 1: Controls auf "pass" ohne Evidence
|
||||
|
||||
```sql
|
||||
-- Controls die "pass" oder "partial" sind, aber keine Evidence haben
|
||||
SELECT control_id FROM compliance_controls
|
||||
WHERE status IN ('pass', 'partial')
|
||||
AND id NOT IN (SELECT control_id FROM compliance_evidence)
|
||||
```
|
||||
|
||||
**Warum kritisch:** Ein Control auf "pass" ohne jeden Nachweis ist die Definition
|
||||
von Compliance-Theater. Der Auditor wird sofort fragen: "Wo ist der Nachweis?"
|
||||
|
||||
### Block 2: Controls auf "pass" mit nur E0/E1-Evidence
|
||||
|
||||
```sql
|
||||
-- Controls auf "pass" deren beste Evidence nur E0 oder E1 ist
|
||||
SELECT c.control_id FROM compliance_controls c
|
||||
JOIN compliance_evidence e ON e.control_id = c.id
|
||||
WHERE c.status = 'pass'
|
||||
GROUP BY c.control_id
|
||||
HAVING MAX(CASE
|
||||
WHEN e.confidence_level = 'E4' THEN 4
|
||||
WHEN e.confidence_level = 'E3' THEN 3
|
||||
WHEN e.confidence_level = 'E2' THEN 2
|
||||
WHEN e.confidence_level = 'E1' THEN 1
|
||||
ELSE 0
|
||||
END) < 2 -- Nur E0 oder E1
|
||||
```
|
||||
|
||||
**Warum kritisch:** Ein LLM-generierter Text (E0) oder ein ungeprüeftes Upload (E1)
|
||||
reicht nicht aus, um Compliance zu behaupten. Mindestens ein intern geprüeftes
|
||||
Dokument (E2) ist erforderlich.
|
||||
|
||||
---
|
||||
|
||||
## Datenbank-Schema (Komplett)
|
||||
|
||||
### ENUM-Typen
|
||||
|
||||
```sql
|
||||
CREATE TYPE evidence_confidence_level AS ENUM (
|
||||
'E0', -- Generated / kein echter Nachweis
|
||||
'E1', -- Hochgeladen, ungeprüeft
|
||||
'E2', -- Intern geprüeft, Hash verifiziert
|
||||
'E3', -- System-beobachtet (CI/CD, API mit Hash)
|
||||
'E4' -- Extern validiert (Auditor)
|
||||
);
|
||||
|
||||
CREATE TYPE evidence_truth_status AS ENUM (
|
||||
'generated', -- LLM/System-generiert
|
||||
'uploaded', -- Manuell hochgeladen
|
||||
'observed', -- Automatisch beobachtet
|
||||
'validated_internal', -- Intern geprüeft + bestätigt
|
||||
'rejected', -- Abgelehnt
|
||||
'provided_to_auditor', -- An Auditor üebergeben
|
||||
'accepted_by_auditor' -- Auditor hat akzeptiert
|
||||
);
|
||||
```
|
||||
|
||||
### compliance_evidence — Erweiterte Spalten
|
||||
|
||||
| Spalte | Typ | Default | Beschreibung |
|
||||
|---|---|---|---|
|
||||
| confidence_level | ENUM | E1 | Vertrauensstufe (E0-E4) |
|
||||
| truth_status | ENUM | uploaded | Wahrheitsstatus |
|
||||
| generation_mode | VARCHAR(100) | NULL | 'draft_assistance', 'auto_generation' |
|
||||
| may_be_used_as_evidence | BOOLEAN | TRUE | FALSE fuer LLM-Output |
|
||||
| reviewed_by | VARCHAR(200) | NULL | Reviewer E-Mail |
|
||||
| reviewed_at | TIMESTAMPTZ | NULL | Zeitpunkt des Reviews |
|
||||
| approval_status | VARCHAR(30) | 'none' | Four-Eyes Status |
|
||||
| first_reviewer | VARCHAR(200) | NULL | Erster Reviewer |
|
||||
| first_reviewed_at | TIMESTAMPTZ | NULL | Zeitpunkt erster Review |
|
||||
| second_reviewer | VARCHAR(200) | NULL | Zweiter Reviewer (muss anders sein!) |
|
||||
| second_reviewed_at | TIMESTAMPTZ | NULL | Zeitpunkt zweiter Review |
|
||||
| requires_four_eyes | BOOLEAN | FALSE | Ob Four-Eyes-Pflicht besteht |
|
||||
|
||||
### compliance_llm_generation_audit
|
||||
|
||||
Jeder LLM-generierte Inhalt wird in dieser Tabelle protokolliert:
|
||||
|
||||
| Spalte | Typ | Beschreibung |
|
||||
|---|---|---|
|
||||
| id | VARCHAR(36) | PK |
|
||||
| tenant_id | VARCHAR(36) | Mandant |
|
||||
| entity_type | VARCHAR(50) | 'evidence', 'control', 'document' |
|
||||
| entity_id | VARCHAR(36) | FK zur generierten Entitaet |
|
||||
| generation_mode | VARCHAR(100) | 'draft_assistance', 'auto_generation' |
|
||||
| truth_status | ENUM | Default: 'generated' |
|
||||
| may_be_used_as_evidence | BOOLEAN | Default: FALSE |
|
||||
| llm_model | VARCHAR(100) | z.B. 'qwen3:30b-a3b' |
|
||||
| llm_provider | VARCHAR(50) | 'ollama', 'anthropic' |
|
||||
| prompt_hash | VARCHAR(64) | SHA-256 des Prompts |
|
||||
| input_summary | TEXT | Zusammenfassung des Inputs |
|
||||
| output_summary | TEXT | Zusammenfassung des Outputs |
|
||||
|
||||
### compliance_assertions
|
||||
|
||||
Die Assertion Engine trennt Behauptungen von Fakten:
|
||||
|
||||
| Spalte | Typ | Beschreibung |
|
||||
|---|---|---|
|
||||
| id | VARCHAR(36) | PK |
|
||||
| tenant_id | VARCHAR(36) | Mandant |
|
||||
| entity_type | VARCHAR(50) | 'control', 'evidence', 'document', 'obligation' |
|
||||
| entity_id | VARCHAR(36) | FK zur Entitaet |
|
||||
| sentence_text | TEXT | Originalsatz |
|
||||
| sentence_index | INTEGER | Position im Text |
|
||||
| assertion_type | VARCHAR(20) | 'assertion', 'fact', 'rationale' |
|
||||
| evidence_ids | JSONB | Verlinkte Evidence-IDs |
|
||||
| confidence | FLOAT | 0.0-1.0 Konfidenz |
|
||||
| normative_tier | VARCHAR(20) | 'pflicht', 'empfehlung', 'kann' |
|
||||
| verified_by | VARCHAR(200) | Verifizierer |
|
||||
| verified_at | TIMESTAMPTZ | Zeitpunkt der Verifikation |
|
||||
|
||||
---
|
||||
|
||||
## Vollstaendige API-Referenz
|
||||
|
||||
### Evidence-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung | Auth |
|
||||
|---|---|---|---|
|
||||
| GET | `/evidence` | Evidence auflisten (mit Filtern) | Tenant |
|
||||
| POST | `/evidence` | Neues Evidence erstellen | Tenant |
|
||||
| DELETE | `/evidence/{id}` | Evidence loeschen | Tenant |
|
||||
| POST | `/evidence/upload` | Evidence-Datei hochladen | Tenant |
|
||||
| POST | `/evidence/collect` | CI/CD Evidence sammeln (automatisch) | API-Key |
|
||||
| GET | `/evidence/ci-status` | CI/CD Evidence Statusuebersicht | Tenant |
|
||||
| **PATCH** | **`/evidence/{id}/review`** | **Evidence reviewen (Four-Eyes)** | Tenant |
|
||||
| **PATCH** | **`/evidence/{id}/reject`** | **Evidence ablehnen** | Tenant |
|
||||
|
||||
### Audit-Trail-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---|---|---|
|
||||
| GET | `/audit-trail` | Audit-Trail abfragen (entity_type, entity_id, action) |
|
||||
|
||||
### Assertion-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---|---|---|
|
||||
| POST | `/assertions` | Assertion manuell erstellen |
|
||||
| GET | `/assertions` | Assertions auflisten |
|
||||
| GET | `/assertions/{id}` | Assertion Detail |
|
||||
| PUT | `/assertions/{id}` | Assertion aktualisieren |
|
||||
| POST | `/assertions/{id}/verify` | Als Fakt markieren |
|
||||
| POST | `/assertions/extract` | Automatische Extraktion aus Freitext |
|
||||
| GET | `/assertions/summary` | Stats (total, facts, rationale, unverified) |
|
||||
|
||||
### LLM-Audit-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---|---|---|
|
||||
| POST | `/llm-audit` | LLM-Generierungs-Audit erstellen |
|
||||
| GET | `/llm-audit` | LLM-Audit-Eintraege auflisten |
|
||||
|
||||
### Dashboard-Endpoints
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---|---|---|
|
||||
| GET | `/dashboard/evidence-distribution` | Confidence-Verteilung + Four-Eyes-Status |
|
||||
|
||||
---
|
||||
|
||||
## Haeufige Fragen
|
||||
|
||||
### Kann ich E0-Evidence loeschen?
|
||||
|
||||
Ja, aber es bleibt ein Eintrag in `compliance_llm_generation_audit`. Das stellt sicher,
|
||||
dass die Generierung nachvollziehbar ist, auch wenn der LLM-Output geloescht wurde.
|
||||
|
||||
### Was passiert, wenn der erste Reviewer nicht verfuegbar ist?
|
||||
|
||||
Das Four-Eyes-System hat keinen Timeout. Der erste Review bleibt als `first_approved`
|
||||
gespeichert, bis ein zweiter Reviewer die Evidence prueft. Das ist bewusst so —
|
||||
Compliance-Nachweise sollten nicht durch Zeitdruck kompromittiert werden.
|
||||
|
||||
### Kann ein Admin das Four-Eyes-Prinzip umgehen?
|
||||
|
||||
Nein. Der Same-Person-Check ist auf Backend-Ebene implementiert (HTTP 400 bei
|
||||
gleichem Reviewer). Es gibt keine Admin-Bypass-Option. Dies ist ein bewusstes
|
||||
Design-Prinzip: Wenn selbst der Admin das System umgehen koennte, waere die
|
||||
gesamte Integritaet gefaehrdet.
|
||||
|
||||
### Wie funktioniert die Assertion-Extraktion?
|
||||
|
||||
Der Endpoint `POST /assertions/extract` nimmt einen Freitext und:
|
||||
|
||||
1. **Satz-Splitting**: Teilt den Text an `.!?` gefolgt von Grossbuchstaben
|
||||
2. **Klassifikation**: Prueft jeden Satz auf normative Signal-Woerter
|
||||
3. **Normative Tiers**: "muss/shall/must" → pflicht, "soll/should" → empfehlung, "kann/may" → kann
|
||||
4. **Evidence-Keywords**: Woerter wie "liegt vor", "wurde geprueft" → als tentative Fakten markiert
|
||||
5. **Ergebnis**: Liste von Assertions mit Typ und Normative-Tier
|
||||
|
||||
### Was ist der Unterschied zwischen Truth-Status und Confidence?
|
||||
|
||||
- **Confidence** (E0-E4) sagt: Wie vertrauenswuerdig ist die **Quelle**?
|
||||
(LLM < Upload < Review < CI/CD < Auditor)
|
||||
- **Truth-Status** sagt: In welchem **Lifecycle-Zustand** ist der Nachweis?
|
||||
(generated → uploaded → validated → accepted_by_auditor)
|
||||
|
||||
Ein E3-Evidence (CI/CD) kann Truth-Status "observed" haben und ist damit sofort
|
||||
verwendbar. Ein E1-Evidence (Upload) muss erst "validated_internal" werden.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,652 @@
|
||||
# Control Generator Pipeline
|
||||
|
||||
Automatische Generierung von Canonical Controls aus dem gesamten RAG-Korpus (~105.000 Chunks aus Gesetzen, Verordnungen und Standards).
|
||||
|
||||
**Backend:** `backend-compliance/compliance/services/control_generator.py`
|
||||
**Routes:** `backend-compliance/compliance/api/control_generator_routes.py`
|
||||
**API-Prefix:** `/api/compliance/v1/canonical/generate`
|
||||
|
||||
---
|
||||
|
||||
## Pipeline-Uebersicht
|
||||
|
||||
Die Pipeline durchlaeuft 7 Stufen, um aus RAG-Chunks eigenstaendige Security/Compliance Controls zu erzeugen:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[1. RAG Scan] -->|Alle Chunks laden| B[2. License Classify]
|
||||
B -->|Rule 1/2| C[3a. Structure Batch]
|
||||
B -->|Rule 3| D[3b. Reform Batch]
|
||||
C --> E[4. Harmonize]
|
||||
D --> E
|
||||
E -->|Duplikat| F[Als Duplikat markieren]
|
||||
E -->|Neu| G[5. Anchor Search]
|
||||
G --> H[6. Store Control]
|
||||
H --> I[7. Mark Processed]
|
||||
```
|
||||
|
||||
| Stufe | Name | Beschreibung |
|
||||
|-------|------|-------------|
|
||||
| 1 | **RAG Scan** | Laedt unverarbeitete Chunks aus Qdrant (Scroll-API), filtert per SHA-256-Hash |
|
||||
| 2 | **License Classify** | Bestimmt die Lizenzregel (Rule 1/2/3) anhand `regulation_code` |
|
||||
| 3a | **Structure (Batch)** | Rule 1+2: Strukturiert Originaltext als Control (Anthropic API) |
|
||||
| 3b | **Reform (Batch)** | Rule 3: Vollstaendige Reformulierung ohne Originaltext (Anthropic API) |
|
||||
| 4 | **Harmonize** | Embedding-basierte Duplikaterkennung (bge-m3, Cosine > 0.85) |
|
||||
| 5 | **Anchor Search** | Findet Open-Source-Referenzen (OWASP, NIST, ENISA) |
|
||||
| 6 | **Store** | Persistiert Control in `canonical_controls` mit Metadaten |
|
||||
| 7 | **Mark Processed** | Markiert jeden Chunk als verarbeitet (auch bei Skip/Error/Duplikat) |
|
||||
|
||||
---
|
||||
|
||||
## Pipeline-Versionen
|
||||
|
||||
Die Pipeline hat zwei Versionen. Die Version wird als `pipeline_version` auf `canonical_controls` und `canonical_processed_chunks` gespeichert.
|
||||
|
||||
### v1 (Original)
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|-------------|------|
|
||||
| **Vorfilter** | Lokales LLM (llama3.2 3B) entscheidet ob Chunk relevant |
|
||||
| **Anthropic-Prompt** | Alter Prompt ohne null-Skip |
|
||||
| **Annexe/Anhaenge** | Kein Schutz — wurden haeufig faelschlich als irrelevant uebersprungen |
|
||||
| **`pipeline_version`** | `1` |
|
||||
|
||||
### v2 (Aktuell)
|
||||
|
||||
| Eigenschaft | Wert |
|
||||
|-------------|------|
|
||||
| **Vorfilter** | Optional (`skip_prefilter`). Wenn aktiviert, entscheidet Anthropic API selbst |
|
||||
| **Anthropic-Prompt** | Neuer Prompt mit **null-Skip**: API gibt `null` fuer Chunks ohne Anforderung zurueck |
|
||||
| **Annexe/Anhaenge** | Explizit geschuetzt — Prompt-Anweisung: "Anhaenge/Annexe enthalten oft KONKRETE technische Anforderungen — diese MUESSEN als Control erfasst werden!" |
|
||||
| **`pipeline_version`** | `2` |
|
||||
|
||||
#### Wesentliche Aenderungen v1 → v2
|
||||
|
||||
1. **Relevanz-Entscheidung an Anthropic delegiert** — Das lokale LLM (Vorfilter) ist optional. Die Anthropic API entscheidet selbst, welche Chunks Controls enthalten, indem sie `null` fuer irrelevante Chunks zurueckgibt.
|
||||
2. **null-Skip im JSON-Array** — Das Ergebnis-Array enthaelt `null`-Eintraege fuer Chunks ohne umsetzbare Anforderung. Kein separater Vorfilter-Schritt noetig.
|
||||
3. **Annexe/Anhaenge geschuetzt** — Explizite Prompt-Anweisung verhindert, dass technische Anforderungen in Anhaengen uebersprungen werden.
|
||||
|
||||
#### Datenbank-Feld
|
||||
|
||||
```sql
|
||||
-- Migration 062
|
||||
ALTER TABLE canonical_controls
|
||||
ADD COLUMN pipeline_version smallint NOT NULL DEFAULT 1;
|
||||
|
||||
ALTER TABLE canonical_processed_chunks
|
||||
ADD COLUMN pipeline_version smallint NOT NULL DEFAULT 1;
|
||||
```
|
||||
|
||||
Neue Controls erhalten automatisch `pipeline_version = 2`. Bestehende (v1) behalten `1`, damit sie spaeter identifiziert und ggf. reprocessiert werden koennen.
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration
|
||||
|
||||
### Request-Parameter (`GenerateRequest`)
|
||||
|
||||
| Parameter | Typ | Default | Beschreibung |
|
||||
|-----------|-----|---------|-------------|
|
||||
| `collections` | `List[str]` | Alle 5 Collections | Qdrant-Collections zum Durchsuchen |
|
||||
| `domain` | `str` | — | Filter auf eine Domain (z.B. `AUTH`, `NET`) |
|
||||
| `regulation_filter` | `List[str]` | — | Prefix-Matching auf `regulation_code` (z.B. `["eu_2023_1230", "owasp_"]`) |
|
||||
| `skip_prefilter` | `bool` | `false` | Ueberspringt lokalen LLM-Vorfilter, sendet alle Chunks an die Anthropic API |
|
||||
| `batch_size` | `int` | `5` | Chunks pro Anthropic-API-Call |
|
||||
| `max_controls` | `int` | `50` | Maximale Anzahl Controls pro Job (0 = unbegrenzt) |
|
||||
| `max_chunks` | `int` | `1000` | Maximale Chunks pro Job (0 = unbegrenzt, respektiert Dokumentgrenzen) |
|
||||
| `skip_web_search` | `bool` | `false` | Ueberspringt Web-Suche in der Anchor-Findung (Stufe 5) |
|
||||
| `dry_run` | `bool` | `false` | Trockenlauf ohne DB-Schreibzugriffe (synchron, mit Controls im Response) |
|
||||
|
||||
!!! info "`regulation_filter` — Prefix-Matching"
|
||||
Der Filter vergleicht den `regulation_code` jedes Chunks per Prefix.
|
||||
Beispiel: `["eu_2023_1230"]` erfasst nur Chunks aus der Maschinenverordnung.
|
||||
`["owasp_"]` erfasst alle OWASP-Dokumente (OWASP ASVS, OWASP SAMM, etc.).
|
||||
Gross-/Kleinschreibung wird ignoriert.
|
||||
|
||||
### Umgebungsvariablen
|
||||
|
||||
| Variable | Default | Beschreibung |
|
||||
|----------|---------|-------------|
|
||||
| `ANTHROPIC_API_KEY` | — | API-Key fuer Anthropic Claude (Pflicht) |
|
||||
| `CONTROL_GEN_ANTHROPIC_MODEL` | `claude-sonnet-4-6` | Anthropic-Modell fuer Strukturierung/Reformulierung |
|
||||
| `OLLAMA_URL` | `http://host.docker.internal:11434` | Lokaler Ollama-Server (Vorfilter + QA) |
|
||||
| `CONTROL_GEN_OLLAMA_MODEL` | `qwen3.5:35b-a3b` | Lokales LLM-Modell fuer Vorfilter und QA-Arbitrierung |
|
||||
| `CONTROL_GEN_LLM_TIMEOUT` | `180` | Timeout in Sekunden pro Anthropic-API-Call |
|
||||
|
||||
### Pipeline-interne Konstanten
|
||||
|
||||
| Konstante | Wert | Beschreibung |
|
||||
|-----------|------|-------------|
|
||||
| `PIPELINE_VERSION` | `2` | Aktuelle Pipeline-Version |
|
||||
| `HARMONIZATION_THRESHOLD` | `0.85` | Cosine-Similarity-Schwelle fuer Duplikaterkennung |
|
||||
| `max_tokens` | `8192` | Maximale Token-Laenge der LLM-Antwort |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Alle Endpoints unter `/api/compliance/v1/canonical/`.
|
||||
|
||||
### Uebersicht
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|-------------|
|
||||
| `POST` | `/generate` | Generierungs-Job starten (laeuft im Hintergrund) |
|
||||
| `GET` | `/generate/status/{job_id}` | Status eines laufenden Jobs abfragen |
|
||||
| `GET` | `/generate/jobs` | Alle Jobs auflisten (paginiert) |
|
||||
| `GET` | `/generate/processed-stats` | Verarbeitungsstatistik pro Collection |
|
||||
| `GET` | `/generate/review-queue` | Controls zur manuellen Pruefung |
|
||||
| `POST` | `/generate/review/{control_id}` | Review eines einzelnen Controls abschliessen |
|
||||
| `POST` | `/generate/bulk-review` | Bulk-Review nach `release_state` |
|
||||
| `POST` | `/generate/qa-reclassify` | QA-Reklassifizierung bestehender Controls |
|
||||
| `GET` | `/blocked-sources` | Gesperrte Quellen (Rule 3) auflisten |
|
||||
| `POST` | `/blocked-sources/cleanup` | Cleanup-Workflow fuer gesperrte Quellen starten |
|
||||
|
||||
---
|
||||
|
||||
### POST `/v1/canonical/generate` — Job starten
|
||||
|
||||
Startet einen Generierungs-Job im Hintergrund. Gibt sofort eine `job_id` zurueck.
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"collections": ["bp_compliance_gesetze"],
|
||||
"regulation_filter": ["eu_2023_1230"],
|
||||
"skip_prefilter": false,
|
||||
"batch_size": 5,
|
||||
"max_chunks": 500,
|
||||
"max_controls": 0,
|
||||
"skip_web_search": false,
|
||||
"dry_run": false
|
||||
}
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
|
||||
```json
|
||||
{
|
||||
"job_id": "a1b2c3d4-...",
|
||||
"status": "running",
|
||||
"message": "Generation started in background. Poll /generate/status/{job_id} for progress."
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
# Alle Chunks der Maschinenverordnung verarbeiten
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{
|
||||
"collections": ["bp_compliance_ce"],
|
||||
"regulation_filter": ["eu_2023_1230"],
|
||||
"max_chunks": 200,
|
||||
"batch_size": 5
|
||||
}'
|
||||
```
|
||||
|
||||
```bash
|
||||
# Dry Run: Keine DB-Aenderungen, Controls im Response
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{
|
||||
"collections": ["bp_compliance_gesetze"],
|
||||
"max_chunks": 10,
|
||||
"dry_run": true
|
||||
}'
|
||||
```
|
||||
|
||||
```bash
|
||||
# Ohne Vorfilter: Alle Chunks direkt an Anthropic API
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{
|
||||
"collections": ["bp_compliance_gesetze"],
|
||||
"regulation_filter": ["bdsg"],
|
||||
"skip_prefilter": true,
|
||||
"max_chunks": 100
|
||||
}'
|
||||
```
|
||||
|
||||
!!! warning "Kosten beachten"
|
||||
Ohne `regulation_filter` und mit `max_chunks: 0` werden **alle** ~105.000 Chunks verarbeitet.
|
||||
Das verursacht erhebliche Anthropic-API-Kosten (~$700).
|
||||
|
||||
---
|
||||
|
||||
### GET `/v1/canonical/generate/status/{job_id}` — Job-Status
|
||||
|
||||
Gibt den vollstaendigen Status eines Jobs zurueck inkl. Metriken und Fehler.
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
curl https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/status/a1b2c3d4-... \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a1b2c3d4-...",
|
||||
"status": "completed",
|
||||
"total_chunks_scanned": 500,
|
||||
"controls_generated": 48,
|
||||
"controls_verified": 45,
|
||||
"controls_needs_review": 3,
|
||||
"controls_too_close": 0,
|
||||
"controls_duplicates_found": 12,
|
||||
"controls_qa_fixed": 5,
|
||||
"config": { "..." },
|
||||
"started_at": "2026-03-17T10:00:00+00:00",
|
||||
"completed_at": "2026-03-17T10:15:32+00:00"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET `/v1/canonical/generate/jobs` — Alle Jobs
|
||||
|
||||
Paginierte Liste aller Generierungs-Jobs.
|
||||
|
||||
**Query-Parameter:**
|
||||
|
||||
| Parameter | Default | Beschreibung |
|
||||
|-----------|---------|-------------|
|
||||
| `limit` | `20` | Anzahl Jobs (1-100) |
|
||||
| `offset` | `0` | Offset fuer Paginierung |
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
curl "https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/jobs?limit=5" \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET `/v1/canonical/generate/review-queue` — Review-Queue
|
||||
|
||||
Listet Controls auf, die eine manuelle Pruefung benoetigen.
|
||||
|
||||
**Query-Parameter:**
|
||||
|
||||
| Parameter | Default | Beschreibung |
|
||||
|-----------|---------|-------------|
|
||||
| `release_state` | `needs_review` | Filter: `needs_review`, `too_close`, `duplicate` |
|
||||
| `limit` | `50` | Anzahl (1-200) |
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
curl "https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/review-queue?release_state=needs_review&limit=10" \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST `/v1/canonical/generate/review/{control_id}` — Review abschliessen
|
||||
|
||||
Schliesst die manuelle Pruefung eines Controls ab.
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "approve",
|
||||
"release_state": "draft",
|
||||
"notes": "Inhaltlich korrekt, Severity passt."
|
||||
}
|
||||
```
|
||||
|
||||
**Moegliche `action`-Werte:**
|
||||
|
||||
| Action | Neuer State | Beschreibung |
|
||||
|--------|-------------|-------------|
|
||||
| `approve` | `draft` (oder per `release_state` ueberschreiben) | Control freigeben |
|
||||
| `reject` | `deprecated` | Control verwerfen |
|
||||
| `needs_rework` | `needs_review` | Zurueck in die Queue |
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/review/AUTH-042 \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{"action": "approve", "release_state": "draft"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### POST `/v1/canonical/generate/bulk-review` — Bulk-Review
|
||||
|
||||
Aendert den `release_state` aller Controls, die einen bestimmten State haben.
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"release_state": "needs_review",
|
||||
"action": "approve",
|
||||
"new_state": "draft"
|
||||
}
|
||||
```
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
# Alle needs_review Controls auf draft setzen
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/bulk-review \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{"release_state": "needs_review", "action": "approve", "new_state": "draft"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### GET `/v1/canonical/generate/processed-stats` — Verarbeitungsstatistik
|
||||
|
||||
Liefert Statistiken pro RAG-Collection.
|
||||
|
||||
**Beispiel:**
|
||||
|
||||
```bash
|
||||
curl https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/processed-stats \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"stats": [
|
||||
{
|
||||
"collection": "bp_compliance_gesetze",
|
||||
"processed_chunks": 45200,
|
||||
"direct_adopted": 1850,
|
||||
"llm_reformed": 120,
|
||||
"skipped": 43230,
|
||||
"total_chunks_estimated": 0,
|
||||
"pending_chunks": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Kosten und Performance
|
||||
|
||||
### Kostenabschaetzung
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| **Kosten pro Chunk** | ~$0.0067 (Anthropic API, Batch-Modus) |
|
||||
| **Yield (Controls/Chunks)** | ~4.5-10% (nur Chunks mit konkreten Anforderungen erzeugen Controls) |
|
||||
| **Vorfilter-Ersparnis** | ~55% der API-Kosten wenn aktiviert (irrelevante Chunks werden lokal aussortiert) |
|
||||
|
||||
### Performance-Kennzahlen
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| **Batch-Groesse** | 5 Chunks pro API-Call (Default) |
|
||||
| **API-Aufrufe Reduktion** | ~80% weniger Aufrufe durch Batching |
|
||||
| **LLM-Timeout** | 180 Sekunden pro Call |
|
||||
| **QA-Overhead** | ~2s pro Control (nur bei Disagreement, ~10-15% der Controls) |
|
||||
|
||||
### RAG Collections
|
||||
|
||||
| Collection | Inhalte | Erwartete Regel |
|
||||
|-----------|---------|----------------|
|
||||
| `bp_compliance_gesetze` | Deutsche Gesetze (BDSG, TTDSG, TKG etc.) | Rule 1 |
|
||||
| `bp_compliance_datenschutz` | Datenschutz-Leitlinien + EU-Verordnungen | Rule 1/2 |
|
||||
| `bp_compliance_ce` | CE/Sicherheitsstandards | Rule 1/2/3 |
|
||||
| `bp_dsfa_corpus` | DSFA-Korpus | Rule 1/2 |
|
||||
| `bp_legal_templates` | Rechtsvorlagen | Rule 1 |
|
||||
|
||||
### Aktuelle Groessenordnung
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| RAG-Chunks gesamt | ~105.000 (nach Dedup 2026-03-16) |
|
||||
| Verarbeitete Chunks | ~105.000 |
|
||||
| Generierte Controls | **~4.738** |
|
||||
| Konversionsrate | ~4,5% |
|
||||
|
||||
---
|
||||
|
||||
## Lizenz-Klassifikation (3-Regel-System)
|
||||
|
||||
Jeder Chunk wird basierend auf `regulation_code` einer Lizenzregel zugeordnet:
|
||||
|
||||
| Regel | Typ | Original erlaubt? | Beispiele |
|
||||
|-------|-----|-------------------|----------|
|
||||
| **Rule 1** (free_use) | EU-Gesetze, NIST, DE-Gesetze, Public Domain | Ja | DSGVO, BDSG, NIS2, AI Act |
|
||||
| **Rule 2** (citation_required) | CC-BY, CC-BY-SA | Ja, mit Zitation | OWASP ASVS, OWASP SAMM |
|
||||
| **Rule 3** (restricted) | Proprietaer | Nein, volle Reformulierung | BSI TR-03161, ISO 27001 |
|
||||
|
||||
### Verarbeitung nach Regel
|
||||
|
||||
- **Rule 1+2 → `_structure_batch()`**: Anthropic strukturiert den Originaltext als Control. Ein API-Call fuer den gesamten Batch.
|
||||
- **Rule 3 → `_reformulate_batch()`**: Anthropic reformuliert vollstaendig — kein Originaltext, keine Quellennamen. Ein API-Call fuer den gesamten Batch.
|
||||
|
||||
### Batch Processing
|
||||
|
||||
Die Pipeline sammelt Chunks in Batches (Default: 5 Chunks) und sendet sie in einem einzigen Anthropic-API-Call.
|
||||
|
||||
1. Relevante Chunks werden mit Lizenz-Info in `pending_batch` gesammelt
|
||||
2. Bei `batch_size` erreicht → `_flush_batch()`
|
||||
3. Batch wird nach Lizenzregel getrennt: Rule 1+2 → `_structure_batch()`, Rule 3 → `_reformulate_batch()`
|
||||
4. Ergebnis: JSON-Array mit genau N Elementen (`null` fuer irrelevante Chunks)
|
||||
|
||||
**Fallback:** Bei Batch-Fehler (Timeout, Parsing-Error) wird automatisch auf Einzelverarbeitung zurueckgefallen.
|
||||
|
||||
---
|
||||
|
||||
## Chunk-Tracking (Processed Chunks)
|
||||
|
||||
### Tabelle `canonical_processed_chunks`
|
||||
|
||||
| Spalte | Typ | Beschreibung |
|
||||
|--------|-----|-------------|
|
||||
| `chunk_hash` | VARCHAR(64) | SHA-256 Hash des Chunk-Textes |
|
||||
| `collection` | VARCHAR(100) | Qdrant-Collection |
|
||||
| `regulation_code` | VARCHAR(100) | Quell-Regulation (z.B. `bdsg`, `eu_2016_679`) |
|
||||
| `license_rule` | INTEGER | 1, 2 oder 3 |
|
||||
| `processing_path` | VARCHAR(20) | Wie der Chunk verarbeitet wurde |
|
||||
| `generated_control_ids` | JSONB | UUIDs der generierten Controls |
|
||||
| `pipeline_version` | SMALLINT | Pipeline-Version (1 oder 2) |
|
||||
| `job_id` | UUID | Referenz auf den Generierungs-Job |
|
||||
|
||||
**UNIQUE Constraint:** `(chunk_hash, collection, document_version)` — verhindert Doppelverarbeitung.
|
||||
|
||||
### Processing Paths
|
||||
|
||||
| Wert | Stufe | Bedeutung |
|
||||
|------|-------|-----------|
|
||||
| `prefilter_skip` | 2 | Lokaler LLM-Vorfilter: Chunk nicht relevant |
|
||||
| `structured` | 3a | Einzelner Chunk strukturiert (Rule 1/2) |
|
||||
| `structured_batch` | 3a | Batch-Strukturierung (Rule 1/2) |
|
||||
| `llm_reform` | 3b | Einzelner Chunk reformuliert (Rule 3) |
|
||||
| `llm_reform_batch` | 3b | Batch-Reformulierung (Rule 3) |
|
||||
| `no_control` | 3 | LLM konnte kein Control ableiten (null im Array) |
|
||||
| `store_failed` | 6 | DB-Speichern fehlgeschlagen |
|
||||
| `error` | — | Unerwarteter Fehler |
|
||||
|
||||
---
|
||||
|
||||
## QA Validation (Automatische Qualitaetspruefung)
|
||||
|
||||
Die QA-Stufe validiert die Klassifizierung jedes generierten Controls:
|
||||
|
||||
1. **LLM-Category:** Anthropic liefert `category` und `domain` im JSON-Response
|
||||
2. **Keyword-Detection:** `_detect_category(chunk.text)` liefert eine zweite Meinung
|
||||
3. **Stimmen beide ueberein?** → Schneller Pfad (kein QA noetig)
|
||||
4. **Bei Disagreement:** Lokales LLM (Ollama) arbitriert
|
||||
5. **Auto-Fix:** Category/Domain werden automatisch korrigiert
|
||||
|
||||
Die QA-Metriken werden in `generation_metadata` gespeichert:
|
||||
|
||||
```json
|
||||
{
|
||||
"qa_category_fix": {"from": "authentication", "to": "finance", "reason": "IFRS-Thema"},
|
||||
"qa_domain_fix": {"from": "AUTH", "to": "FIN", "reason": "Finanzregulierung"}
|
||||
}
|
||||
```
|
||||
|
||||
### Recital-Erkennung (Erwägungsgrund-Detektion)
|
||||
|
||||
Die QA-Stufe prueft zusaetzlich, ob der `source_original_text` eines Controls tatsaechlich aus einem Gesetzesartikel stammt — oder aus einem Erwaegungsgrund (Recital). Erwaegungsgruende enthalten keine normativen Pflichten und fuehren zu falsch zugeordneten Controls.
|
||||
|
||||
**Erkennungsmethoden:**
|
||||
|
||||
| Methode | Pattern | Beispiel |
|
||||
|---------|---------|----------|
|
||||
| **Regex** | `\((\d{1,3})\)\s*\n` — Erwaegungsgrund-Nummern | `(126)\nUm den Verwaltungsaufwand...` |
|
||||
| **Phrasen** | Typische Recital-Formulierungen (≥2 Treffer) | "daher sollte", "in Erwägung nachstehender Gründe" |
|
||||
|
||||
**Ergebnis bei Verdacht:**
|
||||
|
||||
- `release_state` wird auf `needs_review` gesetzt
|
||||
- `generation_metadata.recital_suspect = true`
|
||||
- `generation_metadata.recital_detection` enthaelt Details:
|
||||
|
||||
```json
|
||||
{
|
||||
"recital_suspect": true,
|
||||
"recital_detection": {
|
||||
"recital_suspect": true,
|
||||
"recital_numbers": ["126", "127"],
|
||||
"recital_phrases": ["daher sollte"],
|
||||
"detection_method": "regex+phrases"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Funktion:** `_detect_recital(text)` in `control_generator.py`
|
||||
|
||||
**Hintergrund:** Bei der Analyse von ~5.500 Controls mit Quelltext wurden 1.555 (28%) als Erwaegungsgrund-Verdacht identifiziert. Der Document Crawler unterschied nicht zwischen Artikeltext und Erwaegungsgruenden, was zu falschen `article`/`paragraph`-Zuordnungen fuehrte.
|
||||
|
||||
### QA-Reklassifizierung bestehender Controls
|
||||
|
||||
```bash
|
||||
# Dry Run: Welche AUTH-Controls sind falsch klassifiziert?
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/qa-reclassify \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{"limit": 50, "dry_run": true, "filter_domain_prefix": "AUTH"}'
|
||||
|
||||
# Korrekturen anwenden:
|
||||
curl -X POST https://api-dev.breakpilot.ai/api/compliance/v1/canonical/generate/qa-reclassify \
|
||||
-H 'Content-Type: application/json' \
|
||||
-H 'X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000' \
|
||||
-d '{"limit": 50, "dry_run": false, "filter_domain_prefix": "AUTH"}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quelldateien
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `backend-compliance/compliance/services/control_generator.py` | 7-Stufen-Pipeline mit Batch Processing |
|
||||
| `backend-compliance/compliance/api/control_generator_routes.py` | REST API Endpoints |
|
||||
| `backend-compliance/compliance/services/license_gate.py` | Lizenz-Gate-Logik |
|
||||
| `backend-compliance/compliance/services/similarity_detector.py` | Too-Close-Detektor (5 Metriken) |
|
||||
| `backend-compliance/compliance/services/rag_client.py` | RAG-Client (Qdrant Search + Scroll) |
|
||||
| `backend-compliance/migrations/046_control_generator.sql` | Job-Tracking, Chunk-Tracking Tabellen |
|
||||
| `backend-compliance/migrations/048_processing_path_expand.sql` | Erweiterte Processing-Path-Werte |
|
||||
| `backend-compliance/migrations/062_pipeline_version.sql` | `pipeline_version` Spalte |
|
||||
| `backend-compliance/tests/test_control_generator.py` | 98+ Tests (Lizenz, Domain, Batch, Pipeline, Recital, Source-Type) |
|
||||
|
||||
---
|
||||
|
||||
## Pass 0a/0b: Atomare Control-Zerlegung
|
||||
|
||||
Die Pipeline v3 erweitert die 7-Stufen-Pipeline um einen Vor-Pass, der Rich Controls in atomare Controls zerlegt.
|
||||
|
||||
### Pass 0a: Obligation Extraction
|
||||
|
||||
Extrahiert individuelle normative Pflichten aus Rich Controls via LLM.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Rich Control] -->|LLM| B[Obligations]
|
||||
B --> C{Quality Gate}
|
||||
C -->|Pass| D[validated]
|
||||
C -->|Fail| E[rejected]
|
||||
```
|
||||
|
||||
**3-Tier Klassifikation:**
|
||||
|
||||
| Typ | Erkennungsmuster | Beispiel |
|
||||
|-----|-----------------|---------|
|
||||
| **Pflicht** | muss, ist verpflichtet, hat sicherzustellen | "Der Verantwortliche MUSS ein Verzeichnis fuehren" |
|
||||
| **Empfehlung** | soll, sollte, wird empfohlen | "Es SOLLTE eine Risikobewertung durchgefuehrt werden" |
|
||||
| **Kann** | kann, darf, ist berechtigt | "Die Aufsichtsbehoerde KANN Geldbussen verhaengen" |
|
||||
|
||||
**Quality Gate (6 Regeln):**
|
||||
|
||||
1. Nur normative Aussagen (muss, sicherzustellen, verpflichtet)
|
||||
2. Ein Hauptverb pro Obligation
|
||||
3. Test-Obligations separat von operativen
|
||||
4. Reporting-Obligations separat
|
||||
5. Nicht auf Evidence-Ebene splitten
|
||||
6. Parent-Link immer erhalten
|
||||
|
||||
### Pass 0b: Atomic Control Composition
|
||||
|
||||
Verwandelt jede validierte Obligation in ein eigenstaendiges atomares Control.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[Obligation] -->|LLM| B[Atomic Control]
|
||||
B -->|Dedup Check| C{4-Stage Dedup}
|
||||
C -->|NEW| D[Insert + Index]
|
||||
C -->|LINK| E[Parent-Link]
|
||||
C -->|REVIEW| F[Review-Queue]
|
||||
```
|
||||
|
||||
**Konfiguration:**
|
||||
|
||||
| Variable | Default | Beschreibung |
|
||||
|----------|---------|-------------|
|
||||
| `DECOMPOSITION_LLM_MODEL` | `claude-sonnet-4-6` | LLM fuer Pass 0a/0b |
|
||||
| `DECOMPOSITION_BATCH_SIZE` | `5` | Obligations pro LLM-Call |
|
||||
| `DECOMPOSITION_LLM_TIMEOUT` | `120` | Timeout in Sekunden |
|
||||
|
||||
**Ergebnisse (Stand 2026-03-21):**
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| Rich Controls (technisch) | ~6.800 |
|
||||
| Atomare Controls (bisher) | 30 (PoC: 10x CRYP, AUTH, SEC) |
|
||||
| Ziel nach Full Run | ~18.000 unique Master Controls |
|
||||
| Obligations pro Rich Control | ~10 |
|
||||
| Dedup-Reduktion erwartet | ~70% |
|
||||
|
||||
### Quelldateien (Pass 0a/0b)
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `compliance/services/decomposition_pass.py` | Pass 0a + 0b Logik |
|
||||
| `compliance/services/control_dedup.py` | 4-Stufen Dedup-Engine |
|
||||
| `migrations/061_obligation_candidates.sql` | Obligation-Tabelle |
|
||||
| `migrations/074_control_dedup.sql` | Dedup-Tabellen (Parent-Links, Review-Queue) |
|
||||
| `tests/test_decomposition_pass.py` | 90 Tests |
|
||||
| `tests/test_control_dedup.py` | 56 Tests |
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [Canonical Control Library (CP-CLIB)](canonical-control-library.md) — Domains, Datenmodell, Too-Close-Detektor, CI/CD Validation
|
||||
- [Deduplizierungs-Engine](dedup-engine.md) — 4-Stufen Dedup, Multi-Parent-Linking, Review-Queue
|
||||
- [RAG Pipeline Benchmark](../../development/rag-pipeline-benchmark.md) — State-of-the-Art Vergleich, Optimierungsempfehlungen
|
||||
- [Multi-Layer Control Architecture](canonical-control-library.md#multi-layer-control-architecture) — 10-Stage Pipeline-Erweiterung mit Obligations, Patterns, Crosswalk
|
||||
@@ -0,0 +1,463 @@
|
||||
# Control Provenance Wiki
|
||||
|
||||
Dokumentation der rechtlichen und methodischen Grundlage fuer die Control-Erstellung.
|
||||
|
||||
**Frontend:** `https://macmini:3007/sdk/control-provenance`
|
||||
**Production:** `https://admin-dev.breakpilot.ai/sdk/control-provenance`
|
||||
|
||||
---
|
||||
|
||||
## Methodik der Control-Erstellung
|
||||
|
||||
Alle Controls in der Canonical Control Library wurden **eigenstaendig formuliert** und folgen einer
|
||||
**unabhaengigen Taxonomie**. Es werden keine proprietaeren Bezeichner, Nummern oder Strukturen
|
||||
aus geschuetzten Quellen uebernommen.
|
||||
|
||||
### Dreistufiger Prozess
|
||||
|
||||
1. **Offene Recherche** — Identifikation von Security-Anforderungen aus oeffentlichen, frei zugaenglichen
|
||||
Frameworks (OWASP, NIST, ENISA). Jede Anforderung wird aus mindestens 2 unabhaengigen offenen Quellen belegt.
|
||||
|
||||
2. **Eigenstaendige Formulierung** — Jedes Control wird mit eigener Sprache, eigener Struktur und eigener
|
||||
Taxonomie (z.B. AUTH-001, NET-001) verfasst. Kein Copy-Paste, keine Paraphrase geschuetzter Texte.
|
||||
|
||||
3. **Too-Close-Pruefung** — Automatisierte Aehnlichkeitspruefung gegen Quelltexte mit 5 Metriken
|
||||
(Token Overlap, N-Gram Jaccard, Embedding Cosine, LCS Ratio, Exact-Phrase). Nur Controls mit
|
||||
Status PASS oder WARN (+ Human Review) werden freigegeben.
|
||||
|
||||
### Rechtliche Grundlage
|
||||
|
||||
| Gesetz | Bezug |
|
||||
|--------|-------|
|
||||
| **UrhG §44b** | Text & Data Mining erlaubt fuer Analyse; Kopien werden danach geloescht |
|
||||
| **UrhG §23** | Hinreichender Abstand zum Originalwerk durch eigene Formulierung |
|
||||
| **BSI Nutzungsbedingungen** | Kommerzielle Nutzung nur mit Zustimmung; BSI-Dokumente nur als Analysegrundlage |
|
||||
|
||||
---
|
||||
|
||||
## Filter in der Control Library
|
||||
|
||||
Die Control Library bietet **7 Filter-Dropdowns**, um die ueber 3.000 Controls effizient zu durchsuchen:
|
||||
|
||||
### Schweregrad (Severity)
|
||||
|
||||
| Stufe | Farbe | Bedeutung |
|
||||
|-------|-------|-----------|
|
||||
| **Kritisch** | Rot | Sicherheitskritische Controls — Verstoesse fuehren zu schwerwiegenden Risiken |
|
||||
| **Hoch** | Orange | Wichtige Controls — sollten zeitnah umgesetzt werden |
|
||||
| **Mittel** | Gelb | Standardmaessige Controls — empfohlene Umsetzung |
|
||||
| **Niedrig** | Gruen | Nice-to-have Controls — zusaetzliche Haertung |
|
||||
|
||||
### Domain
|
||||
|
||||
Das Praefix der Control-ID (z.B. `AUTH-001`, `SEC-042`). Kennzeichnet den thematischen Bereich.
|
||||
|
||||
| Domain | Anzahl | Thema |
|
||||
|--------|--------|-------|
|
||||
| SEC | ~700 | Allgemeine Sicherheit, Systemhaertung |
|
||||
| COMP | ~470 | Compliance, Regulierung, Nachweispflichten |
|
||||
| DATA | ~400 | Datenschutz, Datenklassifizierung, DSGVO |
|
||||
| AI | ~290 | KI-Regulierung (AI Act, Transparenz, Erklaerbarkeit) |
|
||||
| LOG | ~230 | Logging, Monitoring, SIEM |
|
||||
| AUTH | ~200 | Authentifizierung, Zugriffskontrolle |
|
||||
| NET | ~150 | Netzwerksicherheit, Transport, Firewall |
|
||||
| CRYP | ~90 | Kryptographie, Schluesselmanagement |
|
||||
| ACC | ~25 | Zugriffskontrolle (Access Control) |
|
||||
| INC | ~25 | Incident Response, Vorfallmanagement |
|
||||
|
||||
Zusaetzlich existieren spezialisierte Domains wie CRA, ARC, API, PKI, SUP, VUL, BCP, PHY u.v.m.
|
||||
|
||||
### Status (Release State)
|
||||
|
||||
| Status | Bedeutung |
|
||||
|--------|-----------|
|
||||
| **Draft** | Entwurf — noch nicht freigegeben |
|
||||
| **Approved** | Freigegeben fuer Kunden |
|
||||
| **Review noetig** | Muss manuell geprueft werden (Pipeline-erzeugt) |
|
||||
| **Zu aehnlich** | Too-Close-Check hat Warnung ausgeloest |
|
||||
| **Duplikat** | Wurde als Duplikat eines anderen Controls erkannt |
|
||||
| **Deprecated** | Veraltet/geloescht |
|
||||
|
||||
### Nachweis (Verification Method)
|
||||
|
||||
| Methode | Farbe | Beschreibung |
|
||||
|---------|-------|-------------|
|
||||
| **Code Review** | Blau | Nachweis durch Quellcode-Inspektion |
|
||||
| **Dokument** | Amber | Nachweis durch Richtlinien, Prozesse, Schulungen |
|
||||
| **Tool** | Teal | Nachweis durch automatisierte Scans/Monitoring |
|
||||
| **Hybrid** | Lila | Kombination aus mehreren Methoden |
|
||||
|
||||
### Kategorie
|
||||
|
||||
Thematische Einordnung (23 Kategorien). Kategorien sind **thematisch**, Domains **strukturell**.
|
||||
Ein AUTH-Control kann z.B. die Kategorie "Netzwerksicherheit" haben.
|
||||
|
||||
### Zielgruppe (Target Audience)
|
||||
|
||||
| Zielgruppe | Bedeutung |
|
||||
|------------|-----------|
|
||||
| **Unternehmen** | Fuer Endkunden/Firmen relevant |
|
||||
| **Behoerden** | Spezifisch fuer oeffentliche Verwaltung |
|
||||
| **Anbieter** | Fuer SaaS/Plattform-Anbieter |
|
||||
| **Alle** | Allgemein anwendbar |
|
||||
| **Entwickler** | Software-Entwickler |
|
||||
| **DSB** | Datenschutzbeauftragte |
|
||||
| **GF** | Geschaeftsfuehrung |
|
||||
| **IT** | IT-Abteilung |
|
||||
| **Recht** | Rechtsabteilung |
|
||||
| **Compliance** | Compliance-Officer |
|
||||
| **Personal** | Personalwesen |
|
||||
| **Einkauf** | Beschaffung |
|
||||
| **Produktion** | Fertigung |
|
||||
| **Vertrieb** | Sales |
|
||||
| **Gesundheit** | Gesundheitswesen |
|
||||
| **Finanzen** | Finanzwesen |
|
||||
| **Oeffentl. Dienst** | Oeffentlicher Dienst |
|
||||
|
||||
### Dokumentenursprung (Source)
|
||||
|
||||
Filtert nach der Quelldokument-Herkunft des Controls. Die wichtigsten Quellen:
|
||||
|
||||
| Quelle | Typ |
|
||||
|--------|-----|
|
||||
| KI-Verordnung (EU) 2024/1689 | EU-Recht |
|
||||
| Cyber Resilience Act (EU) 2024/2847 | EU-Recht |
|
||||
| DSGVO (EU) 2016/679 | EU-Recht |
|
||||
| NIS2-Richtlinie (EU) 2022/2555 | EU-Recht |
|
||||
| NIST SP 800-53, CSF 2.0, SSDF | US-Standards |
|
||||
| OWASP Top 10, ASVS, SAMM | Open Source |
|
||||
| ENISA Guidelines | EU-Agentur |
|
||||
| CISA Secure by Design | US-Behoerde |
|
||||
| BDSG, TKG, GewO, HGB | Deutsche Gesetze |
|
||||
| EDPB Leitlinien | EU Datenschutz |
|
||||
|
||||
---
|
||||
|
||||
## Badges & Lizenzregeln
|
||||
|
||||
### Lizenzregel-Badge (Rule 1 / 2 / 3)
|
||||
|
||||
Die Lizenzregel bestimmt, wie ein Control erstellt und genutzt werden darf:
|
||||
|
||||
| Badge | Farbe | Regel | Bedeutung |
|
||||
|-------|-------|-------|-----------|
|
||||
| **Free Use** | Gruen | Rule 1 | Quelle ist Public Domain oder EU-Recht — Originaltext darf gezeigt werden |
|
||||
| **Zitation** | Blau | Rule 2 | Quelle ist CC-BY oder aehnlich — Zitation + Quellenangabe erforderlich |
|
||||
| **Reformuliert** | Amber | Rule 3 | Quelle hat eingeschraenkte Lizenz — eigenstaendig reformuliert, kein Originaltext |
|
||||
|
||||
### Processing Path
|
||||
|
||||
| Pfad | Bedeutung |
|
||||
|------|-----------|
|
||||
| `structured` / `structured_batch` | Control direkt aus dem Quelltext strukturiert (Rule 1/2) |
|
||||
| `llm_reform` / `llm_reform_batch` | Control vollstaendig reformuliert (Rule 3) |
|
||||
| `no_control` | LLM konnte kein Control ableiten (null im Array) |
|
||||
|
||||
### Generation Strategy Badge
|
||||
|
||||
| Badge | Farbe | Bedeutung |
|
||||
|-------|-------|-----------|
|
||||
| **v1** | Grau | Original-Pipeline (ungrouped) |
|
||||
| **v2** | Emerald | Document-grouped Pipeline |
|
||||
|
||||
### Weitere Badges
|
||||
|
||||
| Badge | Bedeutung |
|
||||
|-------|-----------|
|
||||
| Score | Risiko-Score (0-10) |
|
||||
| Severity-Badge | Schweregrad (Kritisch/Hoch/Mittel/Niedrig) |
|
||||
| State-Badge | Freigabestatus (Draft/Approved/etc.) |
|
||||
| Kategorie-Badge | Thematische Kategorie |
|
||||
| Zielgruppe-Badge | Enterprise/Behoerden/Anbieter/Alle/etc. |
|
||||
|
||||
---
|
||||
|
||||
## Unabhaengige Taxonomie
|
||||
|
||||
### Eigenes Klassifikationssystem
|
||||
|
||||
Die Canonical Control Library verwendet ein **eigenes Domain-Schema**, das sich bewusst von
|
||||
proprietaeren Frameworks unterscheidet. Die Domains werden **automatisch** durch den
|
||||
Control Generator vergeben, basierend auf dem Inhalt der Quelldokumente.
|
||||
|
||||
### 16 Standard-Domains
|
||||
|
||||
| Domain | Name | Beschreibung |
|
||||
|--------|------|-------------|
|
||||
| AUTH | Identity & Access Management | Authentisierung, MFA, Token-Management |
|
||||
| CRYP | Cryptographic Operations | Key Management, Rotation, HSM |
|
||||
| NET | Network & Transport Security | TLS, Zertifikate, Netzwerk-Haertung |
|
||||
| DATA | Data Governance & Classification | Datenklassifikation, Schutzmassnahmen |
|
||||
| LOG | Security Operations & Logging | Privacy-Aware Logging, SIEM |
|
||||
| ACC | Access Control | Zugriffskontrolle, Berechtigungen |
|
||||
| SEC | IT Security | Schwachstellen, Haertung, Konfiguration |
|
||||
| INC | Incident Management | Vorfallmanagement, Wiederherstellung |
|
||||
| AI | Artificial Intelligence | KI-Compliance, Bias, Transparenz |
|
||||
| COMP | Compliance | Konformitaet, Audit, Zertifizierung |
|
||||
| GOV | Government & Public Administration | Behoerden, Verwaltung, Aufsicht |
|
||||
| LAB | Labor Law | Arbeitsrecht, Arbeitsschutz |
|
||||
| FIN | Financial Regulation | Finanzregulierung, BaFin |
|
||||
| TRD | Trade Regulation | Gewerbe, Handelsrecht |
|
||||
| ENV | Environmental | Umweltschutz, Nachhaltigkeit |
|
||||
| HLT | Health | Gesundheit, Medizinprodukte |
|
||||
|
||||
### Spezialisierte Domains
|
||||
|
||||
Neben den 16 Standard-Domains gibt es ueber 80 weitere Domains fuer spezifische Themen:
|
||||
CRA, ARC (Architektur), API, PKI, SUP (Supply Chain), VUL, BCP, PHY u.v.m.
|
||||
|
||||
### ID-Format
|
||||
|
||||
Control-IDs folgen dem Muster `DOMAIN-NNN` (z.B. AUTH-001, SEC-042). Dieses Format ist
|
||||
**nicht von BSI oder anderen proprietaeren Standards abgeleitet**, sondern folgt einem
|
||||
allgemein ueblichen Nummerierungsschema.
|
||||
|
||||
!!! warning "Keine BSI-Nomenklatur"
|
||||
Die Domains verwenden bewusst KEINE BSI-Bezeichner (O.Auth_*, O.Netz_*).
|
||||
|
||||
---
|
||||
|
||||
## Offene Referenzquellen
|
||||
|
||||
Alle Controls sind in mindestens einer der folgenden **frei zugaenglichen** Quellen verankert:
|
||||
|
||||
### OWASP (CC BY-SA 4.0 — kommerziell erlaubt)
|
||||
|
||||
- **ASVS** — Application Security Verification Standard v4.0.3
|
||||
- **MASVS** — Mobile Application Security Verification Standard v2.1
|
||||
- **Top 10** — OWASP Top 10 (2021)
|
||||
- **Cheat Sheets** — OWASP Cheat Sheet Series
|
||||
- **SAMM** — Software Assurance Maturity Model
|
||||
|
||||
### NIST (Public Domain — keine Einschraenkungen)
|
||||
|
||||
- **SP 800-53 Rev.5** — Security and Privacy Controls
|
||||
- **SP 800-63B** — Digital Identity Guidelines (Authentication)
|
||||
- **SP 800-57** — Key Management Recommendations
|
||||
- **SP 800-52 Rev.2** — TLS Implementation Guidelines
|
||||
- **SP 800-92** — Log Management Guide
|
||||
- **SP 800-218 (SSDF)** — Secure Software Development Framework
|
||||
|
||||
### ENISA (CC BY 4.0 — kommerziell erlaubt)
|
||||
|
||||
- Good Practices for IoT/Mobile Security
|
||||
- Data Protection Engineering
|
||||
- Algorithms, Key Sizes and Parameters Report
|
||||
|
||||
### Weitere offene Quellen
|
||||
|
||||
- **SLSA** (Supply-chain Levels for Software Artifacts) — Google Open Source
|
||||
- **CIS Controls v8** (CC BY-NC-ND — nur fuer interne Analyse)
|
||||
- **CISA Secure by Design** — US-Behoerde (Public Domain)
|
||||
|
||||
---
|
||||
|
||||
## Geschuetzte Quellen — Nur interne Analyse
|
||||
|
||||
Die folgenden Quellen werden **ausschliesslich intern zur Analyse** verwendet.
|
||||
Kein Text, keine Struktur, keine Bezeichner aus diesen Quellen erscheinen im Produkt.
|
||||
|
||||
### BSI (Nutzungsbedingungen — kommerziell eingeschraenkt)
|
||||
|
||||
- TR-03161 Teil 1-3 (Mobile, Web, Hintergrunddienste)
|
||||
- Nutzung: TDM unter UrhG §44b, Kopien werden geloescht
|
||||
- Kein Shipping von Zitaten, Embeddings oder Strukturen
|
||||
|
||||
### ISO/IEC (Kostenpflichtig — kein Shipping)
|
||||
|
||||
- ISO 27001, ISO 27002
|
||||
- Nutzung: Nur als Referenz fuer Mapping, kein Text im Produkt
|
||||
|
||||
### ETSI (Restriktiv — kein kommerzieller Gebrauch)
|
||||
|
||||
- Nutzung: Nur als Hintergrundwissen, kein direkter Einfluss
|
||||
|
||||
### Trennungsprinzip
|
||||
|
||||
| Ebene | Geschuetzte Quelle | Offene Quelle |
|
||||
|-------|--------------------|---------------|
|
||||
| Analyse | Darf gelesen werden | Darf gelesen werden |
|
||||
| Inspiration | Darf Ideen liefern | Darf Ideen liefern |
|
||||
| Formulierung | Keine Uebernahme | Darf zitiert werden |
|
||||
| Struktur | Keine Uebernahme | Darf verwendet werden |
|
||||
| Produkttext | Nicht erlaubt | Erlaubt |
|
||||
|
||||
---
|
||||
|
||||
## Verifikationsmethoden
|
||||
|
||||
Jedes Control wird einer von vier Verifikationsmethoden zugeordnet. Dies bestimmt,
|
||||
**wie** ein Kunde den Nachweis fuer die Einhaltung erbringen kann:
|
||||
|
||||
| Methode | Beschreibung | Beispiele |
|
||||
|---------|-------------|-----------|
|
||||
| **Code Review** | Quellcode-Inspektion | Input-Validierung, Encryption-Konfiguration, Auth-Logic |
|
||||
| **Dokument** | Richtlinien, Prozesse, Schulungen | Notfallplaene, Schulungsnachweise, Datenschutzkonzepte |
|
||||
| **Tool** | Automatisierte Tools/Scans | SIEM-Logs, Vulnerability-Scans, Monitoring-Dashboards |
|
||||
| **Hybrid** | Kombination mehrerer Methoden | Zugriffskontrollen (Code + Policy + Tool) |
|
||||
|
||||
### Bedeutung fuer Kunden
|
||||
|
||||
- **Code Review Controls** koennen direkt im SDK-Scan geprueft werden
|
||||
- **Dokument Controls** erfordern manuelle Uploads (PDFs, Links)
|
||||
- **Tool Controls** koennen per API-Integration automatisch nachgewiesen werden
|
||||
- **Hybrid Controls** benoetigen mehrere Nachweisarten
|
||||
|
||||
---
|
||||
|
||||
## Thematische Kategorien (23)
|
||||
|
||||
Controls sind in thematische Kategorien gruppiert:
|
||||
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|-------------|
|
||||
| `encryption` | Verschluesselung, Kryptographie, Key Management |
|
||||
| `authentication` | Authentifizierung, Login, MFA, Session-Management |
|
||||
| `network` | Netzwerk, Firewall, VPN, Segmentierung |
|
||||
| `data_protection` | Datenschutz, DSGVO, Anonymisierung |
|
||||
| `logging` | Protokollierung, Monitoring, SIEM |
|
||||
| `incident` | Vorfallmanagement, Meldepflichten |
|
||||
| `continuity` | Business Continuity, Disaster Recovery, Backups |
|
||||
| `compliance` | Konformitaet, Audit, Zertifizierung |
|
||||
| `supply_chain` | Lieferkette, Vendor Risk, SBOM |
|
||||
| `physical` | Physische Sicherheit, Zutritt |
|
||||
| `personnel` | Schulung, Security Awareness |
|
||||
| `application` | Software, Code Review, API, SAST/DAST |
|
||||
| `system` | Haertung, Patch, Konfiguration |
|
||||
| `risk` | Risikobewertung, -analyse, -management |
|
||||
| `governance` | ISMS, Richtlinien, Sicherheitsorganisation |
|
||||
| `hardware` | Hardware, Firmware, TPM, Secure Boot |
|
||||
| `identity` | IAM, SSO, Federation, Verzeichnisdienste |
|
||||
| `public_administration` | Behoerden, Verwaltung |
|
||||
| `labor_law` | Arbeitsrecht, Arbeitsschutz |
|
||||
| `finance` | Finanzregulierung, Rechnungslegung |
|
||||
| `trade_regulation` | Gewerbe, Handelsrecht |
|
||||
| `environmental` | Umweltschutz, Nachhaltigkeit |
|
||||
| `health` | Gesundheit, Medizinprodukte |
|
||||
|
||||
!!! info "Abgrenzung zu Domains"
|
||||
Kategorien sind **thematisch**, Domains (AUTH, NET, etc.) sind **strukturell**.
|
||||
Ein Control AUTH-005 (Domain AUTH) hat die Kategorie "authentication",
|
||||
aber ein Control NET-012 (Domain NET) koennte ebenfalls die Kategorie
|
||||
"authentication" haben, wenn es um Netzwerk-Authentifizierung geht.
|
||||
|
||||
---
|
||||
|
||||
## Master Library Strategie
|
||||
|
||||
### RAG-First Ansatz
|
||||
|
||||
Die Canonical Control Library folgt einer **RAG-First-Strategie** in Wellen:
|
||||
|
||||
| Welle | Quellen | Lizenzregel | Vorteil |
|
||||
|-------|---------|------------|---------|
|
||||
| 1 | OWASP (ASVS, MASVS, Top10) | Rule 2 (CC-BY-SA, Zitation) | Originaltext + Zitation |
|
||||
| 2 | NIST (SP 800-53, CSF, SSDF) | Rule 1 (Public Domain) | Voller Text, keine Einschraenkungen |
|
||||
| 3 | EU-Verordnungen (DSGVO, AI Act, NIS2, CRA) | Rule 1 (EU Law) | Gesetzestext + Erklaerung |
|
||||
| 4 | Deutsche Gesetze (BDSG, TTDSG, TKG) | Rule 1 (DE Law) | Gesetzestext + Erklaerung |
|
||||
|
||||
### Dedup gegen BSI Rule-3 Controls
|
||||
|
||||
Die BSI Rule-3 Controls werden gegen die neuen Rule 1+2 Controls abgeglichen:
|
||||
|
||||
- Wenn ein BSI-Control ein Duplikat eines OWASP/NIST-Controls ist → **OWASP/NIST bevorzugt**
|
||||
(weil Originaltext + Zitation erlaubt)
|
||||
- BSI-Duplikate werden als `deprecated` markiert
|
||||
- Tags und Anchors werden in den behaltenen Control zusammengefuehrt
|
||||
|
||||
---
|
||||
|
||||
## Automatisierte Validierung
|
||||
|
||||
Jedes Control wird bei jedem Commit automatisch geprueft:
|
||||
|
||||
### 1. Schema-Validierung
|
||||
|
||||
- Alle Pflichtfelder vorhanden
|
||||
- Control-ID Format: `^[A-Z]{2,6}-[0-9]{3}$`
|
||||
- Severity: low, medium, high, critical
|
||||
- Risk Score: 0-10
|
||||
|
||||
### 2. No-Leak Scanner
|
||||
|
||||
Regex-Pruefung gegen verbotene Muster in produktfaehigen Feldern:
|
||||
|
||||
- `O.[A-Za-z]+_[0-9]+` — BSI Objective-IDs
|
||||
- `TR-03161` — Direkte BSI-TR-Referenzen
|
||||
- `BSI-TR-` — BSI-spezifische Locators
|
||||
- `Anforderung [A-Z].[0-9]+` — BSI-Anforderungsformat
|
||||
|
||||
### 3. Open Anchor Check
|
||||
|
||||
Jedes freigegebene Control muss mindestens 1 Open-Source-Referenz haben.
|
||||
|
||||
### 4. Too-Close Detektor (5 Metriken)
|
||||
|
||||
| Metrik | Warn | Fail | Beschreibung |
|
||||
|--------|------|------|-------------|
|
||||
| Exact Phrase | ≥8 Tokens | ≥12 Tokens | Laengste identische Token-Sequenz |
|
||||
| Token Overlap | ≥0.20 | ≥0.30 | Jaccard-Aehnlichkeit der Token-Mengen |
|
||||
| 3-Gram Jaccard | ≥0.10 | ≥0.18 | Zeichenketten-Aehnlichkeit |
|
||||
| Embedding Cosine | ≥0.86 | ≥0.92 | Semantische Aehnlichkeit (bge-m3) |
|
||||
| LCS Ratio | ≥0.35 | ≥0.50 | Longest Common Subsequence |
|
||||
|
||||
**Entscheidungslogik:**
|
||||
|
||||
- **PASS** — Kein Fail + max 1 Warn
|
||||
- **WARN** — Max 2 Warn, kein Fail → Human Review erforderlich
|
||||
- **FAIL** — Irgendein Fail → Blockiert, Umformulierung noetig
|
||||
|
||||
---
|
||||
|
||||
## Live-Daten im Frontend
|
||||
|
||||
### Lizenz-Matrix
|
||||
|
||||
Tabelle aller `canonical_control_licenses` mit Berechtigungsbadges:
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|------|-------------|
|
||||
| `license_id` | Eindeutige Lizenz-ID (z.B. `OWASP_CC_BY_SA`) |
|
||||
| `name` | Lizenzname |
|
||||
| `commercial_use` | `allowed` / `restricted` / `prohibited` / `unclear` |
|
||||
| `ai_training_restriction` | KI-Training-Einschraenkung |
|
||||
| `tdm_allowed_under_44b` | TDM nach UrhG §44b erlaubt? (`yes` / `no` / `unclear`) |
|
||||
| `deletion_required` | Loeschpflicht nach Analyse? |
|
||||
|
||||
**API:** `GET /v1/canonical/licenses`
|
||||
|
||||
### Quellenregister
|
||||
|
||||
Karten fuer jede `canonical_control_sources` mit 4 Berechtigungsstufen:
|
||||
|
||||
| Feld | Beschreibung |
|
||||
|------|-------------|
|
||||
| `source_id` | Eindeutige Quell-ID (z.B. `OWASP_ASVS`) |
|
||||
| `title` | Quellenname |
|
||||
| `publisher` | Herausgeber |
|
||||
| `license_name` | Verknuepfte Lizenz |
|
||||
| `allowed_analysis` | Analyse erlaubt? |
|
||||
| `allowed_store_excerpt` | Auszuege speichern erlaubt? |
|
||||
| `allowed_ship_embeddings` | Embeddings im Produkt erlaubt? |
|
||||
| `allowed_ship_in_product` | Text im Produkt erlaubt? |
|
||||
| `vault_retention_days` | Aufbewahrungsfrist in Tagen |
|
||||
| `vault_access_tier` | `restricted` / `internal` / `public` |
|
||||
|
||||
**API:** `GET /v1/canonical/sources`
|
||||
|
||||
---
|
||||
|
||||
## Quelldateien
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `admin-compliance/app/sdk/control-provenance/page.tsx` | Frontend (10 Sektionen + 2 Live-Daten-Tabellen) |
|
||||
| `backend-compliance/migrations/044_canonical_control_library.sql` | DB-Schema (Licenses, Sources, Mappings) |
|
||||
| `backend-compliance/compliance/api/canonical_control_routes.py` | API: `/v1/canonical/licenses`, `/v1/canonical/sources` |
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [Canonical Control Library (CP-CLIB)](canonical-control-library.md) — Domains, Datenmodell, Pipeline, Tests
|
||||
- [Control Generator Pipeline](control-generator-pipeline.md) — 7-Stufen-Pipeline, QA, Recital-Erkennung
|
||||
@@ -0,0 +1,253 @@
|
||||
# Deduplizierungs-Engine (Control Dedup)
|
||||
|
||||
4-stufige Dedup-Pipeline zur Vermeidung doppelter atomarer Controls bei der Pass 0b Komposition. Kern-USP: **"1 Control erfuellt 5 Gesetze"** durch Multi-Parent-Linking.
|
||||
|
||||
**Backend:** `backend-compliance/compliance/services/control_dedup.py`
|
||||
**Migration:** `backend-compliance/migrations/074_control_dedup.sql`
|
||||
**Tests:** `backend-compliance/tests/test_control_dedup.py` (56 Tests)
|
||||
|
||||
---
|
||||
|
||||
## Motivation
|
||||
|
||||
Aus ~6.800 technischen Controls x ~10 Obligations pro Control entstehen ~68.000 atomare Kandidaten. Ziel: ~18.000 einzigartige Master Controls. Viele Obligations aus verschiedenen Gesetzen fuehren zum gleichen technischen Control (z.B. "MFA implementieren" in DSGVO, NIS2, AI Act).
|
||||
|
||||
**Problem:** Embedding-only Deduplizierung ist GEFAEHRLICH fuer Compliance.
|
||||
|
||||
!!! danger "False-Positive Beispiel"
|
||||
- "Admin-Zugriffe muessen MFA nutzen" vs. "Remote-Zugriffe muessen MFA nutzen"
|
||||
- Embedding sagt >0.9 aehnlich
|
||||
- Aber es sind **ZWEI verschiedene Controls** (verschiedene Objekte!)
|
||||
|
||||
---
|
||||
|
||||
## 4-Stufen Entscheidungsbaum
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Kandidat-Control] --> B{Pattern-Gate}
|
||||
B -->|pattern_id verschieden| N1[NEW CONTROL]
|
||||
B -->|pattern_id gleich| C{Action-Check}
|
||||
C -->|Action verschieden| N2[NEW CONTROL]
|
||||
C -->|Action gleich| D{Object-Normalization}
|
||||
D -->|Objekt verschieden| E{Similarity > 0.95?}
|
||||
E -->|Ja| L1[LINK]
|
||||
E -->|Nein| N3[NEW CONTROL]
|
||||
D -->|Objekt gleich| F{Tiered Thresholds}
|
||||
F -->|> 0.92| L2[LINK]
|
||||
F -->|0.85 - 0.92| R[REVIEW QUEUE]
|
||||
F -->|< 0.85| N4[NEW CONTROL]
|
||||
```
|
||||
|
||||
### Stufe 1: Pattern-Gate (hart)
|
||||
|
||||
`pattern_id` muss uebereinstimmen. Verhindert ~80% der False Positives.
|
||||
|
||||
```python
|
||||
if pattern_id != existing.pattern_id:
|
||||
→ NEW CONTROL # Verschiedene Kontrollmuster = verschiedene Controls
|
||||
```
|
||||
|
||||
### Stufe 2: Action-Check (hart)
|
||||
|
||||
Normalisierte Aktionsverben muessen uebereinstimmen. "Implementieren" vs. "Testen" = verschiedene Controls, auch bei gleichem Objekt.
|
||||
|
||||
```python
|
||||
if normalize_action("implementieren") != normalize_action("testen"):
|
||||
→ NEW CONTROL # "implement" != "test"
|
||||
```
|
||||
|
||||
**Action-Normalisierung (Deutsch → Englisch):**
|
||||
|
||||
| Deutsche Verben | Kanonische Form |
|
||||
|----------------|-----------------|
|
||||
| implementieren, umsetzen, einrichten, aktivieren | `implement` |
|
||||
| testen, pruefen, ueberpruefen, verifizieren | `test` |
|
||||
| ueberwachen, monitoring, beobachten | `monitor` |
|
||||
| verschluesseln | `encrypt` |
|
||||
| protokollieren, aufzeichnen, loggen | `log` |
|
||||
| beschraenken, einschraenken, begrenzen | `restrict` |
|
||||
|
||||
### Stufe 3: Object-Normalization (weich)
|
||||
|
||||
Compliance-Objekte werden auf kanonische Token normalisiert.
|
||||
|
||||
```python
|
||||
normalize_object("Admin-Konten") → "privileged_access"
|
||||
normalize_object("Remote-Zugriff") → "remote_access"
|
||||
normalize_object("MFA") → "multi_factor_auth"
|
||||
```
|
||||
|
||||
Bei verschiedenen Objekten gilt ein hoeherer Schwellenwert (0.95 statt 0.92).
|
||||
|
||||
**Objekt-Normalisierung:**
|
||||
|
||||
| Eingabe | Kanonischer Token |
|
||||
|---------|------------------|
|
||||
| MFA, 2FA, Multi-Faktor-Authentifizierung | `multi_factor_auth` |
|
||||
| Admin-Konten, privilegierte Zugriffe | `privileged_access` |
|
||||
| Verschluesselung, Kryptografie | `encryption` |
|
||||
| Schluessel, Key Management | `key_management` |
|
||||
| TLS, SSL, HTTPS | `transport_encryption` |
|
||||
| Firewall | `firewall` |
|
||||
| Audit-Log, Protokoll, Logging | `audit_logging` |
|
||||
|
||||
### Stufe 4: Embedding Similarity (Qdrant)
|
||||
|
||||
Tiered Thresholds basierend auf Cosine-Similarity:
|
||||
|
||||
| Score | Verdict | Aktion |
|
||||
|-------|---------|--------|
|
||||
| > 0.95 | **LINK** | Bei verschiedenen Objekten |
|
||||
| > 0.92 | **LINK** | Parent-Link hinzufuegen |
|
||||
| 0.85 - 0.92 | **REVIEW** | In Review-Queue zur manuellen Pruefung |
|
||||
| < 0.85 | **NEW** | Neues Control anlegen |
|
||||
|
||||
---
|
||||
|
||||
## Canonicalization Layer
|
||||
|
||||
Vor dem Embedding wird der deutsche Compliance-Text in normalisiertes Englisch transformiert:
|
||||
|
||||
```
|
||||
"Administratoren muessen MFA verwenden"
|
||||
→ "implement multi_factor_auth for administratoren verwenden"
|
||||
→ Bessere Matches, weniger Embedding-Rauschen
|
||||
```
|
||||
|
||||
Dies reduziert das Rauschen durch synonyme Formulierungen in verschiedenen Gesetzen.
|
||||
|
||||
---
|
||||
|
||||
## Multi-Parent-Linking (M:N)
|
||||
|
||||
Ein atomares Control kann mehrere Eltern-Controls aus verschiedenen Regulierungen haben:
|
||||
|
||||
```json
|
||||
{
|
||||
"control_id": "AUTH-1072-A01",
|
||||
"parent_links": [
|
||||
{"parent_control_id": "AUTH-1001", "source": "NIST IA-02(01)", "link_type": "decomposition"},
|
||||
{"parent_control_id": "NIS2-045", "source": "NIS2 Art. 21", "link_type": "dedup_merge"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Datenbank-Schema
|
||||
|
||||
```sql
|
||||
-- Migration 074: control_parent_links (M:N)
|
||||
CREATE TABLE control_parent_links (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
control_uuid UUID NOT NULL REFERENCES canonical_controls(id),
|
||||
parent_control_uuid UUID NOT NULL REFERENCES canonical_controls(id),
|
||||
link_type VARCHAR(30) NOT NULL DEFAULT 'decomposition',
|
||||
confidence NUMERIC(3,2) DEFAULT 1.0,
|
||||
source_regulation VARCHAR(100),
|
||||
source_article VARCHAR(100),
|
||||
obligation_candidate_id UUID REFERENCES obligation_candidates(id),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
CONSTRAINT uq_parent_link UNIQUE (control_uuid, parent_control_uuid)
|
||||
);
|
||||
```
|
||||
|
||||
**Link-Typen:**
|
||||
|
||||
| Typ | Bedeutung |
|
||||
|-----|-----------|
|
||||
| `decomposition` | Aus Pass 0b Zerlegung |
|
||||
| `dedup_merge` | Durch Dedup-Engine als Duplikat erkannt |
|
||||
| `manual` | Manuell durch Reviewer verknuepft |
|
||||
| `crosswalk` | Aus Crosswalk-Matrix uebernommen |
|
||||
|
||||
---
|
||||
|
||||
## Review-Queue
|
||||
|
||||
Borderline-Matches (Similarity 0.85-0.92) werden in die Review-Queue geschrieben:
|
||||
|
||||
```sql
|
||||
-- Migration 074: control_dedup_reviews
|
||||
CREATE TABLE control_dedup_reviews (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
candidate_control_id VARCHAR(30) NOT NULL,
|
||||
candidate_title TEXT NOT NULL,
|
||||
candidate_objective TEXT,
|
||||
matched_control_uuid UUID REFERENCES canonical_controls(id),
|
||||
matched_control_id VARCHAR(30),
|
||||
similarity_score NUMERIC(4,3),
|
||||
dedup_stage VARCHAR(40) NOT NULL,
|
||||
review_status VARCHAR(20) DEFAULT 'pending',
|
||||
-- pending → accepted_link | accepted_new | rejected
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Qdrant Collection
|
||||
|
||||
```
|
||||
Collection: atomic_controls
|
||||
Dimension: 1024 (bge-m3)
|
||||
Distance: COSINE
|
||||
Payload: pattern_id, action_normalized, object_normalized, control_id, canonical_text
|
||||
Index: pattern_id (keyword), action_normalized (keyword), object_normalized (keyword)
|
||||
Query: IMMER mit filter: pattern_id == X (reduziert Suche drastisch)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration in Pass 0b
|
||||
|
||||
Die Dedup-Engine ist optional in `DecompositionPass` integriert:
|
||||
|
||||
```python
|
||||
decomp = DecompositionPass(db=session, dedup_enabled=True)
|
||||
stats = await decomp.run_pass0b(limit=100, use_anthropic=True)
|
||||
|
||||
# Stats enthalten Dedup-Metriken:
|
||||
# stats["dedup_linked"] = 15 (Duplikate → Parent-Link)
|
||||
# stats["dedup_review"] = 3 (Borderline → Review-Queue)
|
||||
# stats["controls_created"] = 82 (Neue Controls)
|
||||
```
|
||||
|
||||
**Ablauf bei Pass 0b mit Dedup:**
|
||||
|
||||
1. LLM generiert atomares Control
|
||||
2. Dedup-Engine prueft 4 Stufen
|
||||
3. **LINK:** Kein neues Control, Parent-Link zu bestehendem
|
||||
4. **REVIEW:** Kein neues Control, Eintrag in Review-Queue
|
||||
5. **NEW:** Control anlegen + in Qdrant indexieren
|
||||
|
||||
---
|
||||
|
||||
## Konfiguration
|
||||
|
||||
| Umgebungsvariable | Default | Beschreibung |
|
||||
|-------------------|---------|-------------|
|
||||
| `DEDUP_ENABLED` | `true` | Dedup-Engine ein/ausschalten |
|
||||
| `DEDUP_LINK_THRESHOLD` | `0.92` | Schwelle fuer automatisches Linking |
|
||||
| `DEDUP_REVIEW_THRESHOLD` | `0.85` | Schwelle fuer Review-Queue |
|
||||
| `DEDUP_LINK_THRESHOLD_DIFF_OBJ` | `0.95` | Schwelle bei verschiedenen Objekten |
|
||||
| `DEDUP_QDRANT_COLLECTION` | `atomic_controls` | Qdrant-Collection fuer Dedup-Index |
|
||||
| `QDRANT_URL` | `http://host.docker.internal:6333` | Qdrant-URL |
|
||||
| `EMBEDDING_URL` | `http://embedding-service:8087` | Embedding-Service-URL |
|
||||
|
||||
---
|
||||
|
||||
## Quelldateien
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `compliance/services/control_dedup.py` | 4-Stufen Dedup-Engine |
|
||||
| `compliance/services/decomposition_pass.py` | Pass 0a/0b mit Dedup-Integration |
|
||||
| `migrations/074_control_dedup.sql` | DB-Schema (parent_links, review_queue) |
|
||||
| `tests/test_control_dedup.py` | 56 Unit-Tests |
|
||||
|
||||
---
|
||||
|
||||
## Verwandte Dokumentation
|
||||
|
||||
- [Control Generator Pipeline](control-generator-pipeline.md) — 7-Stufen RAG→Control Pipeline
|
||||
- [Canonical Control Library](canonical-control-library.md) — Datenmodell, Domains, Similarity-Detektor
|
||||
@@ -80,3 +80,13 @@ Im Company-Profile-Wizard erscheint nach Abschluss (`is_complete = true`) ein CT
|
||||
- Alle 5 Template-Generatoren mit verschiedenen Kontext-Variationen
|
||||
- Regulierungs-Flag-Kombinationen
|
||||
- Route-Registrierung
|
||||
|
||||
---
|
||||
|
||||
## Policy-Bibliothek
|
||||
|
||||
Neben der automatischen Dokumentengenerierung aus Stammdaten stehen **29 deutsche Richtlinien-Templates**
|
||||
im Dokumentengenerator als Vorlagen bereit (IT-Sicherheit, Datenschutz, Personal, Lieferanten, BCM).
|
||||
|
||||
Siehe [Policy-Bibliothek](policy-bibliothek.md) fuer die vollstaendige Liste aller Templates,
|
||||
Platzhalter und Kategorien.
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# Evidence Type — Code vs. Prozess Controls
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Nicht jedes Control kann gleich nachgewiesen werden. Ein Verschluesselungs-Control
|
||||
ist im Quellcode pruefbar, ein Risikomanagement-Control erfordert Dokumente und
|
||||
Nachweise. Diese Unterscheidung bestimmt, **wie** die Compliance-Plattform den
|
||||
Nachweis automatisiert oder den Nutzer unterstuetzt.
|
||||
|
||||
Das Feld `evidence_type` auf `canonical_controls` klassifiziert jeden Control
|
||||
in eine von drei Kategorien.
|
||||
|
||||
## Die drei Typen
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph "code"
|
||||
C1["Source Code"]
|
||||
C2["IaC / Terraform"]
|
||||
C3["CI/CD Pipeline"]
|
||||
C4["Cloud Config"]
|
||||
end
|
||||
|
||||
subgraph "process"
|
||||
P1["Policies"]
|
||||
P2["Schulungsnachweise"]
|
||||
P3["Vertraege"]
|
||||
P4["Screenshots / Audits"]
|
||||
end
|
||||
|
||||
subgraph "hybrid"
|
||||
H1["Code + Doku"]
|
||||
H2["Config + Schulung"]
|
||||
end
|
||||
```
|
||||
|
||||
### Code Controls
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **evidence_type** | `code` |
|
||||
| **Nachweis** | Automatisiert pruefbar: Source Code, IaC, CI/CD, Cloud-Konfiguration |
|
||||
| **Automatisierung** | Hoch — SAST, Dependency Scan, Config Audit |
|
||||
| **Beispiel-Domains** | SEC, AUTH, CRYPT, NET, LOG, ACC, API |
|
||||
| **Beispiel** | "AES-256 Verschluesselung at rest" → pruefbar via Code Review / IaC Scan |
|
||||
|
||||
### Prozess Controls
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **evidence_type** | `process` |
|
||||
| **Nachweis** | Dokumente, Policies, Schulungsnachweise, Vertraege, Screenshots |
|
||||
| **Automatisierung** | Gering — erfordert manuelle Uploads oder MCP-Dokumenten-Scan |
|
||||
| **Beispiel-Domains** | GOV, ORG, COMP, LEGAL, HR, FIN, RISK, AUDIT, ENV |
|
||||
| **Beispiel** | "Reallabor-Zugang fuer KMUs bereitstellen" → Nachweis ueber Programm-Dokumentation |
|
||||
|
||||
!!! info "Governance & Regulatorische Controls"
|
||||
Controls wie "Behoerde muss KMUs Zugang zu Reallaboren geben" sind Prozess-Controls.
|
||||
Der Nachweis erfolgt ueber Dokumente — nicht im Source Code.
|
||||
Auch regulatorische Umsetzungspflichten (GOV-Domain) fallen hierunter.
|
||||
|
||||
### Hybrid Controls
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **evidence_type** | `hybrid` |
|
||||
| **Nachweis** | Sowohl Code als auch Dokumente erforderlich |
|
||||
| **Automatisierung** | Teilweise — Code-Teil automatisiert, Prozess-Teil manuell |
|
||||
| **Beispiel-Domains** | DATA, AI, INC, IAM |
|
||||
| **Beispiel** | "MFA implementieren" → Config pruefbar (code) + Nutzer-Schulung noetig (process) |
|
||||
|
||||
## Backfill-Heuristik
|
||||
|
||||
Der Backfill klassifiziert Controls automatisch anhand des Domain-Prefix:
|
||||
|
||||
```
|
||||
POST /api/compliance/v1/canonical/controls/backfill-evidence-type?dry_run=true
|
||||
```
|
||||
|
||||
**Algorithmus:**
|
||||
|
||||
1. Domain-Prefix extrahieren (z.B. `SEC` aus `SEC-042`)
|
||||
2. Gegen vordefinierte Domain-Sets pruefen (code / process / hybrid)
|
||||
3. Falls Domain unbekannt: Category als Fallback nutzen
|
||||
4. Falls auch keine Category: `process` (konservativ)
|
||||
|
||||
### Domain-Zuordnung
|
||||
|
||||
| Typ | Domains |
|
||||
|---|---|
|
||||
| **code** | SEC, AUTH, CRYPT, CRYP, NET, LOG, ACC, APP, SYS, API, WEB, DEV, SDL, PKI, HSM, TEE, TPM, VUL, ... |
|
||||
| **process** | GOV, ORG, COMP, LEGAL, HR, FIN, RISK, AUDIT, ENV, HLT, TRD, LAB, PHYS, PRIV, DPO, ... |
|
||||
| **hybrid** | DATA, AI, INC, IAM, OPS, MNT, INT, ... |
|
||||
|
||||
## Frontend-Anzeige
|
||||
|
||||
In der Control-Library werden Controls mit farbcodierten Badges angezeigt:
|
||||
|
||||
| evidence_type | Badge | Farbe | Bedeutung |
|
||||
|---|---|---|---|
|
||||
| `code` | **Code** | Blau (sky) | Technisch, im Source Code pruefbar |
|
||||
| `process` | **Prozess** | Amber/Orange | Organisatorisch, Dokument-basiert |
|
||||
| `hybrid` | **Hybrid** | Violett | Beides erforderlich |
|
||||
|
||||
Zusaetzlich steht ein Dropdown-Filter "Nachweisart" zur Verfuegung.
|
||||
|
||||
## Zusammenspiel mit anderen Feldern
|
||||
|
||||
| Feld | Zweck | Beispiel |
|
||||
|---|---|---|
|
||||
| `evidence_type` | **WAS** wird nachgewiesen (Code oder Prozess) | `code` |
|
||||
| `verification_method` | **WIE** wird verifiziert | `code_review`, `document`, `tool`, `hybrid` |
|
||||
| `evidence_confidence` | **WIE SICHER** ist der Nachweis (0.0 - 1.0) | `0.92` |
|
||||
| `normative_strength` | **WIE VERBINDLICH** ist das Control | `must`, `should`, `may` |
|
||||
|
||||
!!! warning "evidence_type vs. verification_method"
|
||||
`evidence_type` sagt, ob ein Control technisch oder organisatorisch ist.
|
||||
`verification_method` sagt, mit welcher Methode es geprueft wird.
|
||||
Ein `process`-Control kann trotzdem `verification_method = tool` haben
|
||||
(z.B. wenn ein MCP-Scan Dokumente automatisch prueft).
|
||||
|
||||
## Kuenftige Automatisierung
|
||||
|
||||
### Code Controls
|
||||
- **Git-Repository-Scan**: SAST, Secret Detection, Dependency Check
|
||||
- **IaC-Analyse**: Terraform/Pulumi/CloudFormation Policies
|
||||
- **CI/CD-Integration**: Pipeline-Ergebnisse als Evidence sammeln
|
||||
|
||||
### Prozess Controls
|
||||
- **MCP-Dokumenten-Scan**: Kunden-Laufwerk anbinden, Dokumente automatisch pruefen
|
||||
- **Screenshot-Analyse**: OCR + LLM-Validierung von Screenshots
|
||||
- **Interview-Protokolle**: Strukturierte Audit-Checklisten
|
||||
@@ -88,12 +88,21 @@ compliance_evidence (
|
||||
|
||||
---
|
||||
|
||||
## Anti-Fake-Evidence
|
||||
|
||||
Seit Phase 1 (2026-03-23) werden Nachweise automatisch mit **Confidence Levels** (E0–E4) und **Truth Status** klassifiziert. Details: [Anti-Fake-Evidence Architektur](anti-fake-evidence.md)
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
**Testdatei:** `backend-compliance/tests/test_evidence_routes.py`
|
||||
**Anzahl Tests:** 11 · **Status:** ✅ alle bestanden (Stand 2026-03-05)
|
||||
|
||||
**Anti-Fake-Evidence Tests:** `backend-compliance/tests/test_anti_fake_evidence.py`
|
||||
**Anzahl Tests:** ~45 · Confidence-Klassifikation, State Machine, Multi-Score, LLM Audit
|
||||
|
||||
```bash
|
||||
cd backend-compliance
|
||||
python3 -m pytest tests/test_evidence_routes.py -v
|
||||
python3 -m pytest tests/test_evidence_routes.py tests/test_anti_fake_evidence.py -v
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,84 +17,139 @@ Das IACE-Modul unterstuetzt die vollstaendige CE-Konformitaetsbewertung von Masc
|
||||
|
||||
---
|
||||
|
||||
## SEPA Risikomodell
|
||||
## Risikomodell (Dual-Modus)
|
||||
|
||||
IACE verwendet das **SEPA-Modell** (Severity × Exposure × Probability × Avoidance):
|
||||
IACE unterstuetzt zwei Betriebsmodi fuer die Risikobewertung:
|
||||
|
||||
### Formel
|
||||
### Legacy-Modus (S×E×P)
|
||||
|
||||
| Avoidance | Formel | Beschreibung |
|
||||
|-----------|--------|--------------|
|
||||
| `0` (Standard) | `S × E × P` | Backward-kompatibel, kein Avoidance-Faktor |
|
||||
| `1–5` | `S × E × P × (A / 3.0)` | Avoidance-faktor aktiv (3 = neutral) |
|
||||
| `0` (Standard) | `R = S × E × P` | Backward-kompatibel, kein Avoidance-Faktor |
|
||||
|
||||
### Avoidance-Skala
|
||||
|
||||
| Wert | Bedeutung |
|
||||
|------|-----------|
|
||||
| 1 | Leicht vermeidbar (klare Warnung, langsame Bewegung) |
|
||||
| 2 | Eher vermeidbar |
|
||||
| 3 | Neutral (kein Einfluss) |
|
||||
| 4 | Schwer vermeidbar |
|
||||
| 5 | Nicht vermeidbar (sofortige Auswirkung) |
|
||||
|
||||
### Schwellwerte (Residualrisiko)
|
||||
**Schwellwerte (Legacy):**
|
||||
|
||||
| Schwelle | Level |
|
||||
|----------|-------|
|
||||
| ≥ 75 | critical |
|
||||
| ≥ 40 | high |
|
||||
| ≥ 15 | medium |
|
||||
| ≥ 5 | low |
|
||||
| >= 75 | critical |
|
||||
| >= 40 | high |
|
||||
| >= 15 | medium |
|
||||
| >= 5 | low |
|
||||
| < 5 | negligible |
|
||||
|
||||
### ISO-Modus (S×F×P×A) — ab v2.0
|
||||
|
||||
Wenn `avoidance >= 1`, wird automatisch der ISO-Modus aktiviert:
|
||||
|
||||
| Formel | Beschreibung |
|
||||
|--------|--------------|
|
||||
| `R = S × F × P × A` | Direkte 4-Faktor-Multiplikation (KEIN /3.0) |
|
||||
|
||||
**Faktoren:**
|
||||
|
||||
| Faktor | Skala | Beschreibung |
|
||||
|--------|-------|--------------|
|
||||
| **S** (Severity) | 1–5 | Schwere des moeglichen Schadens |
|
||||
| **F** (Frequency/Exposure) | 1–5 | Haeufigkeit/Dauer der Exposition |
|
||||
| **P** (Probability) | 1–5 | Wahrscheinlichkeit des Eintretens |
|
||||
| **A** (Avoidance) | 1–5 | Moeglichkeit der Vermeidung (1=leicht, 5=nicht vermeidbar) |
|
||||
|
||||
**Schwellwerte (ISO-Modus):**
|
||||
|
||||
| Schwelle | Level | Farbe |
|
||||
|----------|-------|-------|
|
||||
| > 300 | not_acceptable | Dunkelrot |
|
||||
| 151–300 | very_high | Dunkelorange |
|
||||
| 61–150 | high | Rot |
|
||||
| 21–60 | medium | Gelb |
|
||||
| 1–20 | low | Gruen |
|
||||
|
||||
### ALARP-Akzeptanz
|
||||
|
||||
- `residualRisk < 15` → akzeptabel
|
||||
- `residualRisk < 40` + alle Minderungsschritte verifiziert + Begruendung → akzeptabel (ALARP)
|
||||
- `residualRisk ≥ 40` → nicht akzeptabel (blockiert CE-Export)
|
||||
- `residualRisk >= 40` → nicht akzeptabel (blockiert CE-Export)
|
||||
|
||||
### Schutzmassnahmen-Hierarchie (3-Stufen)
|
||||
|
||||
Die Risikobehandlung folgt einer verbindlichen Hierarchie:
|
||||
|
||||
| Stufe | Typ | Beschreibung |
|
||||
|-------|-----|--------------|
|
||||
| 1 | **Design** | Inhaerent sichere Konstruktion (z.B. Begrenzung von Kraeften, Geschwindigkeiten) |
|
||||
| 2 | **Schutzeinrichtung** | Technische Schutzmassnahmen (z.B. Schutzgitter, Lichtvorhang, STO) |
|
||||
| 3 | **Information** | Hinweise, Warnungen, Schulung (z.B. Sicherheitskennzeichnung, Betriebsanleitung) |
|
||||
|
||||
!!! warning "Hierarchie-Regel"
|
||||
Informationsmassnahmen (Stufe 3) duerfen **nicht** als alleinige Primaermassnahme akzeptiert werden, wenn Design- oder Schutzmassnahmen technisch moeglich sind. Das System prueft dies automatisch und gibt eine Warnung aus.
|
||||
|
||||
---
|
||||
|
||||
## Hazard-Library
|
||||
|
||||
Die eingebaute Hazard-Library enthaelt **~140 Eintraege** in 24 Kategorien:
|
||||
Die eingebaute Hazard-Library enthaelt **150+ Eintraege** in 28 Kategorien. Alle Eintraege verwenden eigene Formulierungen und referenzieren Normen nur als Methodenquelle.
|
||||
|
||||
### Urspruengliche Kategorien (12)
|
||||
### KI/Cyber-Kategorien (12)
|
||||
|
||||
| Kategorie | Eintraege | Beschreibung |
|
||||
|-----------|-----------|--------------|
|
||||
| `false_classification` | 4 | Falsche KI-Klassifikation |
|
||||
| `timing_error` | 3 | Echtzeit-Verletzungen |
|
||||
| `data_poisoning` | 2 | Manipulierte Trainingsdaten |
|
||||
| `model_drift` | 3 | Modell-Verschlechterung |
|
||||
| `sensor_spoofing` | 3 | Sensor-Manipulation |
|
||||
| `communication_failure` | 3 | Kommunikationsausfall |
|
||||
| `unauthorized_access` | 4 | Unberechtigter Zugriff |
|
||||
| `firmware_corruption` | 3 | Firmware-Beschaedigung |
|
||||
| `safety_boundary_violation` | 4 | Sicherheitsgrenzwert-Verletzung |
|
||||
| `mode_confusion` | 3 | Betriebsart-Verwechslung |
|
||||
| `unintended_bias` | 2 | Unbeabsichtigte Diskriminierung |
|
||||
| `update_failure` | 3 | Update-Fehler |
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `false_classification` | Falsche KI-Klassifikation |
|
||||
| `timing_error` | Echtzeit-Verletzungen |
|
||||
| `data_poisoning` | Manipulierte Trainingsdaten |
|
||||
| `model_drift` | Modell-Verschlechterung |
|
||||
| `sensor_spoofing` | Sensor-Manipulation |
|
||||
| `communication_failure` | Kommunikationsausfall |
|
||||
| `unauthorized_access` | Unberechtigter Zugriff |
|
||||
| `firmware_corruption` | Firmware-Beschaedigung |
|
||||
| `safety_boundary_violation` | Sicherheitsgrenzwert-Verletzung |
|
||||
| `mode_confusion` | Betriebsart-Verwechslung |
|
||||
| `unintended_bias` | Unbeabsichtigte Diskriminierung |
|
||||
| `update_failure` | Update-Fehler |
|
||||
|
||||
### Neue Kategorien (12, v2.0)
|
||||
### Software/Hardware-Kategorien (12)
|
||||
|
||||
| Kategorie | Eintraege | Beschreibung |
|
||||
|-----------|-----------|--------------|
|
||||
| `software_fault` | 10 | Race Condition, Stack Overflow, Integer Overflow, Deadlock... |
|
||||
| `hmi_error` | 8 | Falsche Einheit, fehlender Alarm, Quittierung ohne Ursache... |
|
||||
| `mechanical_hazard` | 6 | Unerwarteter Anlauf, Restenergie, Teileauswurf... |
|
||||
| `electrical_hazard` | 6 | Elektrischer Schlag, Lichtbogen, gespeicherte Energie... |
|
||||
| `thermal_hazard` | 4 | Ueberhitzung, Brandgefahr, Einfrieren... |
|
||||
| `emc_hazard` | 5 | EMV-Stoerabstrahlung, ESD, HF-Stoerung... |
|
||||
| `configuration_error` | 8 | Falscher Safety-Param, Hard-coded Credentials, Debug-Mode... |
|
||||
| `safety_function_failure` | 8 | Not-Halt, STO, Schutztuer, Zweihand-Taster... |
|
||||
| `logging_audit_failure` | 5 | Fehlende Protokollierung, Log-Manipulation, Overflow... |
|
||||
| `integration_error` | 8 | Datentyp-Mismatch, Endianness, Buffer Overflow, Heartbeat... |
|
||||
| `environmental_hazard` | 5 | Temperatur, Feuchtigkeit, Vibration, Kontamination... |
|
||||
| `maintenance_hazard` | 6 | LOTO fehlt, Wartung bei laufender Maschine, Wiederanlauf... |
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `software_fault` | Race Condition, Stack Overflow, Integer Overflow, Deadlock |
|
||||
| `hmi_error` | Falsche Einheit, fehlender Alarm, Quittierung ohne Ursache |
|
||||
| `mechanical_hazard` | Quetschen, Scheren, Einziehen, Herabfallende Teile, Instabilitaet |
|
||||
| `electrical_hazard` | Elektrischer Schlag, Lichtbogen, Ueberstrom, Erdungsfehler |
|
||||
| `thermal_hazard` | Ueberhitzung, Brandgefahr, Einfrieren, Waermestrahlung |
|
||||
| `emc_hazard` | EMV-Stoerabstrahlung, ESD, HF-Stoerung |
|
||||
| `configuration_error` | Falscher Safety-Param, Hard-coded Credentials, Debug-Mode |
|
||||
| `safety_function_failure` | Not-Halt, STO, Schutztuer, Zweihand-Taster |
|
||||
| `logging_audit_failure` | Fehlende Protokollierung, Log-Manipulation, Overflow |
|
||||
| `integration_error` | Datentyp-Mismatch, Endianness, Buffer Overflow, Heartbeat |
|
||||
| `environmental_hazard` | Temperatur, Feuchtigkeit, Vibration, Kontamination |
|
||||
| `maintenance_hazard` | LOTO fehlt, Wartung bei laufender Maschine, Wiederanlauf |
|
||||
|
||||
**Filter:** `GET /sdk/v1/iace/hazard-library?category=software_fault&componentType=sw`
|
||||
### Physikalische Kategorien (4, ab v2.0)
|
||||
|
||||
| Kategorie | Beschreibung |
|
||||
|-----------|--------------|
|
||||
| `pneumatic_hydraulic` | Druckverlust, Druckfreisetzung, Schlauchpeitschen, unerwartete Bewegung |
|
||||
| `noise_vibration` | Gehoerschaedigung, Hand-Arm-Vibration, Ganzkoerpervibration |
|
||||
| `ergonomic` | Fehlbedienung, Zwangshaltung, Ueberforderung, Repetitive Belastung |
|
||||
| `material_environmental` | Staub, Rauch, Daempfe, chemische Exposition, Leckagen |
|
||||
|
||||
### Erweiterte Felder (ab v2.0)
|
||||
|
||||
Jeder Hazard-Library-Eintrag enthaelt zusaetzlich:
|
||||
|
||||
| Feld | Typ | Beschreibung |
|
||||
|------|-----|--------------|
|
||||
| `default_exposure` | int (1–5) | Standard-Expositionswert |
|
||||
| `default_avoidance` | int (1–5) | Standard-Vermeidbarkeit |
|
||||
| `typical_causes` | string[] | Typische Ursachen |
|
||||
| `typical_harm` | string | Typische Schadensfolge |
|
||||
| `relevant_lifecycle_phases` | string[] | Relevante Lebensphasen |
|
||||
| `recommended_measures_design` | string[] | Empfohlene Design-Massnahmen |
|
||||
| `recommended_measures_technical` | string[] | Empfohlene technische Massnahmen |
|
||||
| `recommended_measures_information` | string[] | Empfohlene Informationsmassnahmen |
|
||||
| `suggested_evidence` | string[] | Vorgeschlagene Nachweisdokumente |
|
||||
| `related_keywords` | string[] | Suchbegriffe |
|
||||
|
||||
**Filter:** `GET /sdk/v1/iace/hazard-library?category=mechanical_hazard&componentType=mechanical`
|
||||
|
||||
---
|
||||
|
||||
@@ -117,14 +172,139 @@ Die Controls-Library enthaelt **200 Eintraege** in 6 Domaenen:
|
||||
|
||||
---
|
||||
|
||||
## Schutzmassnahmen-Bibliothek (ab v2.0)
|
||||
|
||||
Zusaetzlich zur Controls-Library bietet IACE eine **Schutzmassnahmen-Bibliothek** mit **160 Eintraegen**, kategorisiert nach der 3-Stufen-Hierarchie:
|
||||
|
||||
| ReductionType | Eintraege | Beispiele |
|
||||
|---------------|-----------|-----------|
|
||||
| `design` | ~55 | Kraftbegrenzung, Formschluessige Sicherung, Redundante Sensorik |
|
||||
| `protective` | ~60 | Schutzgitter, Lichtvorhang, STO, Druckbegrenzungsventil |
|
||||
| `information` | ~45 | Sicherheitskennzeichnung, Betriebsanleitung, Schulungsprogramm |
|
||||
|
||||
**Filter:** `GET /sdk/v1/iace/protective-measures-library?category=mechanical_hazard`
|
||||
|
||||
---
|
||||
|
||||
## Lebensphasen (25)
|
||||
|
||||
Die Risikobeurteilung beruecksichtigt **25 Lebensphasen** einer Maschine:
|
||||
|
||||
| Phase | DE | EN |
|
||||
|-------|----|----|
|
||||
| `transport` | Transport | Transport |
|
||||
| `storage` | Lagerung | Storage |
|
||||
| `assembly` | Montage | Assembly |
|
||||
| `installation` | Aufstellung | Installation |
|
||||
| `commissioning` | Inbetriebnahme | Commissioning |
|
||||
| `parameterization` | Parametrierung | Parameterization |
|
||||
| `setup` | Einrichten | Setup |
|
||||
| `normal_operation` | Normalbetrieb | Normal Operation |
|
||||
| `auto_operation` | Automatikbetrieb | Automatic Operation |
|
||||
| `manual_operation` | Handbetrieb | Manual Operation |
|
||||
| `teach_mode` | Teach-Modus | Teach Mode |
|
||||
| `production_start` | Produktionsstart | Production Start |
|
||||
| `production_stop` | Produktionsstopp | Production Stop |
|
||||
| `process_monitoring` | Prozessueberwachung | Process Monitoring |
|
||||
| `cleaning` | Reinigung | Cleaning |
|
||||
| `inspection` | Inspektion | Inspection |
|
||||
| `maintenance` | Wartung | Maintenance |
|
||||
| `calibration` | Kalibrierung | Calibration |
|
||||
| `repair` | Reparatur | Repair |
|
||||
| `software_update` | Software-Update | Software Update |
|
||||
| `remote_maintenance` | Fernwartung | Remote Maintenance |
|
||||
| `fault_clearing` | Stoerungsbeseitigung | Fault Clearing |
|
||||
| `changeover` | Umruestung | Changeover |
|
||||
| `decommissioning` | Ausserbetriebnahme | Decommissioning |
|
||||
| `disposal` | Demontage/Entsorgung | Dismantling/Disposal |
|
||||
|
||||
**API:** `GET /sdk/v1/iace/lifecycle-phases`
|
||||
|
||||
---
|
||||
|
||||
## Betroffene Personen (20 Rollen)
|
||||
|
||||
Jede Gefaehrdung kann einer oder mehreren betroffenen Personengruppen zugeordnet werden:
|
||||
|
||||
| Rolle | DE | EN |
|
||||
|-------|----|----|
|
||||
| `operator` | Bediener | Operator |
|
||||
| `setup_personnel` | Einrichter | Setup Personnel |
|
||||
| `maintenance_tech` | Wartungstechniker | Maintenance Technician |
|
||||
| `cleaning_staff` | Reinigungspersonal | Cleaning Staff |
|
||||
| `supervisor` | Aufsichtsperson | Supervisor |
|
||||
| `programmer` | Programmierer/Integrator | Programmer/Integrator |
|
||||
| `trainee` | Auszubildender | Trainee |
|
||||
| `temp_worker` | Leiharbeiter | Temporary Worker |
|
||||
| `visitor` | Besucher | Visitor |
|
||||
| `third_party` | Dritte/Passanten | Third Party/Bystander |
|
||||
| `transport_personnel` | Transportpersonal | Transport Personnel |
|
||||
| `commissioning_eng` | Inbetriebnahme-Ingenieur | Commissioning Engineer |
|
||||
| `quality_inspector` | Qualitaetspruefer | Quality Inspector |
|
||||
| `safety_officer` | Sicherheitsbeauftragter | Safety Officer |
|
||||
| `electrical_tech` | Elektrofachkraft | Electrical Technician |
|
||||
| `it_admin` | IT-Administrator | IT Administrator |
|
||||
| `remote_operator` | Fernbediener | Remote Operator |
|
||||
| `emergency_responder` | Ersthelfer/Rettungskraft | Emergency Responder |
|
||||
| `contractor` | Fremdfirma-Mitarbeiter | Contractor |
|
||||
| `disabled_person` | Person mit Einschraenkung | Person with Disability |
|
||||
|
||||
**API:** `GET /sdk/v1/iace/roles`
|
||||
|
||||
---
|
||||
|
||||
## Nachweistypen (50 in 7 Kategorien)
|
||||
|
||||
Fuer die Verifikation stehen **50 Nachweistypen** zur Auswahl:
|
||||
|
||||
| Kategorie | Beispiele |
|
||||
|-----------|-----------|
|
||||
| **Konstruktion** | Risikobeurteilung, Sicherheitskonzept, Schaltplan, Pneumatikplan |
|
||||
| **Berechnung** | Festigkeitsberechnung, Thermische Simulation, FMEA |
|
||||
| **Pruefung** | Funktionspruefung, Druckpruefung, EMV-Messung, Isolationspruefung |
|
||||
| **Zertifizierung** | Baumuster-Pruefbescheinigung, SIL/PL-Zertifikat, CE-Konformitaetserklaerung |
|
||||
| **Software** | Code-Review-Protokoll, Statische Analyse, Unit-Test-Report, Penetrationstest |
|
||||
| **Betrieb** | Betriebsanleitung, Wartungsplan, Schulungsnachweis, Notfallplan |
|
||||
| **Ueberwachung** | Inspektionsbericht, Kalibrierprotokoll, Audit-Report, Maengelprotokoll |
|
||||
|
||||
**API:** `GET /sdk/v1/iace/evidence-types?category=pruefung`
|
||||
|
||||
---
|
||||
|
||||
## Verifikationsmethoden (10)
|
||||
|
||||
| Methode | Beschreibung |
|
||||
|---------|--------------|
|
||||
| `design_review` | Systematische Pruefung von Konstruktionsunterlagen |
|
||||
| `calculation` | Rechnerischer Nachweis (Festigkeit, Thermik, SIL) |
|
||||
| `test_report` | Pruefbericht aus Labor- oder Feldversuchen |
|
||||
| `validation` | Nachweis der Gebrauchstauglichkeit unter realen Bedingungen |
|
||||
| `electrical_test` | Isolationsmessung, Schutzleiter, Spannungspruefung |
|
||||
| `software_test` | Unit-Test, Integrationstest, statische Analyse |
|
||||
| `penetration_test` | Sicherheitstest der IT/OT-Infrastruktur |
|
||||
| `acceptance_protocol` | Formales Abnahmeprotokoll mit Checkliste |
|
||||
| `user_test` | Anwendertest unter realistischen Einsatzbedingungen |
|
||||
| `documentation_release` | Formale Freigabe von Dokumenten und Anleitungen |
|
||||
|
||||
---
|
||||
|
||||
## API-Endpunkte (30+)
|
||||
|
||||
### Libraries (projektunabhaengig)
|
||||
### Libraries & Referenzdaten (projektunabhaengig)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| GET | `/sdk/v1/iace/hazard-library` | Alle Gefaehrdungen (~140) |
|
||||
| GET | `/sdk/v1/iace/hazard-library` | Alle Gefaehrdungen (150+) |
|
||||
| GET | `/sdk/v1/iace/controls-library` | Alle Controls (200) |
|
||||
| GET | `/sdk/v1/iace/protective-measures-library` | Schutzmassnahmen-Bibliothek (160) |
|
||||
| GET | `/sdk/v1/iace/component-library` | Komponenten-Bibliothek (C001-C120) |
|
||||
| GET | `/sdk/v1/iace/energy-sources` | Energiequellen (EN01-EN20) |
|
||||
| GET | `/sdk/v1/iace/hazard-patterns` | Gefaehrdungs-Patterns (102) |
|
||||
| GET | `/sdk/v1/iace/tags` | Tag-Taxonomie |
|
||||
| GET | `/sdk/v1/iace/lifecycle-phases` | 25 Lebensphasen (DE/EN) |
|
||||
| GET | `/sdk/v1/iace/roles` | 20 betroffene Personengruppen (DE/EN) |
|
||||
| GET | `/sdk/v1/iace/evidence-types` | 50 Nachweistypen in 7 Kategorien |
|
||||
| POST | `/sdk/v1/iace/library-search` | RAG-Bibliothekssuche |
|
||||
|
||||
### Projektmanagement
|
||||
|
||||
@@ -136,12 +316,19 @@ Die Controls-Library enthaelt **200 Eintraege** in 6 Domaenen:
|
||||
| PUT | `/sdk/v1/iace/projects/:id` | Projekt aktualisieren |
|
||||
| DELETE | `/sdk/v1/iace/projects/:id` | Projekt archivieren |
|
||||
|
||||
### Onboarding
|
||||
### Onboarding & Profil-Import
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| POST | `/sdk/v1/iace/projects/:id/init-from-profile` | Projekt aus Company-Profile initialisieren |
|
||||
| POST | `/sdk/v1/iace/projects/:id/completeness-check` | 25-Gates-Pruefung |
|
||||
| POST | `/sdk/v1/iace/projects/:id/completeness-check` | 22-Gates-Pruefung |
|
||||
|
||||
Der `init-from-profile` Endpoint uebernimmt Daten aus dem Company-Profile und Compliance-Scope:
|
||||
|
||||
- **company_profile** → Hersteller-Name, Kontaktdaten
|
||||
- **compliance_scope** → Maschinenname, Typ, Zweckbeschreibung, Software/Firmware/KI-Flags
|
||||
- Erstellt automatisch initiale Komponenten (Software, Firmware, KI-Modell, Netzwerk)
|
||||
- Triggert initiale regulatorische Klassifizierungen fuer anwendbare Verordnungen
|
||||
|
||||
### Komponenten
|
||||
|
||||
@@ -170,7 +357,8 @@ Die Controls-Library enthaelt **200 Eintraege** in 6 Domaenen:
|
||||
| POST | `/sdk/v1/iace/projects/:id/hazards/suggest` | KI-gestuetzte Vorschlaege |
|
||||
| POST | `/sdk/v1/iace/projects/:id/hazards/:hid/assess` | Risikobewertung (SEPA) |
|
||||
| POST | `/sdk/v1/iace/projects/:id/hazards/:hid/reassess` | Neubewertung nach Minderung |
|
||||
| GET | `/sdk/v1/iace/projects/:id/risk-summary` | Aggregierte Risikoübersicht |
|
||||
| GET | `/sdk/v1/iace/projects/:id/risk-summary` | Aggregierte Risikouebersicht |
|
||||
| POST | `/sdk/v1/iace/projects/:id/validate-mitigation-hierarchy` | Hierarchie-Pruefung (3-Stufen) |
|
||||
|
||||
### Minderung & Verifikation
|
||||
|
||||
@@ -188,9 +376,71 @@ Die Controls-Library enthaelt **200 Eintraege** in 6 Domaenen:
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| GET | `/sdk/v1/iace/projects/:id/tech-file` | Technische Akte abrufen |
|
||||
| POST | `/sdk/v1/iace/projects/:id/tech-file/generate` | Akte generieren |
|
||||
| GET | `/sdk/v1/iace/projects/:id/tech-file/export` | Akte exportieren (PDF/Markdown) |
|
||||
| PUT | `/sdk/v1/iace/projects/:id/tech-file/sections/:sid` | Abschnitt aktualisieren |
|
||||
| POST | `/sdk/v1/iace/projects/:id/tech-file/generate` | Alle Sektionen generieren (LLM-basiert) |
|
||||
| POST | `/sdk/v1/iace/projects/:id/tech-file/:section/generate` | Einzelne Sektion (re-)generieren (LLM) |
|
||||
| PUT | `/sdk/v1/iace/projects/:id/tech-file/:section` | Abschnitt manuell aktualisieren |
|
||||
| POST | `/sdk/v1/iace/projects/:id/tech-file/:section/approve` | Abschnitt freigeben |
|
||||
| POST | `/sdk/v1/iace/projects/:id/tech-file/:section/enrich` | Abschnitt mit RAG-Kontext anreichern |
|
||||
| GET | `/sdk/v1/iace/projects/:id/tech-file/export?format=` | Akte exportieren (pdf/xlsx/docx/md/json) |
|
||||
|
||||
#### Export-Formate
|
||||
|
||||
| Format | MIME-Type | Inhalt |
|
||||
|--------|-----------|--------|
|
||||
| `pdf` | application/pdf | Vollstaendige CE-Akte mit Deckblatt, Inhaltsverzeichnis, Risikomatrix, Gefaehrdungsprotokoll |
|
||||
| `xlsx` | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | 5 Worksheets: Uebersicht, Gefaehrdungsprotokoll, Massnahmen, Risikomatrix, Sektionen |
|
||||
| `docx` | application/vnd.openxmlformats-officedocument.wordprocessingml.document | Word-Dokument mit allen Sektionen als formatierte Absaetze |
|
||||
| `md` | text/markdown | Markdown-Dokument mit Projekt-Metadaten und allen Sektionen |
|
||||
| `json` | application/json | JSON-Export mit Projekt, Sektionen, Klassifizierungen, Risikouebersicht |
|
||||
|
||||
#### LLM-basierte Sektionsgenerierung (19 Sektionstypen)
|
||||
|
||||
Die Tech-File-Generierung nutzt LLM (Ollama/Anthropic) mit RAG-Kontext aus dem CE-Corpus:
|
||||
|
||||
| Sektion | Beschreibung |
|
||||
|---------|--------------|
|
||||
| `general_description` | Allgemeine Maschinenbeschreibung |
|
||||
| `risk_assessment_report` | Zusammenfassung der Risikobeurteilung |
|
||||
| `hazard_log_combined` | Tabellarisches Gefaehrdungsprotokoll |
|
||||
| `essential_requirements` | Grundlegende Anforderungen (MVO Anhang III) |
|
||||
| `design_specifications` | Konstruktionsdaten und Zeichnungen |
|
||||
| `test_reports` | Pruefberichte und Verifikationsergebnisse |
|
||||
| `standards_applied` | Angewandte harmonisierte Normen |
|
||||
| `declaration_of_conformity` | EU-Konformitaetserklaerung (MVO Anhang IV) |
|
||||
| `component_list` | Komponentenverzeichnis |
|
||||
| `classification_report` | Regulatorische Klassifikation |
|
||||
| `mitigation_report` | Massnahmen nach 3-Stufen-Hierarchie |
|
||||
| `verification_report` | Verifikationsplan und Ergebnisse |
|
||||
| `evidence_index` | Nachweisdokumenten-Index |
|
||||
| `instructions_for_use` | Sicherheitshinweise / Betriebsanleitung |
|
||||
| `monitoring_plan` | Post-Market Surveillance Plan |
|
||||
| `ai_intended_purpose` | KI: Bestimmungsgemaesser Zweck |
|
||||
| `ai_model_description` | KI: Modellbeschreibung und Trainingsdaten |
|
||||
| `ai_risk_management` | KI: Risikomanagementsystem |
|
||||
| `ai_human_oversight` | KI: Menschliche Aufsicht |
|
||||
|
||||
#### TipTap Rich-Text-Editor (Frontend)
|
||||
|
||||
Die Tech-File-Sektionen werden im Frontend mit einem TipTap WYSIWYG-Editor bearbeitet (`components/sdk/iace/TechFileEditor.tsx`):
|
||||
|
||||
| Feature | Beschreibung |
|
||||
|---------|--------------|
|
||||
| Toolbar | Bold, Italic, Headings (H2-H4), Bullet/Ordered Lists, Tabelle, Blockquote, Code, Undo/Redo |
|
||||
| Auto-Save | Debounced (3 Sekunden nach letzter Aenderung), ruft `PUT /tech-file/:section` auf |
|
||||
| Read-Only | Fuer freigegebene Sektionen (`status: approved`) |
|
||||
| Markdown-Import | LLM-generierter Markdown-Content wird automatisch in TipTap-Nodes konvertiert |
|
||||
| HTML-Speicherung | `editor.getHTML()` → Backend speichert HTML in `iace_tech_file_sections.content` |
|
||||
|
||||
**Pakete (alle MIT-Lizenz):**
|
||||
`@tiptap/react`, `@tiptap/starter-kit`, `@tiptap/extension-table`, `@tiptap/extension-table-row`,
|
||||
`@tiptap/extension-table-header`, `@tiptap/extension-table-cell`, `@tiptap/extension-image`, `@tiptap/pm`
|
||||
|
||||
**Workflow:**
|
||||
1. Sektion generieren → LLM liefert Markdown
|
||||
2. TipTap konvertiert Markdown → ProseMirror-Nodes
|
||||
3. Benutzer bearbeitet im WYSIWYG-Editor
|
||||
4. Auto-Save speichert HTML im Backend
|
||||
5. Export rendert HTML → PDF/DOCX/Excel/Markdown
|
||||
|
||||
### Post-Market Monitoring
|
||||
|
||||
@@ -207,40 +457,59 @@ Die Controls-Library enthaelt **200 Eintraege** in 6 Domaenen:
|
||||
|
||||
---
|
||||
|
||||
## Completeness Gates (25)
|
||||
## Completeness Gates (22)
|
||||
|
||||
Das Modul prueft 25 Vollstaendigkeitstore vor dem CE-Export:
|
||||
Das Modul prueft 22 Vollstaendigkeitstore (20 Required, 2 Recommended) vor dem CE-Export:
|
||||
|
||||
| Gate | Kategorie | Pflicht |
|
||||
|------|-----------|---------|
|
||||
| G01 | Projekt-Grunddaten vollstaendig | ✅ Required |
|
||||
| G02 | CE-Markierungsziel definiert | ✅ Required |
|
||||
| G03 | Mind. 1 Komponente erfasst | ✅ Required |
|
||||
| G04 | Regulatorische Klassifizierung abgeschlossen | ✅ Required |
|
||||
| G05 | HARA-Dokument vorhanden (Evidence) | ✅ Required |
|
||||
| G06 | Mind. 1 Gefaehrdung identifiziert | ✅ Required |
|
||||
| G07 | Alle Gefaehrdungen bewertet | ✅ Required |
|
||||
| G08 | Kein Restrisiko > critical ohne Akzeptanz | ✅ Required |
|
||||
| G09 | Mind. 1 Minderungsmassnahme je Gefaehrdung | ✅ Required |
|
||||
| G10 | Minderungsmassnahmen verifiziert | ✅ Required |
|
||||
| G11 | Verifikationsplan vorhanden | ✅ Required |
|
||||
| G12 | SIL/PL-Dokumentation (Evidence) | ✅ Required |
|
||||
| G13 | Technische Akte generiert | ✅ Required |
|
||||
| G14 | Konformitaetserklaerung bereit | ✅ Required |
|
||||
| G15 | Betriebsanleitung vorhanden | ✅ Required |
|
||||
| G16 | Wartungsanleitung vorhanden | Recommended |
|
||||
| G17 | Post-Market Monitoring aktiv | Recommended |
|
||||
| G18 | Cybersecurity-Massnahmen dokumentiert | Recommended |
|
||||
| G19 | AI-spezifische Anforderungen erfuellt | Recommended (bei AI) |
|
||||
| G20 | Kalibrierprotokolle vorhanden | Recommended |
|
||||
| G21 | SBOM generiert | Optional |
|
||||
| G22 | Penetrationstest durchgefuehrt | Optional |
|
||||
| G23 | EMV-Pruefung dokumentiert | Optional |
|
||||
| G24 | Lebenszyklusplan vorhanden | Optional |
|
||||
| G25 | Monitoring-Ereignisse protokolliert | Optional |
|
||||
### Onboarding (G01-G09)
|
||||
|
||||
| Gate | Label | Pflicht |
|
||||
|------|-------|---------|
|
||||
| G01 | Machine identity set | ✅ Required |
|
||||
| G02 | Intended use described | ✅ Required |
|
||||
| G03 | Operating limits defined | ✅ Required |
|
||||
| G04 | Foreseeable misuse documented | ✅ Required |
|
||||
| G05 | Component tree exists | ✅ Required |
|
||||
| G06 | AI classification done (if applicable) | ✅ Required |
|
||||
| G07 | Safety relevance marked | ✅ Required |
|
||||
| G08 | Manufacturer info present | ✅ Required |
|
||||
| G09 | Pattern matching performed | Recommended |
|
||||
|
||||
### Klassifizierung (G10-G13)
|
||||
|
||||
| Gate | Label | Pflicht |
|
||||
|------|-------|---------|
|
||||
| G10 | AI Act classification complete | ✅ Required |
|
||||
| G11 | Machinery Regulation check done | ✅ Required |
|
||||
| G12 | NIS2 check done | ✅ Required |
|
||||
| G13 | CRA check done | ✅ Required |
|
||||
|
||||
### Gefaehrdungen & Risiko (G20-G24)
|
||||
|
||||
| Gate | Label | Pflicht |
|
||||
|------|-------|---------|
|
||||
| G20 | Hazards identified | ✅ Required |
|
||||
| G21 | All hazards assessed | ✅ Required |
|
||||
| G22 | Critical/High risks mitigated | ✅ Required |
|
||||
| G23 | **Mitigations verified** | ✅ Required |
|
||||
| G24 | Residual risk accepted | ✅ Required |
|
||||
|
||||
!!! warning "G23 — Strenge Verifikationspflicht"
|
||||
Alle Mitigations muessen den Status `verified` oder `rejected` haben. Mitigations im Status `planned` oder `implemented` blockieren den Export. Dies stellt sicher, dass keine Massnahme unueberprueft bleibt.
|
||||
|
||||
### Evidence & Tech File (G30, G40-G42)
|
||||
|
||||
| Gate | Label | Pflicht |
|
||||
|------|-------|---------|
|
||||
| G30 | Test evidence linked | Recommended |
|
||||
| G40 | Risk assessment report generated | ✅ Required |
|
||||
| G41 | Hazard log generated | ✅ Required |
|
||||
| G42 | AI documents present (if applicable) | ✅ Required |
|
||||
|
||||
**Completeness Score:** `(passed_required/total_required)*80 + (passed_recommended/total_recommended)*15 + (passed_optional/total_optional)*5`
|
||||
|
||||
**CanExport** ist nur `true`, wenn alle Required-Gates bestanden sind.
|
||||
|
||||
---
|
||||
|
||||
## CE RAG-Corpus
|
||||
@@ -290,19 +559,30 @@ curl -sk https://macmini:8093/sdk/v1/iace/controls-library | python3 -c "import
|
||||
|
||||
---
|
||||
|
||||
## Beispiel: Risikobewertung mit Avoidance
|
||||
## Beispiel: Risikobewertung
|
||||
|
||||
### Legacy-Modus (A=0)
|
||||
|
||||
```bash
|
||||
curl -sk -X POST https://macmini:8093/sdk/v1/iace/projects/{id}/hazards/{hid}/assess \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Tenant-Id: <tenant-uuid>" \
|
||||
-d '{"severity": 5, "exposure": 3, "probability": 3, "avoidance": 0}'
|
||||
```
|
||||
|
||||
Ergebnis: `R = 5 × 3 × 3 = 45` → **high**
|
||||
|
||||
### ISO-Modus (A >= 1)
|
||||
|
||||
```bash
|
||||
# Risikobewertung mit Avoidance-Faktor (A=5: nicht vermeidbar)
|
||||
curl -sk -X POST https://macmini:8093/sdk/v1/iace/projects/{id}/hazards/{hid}/assess \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Tenant-Id: <tenant-uuid>" \
|
||||
-d '{
|
||||
"hazard_id": "<hid>",
|
||||
"severity": 5,
|
||||
"exposure": 3,
|
||||
"probability": 3,
|
||||
"avoidance": 5,
|
||||
"avoidance": 4,
|
||||
"control_maturity": 2,
|
||||
"control_coverage": 0.6,
|
||||
"test_evidence_strength": 0.5,
|
||||
@@ -310,9 +590,7 @@ curl -sk -X POST https://macmini:8093/sdk/v1/iace/projects/{id}/hazards/{hid}/as
|
||||
}'
|
||||
```
|
||||
|
||||
Ohne Avoidance (A=0): `InherentRisk = 5×3×3 = 45`
|
||||
Mit Avoidance A=5: `InherentRisk = 5×3×3×(5/3) = 75` (kritisch!)
|
||||
Mit Avoidance A=1: `InherentRisk = 5×3×3×(1/3) = 15` (medium)
|
||||
Ergebnis: `R = 5 × 3 × 3 × 4 = 180` → **very_high** (ISO-Schwellwerte)
|
||||
|
||||
---
|
||||
|
||||
@@ -330,6 +608,112 @@ curl -sk "https://macmini:8093/sdk/v1/iace/controls-library?category=software_fa
|
||||
|
||||
---
|
||||
|
||||
## Hazard-Matching-Engine
|
||||
|
||||
Die Pattern Engine automatisiert die Ableitung von Gefaehrdungen, Schutzmassnahmen und Nachweisen aus der Maschinenkonfiguration.
|
||||
|
||||
### Komponentenbibliothek (120 Eintraege)
|
||||
|
||||
```bash
|
||||
# Alle Komponenten abrufen
|
||||
curl -sk "https://macmini:8093/sdk/v1/iace/component-library" | python3 -c \
|
||||
"import sys,json; d=json.load(sys.stdin); print(f'{d[\"total\"]} Komponenten in {len(set(c[\"category\"] for c in d[\"components\"]))} Kategorien')"
|
||||
|
||||
# Nach Kategorie filtern
|
||||
curl -sk "https://macmini:8093/sdk/v1/iace/component-library?category=mechanical"
|
||||
```
|
||||
|
||||
| Kategorie | IDs | Anzahl | Beispiele |
|
||||
|-----------|-----|--------|-----------|
|
||||
| mechanical | C001-C020 | 20 | Roboterarm, Greifer, Foerderband |
|
||||
| structural | C021-C030 | 10 | Maschinenrahmen, Schutzgehaeuse |
|
||||
| drive | C031-C040 | 10 | Elektromotor, Servomotor |
|
||||
| hydraulic | C041-C050 | 10 | Hydraulikpumpe, -zylinder |
|
||||
| pneumatic | C051-C060 | 10 | Pneumatikzylinder, Kompressor |
|
||||
| electrical | C061-C070 | 10 | Schaltschrank, Stromversorgung |
|
||||
| control | C071-C080 | 10 | SPS, Sicherheits-SPS, HMI |
|
||||
| sensor | C081-C090 | 10 | Positionssensor, Kamerasystem |
|
||||
| actuator | C091-C100 | 10 | Magnetventil, Linearantrieb |
|
||||
| safety | C101-C110 | 10 | Not-Halt, Lichtgitter |
|
||||
| it_network | C111-C120 | 10 | Switch, Router, Firewall |
|
||||
|
||||
### Energiequellen (20 Eintraege)
|
||||
|
||||
```bash
|
||||
curl -sk "https://macmini:8093/sdk/v1/iace/energy-sources"
|
||||
```
|
||||
|
||||
### Tag-Taxonomie (~85 Tags)
|
||||
|
||||
| Domaene | Anzahl | Beispiele |
|
||||
|---------|--------|-----------|
|
||||
| component | ~30 | moving_part, rotating_part, high_voltage, networked, has_ai |
|
||||
| energy | ~15 | kinetic, rotational, electrical_energy, hydraulic_pressure |
|
||||
| hazard | ~20 | crush_risk, shear_risk, electric_shock_risk, cyber_risk |
|
||||
| measure | ~10 | guard_measure, interlock_measure, software_safety_measure |
|
||||
| evidence | ~10 | design_evidence, test_evidence, cyber_evidence |
|
||||
|
||||
```bash
|
||||
# Alle Tags einer Domaene
|
||||
curl -sk "https://macmini:8093/sdk/v1/iace/tags?domain=component"
|
||||
```
|
||||
|
||||
### Hazard Patterns (102 Regeln: 44 builtin + 58 extended)
|
||||
|
||||
Jedes Pattern definiert required_component_tags (AND), required_energy_tags (AND) und excluded_component_tags (NOT). Die Engine prueft alle Patterns gegen die aufgeloesten Tags der Projektkomponenten.
|
||||
|
||||
**Builtin (HP001-HP044):** Abstrakte Tag-basierte Patterns fuer 9 Domaenen (mechanisch, elektrisch, thermisch, hydraulik/pneumatik, laerm, ergonomie, software, cyber, KI).
|
||||
|
||||
**Extended (HP045-HP102):** 58 zusaetzliche Patterns aus der Rule Library (R051-R1550). Diese ergaenzen die Builtin-Patterns um komponentenspezifische Regeln mit Lebensphase-Einschraenkung.
|
||||
|
||||
```bash
|
||||
# Patterns auflisten
|
||||
curl -sk "https://macmini:8093/sdk/v1/iace/hazard-patterns" | python3 -c \
|
||||
"import sys,json; d=json.load(sys.stdin); print(f'{d[\"total\"]} Patterns')"
|
||||
```
|
||||
|
||||
### Pattern-Matching Workflow
|
||||
|
||||
```bash
|
||||
# 1. Pattern-Matching ausfuehren
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/projects/{id}/match-patterns" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"component_library_ids": ["C001","C071"], "energy_source_ids": ["EN01","EN07"]}'
|
||||
|
||||
# 2. Ergebnisse uebernehmen
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/projects/{id}/apply-patterns" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"accepted_hazards": [...], "accepted_measures": [...], "accepted_evidence": [...]}'
|
||||
|
||||
# 3. Pro-Hazard Massnahmen vorschlagen
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/projects/{id}/hazards/{hid}/suggest-measures"
|
||||
|
||||
# 4. Pro-Massnahme Nachweise vorschlagen
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/projects/{id}/mitigations/{mid}/suggest-evidence"
|
||||
```
|
||||
|
||||
### RAG-Anreicherung (Phase 6)
|
||||
|
||||
IACE-Bibliotheken (Hazards, Komponenten, Energiequellen, Massnahmen, Nachweise) sind als RAG-Corpus in Qdrant verfuegbar (`bp_iace_libraries`).
|
||||
|
||||
```bash
|
||||
# Semantische Suche in der IACE-Bibliothek
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/library-search" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"query": "Quetschgefahr Roboterarm", "top_k": 5}'
|
||||
|
||||
# Tech-File-Abschnitt mit RAG-Kontext anreichern
|
||||
curl -sk -X POST "https://macmini:8093/sdk/v1/iace/projects/{id}/tech-file/risk_assessment_report/enrich"
|
||||
```
|
||||
|
||||
**Ingestion:**
|
||||
```bash
|
||||
# IACE-Bibliotheken in Qdrant ingestieren (auf Mac Mini)
|
||||
bash ~/Projekte/breakpilot-compliance/scripts/ingest-iace-libraries.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenbank-Tabellen
|
||||
|
||||
| Tabelle | Beschreibung |
|
||||
@@ -338,11 +722,38 @@ curl -sk "https://macmini:8093/sdk/v1/iace/controls-library?category=software_fa
|
||||
| `iace_components` | System-Komponenten |
|
||||
| `iace_regulatory_classifications` | Regulierungsklassifizierungen |
|
||||
| `iace_hazard_library` | Benutzerdefinierte Hazard-Templates |
|
||||
| `iace_hazards` | Projektspezifische Gefaehrdungen |
|
||||
| `iace_risk_assessments` | SEPA-Risikobewertungen (inkl. avoidance) |
|
||||
| `iace_hazards` | Projektspezifische Gefaehrdungen (inkl. lifecycle_phase, affected_person, trigger_event) |
|
||||
| `iace_risk_assessments` | Risikobewertungen (Legacy S×E×P + ISO S×F×P×A) |
|
||||
| `iace_mitigations` | Minderungsmassnahmen |
|
||||
| `iace_verification_plans` | Verifikationsplaene |
|
||||
| `iace_evidence` | Nachweise (Uploads) |
|
||||
| `iace_tech_file_sections` | CE-Akte-Abschnitte |
|
||||
| `iace_monitoring_events` | Post-Market-Ereignisse |
|
||||
| `iace_audit_trail` | Unveraenderbares Audit-Log |
|
||||
| `iace_lifecycle_phases` | 25 Lebensphasen (DE/EN) |
|
||||
| `iace_roles` | 20 betroffene Personengruppen (DE/EN) |
|
||||
| `iace_evidence_types` | 50 Nachweistypen in 7 Kategorien |
|
||||
| `iace_component_library` | 120 Maschinenkomponenten (C001-C120) |
|
||||
| `iace_energy_sources` | 20 Energiequellen (EN01-EN20) |
|
||||
| `iace_pattern_results` | Audit-Trail fuer Pattern-Matching |
|
||||
|
||||
---
|
||||
|
||||
## Rechtlicher Hinweis — Normenreferenz
|
||||
|
||||
!!! warning "Urheberrecht bei Normen"
|
||||
Saemtliche Inhalte des IACE-Moduls (Gefaehrdungsbibliothek, Schutzmassnahmen-Katalog,
|
||||
Risikomodell, Lebensphasen, Rollen) sind **eigenstaendig formuliert**. Normen wie
|
||||
ISO 12100, DIN EN 62443, IEC 61508 etc. werden ausschliesslich als **Methodenreferenz**
|
||||
in den Metadaten gefuehrt (z.B. `"methodology_reference": "ISO 12100"`).
|
||||
|
||||
Es wird **kein normativer Text** reproduziert — keine Tabellen, Risikoklassifikationen,
|
||||
Prozesstexte oder Checklisten aus Normen. Dies ist konform mit §51 UrhG (Zitatrecht).
|
||||
|
||||
Fuer die Anwendung der vollstaendigen Normen muss die jeweilige Norm separat
|
||||
ueber DIN Media (beuth.de) oder ISO erworben werden.
|
||||
|
||||
**Beispiel fuer konforme Normenreferenz in generierten Dokumenten:**
|
||||
|
||||
> Die Risikobeurteilung wurde unter Beruecksichtigung allgemein anerkannter Methoden
|
||||
> der Maschinensicherheit durchgefuehrt (u.a. angelehnt an ISO 12100).
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
# Loeschfristen — Loeschkonzept (Art. 5/17/30 DSGVO)
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Das Loeschfristen-Modul implementiert ein vollstaendiges, auditfaehiges Loeschkonzept gemaess DSGVO Art. 5 Abs. 1 lit. e (Speicherbegrenzung), Art. 17 (Recht auf Loeschung) und Art. 30 (Dokumentation der Loeschfristen im VVT).
|
||||
|
||||
**Kernfunktionen:**
|
||||
|
||||
- 3-Level-Loeschlogik (Zweckende → Aufbewahrungspflicht → Legal Hold)
|
||||
- 25 vordefinierte Baseline-Templates fuer gaengige Datenobjekte
|
||||
- 4-Schritt-Profiling-Wizard zur automatischen Policy-Generierung
|
||||
- 11 automatisierte Compliance-Checks
|
||||
- Druckfertiges Loeschkonzept-Dokument mit 11 Sektionen
|
||||
- JSON/CSV/Markdown-Export
|
||||
|
||||
## 3-Level Loeschlogik
|
||||
|
||||
Die Loeschung personenbezogener Daten folgt einer dreistufigen Priorisierung:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Daten erhoben] --> B{Zweck erfuellt?}
|
||||
B -->|Ja| C{Aufbewahrungspflicht?}
|
||||
B -->|Nein| D[Weiter speichern]
|
||||
C -->|Ja| E{Frist abgelaufen?}
|
||||
C -->|Nein| F{Legal Hold?}
|
||||
E -->|Ja| F
|
||||
E -->|Nein| G[Aufbewahren bis Fristende]
|
||||
F -->|Ja| H[Speichern bis Legal Hold endet]
|
||||
F -->|Nein| I[Loeschung durchfuehren]
|
||||
```
|
||||
|
||||
| Level | Trigger | Beschreibung |
|
||||
|-------|---------|--------------|
|
||||
| 1 | **Zweckende** | Daten werden geloescht, wenn der Verarbeitungszweck entfaellt |
|
||||
| 2 | **Aufbewahrungspflicht** | Gesetzliche Aufbewahrungsfristen verlaengern die Speicherung |
|
||||
| 3 | **Legal Hold** | Aktive Legal Holds setzen die Loeschung aus |
|
||||
|
||||
## Frontend — 5-Tab-Aufbau
|
||||
|
||||
### Tab 1: Uebersicht
|
||||
|
||||
Statistik-Dashboard mit Filterung und Suche:
|
||||
|
||||
- Gesamtanzahl Policies, Status-Verteilung, ueberfaellige Pruefungen
|
||||
- Suche nach Datenobjekt, Tags und Status
|
||||
- Schnellaktionen: Bearbeiten, Klonen, Archivieren
|
||||
|
||||
### Tab 2: Editor
|
||||
|
||||
Vollstaendiges Bearbeitungsformular mit 35+ Feldern:
|
||||
|
||||
- Stammdaten (Datenobjekt, Beschreibung, Betroffenengruppen, Datenkategorien)
|
||||
- 3-Level-Loeschlogik (Trigger, Aufbewahrungstreiber, Frist, Startereignis)
|
||||
- Loeschmethode und -details
|
||||
- Speicherorte (Typ, Provider, Backup-Kennzeichnung)
|
||||
- Legal Hold Management (Hinzufuegen, Aktivieren, Aufheben)
|
||||
- Verantwortlichkeiten und VVT-Verknuepfung
|
||||
- Status-Workflow (Entwurf → Aktiv → Pruefung erforderlich → Archiviert)
|
||||
|
||||
### Tab 3: Generator
|
||||
|
||||
4-Schritt-Profiling-Wizard mit 16 Fragen:
|
||||
|
||||
1. **Organisation** (4 Fragen): Branche, Mitarbeiterzahl, Geschaeftsmodell, Website
|
||||
2. **Datenkategorien** (5 Fragen): HR, Buchhaltung, Vertraege, Marketing, Video
|
||||
3. **Systeme** (4 Fragen): Cloud, Backup, ERP/CRM, Zutrittskontrolle
|
||||
4. **Spezielle Anforderungen** (3 Fragen): Legal Hold, Langzeitarchivierung, Gesundheitsdaten
|
||||
|
||||
Der Wizard generiert automatisch passende Policies aus dem Baseline-Katalog.
|
||||
|
||||
### Tab 4: Export & Compliance
|
||||
|
||||
- **JSON-Export**: Vollstaendiger Policy-Export als JSON
|
||||
- **CSV-Export**: Excel-kompatibel mit BOM und Semikolon-Trennung
|
||||
- **Compliance-Bericht**: Markdown-formatierter Bericht mit Score und Empfehlungen
|
||||
- **11 Compliance-Checks**: Automatisierte Pruefung aller Policies
|
||||
|
||||
### Tab 5: Loeschkonzept-Dokument
|
||||
|
||||
Druckfertiges Loeschkonzept mit 11 Sektionen:
|
||||
|
||||
| # | Sektion | Inhalt |
|
||||
|---|---------|--------|
|
||||
| 0 | Deckblatt | Organisation, DSB, Version, Datum |
|
||||
| — | Inhaltsverzeichnis | Auto-generiert |
|
||||
| 1 | Ziel und Zweck | DSGVO Art. 5/17/30 Bezug |
|
||||
| 2 | Geltungsbereich | Systeme, Speicherorte |
|
||||
| 3 | Grundprinzipien | 5 Kernprinzipien |
|
||||
| 4 | Loeschregeln-Uebersicht | Tabelle aller Policies |
|
||||
| 5 | Detaillierte Loeschregeln | Pro Policy: Alle Felder |
|
||||
| 6 | VVT-Verknuepfung | Cross-Referenz-Tabelle |
|
||||
| 7 | Legal Hold Verfahren | Prozedur + aktive Holds |
|
||||
| 8 | Verantwortlichkeiten | Rollenmatrix |
|
||||
| 9 | Pruef-/Revisionszyklus | Review-Zeitplan |
|
||||
| 10 | Compliance-Status | Score, Issues |
|
||||
| 11 | Aenderungshistorie | Versionstabelle |
|
||||
|
||||
**Ausgabe:** HTML-Download oder PDF-Druck via Browser.
|
||||
|
||||
## Backend API (7 Endpoints)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/api/v1/compliance/loeschfristen` | Alle Policies abrufen (mit Pagination) |
|
||||
| `POST` | `/api/v1/compliance/loeschfristen` | Neue Policy erstellen |
|
||||
| `GET` | `/api/v1/compliance/loeschfristen/{id}` | Einzelne Policy abrufen |
|
||||
| `PUT` | `/api/v1/compliance/loeschfristen/{id}` | Policy aktualisieren |
|
||||
| `DELETE` | `/api/v1/compliance/loeschfristen/{id}` | Policy loeschen |
|
||||
| `GET` | `/api/v1/compliance/loeschfristen/stats` | Statistik-Uebersicht |
|
||||
| `PATCH` | `/api/v1/compliance/loeschfristen/{id}/status` | Status aendern |
|
||||
|
||||
## Datenbank-Schema
|
||||
|
||||
Tabelle: `compliance_loeschfristen`
|
||||
|
||||
| Spalte | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `id` | UUID (PK) | Datenbank-ID |
|
||||
| `tenant_id` | UUID | Mandant |
|
||||
| `project_id` | UUID | Projekt |
|
||||
| `policy_id` | VARCHAR | Display-ID (LF-2026-001) |
|
||||
| `data_object_name` | VARCHAR | Datenobjekt |
|
||||
| `description` | TEXT | Beschreibung |
|
||||
| `affected_groups` | JSONB | Betroffenengruppen |
|
||||
| `data_categories` | JSONB | Datenkategorien |
|
||||
| `primary_purpose` | TEXT | Verarbeitungszweck |
|
||||
| `deletion_trigger` | VARCHAR | Loeschtrigger |
|
||||
| `retention_driver` | VARCHAR | Aufbewahrungstreiber |
|
||||
| `retention_driver_detail` | TEXT | Detail zum Treiber |
|
||||
| `retention_duration` | INTEGER | Aufbewahrungsdauer |
|
||||
| `retention_unit` | VARCHAR | Einheit (DAYS/MONTHS/YEARS) |
|
||||
| `retention_description` | TEXT | Beschreibung der Frist |
|
||||
| `start_event` | VARCHAR | Startereignis |
|
||||
| `has_active_legal_hold` | BOOLEAN | Legal Hold aktiv? |
|
||||
| `legal_holds` | JSONB | Legal Holds (Array) |
|
||||
| `storage_locations` | JSONB | Speicherorte (Array) |
|
||||
| `deletion_method` | VARCHAR | Loeschmethode |
|
||||
| `deletion_method_detail` | TEXT | Detail zur Methode |
|
||||
| `responsible_role` | VARCHAR | Verantwortliche Rolle |
|
||||
| `responsible_person` | VARCHAR | Verantwortliche Person |
|
||||
| `release_process` | TEXT | Freigabeprozess |
|
||||
| `linked_vvt_activity_ids` | JSONB | VVT-Verknuepfungen |
|
||||
| `status` | VARCHAR | Status |
|
||||
| `last_review_date` | TIMESTAMP | Letzte Pruefung |
|
||||
| `next_review_date` | TIMESTAMP | Naechste Pruefung |
|
||||
| `review_interval` | VARCHAR | Pruefintervall |
|
||||
| `tags` | JSONB | Tags |
|
||||
| `created_at` | TIMESTAMP | Erstellt am |
|
||||
| `updated_at` | TIMESTAMP | Geaendert am |
|
||||
|
||||
Migration: `backend-compliance/migrations/017_loeschfristen.sql`
|
||||
|
||||
## Baseline-Katalog (25 Templates)
|
||||
|
||||
| # | Template-ID | Datenobjekt | Treiber | Frist | Trigger |
|
||||
|---|-------------|-------------|---------|-------|---------|
|
||||
| 1 | `personal-akten` | Personalakten | AO 147 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 2 | `buchhaltungsbelege` | Buchhaltungsbelege | HGB 257 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 3 | `rechnungen` | Rechnungen | UStG 14b | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 4 | `geschaeftsbriefe` | Geschaeftsbriefe | HGB 257 | 6 Jahre | Aufbewahrungspflicht |
|
||||
| 5 | `bewerbungsunterlagen` | Bewerbungsunterlagen | AGG 15 | 6 Monate | Aufbewahrungspflicht |
|
||||
| 6 | `kundenstammdaten` | Kundenstammdaten | BGB 195 | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 7 | `newsletter-einwilligungen` | Newsletter-Einwilligungen | — | Bis Widerruf | Zweckende |
|
||||
| 8 | `webserver-logs` | Webserver-Logs | BSIG | 7 Tage | Aufbewahrungspflicht |
|
||||
| 9 | `videoueberwachung` | Videoueberwachung | BDSG 35 | 2 Tage | Aufbewahrungspflicht |
|
||||
| 10 | `gehaltsabrechnungen` | Gehaltsabrechnungen | AO 147 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 11 | `vertraege` | Vertraege | HGB 257 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 12 | `zeiterfassung` | Zeiterfassungsdaten | ArbZG 16 | 2 Jahre | Aufbewahrungspflicht |
|
||||
| 13 | `krankmeldungen` | Krankmeldungen | BGB 195 | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 14 | `steuererklaerungen` | Steuererklaerungen | AO 147 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 15 | `protokolle-gesellschafter` | Gesellschafterprotokolle | HGB 257 | 10 Jahre | Aufbewahrungspflicht |
|
||||
| 16 | `crm-kontakthistorie` | CRM-Kontakthistorie | BGB 195 | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 17 | `backup-daten` | Backup-Daten | BSIG | 90 Tage | Aufbewahrungspflicht |
|
||||
| 18 | `cookie-consent-logs` | Cookie-Consent-Nachweise | BGB 195 | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 19 | `email-archivierung` | E-Mail-Archivierung | HGB 257 | 6 Jahre | Aufbewahrungspflicht |
|
||||
| 20 | `zutrittsprotokolle` | Zutrittsprotokolle | BSIG | 90 Tage | Aufbewahrungspflicht |
|
||||
| 21 | `schulungsnachweise` | Schulungsnachweise | Individuell | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 22 | `betriebsarzt-doku` | Betriebsarzt-Dokumentation | Individuell | 40 Jahre | Aufbewahrungspflicht |
|
||||
| 23 | `kundenreklamationen` | Kundenreklamationen | BGB 195 | 3 Jahre | Aufbewahrungspflicht |
|
||||
| 24 | `lieferantenbewertungen` | Lieferantenbewertungen | HGB 257 | 6 Jahre | Aufbewahrungspflicht |
|
||||
| 25 | `social-media-daten` | Social-Media-Marketingdaten | — | Bis Zweckende | Zweckende |
|
||||
|
||||
## 9 Aufbewahrungstreiber
|
||||
|
||||
| Treiber | Gesetz | Standard-Frist | Beschreibung |
|
||||
|---------|--------|----------------|--------------|
|
||||
| `AO_147` | 147 AO | 10 Jahre | Steuerrelevante Unterlagen |
|
||||
| `HGB_257` | 257 HGB | 10/6 Jahre | Handelsbuecher, -briefe |
|
||||
| `USTG_14B` | 14b UStG | 10 Jahre | Rechnungen |
|
||||
| `BGB_195` | 195 BGB | 3 Jahre | Regelmaessige Verjaehrung |
|
||||
| `ARBZG_16` | 16 Abs. 2 ArbZG | 2 Jahre | Arbeitszeitaufzeichnungen |
|
||||
| `AGG_15` | 15 Abs. 4 AGG | 6 Monate | Entschaedigungsansprueche |
|
||||
| `BDSG_35` | 35 BDSG / Art. 17 DSGVO | Unverzueglich | Zweckwegfall |
|
||||
| `BSIG` | BSIG / IT-SiG 2.0 | 90 Tage | Sicherheitslogs |
|
||||
| `CUSTOM` | Individuell | — | Benutzerdefiniert |
|
||||
|
||||
## 6 Loeschmethoden
|
||||
|
||||
| Methode | Beschreibung |
|
||||
|---------|--------------|
|
||||
| `AUTO_DELETE` | Automatische Loeschung durch System-Job |
|
||||
| `MANUAL_REVIEW_DELETE` | Manuelle Pruefung vor Loeschung |
|
||||
| `ANONYMIZATION` | Anonymisierung (Statistik bleibt erhalten) |
|
||||
| `AGGREGATION` | Statistische Verdichtung |
|
||||
| `CRYPTO_ERASE` | Kryptographische Loeschung (Key Destruction) |
|
||||
| `PHYSICAL_DESTROY` | Physische Vernichtung (DIN 66399) |
|
||||
|
||||
## Profiling Wizard (4 Schritte, 16 Fragen)
|
||||
|
||||
Der Profiling-Wizard generiert automatisch passende Loeschrichtlinien basierend auf dem Unternehmensprofil.
|
||||
|
||||
| Schritt | Titel | Fragen | Inhalt |
|
||||
|---------|-------|--------|--------|
|
||||
| 1 | Organisation | 4 | Branche, Groesse, Geschaeftsmodell, Website |
|
||||
| 2 | Datenkategorien | 5 | HR, Buchhaltung, Vertraege, Marketing, Video |
|
||||
| 3 | Systeme & Infrastruktur | 4 | Cloud, Backup, ERP/CRM, Zutrittskontrolle |
|
||||
| 4 | Spezielle Anforderungen | 3 | Legal Hold, Archivierung, Gesundheitsdaten |
|
||||
|
||||
**Regelbeispiele:**
|
||||
|
||||
- `data-hr = true` → Personalakten, Gehaltsabrechnungen, Zeiterfassung, Bewerbungen, Krankmeldungen, Schulungsnachweise
|
||||
- `data-buchhaltung = true` → Buchhaltungsbelege, Rechnungen, Steuererklaerungen
|
||||
- `data-vertraege = true` → Vertraege, Geschaeftsbriefe, Kundenstammdaten, Kundenreklamationen, Lieferantenbewertungen
|
||||
- `data-marketing = true` → Newsletter, CRM-Kontakthistorie, Cookie-Consent, Social-Media-Daten
|
||||
- `sys-zutritt = true` → Zutrittsprotokolle
|
||||
- `sys-cloud = true` → E-Mail-Archivierung
|
||||
- `special-gesundheit = true` → Krankmeldungen, Betriebsarzt-Dokumentation
|
||||
|
||||
## Compliance Checker (11 Pruefungen)
|
||||
|
||||
| # | Check-Typ | Schweregrad | Ausloeser |
|
||||
|---|-----------|-------------|-----------|
|
||||
| 1 | `MISSING_TRIGGER` | HIGH | Policy ohne Loeschtrigger |
|
||||
| 2 | `MISSING_LEGAL_BASIS` | HIGH | Aufbewahrungspflicht ohne Rechtsgrundlage |
|
||||
| 3 | `OVERDUE_REVIEW` | MEDIUM | Ueberfaellige Pruefung |
|
||||
| 4 | `NO_RESPONSIBLE` | MEDIUM | Keine Verantwortliche Person/Rolle |
|
||||
| 5 | `LEGAL_HOLD_CONFLICT` | CRITICAL | Legal Hold + Auto-Delete aktiv |
|
||||
| 6 | `STALE_DRAFT` | LOW | Entwurf aelter als 90 Tage |
|
||||
| 7 | `UNCOVERED_VVT_CATEGORY` | MEDIUM | VVT-Datenkategorie ohne Loeschfrist |
|
||||
| 8 | `MISSING_DELETION_METHOD` | MEDIUM | Aktive Policy ohne Loeschmethoden-Detail |
|
||||
| 9 | `MISSING_STORAGE_LOCATIONS` | MEDIUM | Aktive Policy ohne Speicherorte |
|
||||
| 10 | `EXCESSIVE_RETENTION` | HIGH | Frist > 2x gesetzliches Maximum |
|
||||
| 11 | `MISSING_DATA_CATEGORIES` | LOW | Nicht-Entwurf ohne Datenkategorien |
|
||||
|
||||
**Score-Berechnung:** `100 - (CRITICAL*15 + HIGH*10 + MEDIUM*5 + LOW*2)`
|
||||
|
||||
## Cross-Modul-Integration
|
||||
|
||||
### VVT-Verknuepfung
|
||||
|
||||
Jede Loeschregel kann mit Verarbeitungstaetigkeiten aus dem VVT verknuepft werden (`linked_vvt_activity_ids`). Das Loeschkonzept-Dokument generiert automatisch eine Cross-Referenz-Tabelle (Sektion 6).
|
||||
|
||||
### Scope Engine Prefill
|
||||
|
||||
Der Profiling-Wizard kann Antworten aus der Compliance Scope Engine uebernehmen. 12 Fragen werden automatisch vorausgefuellt:
|
||||
|
||||
- `org-branche`, `org-mitarbeiter`, `org-geschaeftsmodell`, `org-website`
|
||||
- `data-hr`, `data-buchhaltung`, `data-vertraege`, `data-marketing`, `data-video`
|
||||
- `sys-cloud`, `sys-erp`
|
||||
|
||||
### Document Generator Template
|
||||
|
||||
Das Loeschkonzept kann als eigenstaendiges Template im Document Generator genutzt werden (Template-Typ: `loeschkonzept`).
|
||||
|
||||
## Audit-Faehigkeit
|
||||
|
||||
Das Loeschkonzept erfuellt folgende Audit-Kriterien:
|
||||
|
||||
1. **Vollstaendigkeit:** Alle Datenobjekte mit Loeschfristen, Rechtsgrundlagen und Methoden dokumentiert
|
||||
2. **Nachvollziehbarkeit:** Aenderungshistorie mit Versionsnummern und Autoren
|
||||
3. **Aktualitaet:** Definiertes Pruefintervall mit automatischer Ueberfaelligkeits-Erkennung
|
||||
4. **Verantwortlichkeit:** Rollenmatrix mit klaren Zustaendigkeiten
|
||||
5. **Cross-Referenz:** Verknuepfung mit VVT (Art. 30 DSGVO)
|
||||
6. **Compliance-Nachweis:** Automatisierte 11-Punkt-Pruefung mit Score und Befundprotokoll
|
||||
7. **Druckfertigkeit:** PDF-taugliches Dokument mit Deckblatt, Inhaltsverzeichnis und rechtlichen Verweisen
|
||||
|
||||
## Datei-Uebersicht
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|--------------|
|
||||
| `admin-compliance/app/sdk/loeschfristen/page.tsx` | Frontend-Seite (5 Tabs) |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-types.ts` | TypeScript-Typen und Konstanten |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-baseline-catalog.ts` | 25 Baseline-Templates |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-profiling.ts` | 4-Schritt-Profiling-Wizard |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-compliance.ts` | 11 Compliance-Checks |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-export.ts` | JSON/CSV/Markdown-Export |
|
||||
| `admin-compliance/lib/sdk/loeschfristen-document.ts` | Loeschkonzept-Dokument-Generator |
|
||||
| `backend-compliance/compliance/api/loeschfristen_routes.py` | Backend API-Routen |
|
||||
| `backend-compliance/compliance/db/loeschfristen_models.py` | SQLAlchemy-Modelle |
|
||||
| `backend-compliance/migrations/017_loeschfristen.sql` | Datenbank-Migration |
|
||||
| `backend-compliance/tests/test_loeschfristen_routes.py` | Backend-Tests (58+) |
|
||||
@@ -0,0 +1,201 @@
|
||||
# Normative Verbindlichkeit — Dreistufenmodell
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Nicht jede Quelle, aus der Controls abgeleitet werden, hat die gleiche rechtliche
|
||||
Verbindlichkeit. Ein Control, das aus einem EU-Gesetz stammt, hat ein anderes
|
||||
Gewicht als eines aus einem freiwilligen Framework.
|
||||
|
||||
Das Dreistufenmodell klassifiziert jede Quell-Regulierung und leitet daraus die
|
||||
**effektive normative Staerke** der daraus erzeugten Obligations ab.
|
||||
|
||||
## Die drei Stufen
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Stufe 1 — GESETZ (law)"
|
||||
direction LR
|
||||
A1["DSGVO, NIS2, AI Act, CRA..."]
|
||||
A2["Rechtlich bindend"]
|
||||
A3["Bussgeld bei Verstoss"]
|
||||
A4["normative_strength: must/should/may"]
|
||||
end
|
||||
|
||||
subgraph "Stufe 2 — LEITLINIE (guideline)"
|
||||
direction LR
|
||||
B1["EDPB-Leitlinien, BSI-TR, WP29"]
|
||||
B2["Offizielle Auslegungshilfe"]
|
||||
B3["Beweislastumkehr"]
|
||||
B4["max normative_strength: should"]
|
||||
end
|
||||
|
||||
subgraph "Stufe 3 — FRAMEWORK (framework)"
|
||||
direction LR
|
||||
C1["ENISA, NIST, OWASP, OECD"]
|
||||
C2["Freiwillige Best Practice"]
|
||||
C3["Stand der Technik"]
|
||||
C4["max normative_strength: can"]
|
||||
end
|
||||
|
||||
A1 --> A2 --> A3 --> A4
|
||||
B1 --> B2 --> B3 --> B4
|
||||
C1 --> C2 --> C3 --> C4
|
||||
```
|
||||
|
||||
### Stufe 1: Gesetz (law)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Rechtlich bindend, Bussgeld bei Verstoss |
|
||||
| **normative_strength** | Bleibt wie im Gesetzestext: `must`, `should` oder `may` |
|
||||
| **Beispiele** | DSGVO (EU) 2016/679, NIS2-Richtlinie, KI-Verordnung, CRA, BDSG |
|
||||
| **Warum relevant** | "Sie MUESSEN angemessene technische Massnahmen ergreifen" (Art. 32 DSGVO) |
|
||||
|
||||
!!! warning "Wichtig"
|
||||
Gesetze formulieren Pflichten **abstrakt**. Art. 32 DSGVO sagt:
|
||||
"dem Stand der Technik entsprechende Massnahmen" — aber NICHT
|
||||
"verwende AES-256". Das WAS ist Pflicht, das WIE bleibt offen.
|
||||
|
||||
### Stufe 2: Leitlinie (guideline)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Nicht direkt bindend, aber Beweislastumkehr |
|
||||
| **normative_strength** | Maximal `should` — auch wenn die Leitlinie intern "must" schreibt |
|
||||
| **Beispiele** | EDPB-Leitlinien, BSI Technische Richtlinien, WP29-Dokumente |
|
||||
| **Warum relevant** | "Daten at rest muessen verschluesselt werden" (BSI-TR) → `should` |
|
||||
|
||||
!!! info "Beweislastumkehr"
|
||||
Wenn eine Aufsichtsbehoerde fragt "Warum verschluesselt ihr nicht?",
|
||||
muss die Firma begruenden, warum sie von der Leitlinie abweicht.
|
||||
Die Firma muss aber nicht genau so verschluesseln wie die BSI vorschlaegt.
|
||||
|
||||
### Stufe 3: Framework (framework)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Freiwillig, nicht rechtsverbindlich |
|
||||
| **normative_strength** | Maximal `can` — unabhaengig von interner Sprache |
|
||||
| **Beispiele** | ENISA CCM, NIST CSF, OWASP Top 10, OECD KI-Empfehlung |
|
||||
| **Warum relevant** | "Organizations SHALL implement..." (ENISA) → `can` fuer den Anwender |
|
||||
|
||||
!!! tip "Stand der Technik"
|
||||
NIS2 Art. 21 verweist auf ENISA-Leitlinien als Referenz fuer den
|
||||
"Stand der Technik". Das hebt ENISA-Controls faktisch auf Stufe 2 (`should`)
|
||||
— aber nur im Kontext von NIS2-pflichtigen Unternehmen, nicht generell.
|
||||
|
||||
## Ableitungskette
|
||||
|
||||
Die vollstaendige Kette von der Rechtsquelle zum atomaren Control:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
R["Regulierung<br/>(DSGVO Art. 32)"] -->|"MUSS"| O["Obligation<br/>(Daten schuetzen)"]
|
||||
O -->|decomposition| RC["Rich Control<br/>(Verschluesselung)"]
|
||||
RC -->|pass0b| AC["Atomares Control<br/>(AES-256 at rest)"]
|
||||
|
||||
R2["Framework<br/>(ENISA CCM)"] -->|"KANN"| AC
|
||||
|
||||
style R fill:#fee2e2,stroke:#dc2626
|
||||
style R2 fill:#dbeafe,stroke:#2563eb
|
||||
style O fill:#fef3c7,stroke:#d97706
|
||||
style RC fill:#e0e7ff,stroke:#4f46e5
|
||||
style AC fill:#d1fae5,stroke:#059669
|
||||
```
|
||||
|
||||
**Beispiel**: Das atomare Control "AES-256 Verschluesselung at rest"
|
||||
|
||||
- Aus DSGVO Art. 32 abgeleitet → Obligation "must secure data" → **MUSS** (die Pflicht zu schuetzen)
|
||||
- Aus ENISA CCM konkretisiert → **KANN** (AES-256 ist *eine* moegliche Umsetzung)
|
||||
- Resultat: Die Firma MUSS verschluesseln, KANN aber waehlen wie
|
||||
|
||||
## Multi-Parent-Links
|
||||
|
||||
Ein atomares Control kann aus mehreren Quellen stammen:
|
||||
|
||||
| Control | Parent 1 | Parent 2 | Parent 3 | Effektive Staerke |
|
||||
|---|---|---|---|---|
|
||||
| SEC-042 (Encrypt at rest) | DSGVO Art. 32 (law) | NIS2 Art. 21 (law) | ENISA CCM (framework) | **must** (Gesetz uebertrumpft) |
|
||||
| NET-015 (Zero Trust) | NIST SP 800-207 (framework) | CISA (framework) | — | **can** (nur Frameworks) |
|
||||
| AUTH-003 (MFA) | DSGVO Art. 32 (law) | BSI-TR (guideline) | OWASP ASVS (framework) | **must** (Gesetz vorhanden) |
|
||||
|
||||
**Regel**: Der hoechste source_type bestimmt, ob die normative_strength begrenzt wird.
|
||||
Wenn mindestens ein Parent-Link ein Gesetz ist, bleibt die Staerke wie extrahiert.
|
||||
|
||||
## Technische Umsetzung
|
||||
|
||||
### Klassifikations-Map
|
||||
|
||||
Datei: `backend-compliance/compliance/data/source_type_classification.py`
|
||||
|
||||
Jeder `source_regulation`-Wert aus `control_parent_links` wird klassifiziert:
|
||||
|
||||
```python
|
||||
SOURCE_REGULATION_CLASSIFICATION = {
|
||||
"DSGVO (EU) 2016/679": "law",
|
||||
"EDPB Leitlinien 01/2020 (Datentransfers)": "guideline",
|
||||
"NIST Cybersecurity Framework 2.0": "framework",
|
||||
# ... 55+ Eintraege
|
||||
}
|
||||
```
|
||||
|
||||
### Backfill-Endpoint
|
||||
|
||||
```
|
||||
POST /api/compliance/v1/canonical/controls/backfill-normative-strength?dry_run=true
|
||||
```
|
||||
|
||||
Ablauf:
|
||||
|
||||
1. Alle aktiven `obligation_candidates` laden
|
||||
2. Fuer jede Obligation den Parent-Control finden
|
||||
3. Ueber `control_parent_links` die source_regulations ermitteln
|
||||
4. Hoechsten source_type bestimmen
|
||||
5. `normative_strength` begrenzen falls noetig
|
||||
6. Bei `dry_run=false`: Aenderungen in die DB schreiben
|
||||
|
||||
### Cap-Funktion
|
||||
|
||||
```python
|
||||
def cap_normative_strength(original: str, source_type: str) -> str:
|
||||
"""
|
||||
cap_normative_strength("must", "framework") → "may"
|
||||
cap_normative_strength("should", "law") → "should"
|
||||
cap_normative_strength("must", "guideline") → "should"
|
||||
"""
|
||||
```
|
||||
|
||||
## Frontend-Anzeige
|
||||
|
||||
In der Control-Detail-Ansicht werden Obligations mit farbcodierten Badges angezeigt:
|
||||
|
||||
| normative_strength | Badge | Farbe | Bedeutung |
|
||||
|---|---|---|---|
|
||||
| `must` | **MUSS** | Rot | Gesetzliche Pflicht |
|
||||
| `should` | **SOLL** | Gelb/Amber | Empfohlen, Begruendungspflicht bei Abweichung |
|
||||
| `may` | **KANN** | Gruen | Freiwillige Best Practice |
|
||||
|
||||
## Haeufige Fragen
|
||||
|
||||
### Warum steht bei einem ENISA-Control "MUSS"?
|
||||
|
||||
**Vor dem Backfill**: Das System uebernahm die Sprache des Quelldokuments 1:1.
|
||||
ENISA schreibt intern "shall/must" weil es innerhalb seines Frameworks
|
||||
verbindlich formuliert. Fuer den Anwender ist das ENISA-Dokument aber nicht
|
||||
rechtsverbindlich.
|
||||
|
||||
**Nach dem Backfill**: ENISA-Controls zeigen maximal "KANN", es sei denn
|
||||
ein Gesetz (z.B. NIS2) referenziert dasselbe Control — dann gilt die
|
||||
gesetzliche Verbindlichkeit.
|
||||
|
||||
### Was bedeutet "Stand der Technik"?
|
||||
|
||||
NIS2 und DSGVO verweisen auf den "Stand der Technik", ohne ihn zu definieren.
|
||||
In der Praxis werden ENISA- und BSI-Dokumente als Referenz herangezogen.
|
||||
Das macht ihre Empfehlungen relevant ("SOLL"), aber nicht zu Gesetzen ("MUSS").
|
||||
|
||||
### Wie gehe ich mit unbekannten Quellen um?
|
||||
|
||||
Neue Regulierungen muessen in der `SOURCE_REGULATION_CLASSIFICATION` Map
|
||||
eingetragen werden. Der Fallback fuer unbekannte Quellen ist `framework`
|
||||
(konservativstes Ergebnis — geringste Verbindlichkeit zugewiesen).
|
||||
@@ -291,17 +291,60 @@ POST /sdk/v1/ucca/obligations/gap-analysis
|
||||
|
||||
---
|
||||
|
||||
## Frontend
|
||||
## Frontend — 5-Tab-Aufbau
|
||||
|
||||
**URL:** `https://macmini:3007/sdk/obligations`
|
||||
|
||||
Die Obligations-Seite zeigt:
|
||||
Die Obligations-Seite ist in 5 Tabs gegliedert:
|
||||
|
||||
- **Überblick-Kacheln:** Gesamtanzahl, nach Priorität, nach Regulierung
|
||||
- **Regulierungs-Tabs:** Pflichten gefiltert nach DSGVO, AI Act, NIS2, etc.
|
||||
- **Gap-Analyse-View:** Fehlende TOM-Controls visualisiert als Heatmap
|
||||
- **TOM-Control-Panel:** Mapping von Pflichten → Controls mit Status
|
||||
- **Export:** C-Level-Memo (Markdown) direkt aus dem Frontend
|
||||
| Tab | Inhalt |
|
||||
|-----|--------|
|
||||
| **Uebersicht** | Statistik-Kacheln, Compliance-Score, Regulierungs-Filter, Pflichten-Liste (Cards), Compliance-Befunde |
|
||||
| **Detail-Editor** | Pflichtenliste mit Bearbeitungsfunktion, Status-/Prioritaets-Badges |
|
||||
| **Profiling** | Auto-Profiling aus CompanyProfile + Compliance-Scope, anwendbare Regulierungen |
|
||||
| **Gap-Analyse** | GapAnalysisView + TOMControlPanel (UCCA-Integration) |
|
||||
| **Pflichtenregister** | Druckbares HTML-Dokument (12 Sektionen), Org-Header, Revisionen |
|
||||
|
||||
### 12 Compliance-Checks
|
||||
|
||||
Der Compliance-Checker (`obligations-compliance.ts`) prueft automatisch:
|
||||
|
||||
| # | Check | Severity | Ausloeser |
|
||||
|---|-------|----------|-----------|
|
||||
| 1 | `MISSING_RESPONSIBLE` | MEDIUM | Pflicht ohne Verantwortlichen |
|
||||
| 2 | `OVERDUE_DEADLINE` | HIGH | Frist ueberschritten, Status != completed |
|
||||
| 3 | `MISSING_EVIDENCE` | HIGH | Abgeschlossene Pflicht ohne Nachweis |
|
||||
| 4 | `MISSING_DESCRIPTION` | MEDIUM | Pflicht ohne Beschreibung |
|
||||
| 5 | `NO_LEGAL_REFERENCE` | HIGH | Pflicht ohne Artikel-Referenz |
|
||||
| 6 | `INCOMPLETE_REGULATION` | HIGH | Regulierung mit allen Pflichten pending/overdue |
|
||||
| 7 | `HIGH_PRIORITY_NOT_STARTED` | CRITICAL | Critical/High-Pflicht seit >30d pending |
|
||||
| 8 | `STALE_PENDING` | LOW | Pflicht seit >90d pending |
|
||||
| 9 | `MISSING_LINKED_SYSTEMS` | MEDIUM | Pflicht ohne Systemzuordnung |
|
||||
| 10 | `NO_REVIEW_PROCESS` | MEDIUM | Keine Pflicht hat review_date |
|
||||
| 11 | `CRITICAL_WITHOUT_EVIDENCE` | CRITICAL | Kritische Pflicht ohne Nachweis |
|
||||
| 12 | `MISSING_VENDOR_LINK` | MEDIUM | Art.-28-Pflicht ohne verknuepften Auftragsverarbeiter |
|
||||
|
||||
**Score:** `100 - (CRITICAL*15 + HIGH*10 + MEDIUM*5 + LOW*2)`, min 0.
|
||||
|
||||
### Pflichtenregister-Dokument (12 Sektionen)
|
||||
|
||||
Das druckbare HTML-Dokument (`obligations-document.ts`) umfasst:
|
||||
|
||||
| # | Sektion | Datenquelle |
|
||||
|---|---------|-------------|
|
||||
| 0 | Deckblatt | orgHeader |
|
||||
| — | Inhaltsverzeichnis | statisch |
|
||||
| 1 | Ziel und Zweck | statisch |
|
||||
| 2 | Geltungsbereich | orgHeader, obligations (distinct sources) |
|
||||
| 3 | Methodik | statisch |
|
||||
| 4 | Regulatorische Grundlagen | obligations gruppiert nach source |
|
||||
| 5 | Pflichtenuebersicht | obligations nach Status |
|
||||
| 6 | Detaillierte Pflichten | Pro Regulierung: Detail-Karten |
|
||||
| 7 | Verantwortlichkeiten | Rollenmatrix |
|
||||
| 8 | Fristen und Termine | Ueberfaellige + anstehende Deadlines |
|
||||
| 9 | Nachweisverzeichnis | Evidence pro Pflicht |
|
||||
| 10 | Compliance-Status | Score + Issues |
|
||||
| 11 | Aenderungshistorie | Revisionstabelle |
|
||||
|
||||
---
|
||||
|
||||
@@ -333,3 +376,45 @@ cd ai-compliance-sdk && go test ./internal/ucca/... -v -run TestObligationCondit
|
||||
**Weitere Tests:**
|
||||
- `tom_mapper_test.go` — TOM-Mapping Tests
|
||||
- `v2_loader_test.go` — JSON-Loader für Regulierungs-Dateien
|
||||
- `backend-compliance/tests/test_obligation_routes.py` — 39 Backend-API-Tests
|
||||
|
||||
---
|
||||
|
||||
## Cross-Modul-Integration
|
||||
|
||||
| Modul | Integration |
|
||||
|-------|------------|
|
||||
| **VVT** | Pflichten referenzieren Verarbeitungstaetigkeiten ueber `linked_systems` |
|
||||
| **TOM** | TOM-Control-Mapping (UCCA) zeigt erforderliche Massnahmen pro Pflicht |
|
||||
| **Loeschfristen** | Loeschpflichten (Art. 17 DSGVO) im Pflichtenregister referenziert |
|
||||
| **Vendor Compliance** | Art.-28-Pflichten verknuepfbar mit Auftragsverarbeitern ueber `linked_vendor_ids` (DB: JSONB). Compliance-Check #12 (`MISSING_VENDOR_LINK`) prueft fehlende Verknuepfung. |
|
||||
| **UCCA** | Condition Engine bewertet Pflichten gegen UnifiedFacts |
|
||||
| **Compliance-Scope** | Auto-Profiling nutzt Scope-Antworten fuer Regulierungs-Ableitung |
|
||||
|
||||
---
|
||||
|
||||
## Audit-Faehigkeit
|
||||
|
||||
Das Pflichtenregister ist auditfaehig durch:
|
||||
|
||||
1. **Druckbares HTML-Dokument** mit 12 Sektionen, A4-Layout, `@media print`
|
||||
2. **11 automatische Compliance-Checks** mit Score (0-100) und Befunden nach Schweregrad
|
||||
3. **Nachweisverzeichnis** (Sektion 9) dokumentiert Evidence pro Pflicht
|
||||
4. **Aenderungshistorie** (Sektion 11) mit Version, Datum, Autor, Beschreibung
|
||||
5. **Fristen-Tracking** (Sektion 8) mit ueberfaelligen und anstehenden Terminen
|
||||
|
||||
---
|
||||
|
||||
## Datei-Uebersicht
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `admin-compliance/app/sdk/obligations/page.tsx` | Haupt-Seite (5-Tab-Layout) |
|
||||
| `admin-compliance/lib/sdk/obligations-compliance.ts` | 11 Compliance-Checks + Obligation-Type |
|
||||
| `admin-compliance/lib/sdk/obligations-document.ts` | HTML-Dokument-Generator (12 Sektionen) |
|
||||
| `admin-compliance/components/sdk/obligations/ObligationDocumentTab.tsx` | Pflichtenregister-Tab-Komponente |
|
||||
| `admin-compliance/components/sdk/obligations/GapAnalysisView.tsx` | Gap-Analyse-Komponente |
|
||||
| `admin-compliance/components/sdk/obligations/TOMControlPanel.tsx` | TOM-Control-Panel |
|
||||
| `backend-compliance/compliance/api/obligation_routes.py` | 7 Backend-API-Endpoints |
|
||||
| `backend-compliance/migrations/013_obligations.sql` | DB-Schema |
|
||||
| `ai-compliance-sdk/policies/obligations/v2/` | 325 Pflichten, 9 Regulierungen |
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
# Policy-Bibliothek (Migration 054)
|
||||
|
||||
Die Policy-Bibliothek stellt **29 deutsche Richtlinien-Templates** bereit, die ueber den Dokumentengenerator
|
||||
als Vorlagen genutzt werden koennen. Alle Templates sind nach deutschen Compliance-Standards strukturiert
|
||||
und enthalten Platzhalter fuer unternehmensspezifische Anpassung.
|
||||
|
||||
---
|
||||
|
||||
## Uebersicht
|
||||
|
||||
| Kategorie | Anzahl | UI-Pill | Beschreibung |
|
||||
|-----------|--------|---------|--------------|
|
||||
| [IT-Sicherheit](#it-sicherheit-policies) | 14 | `IT-Sicherheit Policies` | Informationssicherheit, Zugriffskontrollen, Verschluesselung, Patch-Mgmt. |
|
||||
| [Daten-Policies](#daten-policies) | 5 | `Daten-Policies` | Datenschutz, Klassifizierung, Aufbewahrung, Transfer |
|
||||
| [Personal-Policies](#personal-policies) | 4 | `Personal-Policies` | Mitarbeitersicherheit, Awareness, Remote Work, Offboarding |
|
||||
| [Lieferanten-Policies](#lieferanten-policies) | 3 | `Lieferanten-Policies` | Vendor Risk, Drittanbieter, Lieferanten-Sicherheit |
|
||||
| [BCM/Notfall](#bcmnotfall) | 3 | `BCM/Notfall` | Business Continuity, Disaster Recovery, Krisenmanagement |
|
||||
|
||||
**Gesamt:** 29 Policy-Templates + 7 Sicherheitskonzepte (Migration 051) + 16 Basis-Dokumenttypen = **52 Dokumenttypen**
|
||||
|
||||
---
|
||||
|
||||
## Template-Struktur
|
||||
|
||||
Alle 29 Policy-Templates folgen einer einheitlichen **9-Abschnitte-Struktur:**
|
||||
|
||||
1. **Zweck** — Zielsetzung der Richtlinie
|
||||
2. **Geltungsbereich** — Fuer wen gilt die Richtlinie
|
||||
3. **Begriffe** — Definitionen
|
||||
4. **Verantwortlichkeiten** — Rollen und Zustaendigkeiten
|
||||
5. **Richtlinie** (Kern) — Die eigentlichen Regelungen
|
||||
6. **Massnahmen** — Konkrete Umsetzungsschritte
|
||||
7. **Ueberwachung & Audit** — Pruef- und Kontrollmechanismen
|
||||
8. **Schulung** — Anforderungen an Mitarbeiterschulung
|
||||
9. **Inkrafttreten** — Gueltigkeitsdatum und Freigabe
|
||||
|
||||
### Gemeinsame Platzhalter
|
||||
|
||||
| Platzhalter | Beschreibung |
|
||||
|-------------|--------------|
|
||||
| `{{COMPANY_NAME}}` | Firmenname |
|
||||
| `{{SECURITY_OFFICER}}` / `{{ISB_NAME}}` | Informationssicherheitsbeauftragter |
|
||||
| `{{GF_NAME}}` | Geschaeftsfuehrung (Freigabe) |
|
||||
| `{{VERSION}}` | Dokumentversion |
|
||||
| `{{DATE}}` | Erstellungsdatum |
|
||||
| `{{SCOPE_DESCRIPTION}}` | Geltungsbereich |
|
||||
| `{{NEXT_REVIEW_DATE}}` | Naechster Prueftermin |
|
||||
|
||||
---
|
||||
|
||||
## IT-Sicherheit Policies
|
||||
|
||||
14 Templates fuer die IT-Sicherheitsorganisation:
|
||||
|
||||
| Typ | Titel | Spezifische Platzhalter |
|
||||
|-----|-------|------------------------|
|
||||
| `information_security_policy` | Informationssicherheits-Richtlinie | `SCOPE_DESCRIPTION` |
|
||||
| `access_control_policy` | Zugriffskontrollen-Richtlinie | `REVIEW_INTERVAL_DAYS` |
|
||||
| `password_policy` | Passwort-Richtlinie | `MIN_PASSWORD_LENGTH`, `MAX_AGE_DAYS`, `HISTORY_COUNT` |
|
||||
| `encryption_policy` | Verschluesselungs-Richtlinie | `MIN_KEY_LENGTH` |
|
||||
| `logging_policy` | Logging-Richtlinie | `LOG_RETENTION_DAYS` |
|
||||
| `backup_policy` | Backup-Richtlinie | `RPO_HOURS`, `RTO_HOURS`, `BACKUP_RETENTION_DAYS` |
|
||||
| `incident_response_policy` | Incident-Response-Richtlinie | `INCIDENT_HOTLINE`, `NOTIFICATION_HOURS` |
|
||||
| `change_management_policy` | Change-Management-Richtlinie | `CAB_SCHEDULE` |
|
||||
| `patch_management_policy` | Patch-Management-Richtlinie | `CRITICAL_PATCH_HOURS`, `PATCH_WINDOW` |
|
||||
| `asset_management_policy` | Asset-Management-Richtlinie | `INVENTORY_TOOL` |
|
||||
| `cloud_security_policy` | Cloud-Sicherheits-Richtlinie | `APPROVED_PROVIDERS` |
|
||||
| `devsecops_policy` | DevSecOps-Richtlinie | `CI_TOOL`, `SAST_TOOL` |
|
||||
| `secrets_management_policy` | Secrets-Management-Richtlinie | `VAULT_URL`, `ROTATION_DAYS` |
|
||||
| `vulnerability_management_policy` | Schwachstellenmanagement-Richtlinie | `SCAN_FREQUENCY`, `SCANNER_TOOL` |
|
||||
|
||||
---
|
||||
|
||||
## Daten-Policies
|
||||
|
||||
5 Templates fuer Datenschutz und Datenmanagement:
|
||||
|
||||
| Typ | Titel | Spezifische Platzhalter |
|
||||
|-----|-------|------------------------|
|
||||
| `data_protection_policy` | Datenschutz-Richtlinie | `DSB_NAME`, `DSB_EMAIL`, `SUPERVISORY_AUTHORITY` |
|
||||
| `data_classification_policy` | Datenklassifizierungs-Richtlinie | `CLASSIFICATION_TOOL` |
|
||||
| `data_retention_policy` | Datenaufbewahrungs-Richtlinie | `DEFAULT_RETENTION_YEARS`, `DELETION_TOOL` |
|
||||
| `data_transfer_policy` | Datentransfer-Richtlinie | `DPO_EMAIL` |
|
||||
| `privacy_incident_policy` | Datenschutzvorfall-Richtlinie | `DPO_EMAIL`, `NOTIFICATION_HOURS` |
|
||||
|
||||
---
|
||||
|
||||
## Personal-Policies
|
||||
|
||||
4 Templates fuer Mitarbeitersicherheit:
|
||||
|
||||
| Typ | Titel | Spezifische Platzhalter |
|
||||
|-----|-------|------------------------|
|
||||
| `employee_security_policy` | Mitarbeiter-Sicherheitsrichtlinie | `HR_CONTACT` |
|
||||
| `security_awareness_policy` | Security-Awareness-Richtlinie | `TRAINING_FREQUENCY`, `LMS_URL` |
|
||||
| `remote_work_policy` | Remote-Work-Richtlinie | `VPN_TOOL`, `MDM_TOOL` |
|
||||
| `offboarding_policy` | Offboarding-Richtlinie | `HR_CONTACT`, `IT_CONTACT` |
|
||||
|
||||
---
|
||||
|
||||
## Lieferanten-Policies
|
||||
|
||||
3 Templates fuer die Lieferkette:
|
||||
|
||||
| Typ | Titel | Spezifische Platzhalter |
|
||||
|-----|-------|------------------------|
|
||||
| `vendor_risk_management_policy` | Lieferanten-Risikomanagement-Richtlinie | `PROCUREMENT_CONTACT`, `REVIEW_FREQUENCY` |
|
||||
| `third_party_security_policy` | Drittanbieter-Sicherheitsrichtlinie | `SECURITY_CONTACT` |
|
||||
| `supplier_security_policy` | Lieferanten-Sicherheitsrichtlinie | `PROCUREMENT_CONTACT` |
|
||||
|
||||
---
|
||||
|
||||
## BCM/Notfall
|
||||
|
||||
3 Templates fuer Business Continuity:
|
||||
|
||||
| Typ | Titel | Spezifische Platzhalter |
|
||||
|-----|-------|------------------------|
|
||||
| `business_continuity_policy` | Business-Continuity-Richtlinie | `RTO_HOURS`, `RPO_HOURS`, `BC_COORDINATOR` |
|
||||
| `disaster_recovery_policy` | Disaster-Recovery-Richtlinie | `DR_SITE`, `RTO_HOURS`, `RPO_HOURS` |
|
||||
| `crisis_management_policy` | Krisenmanagement-Richtlinie | `CRISIS_HOTLINE`, `CRISIS_TEAM_LEAD` |
|
||||
|
||||
---
|
||||
|
||||
## API
|
||||
|
||||
Die Policy-Templates werden ueber die bestehende Legal-Templates-API verwaltet:
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/api/compliance/legal-templates` | Templates listen (Filter: `document_type`, `status`, `language`) |
|
||||
| `GET` | `/api/compliance/legal-templates/status` | Anzahl nach Typ und Status |
|
||||
| `GET` | `/api/compliance/legal-templates/{id}` | Einzelnes Template laden |
|
||||
| `POST` | `/api/compliance/legal-templates` | Neues Template erstellen |
|
||||
| `PUT` | `/api/compliance/legal-templates/{id}` | Template aktualisieren |
|
||||
| `DELETE` | `/api/compliance/legal-templates/{id}` | Template loeschen |
|
||||
|
||||
### Beispiel: Policy-Templates nach Kategorie filtern
|
||||
|
||||
```bash
|
||||
# Alle IT-Sicherheit Policies
|
||||
curl "https://api-dev.breakpilot.ai/api/compliance/legal-templates?document_type=information_security_policy"
|
||||
|
||||
# Alle Policy-Templates (mehrere Typen)
|
||||
curl "https://api-dev.breakpilot.ai/api/compliance/legal-templates/status"
|
||||
```
|
||||
|
||||
### Beispiel: Template erstellen
|
||||
|
||||
```bash
|
||||
curl -X POST "https://api-dev.breakpilot.ai/api/compliance/legal-templates" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-Tenant-Id: <tenant-id>" \
|
||||
-d '{
|
||||
"document_type": "password_policy",
|
||||
"title": "Passwort-Richtlinie",
|
||||
"content": "# Passwort-Richtlinie\n\n...",
|
||||
"placeholders": ["{{COMPANY_NAME}}", "{{MIN_PASSWORD_LENGTH}}"],
|
||||
"language": "de",
|
||||
"jurisdiction": "DE"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend
|
||||
|
||||
Die Policy-Templates sind im **Dokumentengenerator** (`/sdk/document-generator`) unter 5 neuen Kategorie-Pills erreichbar:
|
||||
|
||||
| UI-Pill | Enthaltene Typen |
|
||||
|---------|-----------------|
|
||||
| IT-Sicherheit Policies | 14 Typen (information_security_policy bis vulnerability_management_policy) |
|
||||
| Daten-Policies | 5 Typen (data_protection_policy bis privacy_incident_policy) |
|
||||
| Personal-Policies | 5 Typen (employee_security_policy, security_awareness_policy, acceptable_use, remote_work_policy, offboarding_policy) |
|
||||
| Lieferanten-Policies | 3 Typen (vendor_risk_management_policy bis supplier_security_policy) |
|
||||
| BCM/Notfall | 3 Typen (business_continuity_policy bis crisis_management_policy) |
|
||||
|
||||
---
|
||||
|
||||
## Seeding
|
||||
|
||||
Das Seed-Script `backend-compliance/scripts/seed_policy_templates.py` fuegt alle 29 Templates ueber die API ein:
|
||||
|
||||
```bash
|
||||
python3 backend-compliance/scripts/seed_policy_templates.py
|
||||
```
|
||||
|
||||
Das Script nutzt die Production-API (`https://api-dev.breakpilot.ai`) und benoetigt einen gueltigen
|
||||
`X-Tenant-Id` Header. Bereits existierende Templates werden nicht dupliziert (Upsert ueber `document_type`).
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
- **48 Tests** in `test_policy_templates.py`
|
||||
- Alle 29 Dokumenttypen gegen `VALID_DOCUMENT_TYPES` validiert
|
||||
- API-Akzeptanz (POST 201) fuer jeden Typ (parametrized)
|
||||
- Filterung nach Kategorie (GET 200)
|
||||
- Platzhalter-Validierung
|
||||
- Seed-Script-Struktur (29 Templates, Pflichtfelder, deutsche Inhalte)
|
||||
- Ablehnung ungeltiger Typen (400)
|
||||
|
||||
---
|
||||
|
||||
## Zusammenspiel mit anderen Modulen
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[Company Profile] --> B[Compliance Engine]
|
||||
B --> C[Policy-Bibliothek]
|
||||
C --> D[Dokumentengenerator]
|
||||
D --> E[Change-Requests]
|
||||
E --> F[Document Workflow]
|
||||
C --> G[Controls / Mapping-Matrix]
|
||||
G --> H[Process Manager Tasks]
|
||||
G --> I[Evidence Checks]
|
||||
```
|
||||
|
||||
Die Policy-Bibliothek bildet die **Vorlage-Ebene** der geplanten Compliance Engine:
|
||||
|
||||
1. **CompanyProfile + Scope** → bestimmt welche Regulations/Controls relevant sind
|
||||
2. **Controls** → verweisen auf relevante **Policies** aus der Bibliothek
|
||||
3. **Policies** → werden ueber den Dokumentengenerator als Entwuerfe erstellt
|
||||
4. **Entwuerfe** → durchlaufen den Document Workflow (Review → Freigabe → Veroeffentlichung)
|
||||
@@ -412,3 +412,8 @@ Die **Einwilligungen** legen fest, welche Datenpunkte einer Einwilligung beduerf
|
||||
Die **Rechtlichen Vorlagen** erstellen die zugehoerigen Dokumente (DSE, AGB, etc.).
|
||||
Der **Cookie Banner** konfiguriert das Frontend-Consent-Widget.
|
||||
Der **Document Workflow** fuhrt alle Dokumente durch den Freigabeprozess vor der Veroeffentlichung.
|
||||
|
||||
!!! info "Policy-Bibliothek (Migration 054)"
|
||||
Zusaetzlich zu den rechtlichen Texten stehen **29 deutsche Richtlinien-Templates** zur Verfuegung
|
||||
(IT-Sicherheit, Datenschutz, Personal, Lieferanten, BCM). Siehe
|
||||
[Policy-Bibliothek](policy-bibliothek.md) fuer Details.
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
# TOM — Technische und Organisatorische Massnahmen (Art. 32 DSGVO)
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Das TOM-Modul implementiert die systematische Ableitung, Dokumentation und Ueberpruefung technischer und organisatorischer Massnahmen gemaess Art. 32 DSGVO. Es bietet einen 6-Schritt-Generator-Wizard, eine regelbasierte Kontrollbibliothek mit 88 Massnahmen, Gap-Analyse, SDM-Mapping, auditfaehige Dokumentengenerierung und 11 Compliance-Checks.
|
||||
|
||||
**Route:** `/sdk/tom` | **Backend:** `backend-compliance:8002` | **Checkpoint:** `CP-TOM`
|
||||
|
||||
---
|
||||
|
||||
## Art. 32 DSGVO Anforderungen
|
||||
|
||||
| Absatz | Anforderung | TOM-Modul Umsetzung |
|
||||
|--------|-------------|---------------------|
|
||||
| Art. 32 Abs. 1 lit. a | Pseudonymisierung und Verschluesselung | Kategorien ENCRYPTION (5 Controls) und PSEUDONYMIZATION (4 Controls) |
|
||||
| Art. 32 Abs. 1 lit. b | Vertraulichkeit, Integritaet, Verfuegbarkeit, Belastbarkeit | 8 Kategorien: ACCESS_CONTROL, ADMISSION_CONTROL, ACCESS_AUTHORIZATION, TRANSFER_CONTROL, RESILIENCE, AVAILABILITY, SEPARATION, INPUT_CONTROL |
|
||||
| Art. 32 Abs. 1 lit. c | Rasche Wiederherstellung nach Zwischenfall | Kategorie RECOVERY (5 Controls) |
|
||||
| Art. 32 Abs. 1 lit. d | Regelmaessige Ueberpruefung und Bewertung | Kategorie REVIEW (11 Controls) + Compliance-Check NO_REVIEW_PROCESS |
|
||||
|
||||
---
|
||||
|
||||
## TOM-Ableitung — Zwei Quellen
|
||||
|
||||
TOMs werden aus zwei unabhaengigen Quellen abgeleitet:
|
||||
|
||||
### 1. Scope/Profil-Module (Embedded Controls)
|
||||
|
||||
Der 6-Schritt-Wizard erfasst:
|
||||
|
||||
- **CompanyProfile**: Branche, Groesse, Rolle (Controller/Processor)
|
||||
- **DataProfile**: Datenkategorien, besondere Kategorien (Art. 9), Betroffene
|
||||
- **ArchitectureProfile**: Hosting, Cloud-Provider, Mandantenfaehigkeit
|
||||
- **SecurityProfile**: Auth, Backup, Logging, DR-Plan
|
||||
- **RiskProfile**: CIA-Bewertung, Schutzniveau
|
||||
|
||||
Die **Rules Engine** wertet 88 Embedded Controls gegen die Profile-Daten aus. Jeder Control hat `applicabilityConditions` mit Operatoren (EQUALS, IN, GREATER_THAN, CONTAINS) und Prioritaeten.
|
||||
|
||||
### 2. Canonical Control Library (CP-CLIB)
|
||||
|
||||
Die dynamisch generierte **Canonical Control Library** (`/sdk/control-library`) enthaelt unternehmensrelevante Security-Controls aus OWASP, NIST, ENISA und weiteren Frameworks. Diese werden durch den Control Generator Pipeline erzeugt und haben einen eigenen Review-Workflow.
|
||||
|
||||
!!! info "Herkunftsdokumentation"
|
||||
Im TOM-Dokument (Sektion 2 und 6) wird die **Herkunft** jeder Massnahme dokumentiert —
|
||||
ob sie aus der Embedded Control Library oder der Canonical Control Library stammt.
|
||||
|
||||
---
|
||||
|
||||
## Frontend — 5-Tab-Aufbau
|
||||
|
||||
| Tab | Beschreibung |
|
||||
|-----|-------------|
|
||||
| **Uebersicht** | Alle TOMs mit Filter (Kategorie, Typ, Status, Applicability), SDM-Abdeckung, Statistiken |
|
||||
| **Detail-Editor** | Einzelne TOM bearbeiten: Status, Verantwortlich, Evidence, Review-Datum |
|
||||
| **Generator** | 6-Schritt-Wizard starten, Quick-Stats |
|
||||
| **Gap-Analyse & Export** | Gap-Analyse-Ergebnisse, SDM/Modul-Abdeckung, JSON/DOCX Export |
|
||||
| **TOM-Dokument** | Auditfaehiges HTML-Dokument (12 Sektionen), Org-Header, Revisionsmanager, PDF-Druck |
|
||||
|
||||
---
|
||||
|
||||
## Kontrollbibliothek (88 Massnahmen)
|
||||
|
||||
| Kategorie | Code-Prefix | Anzahl | DSGVO-Referenz |
|
||||
|-----------|-------------|--------|----------------|
|
||||
| Zutrittskontrolle | TOM-AC | 6 | Art. 32 Abs. 1 lit. b |
|
||||
| Zugangskontrolle | TOM-ADM | 6 | Art. 32 Abs. 1 lit. b |
|
||||
| Zugriffskontrolle | TOM-AZ | 7 | Art. 32 Abs. 1 lit. b |
|
||||
| Weitergabekontrolle | TOM-TR | 7 | Art. 32 Abs. 1 lit. b |
|
||||
| Eingabekontrolle | TOM-IN | 5 | Art. 32 Abs. 1 lit. b |
|
||||
| Auftragskontrolle | TOM-OR | 6 | Art. 28 |
|
||||
| Verfuegbarkeit | TOM-AV | 7 | Art. 32 Abs. 1 lit. b, c |
|
||||
| Trennbarkeit | TOM-SE | 6 | Art. 32 Abs. 1 lit. b |
|
||||
| Verschluesselung | TOM-ENC | 5 | Art. 32 Abs. 1 lit. a |
|
||||
| Pseudonymisierung | TOM-PS | 4 | Art. 32 Abs. 1 lit. a |
|
||||
| Belastbarkeit | TOM-RE | 5 | Art. 32 Abs. 1 lit. b |
|
||||
| Wiederherstellbarkeit | TOM-RC | 5 | Art. 32 Abs. 1 lit. c |
|
||||
| Ueberpruefung & Bewertung | TOM-RV / TOM-DL / TOM-TR | 11 | Art. 32 Abs. 1 lit. d |
|
||||
|
||||
---
|
||||
|
||||
## SDM Gewaehrleistungsziele
|
||||
|
||||
Das [Standard-Datenschutzmodell](https://www.datenschutz-wiki.de/SDM) definiert 7 Gewaehrleistungsziele:
|
||||
|
||||
| Ziel | Relevante Kategorien |
|
||||
|------|---------------------|
|
||||
| Verfuegbarkeit | AVAILABILITY, RESILIENCE, RECOVERY |
|
||||
| Integritaet | ADMISSION_CONTROL, TRANSFER_CONTROL, INPUT_CONTROL, ENCRYPTION, RECOVERY |
|
||||
| Vertraulichkeit | ACCESS_CONTROL, ADMISSION_CONTROL, ACCESS_AUTHORIZATION, TRANSFER_CONTROL, ENCRYPTION |
|
||||
| Nichtverkettung | ACCESS_AUTHORIZATION, SEPARATION, PSEUDONYMIZATION |
|
||||
| Intervenierbarkeit | ORDER_CONTROL, REVIEW |
|
||||
| Transparenz | INPUT_CONTROL, ORDER_CONTROL, REVIEW |
|
||||
| Datenminimierung | SEPARATION, PSEUDONYMIZATION |
|
||||
|
||||
!!! tip "SDM-Abdeckung"
|
||||
Die Gap-Analyse (Tab 4) zeigt pro SDM-Gewaehrleistungsziel den Abdeckungsgrad
|
||||
in Prozent. Ziele mit 0% Abdeckung loesen den Compliance-Check `UNCOVERED_SDM_GOAL`
|
||||
(Schweregrad HIGH) aus.
|
||||
|
||||
---
|
||||
|
||||
## 11 Compliance-Checks
|
||||
|
||||
| # | Check | Schweregrad | Ausloeser |
|
||||
|---|-------|-------------|-----------|
|
||||
| 1 | `MISSING_RESPONSIBLE` | MEDIUM | REQUIRED-TOM ohne verantwortliche Person/Abteilung |
|
||||
| 2 | `OVERDUE_REVIEW` | MEDIUM | TOM mit reviewDate in der Vergangenheit |
|
||||
| 3 | `MISSING_EVIDENCE` | HIGH | IMPLEMENTED-TOM ohne Evidence (obwohl evidenceRequirements > 0) |
|
||||
| 4 | `INCOMPLETE_CATEGORY` | HIGH | Kategorie, in der alle REQUIRED-Controls NOT_IMPLEMENTED sind |
|
||||
| 5 | `NO_ENCRYPTION_MEASURES` | CRITICAL | Kein ENCRYPTION-Control implementiert |
|
||||
| 6 | `NO_PSEUDONYMIZATION` | MEDIUM | Besondere Datenkategorien (Art. 9) ohne PSEUDONYMIZATION |
|
||||
| 7 | `MISSING_AVAILABILITY` | HIGH | Kein AVAILABILITY/RECOVERY implementiert + kein DR-Plan |
|
||||
| 8 | `NO_REVIEW_PROCESS` | MEDIUM | Kein REVIEW-Control implementiert |
|
||||
| 9 | `UNCOVERED_SDM_GOAL` | HIGH | SDM-Gewaehrleistungsziel mit 0% Abdeckung |
|
||||
| 10 | `HIGH_RISK_WITHOUT_MEASURES` | CRITICAL | Schutzniveau VERY_HIGH aber < 50% implementiert |
|
||||
| 11 | `STALE_NOT_IMPLEMENTED` | LOW | REQUIRED-TOM seit > 90 Tagen NOT_IMPLEMENTED |
|
||||
|
||||
**Score-Berechnung:** `100 - (CRITICAL*15 + HIGH*10 + MEDIUM*5 + LOW*2)`, Minimum 0.
|
||||
|
||||
---
|
||||
|
||||
## Backend API (9+ Endpoints)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/api/v1/tom/state` | TOM-Generator-State laden |
|
||||
| `POST` | `/api/v1/tom/state` | State speichern |
|
||||
| `POST` | `/api/v1/tom/evaluate` | Controls evaluieren (Rules Engine) |
|
||||
| `POST` | `/api/v1/tom/gap-analysis` | Gap-Analyse durchfuehren |
|
||||
| `POST` | `/api/v1/tom/evidence/upload` | Evidence-Dokument hochladen |
|
||||
| `POST` | `/api/v1/tom/evidence/analyze` | KI-Analyse eines Evidence-Dokuments |
|
||||
| `POST` | `/api/v1/tom/export` | Export (JSON, DOCX, PDF, ZIP) |
|
||||
| `GET` | `/api/v1/tom/controls` | Kontrollbibliothek abrufen |
|
||||
| `GET` | `/api/v1/tom/controls/:id` | Einzelnen Control abrufen |
|
||||
|
||||
### TOM ↔ Canonical Control Mapping API
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `POST` | `/api/compliance/tom-mappings/sync` | Controls synchronisieren (Profil-basiert) |
|
||||
| `GET` | `/api/compliance/tom-mappings` | Alle Mappings auflisten |
|
||||
| `GET` | `/api/compliance/tom-mappings/by-tom/{code}` | Mappings pro TOM-Kategorie |
|
||||
| `GET` | `/api/compliance/tom-mappings/stats` | Coverage-Statistiken |
|
||||
| `POST` | `/api/compliance/tom-mappings/manual` | Manuelle Zuordnung |
|
||||
| `DELETE` | `/api/compliance/tom-mappings/{id}` | Zuordnung entfernen |
|
||||
|
||||
!!! info "Proxy-Route (Frontend)"
|
||||
Das Admin-Frontend ruft die Endpoints ueber den Next.js-Proxy auf:
|
||||
`/api/sdk/v1/tom/**` → `backend-compliance:8002/api/v1/tom/**`
|
||||
`/api/sdk/v1/compliance/tom-mappings/**` → `backend-compliance:8002/api/compliance/tom-mappings/**`
|
||||
|
||||
---
|
||||
|
||||
## TOM-Dokument (12 Sektionen)
|
||||
|
||||
| # | Sektion | Inhalt |
|
||||
|---|---------|--------|
|
||||
| 0 | Deckblatt | Organisation, DSB, IT-Sicherheit, Version |
|
||||
| — | Inhaltsverzeichnis | Automatisch generiert |
|
||||
| 1 | Ziel und Zweck | Art. 32 DSGVO Rechtsrahmen |
|
||||
| 2 | Geltungsbereich | Unternehmen, Hosting, Systeme, Control-Quellen |
|
||||
| 3 | Grundprinzipien Art. 32 | Vertraulichkeit, Integritaet, Verfuegbarkeit, Belastbarkeit, Wirksamkeitspruefung |
|
||||
| 4 | Schutzbedarf und Risikoanalyse | CIA-Bewertung, Schutzniveau, DSFA-Pflicht |
|
||||
| 5 | Massnahmen-Uebersicht | Tabelle: Kategorie, Anzahl, Status-Verteilung |
|
||||
| 6 | Detaillierte Massnahmen | Pro Kategorie: Detail-Karten mit Code, Status, Evidence, Mappings |
|
||||
| 7 | SDM Gewaehrleistungsziele | Abdeckungstabelle (7 Ziele x Prozent) |
|
||||
| 8 | Verantwortlichkeiten | Rollenmatrix |
|
||||
| 9 | Pruef- und Revisionszyklus | Review-Zeitplan |
|
||||
| 10 | Compliance-Status | Score, Issues nach Schweregrad |
|
||||
| 11 | Aenderungshistorie | Versionstabelle |
|
||||
|
||||
**Ausgabe:** HTML-Download oder PDF-Druck via Browser (`window.print()`).
|
||||
|
||||
---
|
||||
|
||||
## Drei-Schichten-Architektur (TOM ↔ Canonical Controls)
|
||||
|
||||
Die Audit-faehige Dokumentation nutzt eine **Drei-Schichten-Architektur**:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
TOM["TOM-Massnahmen (~88, Audit-Level)"]
|
||||
MAP["tom_control_mappings (Bridge-Tabelle)"]
|
||||
CC["Canonical Controls (10.000+, Implementation-Level)"]
|
||||
TOM --> MAP --> CC
|
||||
```
|
||||
|
||||
| Schicht | Beschreibung | Beispiel |
|
||||
|---------|-------------|---------|
|
||||
| **TOM-Massnahmen** | 88 abstrakte, auditfaehige Massnahmen in 13 Kategorien | TOM-ENC-01: Transportverschluesselung |
|
||||
| **Mapping-Bridge** | Verknuepfung TOM-Kategorie → Canonical Controls per Profil | ENCRYPTION → encryption-Kategorie, Industry-Filter |
|
||||
| **Canonical Controls** | 10.000+ konkrete Security-Controls aus OWASP, NIST, ENISA | CRYP-001: Cryptographic Key Lifecycle |
|
||||
|
||||
### Kategorie-Zuordnung
|
||||
|
||||
| TOM-Kategorie | Canonical Kategorien |
|
||||
|---------------|---------------------|
|
||||
| ACCESS_CONTROL | authentication, identity, physical |
|
||||
| ADMISSION_CONTROL | authentication, identity, system |
|
||||
| ACCESS_AUTHORIZATION | authentication, identity |
|
||||
| TRANSFER_CONTROL | network, data_protection, encryption |
|
||||
| INPUT_CONTROL | application, data_protection |
|
||||
| ORDER_CONTROL | supply_chain, compliance |
|
||||
| AVAILABILITY | continuity, system |
|
||||
| SEPARATION | network, data_protection |
|
||||
| ENCRYPTION | encryption |
|
||||
| PSEUDONYMIZATION | data_protection, encryption |
|
||||
| RESILIENCE | continuity, system |
|
||||
| RECOVERY | continuity |
|
||||
| REVIEW | compliance, governance, risk |
|
||||
|
||||
### Sync-Algorithmus
|
||||
|
||||
1. CompanyProfile (Branche, Groesse) wird gehasht
|
||||
2. Aenderung erkannt → alte auto-Mappings loeschen
|
||||
3. Pro TOM-Kategorie: Canonical Controls mit passender `category`, `applicable_industries`, `applicable_company_size` und `release_state = 'approved'` suchen
|
||||
4. Neue Mappings inserieren (ON CONFLICT DO NOTHING)
|
||||
5. Sync-State aktualisieren
|
||||
|
||||
---
|
||||
|
||||
## Cross-Modul-Integration
|
||||
|
||||
| Modul | Integration |
|
||||
|-------|------------|
|
||||
| **DSFA** | TOM-Controls als Massnahmen in Datenschutz-Folgenabschaetzung |
|
||||
| **VVT** | Verknuepfung von TOMs mit Verarbeitungstaetigkeiten |
|
||||
| **Loeschfristen** | Loeschkontrollen (TOM-DL) referenzieren Loeschfristen-Policies |
|
||||
| **Control Library** | Canonical Controls als belegende Security-Controls pro TOM-Kategorie |
|
||||
| **Risk Assessment** | RiskProfile steuert Applicability der Controls |
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Scope["Scope Engine"] -->|Profile| TOM["TOM (Art. 32)"]
|
||||
CLIB["Control Library"] -->|Canonical Controls| TOM
|
||||
TOM --> DSFA["DSFA (Art. 35)"]
|
||||
TOM --> VVT["VVT (Art. 30)"]
|
||||
TOM --> Loeschfristen["Loeschfristen"]
|
||||
Risk["Risk Assessment"] -->|RiskProfile| TOM
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Audit-Faehigkeit
|
||||
|
||||
Das TOM-Modul erfuellt 7 Audit-Kriterien:
|
||||
|
||||
1. **Vollstaendigkeit**: 88 Controls decken alle Art. 32 Anforderungen ab
|
||||
2. **Nachvollziehbarkeit**: Rules Engine dokumentiert Applicability-Gruende
|
||||
3. **Aktualitaet**: Review-Zyklen + Compliance-Check OVERDUE_REVIEW
|
||||
4. **Verantwortlichkeit**: Rollenmatrix im TOM-Dokument
|
||||
5. **Evidence**: Evidence-Verknuepfung + Gap-Analyse
|
||||
6. **Druckfaehigkeit**: Auditfaehiges HTML-Dokument mit 12 Sektionen
|
||||
7. **Wirksamkeitspruefung**: 11 Compliance-Checks + Score
|
||||
|
||||
---
|
||||
|
||||
## Datei-Uebersicht
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|--------------|
|
||||
| `admin-compliance/app/sdk/tom/page.tsx` | Haupt-Seite mit 5 Tabs |
|
||||
| `admin-compliance/components/sdk/tom-dashboard/` | Tab-Komponenten (Overview, Editor, GapExport, Document) |
|
||||
| `admin-compliance/lib/sdk/tom-generator/types.ts` | Alle TypeScript-Typen |
|
||||
| `admin-compliance/lib/sdk/tom-generator/controls/loader.ts` | 88 Embedded Controls |
|
||||
| `admin-compliance/lib/sdk/tom-generator/rules-engine.ts` | Applicability-Auswertung |
|
||||
| `admin-compliance/lib/sdk/tom-generator/context.tsx` | State Management (Reducer) |
|
||||
| `admin-compliance/lib/sdk/tom-generator/sdm-mapping.ts` | SDM-Gewaehrleistungsziel-Mapping |
|
||||
| `admin-compliance/lib/sdk/tom-compliance.ts` | 11 Compliance-Checks |
|
||||
| `admin-compliance/lib/sdk/tom-document.ts` | HTML-Dokument-Generator |
|
||||
| `backend-compliance/compliance/api/tom_mapping_routes.py` | TOM ↔ Canonical Control Mapping API (6 Endpoints) |
|
||||
| `backend-compliance/migrations/068_tom_control_mappings.sql` | DB-Schema fuer Mapping-Bridge |
|
||||
@@ -1,22 +1,27 @@
|
||||
# Training Engine (CP-TRAIN)
|
||||
|
||||
KI-generierte Schulungsinhalte, Rollenmatrix, Quiz-Engine und Zertifikatsverwaltung für Compliance-Schulungen.
|
||||
KI-generierte Schulungsinhalte, Rollenmatrix, Quiz-Engine, Zertifikate und Training Blocks fuer Compliance-Schulungen.
|
||||
|
||||
**Prefix:** `CP-TRAIN` · **seq:** 4800 · **Frontend:** `https://macmini:3007/sdk/training`
|
||||
**Learner-Portal:** `https://macmini:3007/sdk/training/learner`
|
||||
**Service:** `ai-compliance-sdk` (Go/Gin, Port 8093)
|
||||
**Proxy:** `/api/sdk/v1/training/[[...path]]` → `ai-compliance-sdk:8090/sdk/v1/training/...`
|
||||
**Developer Portal:** `https://macmini:3006/api/training`
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- Rollenbasierte Schulungsmatrix (Wer muss was absolvieren?)
|
||||
- Rollenbasierte Schulungsmatrix (CTM) — 10 Rollen (R1–R10)
|
||||
- KI-generierte Schulungsinhalte (Text, Audio, Video via TTS-Service)
|
||||
- Quiz-Engine mit automatischer Auswertung
|
||||
- Deadline-Tracking und Eskalation bei Überziehung
|
||||
- Quiz-Engine mit automatischer Auswertung und Bestehensgrenze
|
||||
- Deadline-Tracking und 4-stufige Eskalation (7/14/30/45 Tage)
|
||||
- Aufgabenzuweisung (Assignments) mit Fortschrittstracking
|
||||
- Unveränderliches Audit-Log für Compliance-Nachweise
|
||||
- Bulk-Content-Generierung für alle Module auf einmal
|
||||
- PDF-Zertifikate nach erfolgreichem Abschluss
|
||||
- Training Blocks — automatische Modul-Erstellung aus Canonical Controls
|
||||
- Learner-Portal fuer Mitarbeiter (Schulung absolvieren, Quiz, Zertifikat)
|
||||
- Unveraenderliches Audit-Log fuer Compliance-Nachweise
|
||||
- Bulk-Content-Generierung fuer alle Module auf einmal
|
||||
|
||||
---
|
||||
|
||||
@@ -25,13 +30,15 @@ KI-generierte Schulungsinhalte, Rollenmatrix, Quiz-Engine und Zertifikatsverwalt
|
||||
| Artikel | Bezug |
|
||||
|---------|-------|
|
||||
| Art. 39 Abs. 1b DSGVO | DSB-Aufgabe: Sensibilisierung und Schulung |
|
||||
| Art. 5 AI Act | Schulungspflicht für verbotene KI-Praktiken |
|
||||
| Art. 4 Abs. 2 AI Act | Schulung für KI-Alphabetisierung |
|
||||
| Art. 5 AI Act | Schulungspflicht fuer verbotene KI-Praktiken |
|
||||
| Art. 4 Abs. 2 AI Act | Schulung fuer KI-Alphabetisierung |
|
||||
|
||||
---
|
||||
|
||||
## Tabs / Ansichten
|
||||
|
||||
### Admin-Frontend (`/sdk/training`)
|
||||
|
||||
| Tab | Inhalt |
|
||||
|-----|--------|
|
||||
| `overview` | Statistiken, Deadline-Warnung, Eskalation-Check |
|
||||
@@ -39,7 +46,23 @@ KI-generierte Schulungsinhalte, Rollenmatrix, Quiz-Engine und Zertifikatsverwalt
|
||||
| `matrix` | Rollen-Modul-Matrix — wer muss welche Schulung absolvieren |
|
||||
| `assignments` | Zugewiesene Schulungen mit Fortschritt und Deadline |
|
||||
| `content` | KI-generierter Inhalt pro Modul + Audio/Video-Player |
|
||||
| `audit` | Unveränderliches Audit-Log aller Schulungsaktivitäten |
|
||||
| `audit` | Unveraenderliches Audit-Log aller Schulungsaktivitaeten |
|
||||
|
||||
### Learner-Portal (`/sdk/training/learner`)
|
||||
|
||||
| Tab | Inhalt |
|
||||
|-----|--------|
|
||||
| Meine Schulungen | Zuweisungsliste mit Status-Badges, Fortschrittsbalken, Deadline |
|
||||
| Schulungsinhalt | Modul-Content (Markdown), AudioPlayer, VideoPlayer |
|
||||
| Quiz | Fragen mit Antwortauswahl, Timer, Ergebnis-Anzeige |
|
||||
| Zertifikate | Grid mit abgeschlossenen Schulungen, PDF-Download |
|
||||
|
||||
**Learner-Workflow:**
|
||||
|
||||
1. Mitarbeiter sieht seine Zuweisungen
|
||||
2. Klickt "Schulung starten" → Content lesen, Audio/Video anhoeren
|
||||
3. "Quiz starten" → Fragen beantworten
|
||||
4. Bei Bestehen: "Zertifikat generieren" → PDF-Download
|
||||
|
||||
---
|
||||
|
||||
@@ -49,65 +72,122 @@ KI-generierte Schulungsinhalte, Rollenmatrix, Quiz-Engine und Zertifikatsverwalt
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/modules` | Alle Module (`status`, `regulation` Filter) |
|
||||
| `GET` | `/sdk/v1/training/modules/:id` | Modul-Detail |
|
||||
| `GET` | `/sdk/v1/training/modules` | Alle Module (`regulation_area`, `frequency_type`, `search` Filter) |
|
||||
| `GET` | `/sdk/v1/training/modules/:id` | Modul-Detail mit Content und Quiz-Fragen |
|
||||
| `POST` | `/sdk/v1/training/modules` | Modul erstellen |
|
||||
| `PUT` | `/sdk/v1/training/modules/:id` | Modul aktualisieren |
|
||||
| `DELETE` | `/sdk/v1/training/modules/:id` | Modul löschen |
|
||||
| `DELETE` | `/sdk/v1/training/modules/:id` | Modul loeschen |
|
||||
|
||||
### Rollenmatrix
|
||||
### Rollenmatrix (CTM)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/matrix` | Vollständige Rollenmatrix |
|
||||
| `GET` | `/sdk/v1/training/matrix/role/:role` | Matrix für eine Rolle |
|
||||
| `POST` | `/sdk/v1/training/matrix` | Eintrag hinzufügen |
|
||||
| `GET` | `/sdk/v1/training/matrix` | Vollstaendige Rollenmatrix |
|
||||
| `GET` | `/sdk/v1/training/matrix/:role` | Matrix fuer eine Rolle |
|
||||
| `POST` | `/sdk/v1/training/matrix` | Eintrag hinzufuegen |
|
||||
| `DELETE` | `/sdk/v1/training/matrix/:role/:moduleId` | Eintrag entfernen |
|
||||
|
||||
**Rollen:** R1 Geschaeftsfuehrung, R2 IT-Leitung, R3 DSB, R4 ISB, R5 HR, R6 Einkauf, R7 Fachabteilung, R8 IT-Admin, R9 Alle Mitarbeiter, R10 Behoerden
|
||||
|
||||
### Assignments
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/assignments` | Alle Zuweisungen (Filter: `status`, `role`, `moduleId`) |
|
||||
| `GET` | `/sdk/v1/training/assignments` | Alle Zuweisungen (Filter: `user_id`, `module_id`, `role`, `status`) |
|
||||
| `GET` | `/sdk/v1/training/assignments/:id` | Zuweisung-Detail |
|
||||
| `POST` | `/sdk/v1/training/assignments/compute` | Zuweisungen aus Matrix berechnen |
|
||||
| `POST` | `/sdk/v1/training/assignments/:id/start` | Schulung starten |
|
||||
| `POST` | `/sdk/v1/training/assignments/:id/progress` | Fortschritt aktualisieren |
|
||||
| `POST` | `/sdk/v1/training/assignments/:id/complete` | Schulung abschliessen |
|
||||
| `PUT` | `/sdk/v1/training/assignments/:id` | Deadline aktualisieren |
|
||||
| `POST` | `/sdk/v1/training/assignments/:id/complete` | Schulung abschließen |
|
||||
|
||||
### KI-Inhalt
|
||||
### KI-Content-Generierung
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/modules/:id/content` | Aktueller Inhalt |
|
||||
| `POST` | `/sdk/v1/training/modules/:id/generate` | Inhalt KI-generieren (Skript, Audio, Video) |
|
||||
| `POST` | `/sdk/v1/training/content/generate` | Inhalt KI-generieren (Markdown) |
|
||||
| `POST` | `/sdk/v1/training/content/generate-quiz` | Quiz-Fragen KI-generieren |
|
||||
| `GET` | `/sdk/v1/training/content/:moduleId` | Veroeffentlichten Content laden |
|
||||
| `POST` | `/sdk/v1/training/content/:contentId/publish` | Inhalt freigeben |
|
||||
| `POST` | `/sdk/v1/training/content/generate-all` | Alle Module bulk-generieren |
|
||||
| `POST` | `/sdk/v1/training/content/:id/publish` | Inhalt freigeben |
|
||||
| `POST` | `/sdk/v1/training/content/generate-all-quiz` | Alle Quizzes bulk-generieren |
|
||||
|
||||
### Quiz
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/modules/:id/quiz` | Quiz-Fragen |
|
||||
| `POST` | `/sdk/v1/training/modules/:id/quiz/generate` | Quiz KI-generieren |
|
||||
| `POST` | `/sdk/v1/training/modules/:id/quiz/generate-all` | Alle Quizzes bulk-generieren |
|
||||
| `POST` | `/sdk/v1/training/modules/:id/quiz/submit` | Antworten einreichen |
|
||||
| `GET` | `/sdk/v1/training/assignments/:id/attempts` | Quiz-Versuche |
|
||||
| `GET` | `/sdk/v1/training/quiz/:moduleId` | Quiz-Fragen fuer ein Modul |
|
||||
| `POST` | `/sdk/v1/training/quiz/:moduleId/submit` | Antworten einreichen |
|
||||
| `GET` | `/sdk/v1/training/quiz/attempts/:assignmentId` | Quiz-Versuche anzeigen |
|
||||
|
||||
### Media (Audio/Video)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `POST` | `/sdk/v1/training/content/:moduleId/generate-audio` | Audio generieren (Piper TTS) |
|
||||
| `POST` | `/sdk/v1/training/content/:moduleId/generate-video` | Video generieren (TTS + Folien) |
|
||||
| `POST` | `/sdk/v1/training/content/:moduleId/preview-script` | Video-Script Vorschau (JSON) |
|
||||
| `GET` | `/sdk/v1/training/media/module/:moduleId` | Alle Medien eines Moduls |
|
||||
| `GET` | `/sdk/v1/training/media/:mediaId/url` | Metadaten (Bucket, Object Key) |
|
||||
| `POST` | `/sdk/v1/training/media/:mediaId/publish` | Media veroeffentlichen |
|
||||
| `GET` | `/sdk/v1/training/media/:mediaId/stream` | **Media streamen** (307 → Presigned URL) |
|
||||
|
||||
**Media-Streaming:** Der `/stream`-Endpoint liefert einen `307 Temporary Redirect` zu einer zeitlich begrenzten Presigned URL (MinIO/S3). Browser und Player folgen dem Redirect automatisch. Der Next.js-Proxy leitet den Redirect transparent weiter.
|
||||
|
||||
### Deadlines & Eskalation
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/deadlines` | Bevorstehende Deadlines |
|
||||
| `GET` | `/sdk/v1/training/deadlines/overdue` | Überfällige Deadlines |
|
||||
| `POST` | `/sdk/v1/training/escalations/check` | Eskalation auslösen |
|
||||
| `GET` | `/sdk/v1/training/deadlines/overdue` | Ueberfaellige Deadlines |
|
||||
| `POST` | `/sdk/v1/training/escalation/check` | Eskalation ausfuehren |
|
||||
|
||||
**Eskalationsstufen:**
|
||||
|
||||
| Stufe | Tage ueberfaellig | Aktion |
|
||||
|-------|-------------------|--------|
|
||||
| 1 | 7 Tage | Erinnerung an Mitarbeiter |
|
||||
| 2 | 14 Tage | Benachrichtigung Teamleitung |
|
||||
| 3 | 30 Tage | Benachrichtigung Management |
|
||||
| 4 | 45 Tage | Benachrichtigung Compliance Officer |
|
||||
|
||||
### Zertifikate
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `POST` | `/sdk/v1/training/certificates/generate/:assignmentId` | Zertifikat generieren |
|
||||
| `GET` | `/sdk/v1/training/certificates` | Alle Zertifikate des Tenants |
|
||||
| `GET` | `/sdk/v1/training/certificates/:id/pdf` | Zertifikat als PDF herunterladen |
|
||||
| `GET` | `/sdk/v1/training/certificates/:id/verify` | Zertifikat verifizieren |
|
||||
|
||||
**Voraussetzungen:** Status `completed` UND `quiz_passed = true`. Das PDF wird im Querformat (A4 Landscape) generiert und enthaelt Modul-Titel, Benutzer-Name, Datum und eine eindeutige Zertifikats-ID.
|
||||
|
||||
### Training Blocks (Controls → Module)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/blocks` | Alle Block-Konfigurationen |
|
||||
| `POST` | `/sdk/v1/training/blocks` | Block-Konfiguration erstellen |
|
||||
| `GET` | `/sdk/v1/training/blocks/:id` | Block-Konfiguration laden |
|
||||
| `PUT` | `/sdk/v1/training/blocks/:id` | Block-Konfiguration aktualisieren |
|
||||
| `DELETE` | `/sdk/v1/training/blocks/:id` | Block-Konfiguration loeschen |
|
||||
| `POST` | `/sdk/v1/training/blocks/:id/preview` | Vorschau: Welche Controls/Module? |
|
||||
| `POST` | `/sdk/v1/training/blocks/:id/generate` | Module generieren (Content + CTM) |
|
||||
| `GET` | `/sdk/v1/training/blocks/:id/controls` | Verlinkte Controls anzeigen |
|
||||
|
||||
### Canonical Controls
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/canonical/controls` | Controls auflisten (Filter: domain, category, severity, target_audience) |
|
||||
| `GET` | `/sdk/v1/training/canonical/meta` | Metadaten (Domains, Kategorien, Audiences mit Counts) |
|
||||
|
||||
### Statistiken & Audit
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/sdk/v1/training/stats` | Überblick-Statistiken |
|
||||
| `GET` | `/sdk/v1/training/audit` | Audit-Log (Filter: `action`, `moduleId`, `userId`) |
|
||||
| `GET` | `/sdk/v1/training/stats` | Ueberblick-Statistiken |
|
||||
| `GET` | `/sdk/v1/training/audit-log` | Audit-Log (Filter: `action`, `entity_type`, `limit`, `offset`) |
|
||||
|
||||
---
|
||||
|
||||
@@ -118,7 +198,7 @@ seq 4700: Academy (Compliance Academy — manuelle Schulungen)
|
||||
seq 4800: Training Engine (KI-generierte Schulungen, Matrix, Quiz)
|
||||
```
|
||||
|
||||
Das Training-Modul erweitert die Academy um KI-generierte Inhalte. Während die Academy einfache Schulungseinheiten verwaltet, bietet die Training Engine automatische Inhaltsgenerierung, eine Rollenmatrix und eine vollständige Quiz-Engine.
|
||||
Das Training-Modul erweitert die Academy um KI-generierte Inhalte. Waehrend die Academy einfache Schulungseinheiten verwaltet, bietet die Training Engine automatische Inhaltsgenerierung, eine Rollenmatrix und eine vollstaendige Quiz-Engine.
|
||||
|
||||
---
|
||||
|
||||
@@ -128,25 +208,39 @@ KI-generierte Inhalte werden via `compliance-tts-service` (Port 8095) in Audio u
|
||||
|
||||
- **Audio:** Piper TTS → MP3 (Modell: `de_DE-thorsten-high.onnx`)
|
||||
- **Video:** FFmpeg → MP4 (Skript + Stimme + Untertitel)
|
||||
- **Storage:** Hetzner Object Storage (`nbg1.your-objectstorage.com`, S3-kompatibel)
|
||||
- **Storage:** S3-kompatibles Object Storage (Hetzner, TLS)
|
||||
- **Streaming:** `/media/:id/stream` → 307 Redirect zu MinIO Presigned URL
|
||||
|
||||
```
|
||||
AudioPlayer → /sdk/v1/training/modules/:id/media (audio)
|
||||
VideoPlayer → /sdk/v1/training/modules/:id/media (video)
|
||||
AudioPlayer → /sdk/v1/training/media/:mediaId/stream
|
||||
VideoPlayer → /sdk/v1/training/media/:mediaId/stream
|
||||
TTS Service → POST /presigned-url (returns pre-signed MinIO URL)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Frontend
|
||||
|
||||
### Admin-Frontend
|
||||
|
||||
**URL:** `https://macmini:3007/sdk/training`
|
||||
|
||||
6-Tab-Oberfläche mit:
|
||||
6-Tab-Oberflaeche mit:
|
||||
- Statistik-Dashboard (Abschlussquote, offene Schulungen, Deadlines)
|
||||
- Modul-CRUD mit Regulierungs-Badges
|
||||
- Modul-CRUD mit Regulierungs-Badges und Loeschfunktion
|
||||
- Interaktive Rollenmatrix (Checkboxen)
|
||||
- Fortschritts-Balken pro Assignment
|
||||
- Eingebetteter Audio/Video-Player für KI-generierte Inhalte
|
||||
- Eingebetteter Audio/Video-Player fuer KI-generierte Inhalte
|
||||
|
||||
### Learner-Portal
|
||||
|
||||
**URL:** `https://macmini:3007/sdk/training/learner`
|
||||
|
||||
4-Tab-Oberflaeche fuer Mitarbeiter:
|
||||
- Meine Schulungen: Zuweisungsliste mit Status, Fortschritt, Deadline
|
||||
- Schulungsinhalt: Markdown-Rendering, Audio-Player, Video-Player
|
||||
- Quiz: Multiple-Choice mit Timer, Ergebnis-Anzeige, Bestehens-Logik
|
||||
- Zertifikate: Uebersicht abgeschlossener Schulungen, PDF-Download
|
||||
|
||||
---
|
||||
|
||||
@@ -155,11 +249,29 @@ VideoPlayer → /sdk/v1/training/modules/:id/media (video)
|
||||
Die Training Engine verwendet eigene Tabellen im `compliance` Schema:
|
||||
|
||||
```sql
|
||||
training_modules -- Schulungsmodule (title, regulation, frequency, ...)
|
||||
training_matrix -- Rollen-Modul-Zuordnungen
|
||||
training_assignments -- Zuweisungen (user, module, status, progress, deadline)
|
||||
training_content -- KI-generierter Inhalt (script, audio_url, video_url, ...)
|
||||
training_quiz -- Quiz-Fragen pro Modul
|
||||
training_quiz_attempts -- Eingereichter Antworten + Score
|
||||
training_audit_log -- Unveränderliches Audit-Log
|
||||
training_modules -- Schulungsmodule (title, regulation, frequency, ...)
|
||||
training_matrix -- Rollen-Modul-Zuordnungen (CTM)
|
||||
training_assignments -- Zuweisungen (user, module, status, progress, deadline, certificate_id)
|
||||
training_content -- KI-generierter Inhalt (markdown, summary, llm_model)
|
||||
training_quiz_questions -- Quiz-Fragen pro Modul (options JSONB, correct_index)
|
||||
training_quiz_attempts -- Eingereichte Antworten + Score
|
||||
training_media -- Audio/Video-Dateien (bucket, object_key, status)
|
||||
training_audit_log -- Unveraenderliches Audit-Log
|
||||
training_block_configs -- Block-Konfigurationen (Filter, Prefix, Frequenz)
|
||||
training_block_controls -- Verlinkte Canonical Controls pro Block
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
# Handler-Tests (47 Tests)
|
||||
cd ai-compliance-sdk && go test -v ./internal/api/handlers/ -run "TestGet|TestCreate|TestUpdate|TestDelete|..."
|
||||
|
||||
# Escalation + Content Generator Tests (43 Tests)
|
||||
cd ai-compliance-sdk && go test -v ./internal/training/ -run "TestEscalation|TestBuildContent|TestParseQuiz|..."
|
||||
|
||||
# Block Generator Tests (14 Tests)
|
||||
cd ai-compliance-sdk && go test -v ./internal/training/ -run "TestBlock"
|
||||
```
|
||||
|
||||
@@ -32,3 +32,28 @@ Seite unter `/sdk/vendor-compliance` mit Vendor-Tabelle, Risiko-Matrix und Vertr
|
||||
## Datenbank
|
||||
|
||||
Migration in der AI Compliance SDK erstellt Tabellen fuer Vendors, Risikobewertungen, Vertraege und AVV-Klauseln.
|
||||
|
||||
- `vendor_vendors` — Stammdaten, Rolle, Risiko-Scores, Kontakte
|
||||
- `vendor_contracts` — AVV-Dokumente, Pruefstatus
|
||||
- `vendor_findings` — Findings aus Pruefungen
|
||||
- `vendor_control_instances` — Control-Instanzen pro Vendor (inkl. 6 TOM-Controls VND-TOM-01..06)
|
||||
- `compliance_templates` — Shared Templates
|
||||
|
||||
## Cross-Modul-Integration
|
||||
|
||||
Seit der Vendor-Compliance Cross-Modul-Integration (2026-03-19) ist das Modul mit vier DSGVO-Modulen verknuepft:
|
||||
|
||||
| Modul | Integration | Richtung |
|
||||
|-------|------------|----------|
|
||||
| **VVT** | Processor-Tab (Art. 30 Abs. 2) liest Vendors mit `role=PROCESSOR/SUB_PROCESSOR` aus Vendor-API. Kein eigener State mehr — Single Source of Truth. | Read |
|
||||
| **Obligations** | Art.-28-Pflichten verknuepfbar via `linked_vendor_ids` (JSONB). Compliance-Check `MISSING_VENDOR_LINK` prueft fehlende Verknuepfung. | Read/Write |
|
||||
| **TOM** | Uebersicht-Tab zeigt Vendor-TOM-Controls (VND-TOM-01..06) als Querverweis-Tabelle. | Read |
|
||||
| **Loeschfristen** | Loeschfrist-Policies verknuepfbar via `linked_vendor_ids` (JSONB). Loeschkonzept-Dokument listet verknuepfte Auftragsverarbeiter. | Read/Write |
|
||||
|
||||
### Sidebar-Position
|
||||
|
||||
Das Vendor-Compliance-Modul steht in der SDK-Sidebar bei **seq 2500** (Paket "dokumentation"), direkt nach VVT (seq 2400):
|
||||
|
||||
```
|
||||
obligations (2000) → dsfa (2100) → tom (2200) → loeschfristen (2300) → vvt (2400) → vendor-compliance (2500)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,759 @@
|
||||
# VVT — Verzeichnis von Verarbeitungstaetigkeiten (Art. 30 DSGVO)
|
||||
|
||||
Das VVT-Modul implementiert das Verarbeitungsverzeichnis gemaess Art. 30 DSGVO mit einer
|
||||
3-Schichten-Architektur: **Master-Libraries** (globale Stammdaten) + **Prozess-Templates**
|
||||
(vorgefertigte Verarbeitungsvorlagen) + **Tenant-Instanzen** (individuelle Verarbeitungstaetigkeiten).
|
||||
|
||||
**Route:** `/sdk/vvt` | **Backend:** `backend-compliance:8002` | **Migrationen:** `006`, `033`, `035`, `064`-`067`
|
||||
|
||||
---
|
||||
|
||||
## Uebersicht
|
||||
|
||||
| Checkpoint | Reviewer | Rechtsgrundlage | Status |
|
||||
|-----------|----------|-----------------|--------|
|
||||
| CP-VVT (REQUIRED) | DSB | Art. 30 DSGVO | Phase 1 abgeschlossen |
|
||||
|
||||
**Wann ist ein VVT Pflicht?**
|
||||
|
||||
Jeder Verantwortliche und jeder Auftragsverarbeiter muss ein Verzeichnis aller
|
||||
Verarbeitungstaetigkeiten fuehren (Art. 30 Abs. 1 und Abs. 2 DSGVO). Ausnahmen gelten
|
||||
nur fuer Unternehmen mit weniger als 250 Beschaeftigten — und auch nur dann, wenn die
|
||||
Verarbeitung kein Risiko birgt, nur gelegentlich erfolgt und keine besonderen Datenkategorien
|
||||
(Art. 9/10 DSGVO) betroffen sind. In der Praxis entfaellt die Ausnahme fast nie.
|
||||
|
||||
---
|
||||
|
||||
## 3-Schichten-Architektur
|
||||
|
||||
Das VVT-Modul arbeitet mit drei Abstraktionsebenen:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Ebene A: Master-Libraries] -->|IDs referenzieren| B[Ebene B: Prozess-Templates]
|
||||
B -->|Instanziierung| C[Ebene C: Tenant-Aktivitaeten]
|
||||
A -->|Direkte Auswahl| C
|
||||
```
|
||||
|
||||
### Ebene A — Master-Libraries (Global)
|
||||
|
||||
8 globale Referenztabellen, die **ohne `tenant_id`** arbeiten und allen Mandanten zur Verfuegung stehen:
|
||||
|
||||
| Library | Tabelle | Eintraege | Beschreibung |
|
||||
|---------|---------|-----------|-------------|
|
||||
| Betroffenenkategorien | `vvt_lib_data_subjects` | 15 | Mitarbeiter, Kunden, Bewerber, ... |
|
||||
| Datenkategorien | `vvt_lib_data_categories` | 35 | Hierarchisch mit Parent/Child (9 Oberkategorien + 26 Unterkategorien) |
|
||||
| Empfaenger | `vvt_lib_recipients` | 15 | Intern, Auftragsverarbeiter, Behoerden |
|
||||
| Rechtsgrundlagen | `vvt_lib_legal_bases` | 12 | Art. 6, Art. 9, BDSG, UWG |
|
||||
| Loeschfristen | `vvt_lib_retention_rules` | 12 | HGB 10J, AO 6J/10J, AGG 6M, ... |
|
||||
| Transfermechanismen | `vvt_lib_transfer_mechanisms` | 8 | Angemessenheit, SCC, BCR, DPF, ... |
|
||||
| Verarbeitungszwecke | `vvt_lib_purposes` | 20 | Personalverwaltung, CRM, Analytics, ... |
|
||||
| TOMs | `vvt_lib_toms` | 20 | RBAC, MFA, Verschluesselung, Backup, ... |
|
||||
|
||||
#### Datenkategorien-Hierarchie
|
||||
|
||||
Die Datenkategorien sind zweistufig organisiert:
|
||||
|
||||
| Oberkategorie | Unterkategorien | Art. 9 |
|
||||
|--------------|----------------|--------|
|
||||
| Identifikationsdaten | Name, Geburtsdatum, Ausweisnummer, SV-Nummer | Nein |
|
||||
| Kontaktdaten | Anschrift, Kontaktinformationen | Nein |
|
||||
| Finanzdaten | Steuer-ID, Bankverbindung, Zahlungsdaten, Gehaltsdaten | Nein |
|
||||
| Beschaeftigungsdaten | Arbeitsvertragsdaten, Ausbildungsdaten | Nein |
|
||||
| Digitale Identitaet | IP-Adresse, Geraete-ID, Zugangsdaten, Nutzungsdaten | Nein |
|
||||
| Kommunikationsdaten | Korrespondenz, Vertragsdaten | Nein |
|
||||
| Medien- und Standortdaten | Bild/Video, Standortdaten | Nein |
|
||||
| Besondere Kategorien (Art. 9) | Gesundheit, Genetik, Biometrie, Ethnische Herkunft, Politische Meinungen, Religion, Gewerkschaft, Sexualleben | **Ja** |
|
||||
| Strafrechtliche Daten (Art. 10) | Verurteilungen, Straftaten | Art. 10 |
|
||||
|
||||
### Ebene B — Prozess-Templates (System + Tenant)
|
||||
|
||||
18 vorgefertigte Prozess-Templates, die Library-IDs referenzieren und mit einem Klick
|
||||
in eine VVT-Aktivitaet instanziiert werden koennen:
|
||||
|
||||
| Template-ID | Name | Business Function | Loeschfrist |
|
||||
|-------------|------|-------------------|-------------|
|
||||
| `hr-mitarbeiterverwaltung` | Mitarbeiterverwaltung | Personal | 10 Jahre (HGB) |
|
||||
| `hr-gehaltsabrechnung` | Gehaltsabrechnung | Personal | 10 Jahre (AO) |
|
||||
| `hr-bewerbermanagement` | Bewerbermanagement | Personal | 6 Monate (AGG) |
|
||||
| `hr-zeiterfassung` | Zeiterfassung | Personal | 2 Jahre (ArbZG) |
|
||||
| `finance-buchhaltung` | Buchhaltung | Finanzen | 10 Jahre (HGB) |
|
||||
| `finance-zahlungsverkehr` | Zahlungsverkehr | Finanzen | 10 Jahre (HGB) |
|
||||
| `sales-kundenverwaltung` | Kundenverwaltung | Vertrieb | 3 Jahre (BGB) |
|
||||
| `sales-vertriebssteuerung` | Vertriebssteuerung | Vertrieb | 3 Jahre (BGB) |
|
||||
| `marketing-newsletter` | Newsletter-Versand | Marketing | Bis Widerruf |
|
||||
| `marketing-website-analytics` | Website-Analyse | Marketing | 14 Monate |
|
||||
| `marketing-social-media` | Social-Media-Marketing | Marketing | Bis Zweckerfuellung |
|
||||
| `support-ticketsystem` | Ticketsystem | Support | 3 Jahre (BGB) |
|
||||
| `it-systemadministration` | IT-Systemadministration | IT | 90 Tage |
|
||||
| `it-backup` | Datensicherung | IT | 90 Tage |
|
||||
| `it-logging` | Logging und Ueberwachung | IT | 90 Tage |
|
||||
| `it-iam` | Identitaetsmanagement | IT | 6 Monate (AGG) |
|
||||
| `other-videokonferenz` | Videokonferenz | Sonstiges | Bis Zweckerfuellung |
|
||||
| `other-besuchermanagement` | Besuchermanagement | Sonstiges | 30 Tage |
|
||||
|
||||
### Ebene C — Tenant-Aktivitaeten
|
||||
|
||||
Individuelle Verarbeitungstaetigkeiten pro Mandant. Jede Aktivitaet kann sowohl
|
||||
**Freitext-Felder** (bestehendes Schema) als auch **Library-Referenzen** (neue `*_refs`-Felder)
|
||||
enthalten. Beide existieren parallel — die Library-Referenzen ergaenzen die Freitext-Felder
|
||||
fuer strukturierte Auswertungen und Cross-Modul-Verknuepfungen.
|
||||
|
||||
---
|
||||
|
||||
## Funktionen
|
||||
|
||||
- **CRUD-Operationen:** Anlegen, Lesen, Aktualisieren, Loeschen von VVT-Aktivitaeten
|
||||
- **Organisations-Header:** DSB-Kontakt, VVT-Version, Pruefintervall
|
||||
- **Template-Instanziierung:** Neue Aktivitaet aus Prozess-Template erstellen (inkl. automatischer Freitext-Befuellung)
|
||||
- **Library-Referenzen:** Strukturierte Auswahl aus 8 Master-Libraries (parallel zu Freitext)
|
||||
- **Art. 30 Completeness-Check:** Automatische Bewertung der Pflichtfelder-Vollstaendigkeit
|
||||
- **Scope-basierte Generierung:** Automatische Erstellung von Aktivitaeten aus Compliance-Scope-Antworten
|
||||
- **Status-Workflow:** `DRAFT` -> `REVIEW` -> `APPROVED` / `ARCHIVED`
|
||||
- **Export:** JSON und CSV (Excel-kompatibel mit BOM, Semikolon-getrennt)
|
||||
- **Audit-Log:** Protokollierung aller Aktionen (CREATE / UPDATE / DELETE / EXPORT)
|
||||
- **Statistiken:** Zaehler nach Status, Geschaeftsbereich, DSFA-Pflicht, Drittlandtransfers
|
||||
- **Cross-Modul-Links:** Verknuepfung zu Loeschfristen und TOM-Massnahmen (Phase 2)
|
||||
|
||||
---
|
||||
|
||||
## API-Endpoints
|
||||
|
||||
### Aktivitaeten (CRUD)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/vvt/activities` | Liste (Filter: status, business_function, search, review_overdue) |
|
||||
| `POST` | `/vvt/activities` | Neue Aktivitaet anlegen -> HTTP 201 |
|
||||
| `GET` | `/vvt/activities/{id}` | Einzelne Aktivitaet abrufen |
|
||||
| `PUT` | `/vvt/activities/{id}` | Aktivitaet aktualisieren |
|
||||
| `DELETE` | `/vvt/activities/{id}` | Aktivitaet loeschen |
|
||||
| `GET` | `/vvt/activities/{id}/completeness` | Art. 30 Vollstaendigkeits-Check |
|
||||
| `GET` | `/vvt/activities/{id}/versions` | Versionshistorie |
|
||||
|
||||
### Organisation
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/vvt/organization` | Organisations-Header laden |
|
||||
| `PUT` | `/vvt/organization` | Organisations-Header speichern (Upsert) |
|
||||
|
||||
### Export & Statistik
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/vvt/export?format=json` | JSON-Export aller Aktivitaeten |
|
||||
| `GET` | `/vvt/export?format=csv` | CSV-Export (Semikolon, UTF-8 BOM) |
|
||||
| `GET` | `/vvt/stats` | Statistik-Zusammenfassung |
|
||||
| `GET` | `/vvt/audit-log` | Audit-Trail (limit, offset) |
|
||||
|
||||
### Master-Libraries (read-only, global)
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/vvt/libraries` | Uebersicht: alle 8 Library-Typen mit Anzahl |
|
||||
| `GET` | `/vvt/libraries/data-subjects` | Betroffenenkategorien (Filter: `typical_for`) |
|
||||
| `GET` | `/vvt/libraries/data-categories` | Datenkategorien — hierarchisch oder `?flat=true` (Filter: `parent_id`, `is_art9`) |
|
||||
| `GET` | `/vvt/libraries/recipients` | Empfaenger (Filter: `type`) |
|
||||
| `GET` | `/vvt/libraries/legal-bases` | Rechtsgrundlagen (Filter: `is_art9`, `type`) |
|
||||
| `GET` | `/vvt/libraries/retention-rules` | Loeschfristen mit Dauer, Einheit, Rechtsgrundlage |
|
||||
| `GET` | `/vvt/libraries/transfer-mechanisms` | Uebermittlungsmechanismen |
|
||||
| `GET` | `/vvt/libraries/purposes` | Verarbeitungszwecke (Filter: `typical_for`) |
|
||||
| `GET` | `/vvt/libraries/toms` | Technisch-Organisatorische Massnahmen (Filter: `category`) |
|
||||
|
||||
### Prozess-Templates
|
||||
|
||||
| Methode | Pfad | Beschreibung |
|
||||
|---------|------|--------------|
|
||||
| `GET` | `/vvt/templates` | Alle Templates (Filter: `business_function`, `search`) |
|
||||
| `GET` | `/vvt/templates/{id}` | Einzelnes Template mit aufgeloesten Library-Labels |
|
||||
| `POST` | `/vvt/templates/{id}/instantiate` | VVT-Aktivitaet aus Template erstellen -> HTTP 201 |
|
||||
|
||||
!!! info "Proxy-Route (Frontend)"
|
||||
Das Admin-Frontend ruft die Endpoints ueber den Next.js-Proxy auf:
|
||||
`/api/sdk/v1/compliance/vvt/**` -> `backend-compliance:8002/api/compliance/vvt/**`
|
||||
|
||||
---
|
||||
|
||||
## Datenfluss: Template-Instanziierung
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant F as Frontend
|
||||
participant B as Backend
|
||||
participant DB as PostgreSQL
|
||||
|
||||
F->>B: POST /vvt/templates/hr-mitarbeiterverwaltung/instantiate
|
||||
B->>DB: SELECT FROM vvt_process_templates WHERE id = 'hr-mitarbeiterverwaltung'
|
||||
DB-->>B: Template mit Library-Referenzen
|
||||
|
||||
Note over B: Fuer jeden ref-Typ:<br/>Library-IDs -> Labels aufloesen
|
||||
|
||||
B->>DB: SELECT FROM vvt_lib_purposes WHERE id IN ('EMPLOYMENT_ADMIN', 'PAYROLL')
|
||||
DB-->>B: Labels: "Personalverwaltung", "Gehaltsabrechnung"
|
||||
|
||||
B->>DB: SELECT FROM vvt_lib_retention_rules WHERE id = 'HGB_257_10Y'
|
||||
DB-->>B: "10 Jahre (HGB § 257)"
|
||||
|
||||
Note over B: Neue VVT-Aktivitaet erstellen:<br/>- Freitext-Felder mit Labels befuellt<br/>- *_refs-Felder mit Library-IDs
|
||||
|
||||
B->>DB: INSERT INTO compliance_vvt_activities (...)
|
||||
B->>DB: INSERT INTO compliance_vvt_audit_log (action='CREATE', ...)
|
||||
DB-->>B: Neue Aktivitaet mit UUID
|
||||
|
||||
B-->>F: 201 Created + VVTActivityResponse
|
||||
F->>F: Wechsel zu Editor-Tab
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Datenmodell
|
||||
|
||||
### VVT-Aktivitaet (Response) — vollstaendig
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"vvt_id": "VVT-0001",
|
||||
"name": "Mitarbeiterverwaltung",
|
||||
"description": "Verwaltung des Beschaeftigungsverhaeltnisses",
|
||||
"status": "DRAFT",
|
||||
"business_function": "hr",
|
||||
|
||||
"purposes": ["Personalverwaltung", "Gehaltsabrechnung"],
|
||||
"legal_bases": [{"type": "BDSG_26", "description": "Beschaeftigtenverhaeltnis"}],
|
||||
"data_subject_categories": ["Beschaeftigte"],
|
||||
"personal_data_categories": ["Name", "Geburtsdatum", "Bankverbindung"],
|
||||
"recipient_categories": [{"type": "INTERNAL", "name": "Personalabteilung"}],
|
||||
"third_country_transfers": [],
|
||||
"retention_period": {
|
||||
"description": "10 Jahre (HGB § 257)",
|
||||
"legalBasis": "HGB § 257",
|
||||
"deletionProcedure": "Vernichtung",
|
||||
"duration": 10,
|
||||
"durationUnit": "YEARS"
|
||||
},
|
||||
"tom_description": "Rollenbasierte Zugriffskontrolle, Verschluesselung",
|
||||
"structured_toms": {
|
||||
"accessControl": ["RBAC", "Need-to-Know"],
|
||||
"confidentiality": ["Verschluesselung ruhender Daten"],
|
||||
"integrity": ["Audit-Logging"],
|
||||
"availability": [],
|
||||
"separation": ["Mandantentrennung"]
|
||||
},
|
||||
|
||||
"systems": [{"systemId": "HR-Software", "name": "HR-Software"}],
|
||||
"deployment_model": "cloud",
|
||||
"protection_level": "HIGH",
|
||||
"dpia_required": true,
|
||||
"responsible": "Max Mustermann",
|
||||
"owner": "Personalabteilung",
|
||||
|
||||
"purpose_refs": ["EMPLOYMENT_ADMIN", "PAYROLL"],
|
||||
"legal_basis_refs": ["BDSG_26", "ART6_1B"],
|
||||
"data_subject_refs": ["EMPLOYEES"],
|
||||
"data_category_refs": ["NAME", "DOB", "ADDRESS", "BANK_ACCOUNT", "EMPLOYMENT_DATA"],
|
||||
"recipient_refs": ["INTERNAL_HR", "INTERNAL_FINANCE", "PROCESSOR_PAYROLL"],
|
||||
"retention_rule_ref": "HGB_257_10Y",
|
||||
"transfer_mechanism_refs": null,
|
||||
"tom_refs": ["AC_RBAC", "AC_NEED_TO_KNOW", "CONF_ENCRYPTION_REST", "INT_AUDIT_LOG"],
|
||||
"source_template_id": "hr-mitarbeiterverwaltung",
|
||||
"risk_score": 3,
|
||||
"linked_loeschfristen_ids": null,
|
||||
"linked_tom_measure_ids": null,
|
||||
"art30_completeness": {
|
||||
"score": 90,
|
||||
"missing": ["responsible"],
|
||||
"warnings": [],
|
||||
"passed": 9,
|
||||
"total": 10
|
||||
},
|
||||
|
||||
"created_at": "2026-03-19T10:00:00Z",
|
||||
"updated_at": "2026-03-19T12:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
!!! warning "Dual-Schema: Freitext + Library-Referenzen"
|
||||
Jede Aktivitaet hat **zwei parallele Repraesentationen** fuer Datenkategorien,
|
||||
Betroffene, Zwecke etc.: Die bestehenden Freitext-Felder (`purposes`, `legal_bases`, ...)
|
||||
und die neuen Library-Referenz-Felder (`purpose_refs`, `legal_basis_refs`, ...).
|
||||
Beide existieren parallel. Bei Template-Instanziierung werden beide automatisch befuellt.
|
||||
Bestehende Aktivitaeten ohne `*_refs` bleiben voll funktionsfaehig.
|
||||
|
||||
### Art. 30 Completeness-Check
|
||||
|
||||
Der Completeness-Endpoint prueft 10 Pflichtfelder nach Art. 30 Abs. 1 DSGVO:
|
||||
|
||||
| Nr. | Pruefung | Quelle |
|
||||
|-----|---------|--------|
|
||||
| 1 | Name der Verarbeitung | `name` |
|
||||
| 2 | Verarbeitungszweck(e) | `purposes` ODER `purpose_refs` |
|
||||
| 3 | Rechtsgrundlage | `legal_bases` ODER `legal_basis_refs` |
|
||||
| 4 | Betroffenenkategorien | `data_subject_categories` ODER `data_subject_refs` |
|
||||
| 5 | Datenkategorien | `personal_data_categories` ODER `data_category_refs` |
|
||||
| 6 | Empfaenger | `recipient_categories` ODER `recipient_refs` |
|
||||
| 7 | Drittland-Uebermittlung | Immer bestanden (kein Transfer ist valide) |
|
||||
| 8 | Loeschfristen | `retention_period.description` ODER `retention_rule_ref` |
|
||||
| 9 | TOM-Beschreibung | `tom_description` ODER `tom_refs` ODER `structured_toms` |
|
||||
| 10 | Verantwortlicher | `responsible` |
|
||||
|
||||
**Warnings** (nicht im Score, aber angezeigt):
|
||||
|
||||
- `dpia_required_but_no_dsfa_linked` — DSFA erforderlich, aber keine DSFA verknuepft
|
||||
- `third_country_transfer_without_mechanism` — Drittlandtransfer ohne Transfermechanismus
|
||||
|
||||
```json
|
||||
{
|
||||
"score": 80,
|
||||
"missing": ["retention_period", "responsible"],
|
||||
"warnings": ["dpia_required_but_no_dsfa_linked"],
|
||||
"passed": 8,
|
||||
"total": 10
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Library-Datenmodell
|
||||
|
||||
### Master-Library-Eintrag (allgemein)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "EMPLOYEES",
|
||||
"label_de": "Beschaeftigte",
|
||||
"description_de": "Aktuelle Mitarbeiterinnen und Mitarbeiter",
|
||||
"sort_order": 1
|
||||
}
|
||||
```
|
||||
|
||||
### Datenkategorie (hierarchisch)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "IDENTIFICATION",
|
||||
"label_de": "Identifikationsdaten",
|
||||
"children": [
|
||||
{
|
||||
"id": "NAME",
|
||||
"parent_id": "IDENTIFICATION",
|
||||
"label_de": "Name",
|
||||
"is_art9": false,
|
||||
"risk_weight": 1
|
||||
},
|
||||
{
|
||||
"id": "DOB",
|
||||
"parent_id": "IDENTIFICATION",
|
||||
"label_de": "Geburtsdatum",
|
||||
"is_art9": false,
|
||||
"risk_weight": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Loeschfrist (Retention Rule)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "HGB_257_10Y",
|
||||
"label_de": "10 Jahre (HGB § 257)",
|
||||
"legal_basis": "HGB § 257",
|
||||
"duration": 10,
|
||||
"duration_unit": "YEARS",
|
||||
"start_event": "Ende des Kalenderjahres",
|
||||
"deletion_procedure": "Vernichtung nach Ablauf der Aufbewahrungsfrist"
|
||||
}
|
||||
```
|
||||
|
||||
### Prozess-Template
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "hr-mitarbeiterverwaltung",
|
||||
"name": "Mitarbeiterverwaltung",
|
||||
"business_function": "hr",
|
||||
"purpose_refs": ["EMPLOYMENT_ADMIN", "PAYROLL"],
|
||||
"legal_basis_refs": ["BDSG_26", "ART6_1B"],
|
||||
"data_subject_refs": ["EMPLOYEES"],
|
||||
"data_category_refs": ["NAME", "DOB", "ADDRESS", "CONTACT", "SOCIAL_SECURITY", "BANK_ACCOUNT", "EMPLOYMENT_DATA", "HEALTH_DATA"],
|
||||
"recipient_refs": ["INTERNAL_HR", "INTERNAL_FINANCE", "PROCESSOR_PAYROLL"],
|
||||
"tom_refs": ["AC_RBAC", "AC_NEED_TO_KNOW", "CONF_ENCRYPTION_REST", "INT_AUDIT_LOG"],
|
||||
"retention_rule_ref": "HGB_257_10Y",
|
||||
"typical_systems": ["HR-Software", "Personalakte (digital)"],
|
||||
"protection_level": "HIGH",
|
||||
"dpia_required": true,
|
||||
"risk_score": 3,
|
||||
"tags": ["personal", "pflicht"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Status-Workflow
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> DRAFT : Neue Aktivitaet
|
||||
DRAFT --> REVIEW : Zur Pruefung
|
||||
REVIEW --> APPROVED : Genehmigt
|
||||
REVIEW --> DRAFT : Zurueck zu Entwurf
|
||||
APPROVED --> REVIEW : Erneute Pruefung
|
||||
APPROVED --> ARCHIVED : Archivieren
|
||||
ARCHIVED --> DRAFT : Reaktivieren
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DB-Tabellen
|
||||
|
||||
### Migrationshistorie
|
||||
|
||||
| Migration | Datei | Inhalt |
|
||||
|-----------|-------|--------|
|
||||
| 006 | `006_vvt.sql` | Initiale VVT-Tabellen (organization, activities, audit_log) |
|
||||
| 033 | `033_vvt_consolidation.sql` | Schema-Konsolidierung |
|
||||
| 035 | `035_vvt_tenant_isolation.sql` | Tenant-Isolation |
|
||||
| **064** | `064_vvt_master_libraries.sql` | 8 Library-Tabellen (global) |
|
||||
| **065** | `065_vvt_library_seed.sql` | Seed-Daten (~150 Eintraege) |
|
||||
| **066** | `066_vvt_process_templates.sql` | Template-Tabelle + 13 neue Spalten auf `compliance_vvt_activities` |
|
||||
| **067** | `067_vvt_process_templates_seed.sql` | 18 Prozess-Templates |
|
||||
|
||||
### Tabellen-Uebersicht
|
||||
|
||||
| Tabelle | Typ | Tenant-scoped | Eintraege |
|
||||
|---------|-----|--------------|-----------|
|
||||
| `compliance_vvt_organization` | Daten | Ja | 1 pro Tenant |
|
||||
| `compliance_vvt_activities` | Daten | Ja | n pro Tenant |
|
||||
| `compliance_vvt_audit_log` | Audit | Ja | Unbegrenzt |
|
||||
| `vvt_lib_data_subjects` | Library | **Nein** (global) | 15 |
|
||||
| `vvt_lib_data_categories` | Library | **Nein** (global) | 35 |
|
||||
| `vvt_lib_recipients` | Library | **Nein** (global) | 15 |
|
||||
| `vvt_lib_legal_bases` | Library | **Nein** (global) | 12 |
|
||||
| `vvt_lib_retention_rules` | Library | **Nein** (global) | 12 |
|
||||
| `vvt_lib_transfer_mechanisms` | Library | **Nein** (global) | 8 |
|
||||
| `vvt_lib_purposes` | Library | **Nein** (global) | 20 |
|
||||
| `vvt_lib_toms` | Library | **Nein** (global) | 20 |
|
||||
| `vvt_process_templates` | Hybrid | System + Tenant | 18 (System) |
|
||||
|
||||
---
|
||||
|
||||
## Datei-Uebersicht
|
||||
|
||||
### Backend
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `compliance/db/vvt_models.py` | SQLAlchemy Models: Organization, Activity, AuditLog |
|
||||
| `compliance/db/vvt_library_models.py` | SQLAlchemy Models: 8 Libraries + ProcessTemplate |
|
||||
| `compliance/api/vvt_routes.py` | Activity CRUD, Export, Stats, Completeness |
|
||||
| `compliance/api/vvt_library_routes.py` | Library GET-Endpoints, Template CRUD + Instantiate |
|
||||
| `compliance/api/schemas.py` | Pydantic Schemas (VVTActivityCreate/Update/Response) |
|
||||
|
||||
### Frontend
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `admin-compliance/app/sdk/vvt/page.tsx` | VVT-Seite (3 Tabs: Verzeichnis, Editor, Export) |
|
||||
| `admin-compliance/lib/sdk/vvt-types.ts` | TypeScript-Typen, Library-Interfaces, Helper |
|
||||
| `admin-compliance/lib/sdk/vvt-profiling.ts` | Scope-basierte Generierung |
|
||||
|
||||
### Migrationen
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `migrations/064_vvt_master_libraries.sql` | 8 Library-Tabellen |
|
||||
| `migrations/065_vvt_library_seed.sql` | Seed: ~150 Eintraege |
|
||||
| `migrations/066_vvt_process_templates.sql` | Template-Tabelle + 13 Spalten auf Activities |
|
||||
| `migrations/067_vvt_process_templates_seed.sql` | Seed: 18 Prozess-Templates |
|
||||
|
||||
---
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
# Backend: Alle VVT-Tests (Library + Routes)
|
||||
cd backend-compliance && python3 -m pytest tests/test_vvt_library_routes.py tests/test_vvt_routes.py -v
|
||||
|
||||
# Backend: Nur Library-Tests (54 Tests)
|
||||
python3 -m pytest tests/test_vvt_library_routes.py -v
|
||||
|
||||
# Backend: Nur bestehende VVT-Tests (49 Tests)
|
||||
python3 -m pytest tests/test_vvt_routes.py -v
|
||||
|
||||
# Frontend: Scope → VVT Integration (35 Tests)
|
||||
cd admin-compliance && npx vitest run lib/sdk/__tests__/vvt-scope-integration.test.ts
|
||||
```
|
||||
|
||||
### Testabdeckung
|
||||
|
||||
| Testdatei | Tests | Abdeckung |
|
||||
|-----------|-------|-----------|
|
||||
| `test_vvt_library_routes.py` | 54 | Models, Helpers, Endpoints, Completeness, Schema-Compat |
|
||||
| `test_vvt_routes.py` | 49 | CRUD, Export, Stats, Audit, Organization |
|
||||
| `vvt-scope-integration.test.ts` | 35 | Profile→Scope Prefill, Scope→VVT Export, Generator, Full Pipeline, Dept. Data Categories, Block 9 Mapping |
|
||||
| **Gesamt** | **138** | |
|
||||
|
||||
---
|
||||
|
||||
## Frontend: 5-Tab-Aufbau
|
||||
|
||||
### Tab 1: Verzeichnis
|
||||
|
||||
- **Statistik-Kacheln:** Gesamt, Genehmigt, Entwurf, Drittland, Art. 9
|
||||
- **Filter:** Status, Drittland, Art. 9
|
||||
- **Suche:** VVT-ID, Name, Beschreibung
|
||||
- **Sortierung:** Name, Datum, Status
|
||||
- **"Aus Vorlage erstellen":** Template-Picker-Modal mit 18 Templates, filterbar nach Geschaeftsbereich
|
||||
- **"Neue Verarbeitung":** Leere Aktivitaet erstellen
|
||||
- **"Aus Scope generieren":** Automatische Generierung aus Compliance-Scope-Antworten
|
||||
|
||||
### Tab 2: Editor
|
||||
|
||||
- **Formular-Sections:** Grunddaten, Zwecke, Rechtsgrundlagen, Betroffene, Datenkategorien, Empfaenger, Drittlandtransfers, Loeschfristen, TOMs, Systeme, Datenquellen, Datenfluesse
|
||||
- **Library-Unterstuetzung:** Dropdown-Auswahl aus Master-Libraries fuer alle strukturierten Felder
|
||||
- **Freitext-Fallback:** Manuelle Eingabe bleibt immer moeglich
|
||||
|
||||
### Tab 3: Export & Compliance
|
||||
|
||||
- **Compliance-Check:** Pruefung aller Pflichtfelder nach Art. 30 DSGVO
|
||||
- **Art. 30 Vollstaendigkeit:** Pro-Aktivitaet-Fortschrittsbalken
|
||||
- **Organisations-Header:** DSB-Kontakt, Version, Pruefintervall
|
||||
- **JSON-Export:** Vollstaendiger Export aller Aktivitaeten + Metadaten
|
||||
- **CSV-Export:** Excel-kompatibel (Semikolon, UTF-8 BOM)
|
||||
- **Statistik:** Zaehler nach Geschaeftsbereichen und Datenkategorien
|
||||
|
||||
### Tab 4: VVT-Dokument (Druckansicht)
|
||||
|
||||
Generiert ein vollstaendiges, druckbares VVT-Dokument gemaess Art. 30 Abs. 1 DSGVO:
|
||||
|
||||
- **Deckblatt:** Organisation, DSB-Kontakt, Versionierung, Pruefintervall, Erstellungsdatum
|
||||
- **Inhaltsverzeichnis:** Automatisch nummerierte Eintraege aller Verarbeitungstaetigkeiten
|
||||
- **Aktivitaeten-Tabellen:** Pro Aktivitaet eine zweispaltige Tabelle mit allen Pflichtfeldern:
|
||||
- Zweck der Verarbeitung, Rechtsgrundlagen, Betroffene Personen
|
||||
- Datenkategorien (mit Art. 9/10 Kennzeichnung), Empfaengerkategorien
|
||||
- Drittlandtransfers mit Transfermechanismus
|
||||
- Loeschfristen, TOMs, Systeme, Schutzbedarfsstufe
|
||||
- **Library-Aufloesung:** IDs werden automatisch zu deutschen Labels aufgeloest (DATA_SUBJECT_CATEGORY_META, PERSONAL_DATA_CATEGORY_META, LEGAL_BASIS_META, TRANSFER_MECHANISM_META)
|
||||
- **PDF-Druck:** Via `window.open()` + `window.print()` — eigenstaendiges HTML mit Inline-CSS und `@media print`-Regeln fuer Seitenumbrueche
|
||||
- **HTML-Download:** Vollstaendiges Dokument als HTML-Datei speicherbar
|
||||
|
||||
### Tab 5: Auftragsverarbeiter (Art. 30 Abs. 2)
|
||||
|
||||
Eigenstaendiges Verzeichnis fuer Auftragsverarbeiter-Taetigkeiten gemaess Art. 30 Abs. 2 DSGVO:
|
||||
|
||||
- **Pflichtfelder (Art. 30 Abs. 2):**
|
||||
- Name und Kontakt des Auftragsverarbeiters
|
||||
- Kategorien der Verarbeitungen (fuer jeden Verantwortlichen)
|
||||
- Unterauftragsverarbeiter-Kette (Name, Zweck, Land, Drittland-Kennzeichnung)
|
||||
- Drittlandtransfers mit Transfermechanismen
|
||||
- Technisch-Organisatorische Massnahmen (TOMs)
|
||||
- **Editor:** Vollstaendiges Formular mit FormSection/FormField-Komponenten
|
||||
- **Listenansicht:** Karten mit Auftraggeber, Anzahl Kategorien, Unterauftragnehmer, Status-Badge
|
||||
- **Status-Workflow:** DRAFT → REVIEW → APPROVED → ARCHIVED
|
||||
- **PDF-Druck:** Eigene Druckfunktion fuer das Auftragsverarbeiter-Verzeichnis
|
||||
- **Rechtshinweis:** Infobox mit Erlaeuterung der Art. 30 Abs. 2 Anforderungen
|
||||
|
||||
!!! note "Datenhaltung"
|
||||
Auftragsverarbeiter-Eintraege werden aktuell im Frontend-State verwaltet.
|
||||
Backend-Persistenz (eigene DB-Tabelle + API-Endpoints) ist fuer Phase 2 geplant.
|
||||
|
||||
---
|
||||
|
||||
## Compliance-Kontext
|
||||
|
||||
| Verknuepftes Modul | Beziehung |
|
||||
|-------------------|-----------|
|
||||
| **Company Profile** | Stammdaten (DSB, Branche, Mitarbeiter, Angebote) fliessen via Scope in VVT |
|
||||
| **Compliance Scope** | Scope-Antworten (Block 8+9) generieren VVT-Aktivitaeten per Knopfdruck |
|
||||
| **DSFA** (Art. 35) | VVT-Aktivitaet referenziert DSFA ueber `dsfa_id` |
|
||||
| **Loeschfristen** | Cross-Modul-Link ueber `linked_loeschfristen_ids` (Phase 2) |
|
||||
| **TOM** (Art. 32) | Cross-Modul-Link ueber `linked_tom_measure_ids` (Phase 2) |
|
||||
|
||||
---
|
||||
|
||||
## Datenfluss: Company Profile → Compliance Scope → VVT Generator
|
||||
|
||||
Das VVT-Modul bezieht seine Daten aus einer **3-stufigen Pipeline**, in der keine Daten doppelt
|
||||
abgefragt werden. Das Company Profile (`/sdk/company-profile`) dient als Single Source of Truth
|
||||
fuer Stammdaten. Der Compliance Scope (`/sdk/compliance-scope`) fungiert als impliziter
|
||||
**Daten-Verteilungs-Agent** — jede Scope-Frage deklariert per `mapsToVVTQuestion`-Eigenschaft,
|
||||
welche VVT-Frage sie befuellt.
|
||||
|
||||
### Gesamtablauf
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant CP as Company Profile
|
||||
participant SE as Compliance Scope<br/>(Block 1-9)
|
||||
participant VG as VVT Generator
|
||||
participant DB as PostgreSQL
|
||||
|
||||
Note over CP: Stammdaten:<br/>DSB, Branche, Mitarbeiter,<br/>Angebote, Geschaeftsmodell
|
||||
|
||||
CP->>SE: prefillFromCompanyProfile(profile)<br/>+ getAutoFilledScoringAnswers(profile)
|
||||
Note over SE: Auto-Prefill:<br/>dpoName → org_has_dsb<br/>offerings → prod_type<br/>employeeCount → org_employee_count
|
||||
|
||||
SE->>SE: Block 8: Abteilungswahl<br/>(personal, finanzen, marketing, ...)
|
||||
SE->>SE: Block 9: Datenkategorien pro Abteilung<br/>(dk_dept_hr → NAME, SALARY_DATA, ...)
|
||||
|
||||
SE->>VG: exportToVVTAnswers(scopeAnswers)<br/>→ mapsToVVTQuestion-Mapping
|
||||
Note over VG: prefillFromScopeAnswers():<br/>Scope-Antworten → ProfilingAnswers
|
||||
|
||||
VG->>VG: generateActivities(profilingAnswers)<br/>→ Template-Triggering + Enrichment
|
||||
|
||||
VG->>DB: POST /vvt/activities (je Aktivitaet)
|
||||
Note over DB: VVT-Aktivitaeten gespeichert<br/>mit Library-Refs + Freitext
|
||||
```
|
||||
|
||||
### Stufe 1: Company Profile → Scope (automatisches Prefill)
|
||||
|
||||
Beim Oeffnen des Compliance-Scope-Moduls werden Stammdaten aus dem Company Profile automatisch
|
||||
in Scope-Antworten ueberfuehrt — der Nutzer muss diese Daten nicht erneut eingeben.
|
||||
|
||||
**Funktion:** `prefillFromCompanyProfile()` (`compliance-scope-profiling.ts:764`)
|
||||
|
||||
| Company Profile Feld | Scope-Frage | Mapping |
|
||||
|----------------------|-------------|---------|
|
||||
| `dpoName` (nicht leer) | `org_has_dsb` | `true` |
|
||||
| `offerings` (WebApp) | `prod_type` | `['webapp']` |
|
||||
| `offerings` (SaaS) | `prod_type` | `['saas']` |
|
||||
| `offerings` (Webshop) | `prod_webshop` | `true` |
|
||||
|
||||
**Funktion:** `getAutoFilledScoringAnswers()` (`compliance-scope-profiling.ts:844`)
|
||||
|
||||
| Company Profile Feld | Scope-Frage | Zweck |
|
||||
|----------------------|-------------|-------|
|
||||
| `employeeCount` | `org_employee_count` | Scoring + Art. 30 Abs. 5 Pruefung |
|
||||
| `annualRevenue` | `org_annual_revenue` | Scoring |
|
||||
| `industry` | `org_industry` | Scoring + Branchenkontext |
|
||||
| `businessModel` | `org_business_model` | Scoring |
|
||||
|
||||
### Stufe 2: Compliance Scope — Block 8 + Block 9
|
||||
|
||||
#### Block 8: Verarbeitungstaetigkeiten (Abteilungswahl)
|
||||
|
||||
Die Frage `vvt_departments` (Multi-Choice) bestimmt, welche Abteilungen personenbezogene Daten verarbeiten:
|
||||
|
||||
| Auswahl-Wert | Department-Key(s) | Beschreibung |
|
||||
|--------------|-------------------|-------------|
|
||||
| `personal` | `dept_hr`, `dept_recruiting` | Personal + Bewerbermanagement |
|
||||
| `finanzen` | `dept_finance` | Finanzen & Buchhaltung |
|
||||
| `vertrieb` | `dept_sales` | Vertrieb & CRM |
|
||||
| `marketing` | `dept_marketing` | Marketing |
|
||||
| `it` | `dept_it` | IT / Administration |
|
||||
| `recht` | `dept_recht` | Recht / Compliance |
|
||||
| `kundenservice` | `dept_support` | Kundenservice / Support |
|
||||
| `produktion` | `dept_produktion` | Produktion / Fertigung |
|
||||
| `logistik` | `dept_logistik` | Logistik / Versand |
|
||||
| `einkauf` | `dept_einkauf` | Einkauf / Beschaffung |
|
||||
| `facility` | `dept_facility` | Facility Management |
|
||||
|
||||
Das Mapping erfolgt in `ScopeWizardTab.tsx` ueber `DEPT_VALUE_TO_KEY`.
|
||||
|
||||
#### Block 9: Datenkategorien pro Abteilung
|
||||
|
||||
Fuer jede in Block 8 gewaehlte Abteilung wird eine eigene Frage mit spezifischen Datenkategorien angezeigt.
|
||||
Die Datenkategorien stammen aus `DEPARTMENT_DATA_CATEGORIES` (`vvt-profiling.ts:306`).
|
||||
|
||||
**12 Abteilungen mit insgesamt ~80 Datenkategorien:**
|
||||
|
||||
| Abteilung | Scope-Frage | VVT-Mapping | Kategorien (Auswahl) | Art. 9 |
|
||||
|-----------|-------------|-------------|---------------------|--------|
|
||||
| Personal (HR) | `dk_dept_hr` | `dept_hr_categories` | Stammdaten, Gehalt, SV-Nr., Bankverbindung, Gesundheit, Religion | Gesundheit, Religion |
|
||||
| Recruiting | `dk_dept_recruiting` | `dept_recruiting_categories` | Bewerberstammdaten, Bewerbungsunterlagen, Qualifikationen | Gesundheit |
|
||||
| Finanzen | `dk_dept_finance` | `dept_finance_categories` | Kunden-/Lieferantendaten, Bankverbindungen, Steuer-IDs, Rechnungen | — |
|
||||
| Vertrieb | `dk_dept_sales` | `dept_sales_categories` | Kontaktdaten, CRM-Daten, Kommunikation, Vertragsdaten | — |
|
||||
| Marketing | `dk_dept_marketing` | `dept_marketing_categories` | E-Mail, Tracking, Consent, Social-Media, Interessenprofil | — |
|
||||
| Support | `dk_dept_support` | `dept_support_categories` | Kundenstammdaten, Tickets, Kommunikation, Vertragsdaten | — |
|
||||
| IT | `dk_dept_it` | `dept_it_categories` | Benutzerkonten, Logs, Geraete, Netzwerk, E-Mail, Backups | — |
|
||||
| Recht | `dk_dept_recht` | `dept_recht_categories` | Vertraege, Compliance-Daten, Vorfaelle, Strafrechtliche Daten | Strafrechtlich |
|
||||
| Produktion | `dk_dept_produktion` | `dept_produktion_categories` | Schichtplaene, Mitarbeiterdaten, Arbeitsschutz, Zugang | Gesundheit |
|
||||
| Logistik | `dk_dept_logistik` | `dept_logistik_categories` | Empfaenger, Versandadressen, Sendungsverfolgung, Fahrer | — |
|
||||
| Einkauf | `dk_dept_einkauf` | `dept_einkauf_categories` | Lieferantenkontakte, Vertraege, Bankverbindungen | — |
|
||||
| Facility | `dk_dept_facility` | `dept_facility_categories` | Zutrittsdaten, Dienstleister, Videoueberwachung, Besucher | Gesundheit |
|
||||
|
||||
**Smart Auto-Prefill:** Beim erstmaligen Aufklappen einer Abteilungskarte werden automatisch
|
||||
alle Kategorien mit `isTypical: true` vorausgewaehlt. Art.-9-Kategorien werden orange hervorgehoben.
|
||||
|
||||
### Stufe 3: Scope → VVT Generator
|
||||
|
||||
**Funktion:** `exportToVVTAnswers()` (`compliance-scope-profiling.ts:987`)
|
||||
|
||||
Jede Scope-Frage, die ein `mapsToVVTQuestion`-Attribut traegt, wird in das VVT-Antwort-Format
|
||||
ueberfuehrt. Das Mapping ist **deklarativ** — kein imperativer Verteilungscode.
|
||||
|
||||
**Funktion:** `generateActivities()` (`vvt-profiling.ts:459`)
|
||||
|
||||
1. **Template-Triggering:** Jede VVT-Profiling-Frage hat ein `triggersTemplates`-Array. Wird eine Boolean-Frage mit `true` beantwortet, werden alle referenzierten Templates aktiviert.
|
||||
2. **IT-Baseline:** Die 4 IT-Templates (Systemadministration, Backup, Logging, IAM) werden **immer** generiert.
|
||||
3. **Enrichment:** `enrichActivityFromAnswers()` reichert Aktivitaeten an:
|
||||
- `transfer_cloud_us = true` → US-Drittlandtransfer mit SCC + TIA auf jede Aktivitaet
|
||||
- `data_health = true` → `HEALTH_DATA` + Art. 9 Rechtsgrundlage auf HR-Aktivitaeten
|
||||
- `data_minors = true` → `MINORS` als Betroffenenkategorie auf Support/Engineering
|
||||
- `special_ai / special_video_surveillance` → `dpiaRequired = true`
|
||||
4. **Art. 30 Abs. 5 Pruefung:** `< 250 Mitarbeiter UND keine besonderen Kategorien → Ausnahme moeglich`
|
||||
|
||||
### 16 vorbefuellte Fragen (SCOPE_PREFILLED_VVT_QUESTIONS)
|
||||
|
||||
Diese VVT-Profiling-Fragen werden automatisch aus Scope-Antworten befuellt,
|
||||
sodass der Nutzer sie nicht doppelt beantworten muss:
|
||||
|
||||
| VVT-Frage | Quelle (Scope-Block) |
|
||||
|-----------|---------------------|
|
||||
| `org_industry` | Block 1 (Organisation) |
|
||||
| `org_employees` | Company Profile |
|
||||
| `org_b2b_b2c` | Block 1 |
|
||||
| `dept_hr` | Block 2/8 (Abteilungen) |
|
||||
| `dept_finance` | Block 2/8 |
|
||||
| `dept_marketing` | Block 2/8 |
|
||||
| `data_health` | Block 2/4 (Datenkategorien) |
|
||||
| `data_minors` | Block 2/4 |
|
||||
| `data_biometric` | Block 2/4 |
|
||||
| `data_criminal` | Block 2/4 |
|
||||
| `special_ai` | Block 3/7 (Besondere Verarbeitungen) |
|
||||
| `special_video_surveillance` | Block 3/4 |
|
||||
| `special_tracking` | Block 3/4 |
|
||||
| `transfer_cloud_us` | Block 4 (Drittland) |
|
||||
| `transfer_subprocessor` | Block 4 |
|
||||
| `transfer_support_non_eu` | Block 4 |
|
||||
|
||||
### Cross-Modul-Datenverteilung
|
||||
|
||||
Dasselbe deklarative Muster wird auch fuer andere Module verwendet:
|
||||
|
||||
| Modul | Export-Funktion | Mapping-Attribut |
|
||||
|-------|----------------|-----------------|
|
||||
| VVT | `exportToVVTAnswers()` | `mapsToVVTQuestion` |
|
||||
| Loeschfristen | `exportToLoeschfristenAnswers()` | `mapsToLFQuestion` |
|
||||
| TOM Generator | `exportToTOMProfile()` | (direkte Ableitung) |
|
||||
|
||||
**Bidirektionale Mappings:** `prefillFromVVTAnswers()` und `prefillFromLoeschfristenAnswers()`
|
||||
ermoeglichen auch den Rueckfluss: Wenn ein Nutzer zuerst das VVT-Modul verwendet, koennen
|
||||
diese Antworten in den Scope uebernommen werden.
|
||||
|
||||
---
|
||||
|
||||
## Datei-Uebersicht: Scope-Integration
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|-------------|
|
||||
| `lib/sdk/compliance-scope-profiling.ts` | Scope Engine: 9 Bloecke, Export-Funktionen, Company-Profile-Prefill |
|
||||
| `lib/sdk/vvt-profiling.ts` | VVT-Profiling: 25 Fragen, 12 Abteilungs-Datenkategorien, Generator |
|
||||
| `lib/sdk/vvt-baseline-catalog.ts` | 18 Baseline-Templates mit Freitext-Feldern |
|
||||
| `components/sdk/compliance-scope/ScopeWizardTab.tsx` | Block-9-UI: Accordion, Auto-Prefill, Art.-9-Badges |
|
||||
| `lib/sdk/__tests__/vvt-scope-integration.test.ts` | 35 Integrationstests fuer die gesamte Pipeline |
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Geplante Erweiterungen
|
||||
|
||||
| Feature | Beschreibung |
|
||||
|---------|-------------|
|
||||
| Processor Records Backend | DB-Tabelle + API-Endpoints fuer Art. 30 Abs. 2 Auftragsverarbeiter |
|
||||
| Link-Tabellen | M:N-Verknuepfungen VVT <-> Loeschfristen und VVT <-> TOM |
|
||||
| Bidirektionaler Sync | Link-Operation aktualisiert auch Ziel-Modul |
|
||||
| VVT Generator Service | Backend-basierte Auto-Fill Engine mit Company Profile + Templates |
|
||||
| LibraryAutocomplete-Komponente | Wiederverwendbare Frontend-Komponente fuer alle Library-Felder |
|
||||
| Word/DOCX-Export | Ergaenzung des PDF-Drucks um nativen Word-Export |
|
||||
@@ -1,33 +1,253 @@
|
||||
# Whistleblower — Hinweisgebersystem
|
||||
|
||||
HinSchG-konformes Hinweisgebersystem fuer anonyme Meldungen und sichere Fallbearbeitung.
|
||||
HinSchG-konformes Hinweisgebersystem fuer anonyme und namentliche Meldungen, sichere Fallbearbeitung und automatische Fristenueberwachung.
|
||||
|
||||
## Gesetzliche Grundlage
|
||||
|
||||
### Hinweisgeberschutzgesetz (HinSchG)
|
||||
|
||||
Das **Hinweisgeberschutzgesetz (HinSchG)** setzt die EU-Whistleblowing-Richtlinie (EU) 2019/1937 in deutsches Recht um. Es ist seit dem **2. Juli 2023** in Kraft.
|
||||
|
||||
| Datum | Pflicht |
|
||||
|-------|---------|
|
||||
| 02.07.2023 | Inkrafttreten fuer Unternehmen ab 250 Beschaeftigten |
|
||||
| 17.12.2023 | Erweiterung auf Unternehmen ab 50 Beschaeftigten (ss 12 HinSchG) |
|
||||
|
||||
### Sachlicher Anwendungsbereich (ss 2 HinSchG)
|
||||
|
||||
Das Gesetz schuetzt Hinweisgeber, die Informationen ueber **Verstoesse** melden, die unter anderem folgende Bereiche betreffen:
|
||||
|
||||
- **Strafrecht** — Straftaten nach StGB und Nebenstrafrecht
|
||||
- **Datenschutz** — Verstoesse gegen DSGVO und BDSG
|
||||
- **Geldwaesche** — Verstoesse gegen das Geldwaeschegesetz (GwG)
|
||||
- **Produktsicherheit** — Verstoesse gegen Produktsicherheitsvorschriften
|
||||
- **Umweltschutz** — Verstoesse gegen Umweltauflagen und -gesetze
|
||||
- **Arbeitsschutz** — Verstoesse gegen Arbeitnehmerrechte und Arbeitsschutzvorschriften
|
||||
- **Lebensmittelsicherheit** — Verstoesse gegen lebensmittelrechtliche Vorgaben
|
||||
- **Wettbewerbs- und Kartellrecht** — Unlauterer Wettbewerb, Marktmanipulation
|
||||
- **Verbraucherschutz** — Verstoesse gegen Verbraucherschutzvorschriften
|
||||
- **Steuerrecht** — Steuerverstoesse bei Unternehmen
|
||||
|
||||
### Geschuetzte Personengruppen (ss 1 HinSchG)
|
||||
|
||||
- Arbeitnehmerinnen und Arbeitnehmer
|
||||
- Beamtinnen und Beamte
|
||||
- Auszubildende und Praktikanten
|
||||
- Freiwillige und ehrenamtlich Taetige
|
||||
- Selbststaendige und Anteilseigner
|
||||
- Mitglieder von Leitungs- und Aufsichtsorganen
|
||||
- Personen, die im Rahmen einer Bewerbung Informationen erlangt haben
|
||||
|
||||
### Fristen und Pflichten
|
||||
|
||||
| Pflicht | Frist | Rechtsgrundlage |
|
||||
|---------|-------|-----------------|
|
||||
| Eingangsbestaetigung | **7 Tage** nach Meldungseingang | ss 17 Abs. 1 S. 2 HinSchG |
|
||||
| Rueckmeldung ueber Folgemaßnahmen | **3 Monate** nach Eingangsbestaetigung | ss 17 Abs. 2 HinSchG |
|
||||
| Aufbewahrung der Dokumentation | **3 Jahre** nach Abschluss des Verfahrens | ss 11 Abs. 5 HinSchG |
|
||||
|
||||
### Schutz des Hinweisgebers
|
||||
|
||||
| Schutzmechanismus | Rechtsgrundlage | Beschreibung |
|
||||
|-------------------|-----------------|--------------|
|
||||
| **Repressalienverbot** | ss 36 HinSchG | Jede Benachteiligung wegen einer Meldung ist verboten |
|
||||
| **Beweislastumkehr** | ss 36 Abs. 2 HinSchG | Arbeitgeber muss beweisen, dass Maßnahmen nicht mit Meldung zusammenhaengen |
|
||||
| **Schadensersatz** | ss 37 HinSchG | Hinweisgeber hat Anspruch auf Ersatz des erlittenen Schadens |
|
||||
| **Vertraulichkeit** | ss 8 HinSchG | Identitaet darf nur mit Einwilligung oder bei gesetzlicher Pflicht offengelegt werden |
|
||||
|
||||
### Sanktionen (ss 40 HinSchG)
|
||||
|
||||
| Verstoß | Bussgeld |
|
||||
|---------|----------|
|
||||
| Keine interne Meldestelle eingerichtet | Bis zu **20.000 EUR** |
|
||||
| Behinderung einer Meldung | Bis zu **50.000 EUR** |
|
||||
| Verstoß gegen Vertraulichkeitsgebot | Bis zu **50.000 EUR** |
|
||||
| Repressalien gegen Hinweisgeber | Bis zu **50.000 EUR** |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Anonyme Meldungen** — Sichere, anonyme Eingabe von Hinweisen
|
||||
- **Fallbearbeitung** — Workflow fuer Sichtung, Untersuchung und Abschluss
|
||||
- **Fristen-Management** — Automatische Ueberwachung der gesetzlichen Bearbeitungsfristen (7 Tage Eingangsbestaetigung, 3 Monate Rueckmeldung)
|
||||
- **Kommunikationskanal** — Anonymer Austausch zwischen Hinweisgeber und Ombudsperson
|
||||
- **Audit-Trail** — Lueckenlose Dokumentation aller Bearbeitungsschritte
|
||||
- **Anonyme und namentliche Meldungen** — Sichere Eingabe von Hinweisen, wahlweise anonym oder mit Kontaktdaten
|
||||
- **Mehrstufiger Fallbearbeitungs-Workflow** — Neu → Bestaetigt → In Pruefung → Untersuchung → Massnahmen → Abgeschlossen
|
||||
- **Automatisches Fristen-Management** — Ueberwachung der 7-Tage- und 3-Monate-Fristen gemaess ss 17 HinSchG mit Warnungen bei drohender Ueberschreitung
|
||||
- **Anonymer Kommunikationskanal** — Sicherer Austausch zwischen Hinweisgeber und Ombudsperson ohne Identitaetspreisgabe
|
||||
- **Massnahmen-Tracking** — Dokumentation und Nachverfolgung von Folgemaßnahmen
|
||||
- **Prioritaetsstufen** — Klassifizierung nach Niedrig, Normal, Hoch, Kritisch
|
||||
- **Audit-Trail** — Lueckenlose, revisionssichere Dokumentation aller Bearbeitungsschritte
|
||||
- **Kategorisierung** — Vordefinierte Meldekategorien (Korruption, Betrug, Datenschutz, Diskriminierung, Umwelt, Wettbewerb, Produktsicherheit, Steuerhinterziehung)
|
||||
- **Statistik-Dashboard** — Ueberblick ueber Meldungsaufkommen, Bearbeitungsstand und Fristeneinhaltung
|
||||
|
||||
---
|
||||
|
||||
## Architektur
|
||||
|
||||
### Bearbeitungs-Workflow
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Neu: Meldung eingereicht
|
||||
Neu --> Bestaetigt: Eingangsbestaetigung (≤ 7 Tage)
|
||||
Bestaetigt --> InPruefung: Inhaltliche Pruefung
|
||||
InPruefung --> Untersuchung: Formelle Untersuchung
|
||||
Untersuchung --> Massnahmen: Folgemaßnahmen eingeleitet
|
||||
Massnahmen --> Abgeschlossen: Rueckmeldung (≤ 3 Monate)
|
||||
InPruefung --> Abgelehnt: Unbegruendet / nicht zustaendig
|
||||
Abgeschlossen --> [*]
|
||||
Abgelehnt --> [*]
|
||||
```
|
||||
|
||||
### Komponenten
|
||||
|
||||
| Komponente | Technologie | Beschreibung |
|
||||
|------------|-------------|--------------|
|
||||
| Frontend (Admin) | Next.js / React | SDK-Seite unter `/sdk/whistleblower` mit Tabs: Uebersicht, Neue Meldungen, In Untersuchung, Abgeschlossen, Einstellungen |
|
||||
| API-Proxy | Next.js API Route | `/api/sdk/v1/whistleblower/[[...path]]/route.ts` — Proxy zum Backend |
|
||||
| Backend-Handlers | Go / Gin | `whistleblower_handlers.go` — REST API fuer Meldungen, Nachrichten, Statistiken |
|
||||
| Datenschicht | Go | `store.go` — PostgreSQL-Operationen, prepared statements |
|
||||
| Datenmodell | Go | `models.go` — Structs fuer Report, Message, Measure, AuditEntry |
|
||||
| TypeScript Types | TypeScript | `lib/sdk/whistleblower/types.ts` — Frontend-Typen, Deadline-Utilities |
|
||||
| API Client | TypeScript | `lib/sdk/whistleblower/api.ts` — SDK-API-Aufrufe |
|
||||
| DB-Schema | SQL | `migrations/009_whistleblower_schema.sql` |
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
Alle unter `/api/v1/whistleblower/`, benoetigen `X-Tenant-ID` Header.
|
||||
|
||||
### Admin-Endpoints
|
||||
|
||||
| Method | Endpoint | Beschreibung |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/reports` | Meldungen auflisten |
|
||||
| POST | `/reports` | Neue Meldung erstellen |
|
||||
| GET | `/reports/{id}` | Meldungsdetails |
|
||||
| PUT | `/reports/{id}/status` | Status aktualisieren |
|
||||
| POST | `/reports/{id}/messages` | Nachricht hinzufuegen |
|
||||
| GET | `/reports/{id}/messages` | Nachrichten abrufen |
|
||||
| GET | `/statistics` | Statistiken |
|
||||
| GET | `/reports` | Meldungen auflisten (mit Filter und Paginierung) |
|
||||
| POST | `/reports` | Neue Meldung erfassen |
|
||||
| GET | `/reports/{id}` | Meldungsdetails inkl. Nachrichten, Massnahmen, Audit-Trail |
|
||||
| PUT | `/reports/{id}` | Meldung aktualisieren (Status, Prioritaet, Zuweisung) |
|
||||
| DELETE | `/reports/{id}` | Meldung loeschen |
|
||||
| POST | `/reports/{id}/messages` | Nachricht an Hinweisgeber senden (Ombudsperson-Rolle) |
|
||||
| GET | `/reports/{id}/messages` | Nachrichten-Verlauf abrufen |
|
||||
| GET | `/statistics` | Statistiken (Gesamt, nach Kategorie, nach Status, ueberfaellige) |
|
||||
|
||||
### Oeffentliche Endpoints (fuer Hinweisgeber)
|
||||
|
||||
| Method | Endpoint | Beschreibung |
|
||||
|--------|----------|-------------|
|
||||
| POST | `/public/reports` | Anonyme/namentliche Meldung einreichen |
|
||||
| GET | `/public/reports/{accessKey}` | Meldungsstatus mit Zugangscode abfragen |
|
||||
| POST | `/public/reports/{accessKey}/messages` | Nachricht als Hinweisgeber senden |
|
||||
|
||||
---
|
||||
|
||||
## Frontend
|
||||
|
||||
Seite unter `/sdk/whistleblower` mit Meldungsuebersicht, Falldetails und Statistik-Dashboard.
|
||||
### SDK Admin-Seite (`/sdk/whistleblower`)
|
||||
|
||||
Die Seite bietet fuenf Tabs:
|
||||
|
||||
| Tab | Inhalt |
|
||||
|-----|--------|
|
||||
| **Uebersicht** | Statistik-Cards, HinSchG-Info-Box mit Fristen und Anwendungsbereich, Fristenwarnungen, alle Meldungen |
|
||||
| **Neue Meldungen** | Gefilterte Ansicht: nur Status "Neu" — Eingangsbestaetigung steht aus |
|
||||
| **In Untersuchung** | Gefilterte Ansicht: Status "Bestaetigt", "In Pruefung", "Untersuchung", "Massnahmen" |
|
||||
| **Abgeschlossen** | Gefilterte Ansicht: Status "Abgeschlossen" und "Abgelehnt" |
|
||||
| **Einstellungen** | Konfiguration (in spaeteren Versionen) |
|
||||
|
||||
### Funktionen
|
||||
|
||||
- **Meldung erfassen**: Modal-Dialog fuer Titel, Beschreibung, Kategorie, Prioritaet, anonym/namentlich
|
||||
- **Falldetail-Drawer**: Seitenleiste mit Beschreibung, Badges, Status-Transitions, Zuweisungen, Kommentare, Nachrichten-Verlauf
|
||||
- **Filter**: Kategorie, Status, Prioritaet — kombinierbar
|
||||
- **Sortierung**: Ueberfaellige Meldungen zuerst, dann nach Prioritaet, dann nach Datum
|
||||
- **Fristenwarnungen**: Rote Alerts bei ueberschrittenen 7-Tage- oder 3-Monate-Fristen
|
||||
|
||||
---
|
||||
|
||||
## Datenbank
|
||||
|
||||
Migration `009_whistleblower_schema.sql` erstellt Tabellen fuer Meldungen, Nachrichten und Bearbeitungsschritte.
|
||||
### Schema (Migration `009_whistleblower_schema.sql`)
|
||||
|
||||
#### Tabelle: `whistleblower_reports`
|
||||
|
||||
| Spalte | Typ | Beschreibung |
|
||||
|--------|-----|--------------|
|
||||
| `id` | UUID | Primaerschluessel |
|
||||
| `tenant_id` | UUID | Mandanten-Zuordnung |
|
||||
| `reference_number` | VARCHAR | Referenznummer (z.B. "WB-2026-000042") |
|
||||
| `access_key` | VARCHAR | Anonymer Zugangscode (XXXX-XXXX-XXXX) |
|
||||
| `category` | VARCHAR | Meldekategorie |
|
||||
| `status` | VARCHAR | Bearbeitungsstatus |
|
||||
| `priority` | VARCHAR | Prioritaetsstufe |
|
||||
| `title` | VARCHAR | Meldungstitel |
|
||||
| `description` | TEXT | Detailbeschreibung |
|
||||
| `is_anonymous` | BOOLEAN | Anonyme Meldung |
|
||||
| `reporter_name` | VARCHAR | Name (optional) |
|
||||
| `reporter_email` | VARCHAR | E-Mail (optional) |
|
||||
| `assigned_to` | VARCHAR | Zustaendige Person |
|
||||
| `received_at` | TIMESTAMP | Eingangszeitpunkt |
|
||||
| `acknowledged_at` | TIMESTAMP | Eingangsbestaetigung |
|
||||
| `deadline_acknowledgment` | TIMESTAMP | 7-Tage-Frist (automatisch berechnet) |
|
||||
| `deadline_feedback` | TIMESTAMP | 3-Monate-Frist (automatisch berechnet) |
|
||||
| `closed_at` | TIMESTAMP | Abschlusszeitpunkt |
|
||||
|
||||
#### Tabelle: `whistleblower_messages`
|
||||
|
||||
Anonymer Kommunikationskanal zwischen Hinweisgeber und Ombudsperson.
|
||||
|
||||
#### Tabelle: `whistleblower_measures`
|
||||
|
||||
Dokumentation von Folgemaßnahmen mit Status-Tracking (geplant, in Bearbeitung, abgeschlossen).
|
||||
|
||||
#### Tabelle: `whistleblower_audit_trail`
|
||||
|
||||
Lueckenlose, revisionssichere Protokollierung aller Bearbeitungsschritte.
|
||||
|
||||
---
|
||||
|
||||
## Meldekategorien
|
||||
|
||||
| Kategorie | Beschreibung | Beispiele |
|
||||
|-----------|--------------|-----------|
|
||||
| Korruption | Bestechung, Vorteilsnahme | Schmiergeldzahlungen, Kickback-Vereinbarungen |
|
||||
| Betrug | Vermoegensdelikte | Urkundenfaelschung, Bilanzbetrug |
|
||||
| Datenschutz | DSGVO/BDSG-Verstoesse | Unerlaubte Datenweitergabe, fehlende Einwilligung |
|
||||
| Diskriminierung | Benachteiligung | Mobbing, sexuelle Belaestigung, AGG-Verstoesse |
|
||||
| Umwelt | Umweltvergehen | Illegale Entsorgung, Emissionsverstoesse |
|
||||
| Wettbewerb | Kartellrecht | Preisabsprachen, Marktmanipulation |
|
||||
| Produktsicherheit | Sicherheitsmaengel | Mangelhafte Produkte, fehlende Warnhinweise |
|
||||
| Steuerhinterziehung | Steuerverstoesse | Steuerhinterziehung, illegale Steuergestaltung |
|
||||
| Sonstiges | Weitere Verstoesse | Interne Richtlinienverstoesse |
|
||||
|
||||
---
|
||||
|
||||
## Fristen-Tracking
|
||||
|
||||
Das System berechnet Fristen automatisch und warnt bei drohender Ueberschreitung:
|
||||
|
||||
| Frist | Berechnung | Warnstufe |
|
||||
|-------|------------|-----------|
|
||||
| 7-Tage-Eingangsbestaetigung | `received_at + 7 Tage` | Orange ab 2 Tage vorher, Rot bei Ueberschreitung |
|
||||
| 3-Monate-Rueckmeldung | `acknowledged_at + 3 Monate` | Orange ab 14 Tage vorher, Rot bei Ueberschreitung |
|
||||
|
||||
Utility-Funktionen in `types.ts`:
|
||||
|
||||
- `getDaysUntilAcknowledgment(report)` — Verbleibende Tage bis Eingangsbestaetigung
|
||||
- `getDaysUntilFeedback(report)` — Verbleibende Tage bis Rueckmeldungsfrist
|
||||
- `isAcknowledgmentOverdue(report)` — Prueft 7-Tage-Frist
|
||||
- `isFeedbackOverdue(report)` — Prueft 3-Monate-Frist
|
||||
- `generateAccessKey()` — Erzeugt Zugangscode im Format XXXX-XXXX-XXXX
|
||||
|
||||
---
|
||||
|
||||
## Datei-Referenz
|
||||
|
||||
| Datei | Beschreibung |
|
||||
|-------|--------------|
|
||||
| `admin-compliance/app/sdk/whistleblower/page.tsx` | Frontend-Seite (Tabs, Filter, Modals, Detail-Drawer) |
|
||||
| `admin-compliance/app/api/sdk/v1/whistleblower/[[...path]]/route.ts` | API-Proxy zum Backend |
|
||||
| `admin-compliance/lib/sdk/whistleblower/types.ts` | TypeScript-Typen, Enums, Deadline-Utilities |
|
||||
| `admin-compliance/lib/sdk/whistleblower/api.ts` | API-Client fuer SDK-Backend |
|
||||
| `ai-compliance-sdk/internal/whistleblower/models.go` | Go-Datenmodelle |
|
||||
| `ai-compliance-sdk/internal/whistleblower/store.go` | PostgreSQL-Store (CRUD, Queries) |
|
||||
| `ai-compliance-sdk/internal/api/handlers/whistleblower_handlers.go` | REST-API-Handler |
|
||||
| `ai-compliance-sdk/migrations/009_whistleblower_schema.sql` | Datenbankschema |
|
||||
|
||||
Reference in New Issue
Block a user