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
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:
@@ -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
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user