feat: V1 Control Enrichment — Eigenentwicklung-Label, regulatorisches Matching & Vergleichsansicht
All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 39s
CI/CD / test-python-backend-compliance (push) Successful in 32s
CI/CD / test-python-document-crawler (push) Successful in 20s
CI/CD / test-python-dsms-gateway (push) Successful in 16s
CI/CD / validate-canonical-controls (push) Successful in 9s
CI/CD / Deploy (push) Successful in 4s

863 v1-Controls (manuell geschrieben, ohne Rechtsgrundlage) werden als
"Eigenentwicklung" gekennzeichnet und automatisch mit regulatorischen
Controls (DSGVO, NIS2, OWASP etc.) per Embedding-Similarity abgeglichen.

Backend:
- Migration 080: v1_control_matches Tabelle (Cross-Reference)
- v1_enrichment.py: Batch-Matching via BGE-M3 + Qdrant (Threshold 0.75)
- 3 neue API-Endpoints: enrich-v1-matches, v1-matches, v1-enrichment-stats
- 6 Tests (dry-run, execution, matches, pagination, detection)

Frontend:
- Orange "Eigenentwicklung"-Badge statt grauem "v1" (wenn kein Source)
- "Regulatorische Abdeckung"-Sektion im ControlDetail mit Match-Karten
- Side-by-Side V1CompareView (Eigenentwicklung vs. regulatorisch gedeckt)
- Prev/Next Navigation durch alle Matches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-26 10:32:08 +01:00
parent cb034b8009
commit db7c207464
11 changed files with 939 additions and 6 deletions

View File

@@ -547,6 +547,15 @@ async def atomic_stats():
}
@router.get("/controls/v1-enrichment-stats")
async def v1_enrichment_stats_endpoint():
"""
Uebersicht: Wie viele v1 Controls haben regulatorische Abdeckung?
"""
from compliance.services.v1_enrichment import get_v1_enrichment_stats
return await get_v1_enrichment_stats()
@router.get("/controls/{control_id}")
async def get_control(control_id: str):
"""Get a single canonical control by its control_id (e.g. AUTH-001)."""
@@ -1567,6 +1576,57 @@ async def list_licenses():
return get_license_matrix(db)
# =============================================================================
# V1 ENRICHMENT (Eigenentwicklung → Regulatorische Abdeckung)
# =============================================================================
@router.post("/controls/enrich-v1-matches")
async def enrich_v1_matches_endpoint(
dry_run: bool = Query(True, description="Nur zaehlen, nicht schreiben"),
batch_size: int = Query(100, description="Controls pro Durchlauf"),
offset: int = Query(0, description="Offset fuer Paginierung"),
):
"""
Findet regulatorische Abdeckung fuer v1 Eigenentwicklung Controls.
Eigenentwicklung = generation_strategy='ungrouped', pipeline_version=1,
source_citation IS NULL, parent_control_uuid IS NULL.
Workflow:
1. dry_run=true → Statistiken anzeigen
2. dry_run=false&batch_size=100&offset=0 → Erste 100 verarbeiten
3. Wiederholen mit next_offset bis fertig
"""
from compliance.services.v1_enrichment import enrich_v1_matches
return await enrich_v1_matches(
dry_run=dry_run,
batch_size=batch_size,
offset=offset,
)
@router.get("/controls/{control_id}/v1-matches")
async def get_v1_matches_endpoint(control_id: str):
"""
Gibt regulatorische Matches fuer ein v1 Control zurueck.
Returns:
Liste von Matches mit Control-Details, Source, Score.
"""
from compliance.services.v1_enrichment import get_v1_matches
# Resolve control_id to UUID
with SessionLocal() as db:
row = db.execute(text("""
SELECT id FROM canonical_controls WHERE control_id = :cid
"""), {"cid": control_id}).fetchone()
if not row:
raise HTTPException(status_code=404, detail=f"Control {control_id} not found")
return await get_v1_matches(str(row.id))
# =============================================================================
# INTERNAL HELPERS
# =============================================================================