diff --git a/admin-compliance/app/sdk/control-library/components/ControlDetail.tsx b/admin-compliance/app/sdk/control-library/components/ControlDetail.tsx
index 0297d1c..731d2e1 100644
--- a/admin-compliance/app/sdk/control-library/components/ControlDetail.tsx
+++ b/admin-compliance/app/sdk/control-library/components/ControlDetail.tsx
@@ -212,14 +212,14 @@ export function ControlDetail({
) : null}
- {ctrl.requirements.length > 0 && (
+ {Array.isArray(ctrl.requirements) && ctrl.requirements.length > 0 && (
Anforderungen
{ctrl.requirements.map((r, i) => - {r}
)}
)}
- {ctrl.test_procedure.length > 0 && (
+ {Array.isArray(ctrl.test_procedure) && ctrl.test_procedure.length > 0 && (
Pruefverfahren
{ctrl.test_procedure.map((s, i) => - {s}
)}
diff --git a/backend-compliance/compliance/services/canonical_control_service.py b/backend-compliance/compliance/services/canonical_control_service.py
index 6a926ba..23820f0 100644
--- a/backend-compliance/compliance/services/canonical_control_service.py
+++ b/backend-compliance/compliance/services/canonical_control_service.py
@@ -40,6 +40,22 @@ _CONTROL_COLUMNS = """
"""
+def _ensure_list(val: Any) -> list:
+ """Ensure a JSONB value is always a Python list."""
+ if isinstance(val, list):
+ return val
+ if val is None:
+ return []
+ if isinstance(val, str):
+ try:
+ import json
+ parsed = json.loads(val)
+ return parsed if isinstance(parsed, list) else []
+ except (json.JSONDecodeError, TypeError):
+ return []
+ return []
+
+
def _control_row(r: Any) -> dict[str, Any]:
"""Serialize a canonical_controls SELECT row to a response dict."""
return {
@@ -49,19 +65,19 @@ def _control_row(r: Any) -> dict[str, Any]:
"title": r.title,
"objective": r.objective,
"rationale": r.rationale,
- "scope": r.scope,
- "requirements": r.requirements,
- "test_procedure": r.test_procedure,
- "evidence": r.evidence,
+ "scope": r.scope if isinstance(r.scope, dict) else {},
+ "requirements": _ensure_list(r.requirements),
+ "test_procedure": _ensure_list(r.test_procedure),
+ "evidence": _ensure_list(r.evidence),
"severity": r.severity,
"risk_score": float(r.risk_score) if r.risk_score is not None else None,
"implementation_effort": r.implementation_effort,
"evidence_confidence": (
float(r.evidence_confidence) if r.evidence_confidence is not None else None
),
- "open_anchors": r.open_anchors,
+ "open_anchors": _ensure_list(r.open_anchors),
"release_state": r.release_state,
- "tags": r.tags or [],
+ "tags": _ensure_list(r.tags),
"created_at": r.created_at.isoformat() if r.created_at else None,
"updated_at": r.updated_at.isoformat() if r.updated_at else None,
}