diff --git a/admin-compliance/app/(sdk)/sdk/gci/page.tsx b/admin-compliance/app/(sdk)/sdk/gci/page.tsx
new file mode 100644
index 0000000..8eed1fe
--- /dev/null
+++ b/admin-compliance/app/(sdk)/sdk/gci/page.tsx
@@ -0,0 +1,693 @@
+'use client'
+
+import React, { useState, useEffect, useCallback } from 'react'
+import {
+ GCIResult,
+ GCIBreakdown,
+ GCIHistoryResponse,
+ GCIMatrixResponse,
+ NIS2Score,
+ ISOGapAnalysis,
+ WeightProfile,
+ MaturityLevel,
+ MATURITY_INFO,
+ getScoreColor,
+ getScoreRingColor,
+} from '@/lib/sdk/gci/types'
+import {
+ getGCIScore,
+ getGCIBreakdown,
+ getGCIHistory,
+ getGCIMatrix,
+ getNIS2Score,
+ getISOGapAnalysis,
+ getWeightProfiles,
+} from '@/lib/sdk/gci/api'
+
+// =============================================================================
+// TYPES
+// =============================================================================
+
+type TabId = 'overview' | 'breakdown' | 'nis2' | 'iso' | 'matrix' | 'audit'
+
+interface Tab {
+ id: TabId
+ label: string
+}
+
+const TABS: Tab[] = [
+ { id: 'overview', label: 'Uebersicht' },
+ { id: 'breakdown', label: 'Breakdown' },
+ { id: 'nis2', label: 'NIS2' },
+ { id: 'iso', label: 'ISO 27001' },
+ { id: 'matrix', label: 'Matrix' },
+ { id: 'audit', label: 'Audit Trail' },
+]
+
+// =============================================================================
+// HELPER COMPONENTS
+// =============================================================================
+
+function TabNavigation({ tabs, activeTab, onTabChange }: { tabs: Tab[]; activeTab: TabId; onTabChange: (tab: TabId) => void }) {
+ return (
+
+
+
+ )
+}
+
+function ScoreCircle({ score, size = 144, label }: { score: number; size?: number; label?: string }) {
+ const radius = (size / 2) - 12
+ const circumference = 2 * Math.PI * radius
+ const strokeDashoffset = circumference - (score / 100) * circumference
+
+ return (
+
+
+
+ {score.toFixed(1)}
+ {label && {label}}
+
+
+ )
+}
+
+function MaturityBadge({ level }: { level: MaturityLevel }) {
+ const info = MATURITY_INFO[level] || MATURITY_INFO.HIGH_RISK
+ return (
+
+ {info.label}
+
+ )
+}
+
+function AreaScoreBar({ name, score, weight }: { name: string; score: number; weight: number }) {
+ return (
+
+
+ {name}
+ {score.toFixed(1)}%
+
+
+
Gewichtung: {(weight * 100).toFixed(0)}%
+
+ )
+}
+
+function LoadingSpinner() {
+ return (
+
+ )
+}
+
+function ErrorMessage({ message, onRetry }: { message: string; onRetry?: () => void }) {
+ return (
+
+
{message}
+ {onRetry && (
+
+ )}
+
+ )
+}
+
+// =============================================================================
+// TAB: OVERVIEW
+// =============================================================================
+
+function OverviewTab({ gci, history, profiles, selectedProfile, onProfileChange }: {
+ gci: GCIResult
+ history: GCIHistoryResponse | null
+ profiles: WeightProfile[]
+ selectedProfile: string
+ onProfileChange: (p: string) => void
+}) {
+ return (
+
+ {/* Profile Selector */}
+ {profiles.length > 0 && (
+
+
+
+
+ )}
+
+ {/* Main Score */}
+
+
+
+
+
+
Gesamt-Compliance-Index
+
+
+
+ Berechnet: {new Date(gci.calculated_at).toLocaleString('de-DE')}
+
+
+
+
+ {MATURITY_INFO[gci.maturity_level]?.description || ''}
+
+
+
+
+
+ {/* Area Scores */}
+
+
Regulierungsbereiche
+
+ {gci.area_scores.map(area => (
+
+ ))}
+
+
+
+ {/* History Chart (simplified) */}
+ {history && history.snapshots.length > 0 && (
+
+
Verlauf
+
+ {history.snapshots.map((snap, i) => (
+
+
{snap.score.toFixed(0)}
+
+
+ {new Date(snap.calculated_at).toLocaleDateString('de-DE', { month: 'short' })}
+
+
+ ))}
+
+
+ )}
+
+ {/* Adjustments */}
+
+
+
Kritikalitaets-Multiplikator
+
{gci.criticality_multiplier.toFixed(2)}x
+
+
+
Incident-Korrektur
+
+ {gci.incident_adjustment > 0 ? '+' : ''}{gci.incident_adjustment.toFixed(1)}
+
+
+
+
+ )
+}
+
+// =============================================================================
+// TAB: BREAKDOWN
+// =============================================================================
+
+function BreakdownTab({ breakdown }: { breakdown: GCIBreakdown | null; loading: boolean }) {
+ if (!breakdown) return
+
+ return (
+
+ {/* Level 1: Modules */}
+
+
Level 1: Modul-Scores
+
+
+
+
+ | Modul |
+ Kategorie |
+ Zugewiesen |
+ Abgeschlossen |
+ Raw Score |
+ Validitaet |
+ Final |
+
+
+
+ {breakdown.level1_modules.map(m => (
+
+ | {m.module_name} |
+
+
+ {m.category}
+
+ |
+ {m.assigned} |
+ {m.completed} |
+ {(m.raw_score * 100).toFixed(1)}% |
+ {(m.validity_factor * 100).toFixed(0)}% |
+
+ {(m.final_score * 100).toFixed(1)}%
+ |
+
+ ))}
+
+
+
+
+
+ {/* Level 2: Areas */}
+
+
Level 2: Regulierungsbereiche (risikogewichtet)
+
+ {breakdown.level2_areas.map(area => (
+
+
+
{area.area_name}
+
+ {area.area_score.toFixed(1)}%
+
+
+
+ {area.modules.map(m => (
+
+ {m.module_name}
+ {(m.final_score * 100).toFixed(0)}% (w:{m.risk_weight.toFixed(1)})
+
+ ))}
+
+
+ ))}
+
+
+
+ )
+}
+
+// =============================================================================
+// TAB: NIS2
+// =============================================================================
+
+function NIS2Tab({ nis2 }: { nis2: NIS2Score | null }) {
+ if (!nis2) return
+
+ return (
+
+ {/* NIS2 Overall */}
+
+
+
+
+
NIS2 Compliance Score
+
+ Network and Information Security Directive 2 (EU 2022/2555)
+
+
+
+
+
+ {/* NIS2 Areas */}
+
+
NIS2 Bereiche
+
+ {nis2.areas.map(area => (
+
+ ))}
+
+
+
+ {/* NIS2 Roles */}
+ {nis2.role_scores && nis2.role_scores.length > 0 && (
+
+
Rollen-Compliance
+
+ {nis2.role_scores.map(role => (
+
+
{role.role_name}
+
+
+ {(role.completion_rate * 100).toFixed(0)}%
+
+
+ {role.modules_completed}/{role.modules_required} Module
+
+
+
+
+ ))}
+
+
+ )}
+
+ )
+}
+
+// =============================================================================
+// TAB: ISO 27001
+// =============================================================================
+
+function ISOTab({ iso }: { iso: ISOGapAnalysis | null }) {
+ if (!iso) return
+
+ return (
+
+ {/* Coverage Overview */}
+
+
+
+
+
ISO 27001:2022 Gap-Analyse
+
+
+
{iso.covered_full}
+
Voll abgedeckt
+
+
+
{iso.covered_partial}
+
Teilweise
+
+
+
{iso.not_covered}
+
Nicht abgedeckt
+
+
+
+
+
+
+ {/* Category Summaries */}
+
+
Kategorien
+
+ {iso.category_summaries.map(cat => {
+ const coveragePercent = cat.total_controls > 0
+ ? ((cat.covered_full + cat.covered_partial * 0.5) / cat.total_controls) * 100
+ : 0
+ return (
+
+
+ {cat.category_id}: {cat.category_name}
+
+ {cat.covered_full}/{cat.total_controls} Controls
+
+
+
+
+ )
+ })}
+
+
+
+ {/* Gaps */}
+ {iso.gaps && iso.gaps.length > 0 && (
+
+
+ Offene Gaps ({iso.gaps.length})
+
+
+ {iso.gaps.map(gap => (
+
+
+ {gap.priority}
+
+
+
{gap.control_id}: {gap.control_name}
+
{gap.recommendation}
+
+
+ ))}
+
+
+ )}
+
+ )
+}
+
+// =============================================================================
+// TAB: MATRIX
+// =============================================================================
+
+function MatrixTab({ matrix }: { matrix: GCIMatrixResponse | null }) {
+ if (!matrix || !matrix.matrix) return
+
+ const regulations = matrix.matrix.length > 0 ? Object.keys(matrix.matrix[0].regulations) : []
+
+ return (
+
+
+
Compliance-Matrix (Rollen x Regulierungen)
+
+
+
+
+ | Rolle |
+ {regulations.map(r => (
+ {r} |
+ ))}
+ Gesamt |
+ Module |
+
+
+
+ {matrix.matrix.map(entry => (
+
+ | {entry.role_name} |
+ {regulations.map(r => (
+
+
+ {entry.regulations[r].toFixed(0)}%
+
+ |
+ ))}
+
+
+ {entry.overall_score.toFixed(0)}%
+
+ |
+
+ {entry.completed_modules}/{entry.required_modules}
+ |
+
+ ))}
+
+
+
+
+
+ )
+}
+
+// =============================================================================
+// TAB: AUDIT TRAIL
+// =============================================================================
+
+function AuditTab({ gci }: { gci: GCIResult }) {
+ return (
+
+
+
+ Audit Trail - Berechnung GCI {gci.gci_score.toFixed(1)}
+
+
+ Jeder Schritt der GCI-Berechnung ist nachvollziehbar und prueffaehig dokumentiert.
+
+
+ {gci.audit_trail.map((entry, i) => (
+
+
+
+
+ {entry.factor}
+
+ {entry.value > 0 ? '+' : ''}{entry.value.toFixed(2)}
+
+
+
{entry.description}
+
+
+ ))}
+
+
+
+ )
+}
+
+// =============================================================================
+// MAIN PAGE
+// =============================================================================
+
+export default function GCIPage() {
+ const [activeTab, setActiveTab] = useState('overview')
+ const [loading, setLoading] = useState(true)
+ const [error, setError] = useState(null)
+
+ const [gci, setGCI] = useState(null)
+ const [breakdown, setBreakdown] = useState(null)
+ const [history, setHistory] = useState(null)
+ const [matrix, setMatrix] = useState(null)
+ const [nis2, setNIS2] = useState(null)
+ const [iso, setISO] = useState(null)
+ const [profiles, setProfiles] = useState([])
+ const [selectedProfile, setSelectedProfile] = useState('default')
+
+ const loadData = useCallback(async (profile?: string) => {
+ setLoading(true)
+ setError(null)
+ try {
+ const [gciRes, historyRes, profilesRes] = await Promise.all([
+ getGCIScore(profile),
+ getGCIHistory(),
+ getWeightProfiles(),
+ ])
+ setGCI(gciRes)
+ setHistory(historyRes)
+ setProfiles(profilesRes.profiles || [])
+ } catch (err: any) {
+ setError(err.message || 'Fehler beim Laden der GCI-Daten')
+ } finally {
+ setLoading(false)
+ }
+ }, [])
+
+ useEffect(() => {
+ loadData(selectedProfile)
+ }, [selectedProfile, loadData])
+
+ // Lazy-load tab data
+ useEffect(() => {
+ if (activeTab === 'breakdown' && !breakdown && gci) {
+ getGCIBreakdown(selectedProfile).then(setBreakdown).catch(() => {})
+ }
+ if (activeTab === 'nis2' && !nis2) {
+ getNIS2Score().then(setNIS2).catch(() => {})
+ }
+ if (activeTab === 'iso' && !iso) {
+ getISOGapAnalysis().then(setISO).catch(() => {})
+ }
+ if (activeTab === 'matrix' && !matrix) {
+ getGCIMatrix().then(setMatrix).catch(() => {})
+ }
+ }, [activeTab, breakdown, nis2, iso, matrix, gci, selectedProfile])
+
+ const handleProfileChange = (profile: string) => {
+ setSelectedProfile(profile)
+ setBreakdown(null) // reset breakdown to reload
+ }
+
+ return (
+
+ {/* Header */}
+
+
+
Gesamt-Compliance-Index (GCI)
+
+ 4-stufiges, mathematisch fundiertes Compliance-Scoring
+
+
+
+
+
+ {/* Tabs */}
+
+
+ {/* Content */}
+ {error &&
loadData(selectedProfile)} />}
+
+ {loading && !gci ? (
+
+ ) : gci ? (
+
+ {activeTab === 'overview' && (
+
+ )}
+ {activeTab === 'breakdown' && (
+
+ )}
+ {activeTab === 'nis2' &&
}
+ {activeTab === 'iso' &&
}
+ {activeTab === 'matrix' &&
}
+ {activeTab === 'audit' &&
}
+
+ ) : null}
+
+ )
+}
diff --git a/admin-compliance/app/api/sdk/v1/gci/[[...path]]/route.ts b/admin-compliance/app/api/sdk/v1/gci/[[...path]]/route.ts
new file mode 100644
index 0000000..65b7724
--- /dev/null
+++ b/admin-compliance/app/api/sdk/v1/gci/[[...path]]/route.ts
@@ -0,0 +1,89 @@
+/**
+ * GCI API Proxy - Catch-all route
+ * Proxies all /api/sdk/v1/gci/* requests to ai-compliance-sdk backend
+ */
+
+import { NextRequest, NextResponse } from 'next/server'
+
+const SDK_BACKEND_URL = process.env.SDK_API_URL || 'http://ai-compliance-sdk:8090'
+
+async function proxyRequest(
+ request: NextRequest,
+ pathSegments: string[] | undefined,
+ method: string
+) {
+ const pathStr = pathSegments?.join('/') || ''
+ const searchParams = request.nextUrl.searchParams.toString()
+ const basePath = `${SDK_BACKEND_URL}/sdk/v1/gci`
+ const url = pathStr
+ ? `${basePath}/${pathStr}${searchParams ? `?${searchParams}` : ''}`
+ : `${basePath}${searchParams ? `?${searchParams}` : ''}`
+
+ try {
+ const headers: HeadersInit = {
+ 'Content-Type': 'application/json',
+ }
+
+ const headerNames = ['authorization', 'x-tenant-id', 'x-user-id', 'x-namespace-id', 'x-tenant-slug']
+ for (const name of headerNames) {
+ const value = request.headers.get(name)
+ if (value) {
+ headers[name] = value
+ }
+ }
+
+ const fetchOptions: RequestInit = {
+ method,
+ headers,
+ signal: AbortSignal.timeout(30000),
+ }
+
+ if (method === 'POST' || method === 'PUT') {
+ const body = await request.text()
+ if (body) {
+ fetchOptions.body = body
+ }
+ }
+
+ const response = await fetch(url, fetchOptions)
+
+ if (!response.ok) {
+ const errorText = await response.text()
+ let errorJson
+ try {
+ errorJson = JSON.parse(errorText)
+ } catch {
+ errorJson = { error: errorText }
+ }
+ return NextResponse.json(
+ { error: `Backend Error: ${response.status}`, ...errorJson },
+ { status: response.status }
+ )
+ }
+
+ const data = await response.json()
+ return NextResponse.json(data)
+ } catch (error) {
+ console.error('GCI API proxy error:', error)
+ return NextResponse.json(
+ { error: 'Verbindung zum SDK Backend fehlgeschlagen' },
+ { status: 503 }
+ )
+ }
+}
+
+export async function GET(
+ request: NextRequest,
+ { params }: { params: Promise<{ path?: string[] }> }
+) {
+ const { path } = await params
+ return proxyRequest(request, path, 'GET')
+}
+
+export async function POST(
+ request: NextRequest,
+ { params }: { params: Promise<{ path?: string[] }> }
+) {
+ const { path } = await params
+ return proxyRequest(request, path, 'POST')
+}
diff --git a/admin-compliance/components/sdk/Sidebar/SDKSidebar.tsx b/admin-compliance/components/sdk/Sidebar/SDKSidebar.tsx
index dd8f7e3..7ac5b94 100644
--- a/admin-compliance/components/sdk/Sidebar/SDKSidebar.tsx
+++ b/admin-compliance/components/sdk/Sidebar/SDKSidebar.tsx
@@ -561,6 +561,20 @@ export function SDKSidebar({ collapsed = false, onCollapsedChange }: SDKSidebarP
isActive={pathname === '/sdk/reporting'}
collapsed={collapsed}
/>
+
+
+
+
+ }
+ label="GCI Score"
+ isActive={pathname === '/sdk/gci'}
+ collapsed={collapsed}
+ />
(path: string, options?: RequestInit): Promise {
+ const res = await fetch(`${BASE_URL}${path}`, {
+ ...options,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-Tenant-ID': typeof window !== 'undefined'
+ ? (localStorage.getItem('bp-tenant-id') || 'default')
+ : 'default',
+ ...options?.headers,
+ },
+ })
+
+ if (!res.ok) {
+ const error = await res.json().catch(() => ({ error: res.statusText }))
+ throw new Error(error.error || `API Error: ${res.status}`)
+ }
+
+ return res.json()
+}
+
+/** GCI Score abrufen */
+export async function getGCIScore(profile?: string): Promise {
+ const params = profile ? `?profile=${profile}` : ''
+ return apiFetch(`/score${params}`)
+}
+
+/** Detailliertes 4-Level Breakdown abrufen */
+export async function getGCIBreakdown(profile?: string): Promise {
+ const params = profile ? `?profile=${profile}` : ''
+ return apiFetch(`/score/breakdown${params}`)
+}
+
+/** GCI History abrufen */
+export async function getGCIHistory(): Promise {
+ return apiFetch('/score/history')
+}
+
+/** Compliance Matrix abrufen */
+export async function getGCIMatrix(): Promise {
+ return apiFetch('/matrix')
+}
+
+/** Audit Trail abrufen */
+export async function getGCIAuditTrail(profile?: string): Promise<{ tenant_id: string; gci_score: number; audit_trail: any[] }> {
+ const params = profile ? `?profile=${profile}` : ''
+ return apiFetch(`/audit-trail${params}`)
+}
+
+/** Gewichtungsprofile abrufen */
+export async function getWeightProfiles(): Promise<{ profiles: WeightProfile[] }> {
+ return apiFetch<{ profiles: WeightProfile[] }>('/profiles')
+}
+
+/** NIS2 Score abrufen */
+export async function getNIS2Score(): Promise {
+ return apiFetch('/nis2/score')
+}
+
+/** NIS2 Rollen auflisten */
+export async function getNIS2Roles(): Promise<{ roles: NIS2Role[]; total: number }> {
+ return apiFetch<{ roles: NIS2Role[]; total: number }>('/nis2/roles')
+}
+
+/** NIS2 Rolle zuweisen */
+export async function assignNIS2Role(roleId: string, userId: string): Promise {
+ return apiFetch('/nis2/roles/assign', {
+ method: 'POST',
+ body: JSON.stringify({ role_id: roleId, user_id: userId }),
+ })
+}
+
+/** ISO Gap-Analyse abrufen */
+export async function getISOGapAnalysis(): Promise {
+ return apiFetch('/iso/gap-analysis')
+}
+
+/** ISO Mappings abrufen */
+export async function getISOMappings(category?: string): Promise {
+ const params = category ? `?category=${category}` : ''
+ return apiFetch(`/iso/mappings${params}`)
+}
diff --git a/admin-compliance/lib/sdk/gci/types.ts b/admin-compliance/lib/sdk/gci/types.ts
new file mode 100644
index 0000000..50486b6
--- /dev/null
+++ b/admin-compliance/lib/sdk/gci/types.ts
@@ -0,0 +1,246 @@
+/**
+ * GCI (Gesamt-Compliance-Index) Types
+ * TypeScript definitions for the 4-level compliance scoring model
+ */
+
+// =============================================================================
+// MATURITY LEVELS
+// =============================================================================
+
+export type MaturityLevel = 'OPTIMIZED' | 'MANAGED' | 'DEFINED' | 'REACTIVE' | 'HIGH_RISK'
+
+export const MATURITY_INFO: Record = {
+ OPTIMIZED: { label: 'Optimiert', color: 'text-green-700', bgColor: 'bg-green-100', borderColor: 'border-green-300', description: 'Kontinuierliche Verbesserung, proaktive Compliance' },
+ MANAGED: { label: 'Gesteuert', color: 'text-blue-700', bgColor: 'bg-blue-100', borderColor: 'border-blue-300', description: 'Messbare Prozesse, regelmaessige Reviews' },
+ DEFINED: { label: 'Definiert', color: 'text-yellow-700', bgColor: 'bg-yellow-100', borderColor: 'border-yellow-300', description: 'Dokumentierte Prozesse, erste Strukturen' },
+ REACTIVE: { label: 'Reaktiv', color: 'text-orange-700', bgColor: 'bg-orange-100', borderColor: 'border-orange-300', description: 'Ad-hoc Massnahmen, wenig Struktur' },
+ HIGH_RISK: { label: 'Hohes Risiko', color: 'text-red-700', bgColor: 'bg-red-100', borderColor: 'border-red-300', description: 'Erheblicher Handlungsbedarf, Compliance-Luecken' },
+}
+
+// =============================================================================
+// LEVEL 1: MODULE SCORE
+// =============================================================================
+
+export interface ModuleScore {
+ module_id: string
+ module_name: string
+ assigned: number
+ completed: number
+ raw_score: number
+ validity_factor: number
+ final_score: number
+ risk_weight: number
+ category: string
+}
+
+// =============================================================================
+// LEVEL 2: RISK-WEIGHTED AREA SCORE
+// =============================================================================
+
+export interface RiskWeightedScore {
+ area_id: string
+ area_name: string
+ modules: ModuleScore[]
+ weighted_sum: number
+ total_weight: number
+ area_score: number
+}
+
+// =============================================================================
+// LEVEL 3: REGULATION AREA SCORE
+// =============================================================================
+
+export interface RegulationAreaScore {
+ regulation_id: string
+ regulation_name: string
+ score: number
+ weight: number
+ weighted_score: number
+ module_count: number
+ completed_count: number
+}
+
+// =============================================================================
+// LEVEL 4: GCI RESULT
+// =============================================================================
+
+export interface AuditEntry {
+ timestamp: string
+ factor: string
+ description: string
+ value: number
+ impact: 'positive' | 'negative' | 'neutral'
+}
+
+export interface GCIResult {
+ tenant_id: string
+ gci_score: number
+ maturity_level: MaturityLevel
+ maturity_label: string
+ calculated_at: string
+ profile: string
+ area_scores: RegulationAreaScore[]
+ criticality_multiplier: number
+ incident_adjustment: number
+ audit_trail: AuditEntry[]
+}
+
+export interface GCIBreakdown extends GCIResult {
+ level1_modules: ModuleScore[]
+ level2_areas: RiskWeightedScore[]
+}
+
+// =============================================================================
+// GCI HISTORY
+// =============================================================================
+
+export interface GCISnapshot {
+ tenant_id: string
+ score: number
+ maturity_level: MaturityLevel
+ area_scores: Record
+ calculated_at: string
+}
+
+export interface GCIHistoryResponse {
+ tenant_id: string
+ snapshots: GCISnapshot[]
+ total: number
+}
+
+// =============================================================================
+// COMPLIANCE MATRIX
+// =============================================================================
+
+export interface ComplianceMatrixEntry {
+ role: string
+ role_name: string
+ regulations: Record
+ overall_score: number
+ required_modules: number
+ completed_modules: number
+}
+
+export interface GCIMatrixResponse {
+ tenant_id: string
+ matrix: ComplianceMatrixEntry[]
+}
+
+// =============================================================================
+// NIS2
+// =============================================================================
+
+export interface NIS2Role {
+ id: string
+ name: string
+ description: string
+ mandatory_modules: string[]
+ priority: number
+}
+
+export interface NIS2AreaScore {
+ area_id: string
+ area_name: string
+ weight: number
+ score: number
+ weighted_score: number
+}
+
+export interface NIS2RoleScore {
+ role_id: string
+ role_name: string
+ assigned_users: number
+ completion_rate: number
+ modules_completed: number
+ modules_required: number
+}
+
+export interface NIS2Score {
+ tenant_id: string
+ overall_score: number
+ maturity_level: string
+ areas: NIS2AreaScore[]
+ role_scores: NIS2RoleScore[]
+ calculated_at: string
+}
+
+// =============================================================================
+// ISO 27001
+// =============================================================================
+
+export interface ISOControl {
+ id: string
+ name: string
+ description: string
+ category_id: string
+ category_name: string
+ control_type: string
+ is_critical: boolean
+ sdk_modules: string[]
+}
+
+export interface ISOGap {
+ control_id: string
+ control_name: string
+ category: string
+ status: string
+ priority: string
+ recommendation: string
+}
+
+export interface ISOCategorySummary {
+ category_id: string
+ category_name: string
+ total_controls: number
+ covered_full: number
+ covered_partial: number
+ not_covered: number
+}
+
+export interface ISOGapAnalysis {
+ tenant_id: string
+ total_controls: number
+ covered_full: number
+ covered_partial: number
+ not_covered: number
+ coverage_percent: number
+ category_summaries: ISOCategorySummary[]
+ gaps: ISOGap[]
+ calculated_at: string
+}
+
+// =============================================================================
+// WEIGHT PROFILES
+// =============================================================================
+
+export interface WeightProfile {
+ id: string
+ name: string
+ description: string
+ weights: Record
+}
+
+// =============================================================================
+// HELPERS
+// =============================================================================
+
+export function getScoreColor(score: number): string {
+ if (score >= 80) return 'text-green-600'
+ if (score >= 60) return 'text-yellow-600'
+ if (score >= 40) return 'text-orange-600'
+ return 'text-red-600'
+}
+
+export function getScoreBgColor(score: number): string {
+ if (score >= 80) return 'bg-green-500'
+ if (score >= 60) return 'bg-yellow-500'
+ if (score >= 40) return 'bg-orange-500'
+ return 'bg-red-500'
+}
+
+export function getScoreRingColor(score: number): string {
+ if (score >= 80) return '#22c55e'
+ if (score >= 60) return '#eab308'
+ if (score >= 40) return '#f97316'
+ return '#ef4444'
+}
diff --git a/ai-compliance-sdk/cmd/server/main.go b/ai-compliance-sdk/cmd/server/main.go
index 87c69b2..46068d7 100644
--- a/ai-compliance-sdk/cmd/server/main.go
+++ b/ai-compliance-sdk/cmd/server/main.go
@@ -27,6 +27,7 @@ import (
"github.com/breakpilot/ai-compliance-sdk/internal/vendor"
"github.com/breakpilot/ai-compliance-sdk/internal/workshop"
"github.com/breakpilot/ai-compliance-sdk/internal/portfolio"
+ "github.com/breakpilot/ai-compliance-sdk/internal/gci"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/jackc/pgx/v5/pgxpool"
@@ -124,6 +125,10 @@ func main() {
industryHandlers := handlers.NewIndustryHandlers()
dsbHandlers := handlers.NewDSBHandlers(dsbStore)
+ // Initialize GCI engine and handlers
+ gciEngine := gci.NewEngine()
+ gciHandlers := handlers.NewGCIHandlers(gciEngine)
+
// Initialize middleware
rbacMiddleware := rbac.NewMiddleware(rbacService, policyEngine)
@@ -652,6 +657,29 @@ func main() {
dsbRoutes.POST("/assignments/:id/communications", dsbHandlers.CreateCommunication)
dsbRoutes.GET("/assignments/:id/communications", dsbHandlers.ListCommunications)
}
+
+ // GCI routes - Gesamt-Compliance-Index
+ gciRoutes := v1.Group("/gci")
+ {
+ // Core GCI endpoints
+ gciRoutes.GET("/score", gciHandlers.GetScore)
+ gciRoutes.GET("/score/breakdown", gciHandlers.GetScoreBreakdown)
+ gciRoutes.GET("/score/history", gciHandlers.GetHistory)
+ gciRoutes.GET("/matrix", gciHandlers.GetMatrix)
+ gciRoutes.GET("/audit-trail", gciHandlers.GetAuditTrail)
+ gciRoutes.GET("/profiles", gciHandlers.GetWeightProfiles)
+
+ // NIS2 sub-routes
+ gciRoutes.GET("/nis2/score", gciHandlers.GetNIS2Score)
+ gciRoutes.GET("/nis2/roles", gciHandlers.ListNIS2Roles)
+ gciRoutes.POST("/nis2/roles/assign", gciHandlers.AssignNIS2Role)
+
+ // ISO 27001 sub-routes
+ gciRoutes.GET("/iso/gap-analysis", gciHandlers.GetISOGapAnalysis)
+ gciRoutes.GET("/iso/mappings", gciHandlers.ListISOMappings)
+ gciRoutes.GET("/iso/mappings/:controlId", gciHandlers.GetISOMapping)
+ }
+
}
// Create HTTP server
diff --git a/ai-compliance-sdk/internal/api/handlers/gci_handlers.go b/ai-compliance-sdk/internal/api/handlers/gci_handlers.go
new file mode 100644
index 0000000..538b8d3
--- /dev/null
+++ b/ai-compliance-sdk/internal/api/handlers/gci_handlers.go
@@ -0,0 +1,188 @@
+package handlers
+
+import (
+ "net/http"
+
+ "github.com/breakpilot/ai-compliance-sdk/internal/gci"
+ "github.com/breakpilot/ai-compliance-sdk/internal/rbac"
+ "github.com/gin-gonic/gin"
+)
+
+type GCIHandlers struct {
+ engine *gci.Engine
+}
+
+func NewGCIHandlers(engine *gci.Engine) *GCIHandlers {
+ return &GCIHandlers{engine: engine}
+}
+
+// GetScore returns the GCI score for the current tenant
+// GET /sdk/v1/gci/score
+func (h *GCIHandlers) GetScore(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+ profile := c.DefaultQuery("profile", "default")
+
+ result := h.engine.Calculate(tenantID, profile)
+ c.JSON(http.StatusOK, result)
+}
+
+// GetScoreBreakdown returns the detailed 4-level GCI breakdown
+// GET /sdk/v1/gci/score/breakdown
+func (h *GCIHandlers) GetScoreBreakdown(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+ profile := c.DefaultQuery("profile", "default")
+
+ breakdown := h.engine.CalculateBreakdown(tenantID, profile)
+ c.JSON(http.StatusOK, breakdown)
+}
+
+// GetHistory returns historical GCI snapshots for trend analysis
+// GET /sdk/v1/gci/score/history
+func (h *GCIHandlers) GetHistory(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+
+ history := h.engine.GetHistory(tenantID)
+ c.JSON(http.StatusOK, gin.H{
+ "tenant_id": tenantID,
+ "snapshots": history,
+ "total": len(history),
+ })
+}
+
+// GetMatrix returns the compliance matrix (roles x regulations)
+// GET /sdk/v1/gci/matrix
+func (h *GCIHandlers) GetMatrix(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+
+ matrix := h.engine.GetMatrix(tenantID)
+ c.JSON(http.StatusOK, gin.H{
+ "tenant_id": tenantID,
+ "matrix": matrix,
+ })
+}
+
+// GetAuditTrail returns the audit trail for the latest GCI calculation
+// GET /sdk/v1/gci/audit-trail
+func (h *GCIHandlers) GetAuditTrail(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+ profile := c.DefaultQuery("profile", "default")
+
+ result := h.engine.Calculate(tenantID, profile)
+ c.JSON(http.StatusOK, gin.H{
+ "tenant_id": tenantID,
+ "gci_score": result.GCIScore,
+ "audit_trail": result.AuditTrail,
+ })
+}
+
+// GetNIS2Score returns the NIS2-specific compliance score
+// GET /sdk/v1/gci/nis2/score
+func (h *GCIHandlers) GetNIS2Score(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+
+ score := gci.CalculateNIS2Score(tenantID)
+ c.JSON(http.StatusOK, score)
+}
+
+// ListNIS2Roles returns available NIS2 responsibility roles
+// GET /sdk/v1/gci/nis2/roles
+func (h *GCIHandlers) ListNIS2Roles(c *gin.Context) {
+ roles := gci.ListNIS2Roles()
+ c.JSON(http.StatusOK, gin.H{
+ "roles": roles,
+ "total": len(roles),
+ })
+}
+
+// AssignNIS2Role assigns a NIS2 role to a user (stub - returns mock)
+// POST /sdk/v1/gci/nis2/roles/assign
+func (h *GCIHandlers) AssignNIS2Role(c *gin.Context) {
+ var req struct {
+ RoleID string `json:"role_id" binding:"required"`
+ UserID string `json:"user_id" binding:"required"`
+ }
+ if err := c.ShouldBindJSON(&req); err != nil {
+ c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
+ return
+ }
+
+ role, found := gci.GetNIS2Role(req.RoleID)
+ if !found {
+ c.JSON(http.StatusNotFound, gin.H{"error": "NIS2 role not found"})
+ return
+ }
+
+ c.JSON(http.StatusOK, gin.H{
+ "status": "assigned",
+ "role": role,
+ "user_id": req.UserID,
+ })
+}
+
+// GetISOGapAnalysis returns the ISO 27001 gap analysis
+// GET /sdk/v1/gci/iso/gap-analysis
+func (h *GCIHandlers) GetISOGapAnalysis(c *gin.Context) {
+ tenantID := rbac.GetTenantID(c).String()
+
+ analysis := gci.CalculateISOGapAnalysis(tenantID)
+ c.JSON(http.StatusOK, analysis)
+}
+
+// ListISOMappings returns all ISO 27001 control mappings
+// GET /sdk/v1/gci/iso/mappings
+func (h *GCIHandlers) ListISOMappings(c *gin.Context) {
+ category := c.Query("category")
+
+ if category != "" {
+ controls := gci.GetISOControlsByCategory(category)
+ c.JSON(http.StatusOK, gin.H{
+ "controls": controls,
+ "total": len(controls),
+ "category": category,
+ })
+ return
+ }
+
+ categories := []string{"A.5", "A.6", "A.7", "A.8"}
+ result := make(map[string][]gci.ISOControl)
+ total := 0
+ for _, cat := range categories {
+ controls := gci.GetISOControlsByCategory(cat)
+ if len(controls) > 0 {
+ result[cat] = controls
+ total += len(controls)
+ }
+ }
+
+ c.JSON(http.StatusOK, gin.H{
+ "categories": result,
+ "total": total,
+ })
+}
+
+// GetISOMapping returns a single ISO control by ID
+// GET /sdk/v1/gci/iso/mappings/:controlId
+func (h *GCIHandlers) GetISOMapping(c *gin.Context) {
+ controlID := c.Param("controlId")
+
+ control, found := gci.GetISOControlByID(controlID)
+ if !found {
+ c.JSON(http.StatusNotFound, gin.H{"error": "ISO control not found"})
+ return
+ }
+
+ c.JSON(http.StatusOK, control)
+}
+
+// GetWeightProfiles returns available weighting profiles
+// GET /sdk/v1/gci/profiles
+func (h *GCIHandlers) GetWeightProfiles(c *gin.Context) {
+ profiles := []string{"default", "nis2_relevant", "ki_nutzer"}
+ result := make([]gci.WeightProfile, 0, len(profiles))
+ for _, id := range profiles {
+ result = append(result, gci.GetProfile(id))
+ }
+ c.JSON(http.StatusOK, gin.H{
+ "profiles": result,
+ })
+}
diff --git a/ai-compliance-sdk/internal/gci/engine.go b/ai-compliance-sdk/internal/gci/engine.go
new file mode 100644
index 0000000..1599f8b
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/engine.go
@@ -0,0 +1,371 @@
+package gci
+
+import (
+ "fmt"
+ "math"
+ "time"
+)
+
+// Engine calculates the GCI score
+type Engine struct{}
+
+// NewEngine creates a new GCI calculation engine
+func NewEngine() *Engine {
+ return &Engine{}
+}
+
+// Calculate computes the full GCI result for a tenant
+func (e *Engine) Calculate(tenantID string, profileID string) *GCIResult {
+ now := time.Now()
+ profile := GetProfile(profileID)
+ auditTrail := []AuditEntry{}
+
+ // Step 1: Get module data (mock for now)
+ modules := MockModuleData(tenantID)
+ certDates := MockCertificateData()
+
+ // Step 2: Calculate Level 1 - Module Scores with validity
+ for i := range modules {
+ m := &modules[i]
+ if m.Assigned > 0 {
+ m.RawScore = float64(m.Completed) / float64(m.Assigned) * 100.0
+ }
+ // Apply validity factor
+ if validUntil, ok := certDates[m.ModuleID]; ok {
+ m.ValidityFactor = CalculateValidityFactor(validUntil, now)
+ } else {
+ m.ValidityFactor = 1.0 // No certificate tracking = assume valid
+ }
+ m.FinalScore = m.RawScore * m.ValidityFactor
+
+ if m.ValidityFactor < 1.0 {
+ auditTrail = append(auditTrail, AuditEntry{
+ Timestamp: now,
+ Factor: "validity_decay",
+ Description: fmt.Sprintf("Modul '%s': Gueltigkeitsfaktor %.2f (Zertifikat laeuft ab/abgelaufen)", m.ModuleName, m.ValidityFactor),
+ Value: m.ValidityFactor,
+ Impact: "negative",
+ })
+ }
+ }
+
+ // Step 3: Calculate Level 2 - Risk-Weighted Scores per area
+ areaModules := map[string][]ModuleScore{
+ "dsgvo": {},
+ "nis2": {},
+ "iso27001": {},
+ "ai_act": {},
+ }
+ for _, m := range modules {
+ if _, ok := areaModules[m.Category]; ok {
+ areaModules[m.Category] = append(areaModules[m.Category], m)
+ }
+ }
+
+ level2Areas := []RiskWeightedScore{}
+ areaNames := map[string]string{
+ "dsgvo": "DSGVO",
+ "nis2": "NIS2",
+ "iso27001": "ISO 27001",
+ "ai_act": "EU AI Act",
+ }
+
+ for areaID, mods := range areaModules {
+ rws := RiskWeightedScore{
+ AreaID: areaID,
+ AreaName: areaNames[areaID],
+ Modules: mods,
+ }
+ for _, m := range mods {
+ rws.WeightedSum += m.FinalScore * m.RiskWeight
+ rws.TotalWeight += m.RiskWeight
+ }
+ if rws.TotalWeight > 0 {
+ rws.AreaScore = rws.WeightedSum / rws.TotalWeight
+ }
+ level2Areas = append(level2Areas, rws)
+ }
+
+ // Step 4: Calculate Level 3 - Regulation Area Scores
+ areaScores := []RegulationAreaScore{}
+ for _, rws := range level2Areas {
+ weight := profile.Weights[rws.AreaID]
+ completedCount := 0
+ for _, m := range rws.Modules {
+ if m.Completed >= m.Assigned && m.Assigned > 0 {
+ completedCount++
+ }
+ }
+ ras := RegulationAreaScore{
+ RegulationID: rws.AreaID,
+ RegulationName: rws.AreaName,
+ Score: math.Round(rws.AreaScore*100) / 100,
+ Weight: weight,
+ WeightedScore: rws.AreaScore * weight,
+ ModuleCount: len(rws.Modules),
+ CompletedCount: completedCount,
+ }
+ areaScores = append(areaScores, ras)
+
+ auditTrail = append(auditTrail, AuditEntry{
+ Timestamp: now,
+ Factor: "area_score",
+ Description: fmt.Sprintf("Bereich '%s': Score %.1f, Gewicht %.0f%%", rws.AreaName, rws.AreaScore, weight*100),
+ Value: rws.AreaScore,
+ Impact: "neutral",
+ })
+ }
+
+ // Step 5: Calculate raw GCI
+ rawGCI := 0.0
+ totalWeight := 0.0
+ for _, ras := range areaScores {
+ rawGCI += ras.WeightedScore
+ totalWeight += ras.Weight
+ }
+ if totalWeight > 0 {
+ rawGCI = rawGCI / totalWeight
+ }
+
+ // Step 6: Apply Criticality Multiplier
+ criticalityMult := calculateCriticalityMultiplier(modules)
+ auditTrail = append(auditTrail, AuditEntry{
+ Timestamp: now,
+ Factor: "criticality_multiplier",
+ Description: fmt.Sprintf("Kritikalitaetsmultiplikator: %.3f", criticalityMult),
+ Value: criticalityMult,
+ Impact: func() string {
+ if criticalityMult < 1.0 {
+ return "negative"
+ }
+ return "neutral"
+ }(),
+ })
+
+ // Step 7: Apply Incident Adjustment
+ openInc, critInc := MockIncidentData()
+ incidentAdj := calculateIncidentAdjustment(openInc, critInc)
+ auditTrail = append(auditTrail, AuditEntry{
+ Timestamp: now,
+ Factor: "incident_adjustment",
+ Description: fmt.Sprintf("Vorfallsanpassung: %.3f (%d offen, %d kritisch)", incidentAdj, openInc, critInc),
+ Value: incidentAdj,
+ Impact: "negative",
+ })
+
+ // Step 8: Final GCI
+ finalGCI := rawGCI * criticalityMult * incidentAdj
+ finalGCI = math.Max(0, math.Min(100, math.Round(finalGCI*10)/10))
+
+ // Step 9: Determine Maturity Level
+ maturity := determineMaturityLevel(finalGCI)
+
+ auditTrail = append(auditTrail, AuditEntry{
+ Timestamp: now,
+ Factor: "final_gci",
+ Description: fmt.Sprintf("GCI-Endergebnis: %.1f → Reifegrad: %s", finalGCI, MaturityLabels[maturity]),
+ Value: finalGCI,
+ Impact: "neutral",
+ })
+
+ return &GCIResult{
+ TenantID: tenantID,
+ GCIScore: finalGCI,
+ MaturityLevel: maturity,
+ MaturityLabel: MaturityLabels[maturity],
+ CalculatedAt: now,
+ Profile: profileID,
+ AreaScores: areaScores,
+ CriticalityMult: criticalityMult,
+ IncidentAdj: incidentAdj,
+ AuditTrail: auditTrail,
+ }
+}
+
+// CalculateBreakdown returns the full 4-level breakdown
+func (e *Engine) CalculateBreakdown(tenantID string, profileID string) *GCIBreakdown {
+ result := e.Calculate(tenantID, profileID)
+ modules := MockModuleData(tenantID)
+ certDates := MockCertificateData()
+ now := time.Now()
+
+ // Recalculate module scores for the breakdown
+ for i := range modules {
+ m := &modules[i]
+ if m.Assigned > 0 {
+ m.RawScore = float64(m.Completed) / float64(m.Assigned) * 100.0
+ }
+ if validUntil, ok := certDates[m.ModuleID]; ok {
+ m.ValidityFactor = CalculateValidityFactor(validUntil, now)
+ } else {
+ m.ValidityFactor = 1.0
+ }
+ m.FinalScore = m.RawScore * m.ValidityFactor
+ }
+
+ // Build Level 2 areas
+ areaModules := map[string][]ModuleScore{}
+ for _, m := range modules {
+ areaModules[m.Category] = append(areaModules[m.Category], m)
+ }
+
+ areaNames := map[string]string{"dsgvo": "DSGVO", "nis2": "NIS2", "iso27001": "ISO 27001", "ai_act": "EU AI Act"}
+ level2 := []RiskWeightedScore{}
+ for areaID, mods := range areaModules {
+ rws := RiskWeightedScore{AreaID: areaID, AreaName: areaNames[areaID], Modules: mods}
+ for _, m := range mods {
+ rws.WeightedSum += m.FinalScore * m.RiskWeight
+ rws.TotalWeight += m.RiskWeight
+ }
+ if rws.TotalWeight > 0 {
+ rws.AreaScore = rws.WeightedSum / rws.TotalWeight
+ }
+ level2 = append(level2, rws)
+ }
+
+ return &GCIBreakdown{
+ GCIResult: *result,
+ Level1Modules: modules,
+ Level2Areas: level2,
+ }
+}
+
+// GetHistory returns historical GCI snapshots
+func (e *Engine) GetHistory(tenantID string) []GCISnapshot {
+ // Add current score to history
+ result := e.Calculate(tenantID, "default")
+ history := MockGCIHistory(tenantID)
+ current := GCISnapshot{
+ TenantID: tenantID,
+ Score: result.GCIScore,
+ MaturityLevel: result.MaturityLevel,
+ AreaScores: make(map[string]float64),
+ CalculatedAt: result.CalculatedAt,
+ }
+ for _, as := range result.AreaScores {
+ current.AreaScores[as.RegulationID] = as.Score
+ }
+ history = append(history, current)
+ return history
+}
+
+// GetMatrix returns the compliance matrix (roles x regulations)
+func (e *Engine) GetMatrix(tenantID string) []ComplianceMatrixEntry {
+ modules := MockModuleData(tenantID)
+
+ roles := []struct {
+ ID string
+ Name string
+ }{
+ {"management", "Geschaeftsfuehrung"},
+ {"it_security", "IT-Sicherheit / CISO"},
+ {"data_protection", "Datenschutz / DSB"},
+ {"hr", "Personalwesen"},
+ {"general", "Allgemeine Mitarbeiter"},
+ }
+
+ // Define which modules are relevant per role
+ roleModules := map[string][]string{
+ "management": {"dsgvo-grundlagen", "nis2-management", "ai-governance", "iso-isms"},
+ "it_security": {"nis2-risikomanagement", "nis2-incident-response", "iso-zugangssteuerung", "iso-kryptografie", "ai-hochrisiko"},
+ "data_protection": {"dsgvo-grundlagen", "dsgvo-betroffenenrechte", "dsgvo-tom", "dsgvo-dsfa", "dsgvo-auftragsverarbeitung"},
+ "hr": {"dsgvo-grundlagen", "dsgvo-betroffenenrechte", "nis2-management"},
+ "general": {"dsgvo-grundlagen", "nis2-risikomanagement", "ai-risikokategorien", "ai-transparenz"},
+ }
+
+ moduleMap := map[string]ModuleScore{}
+ for _, m := range modules {
+ moduleMap[m.ModuleID] = m
+ }
+
+ entries := []ComplianceMatrixEntry{}
+ for _, role := range roles {
+ entry := ComplianceMatrixEntry{
+ Role: role.ID,
+ RoleName: role.Name,
+ Regulations: map[string]float64{},
+ }
+
+ regScores := map[string][]float64{}
+ requiredModuleIDs := roleModules[role.ID]
+ entry.RequiredModules = len(requiredModuleIDs)
+
+ for _, modID := range requiredModuleIDs {
+ if m, ok := moduleMap[modID]; ok {
+ score := 0.0
+ if m.Assigned > 0 {
+ score = float64(m.Completed) / float64(m.Assigned) * 100
+ }
+ regScores[m.Category] = append(regScores[m.Category], score)
+ if m.Completed >= m.Assigned && m.Assigned > 0 {
+ entry.CompletedModules++
+ }
+ }
+ }
+
+ totalScore := 0.0
+ count := 0
+ for reg, scores := range regScores {
+ sum := 0.0
+ for _, s := range scores {
+ sum += s
+ }
+ avg := sum / float64(len(scores))
+ entry.Regulations[reg] = math.Round(avg*10) / 10
+ totalScore += avg
+ count++
+ }
+ if count > 0 {
+ entry.OverallScore = math.Round(totalScore/float64(count)*10) / 10
+ }
+
+ entries = append(entries, entry)
+ }
+
+ return entries
+}
+
+// Helper functions
+
+func calculateCriticalityMultiplier(modules []ModuleScore) float64 {
+ criticalModules := 0
+ criticalLow := 0
+ for _, m := range modules {
+ if m.RiskWeight >= 2.5 {
+ criticalModules++
+ if m.FinalScore < 50 {
+ criticalLow++
+ }
+ }
+ }
+ if criticalModules == 0 {
+ return 1.0
+ }
+ // Reduce score if critical modules have low completion
+ ratio := float64(criticalLow) / float64(criticalModules)
+ return 1.0 - (ratio * 0.15) // max 15% reduction
+}
+
+func calculateIncidentAdjustment(openIncidents, criticalIncidents int) float64 {
+ adj := 1.0
+ // Each open incident reduces by 1%
+ adj -= float64(openIncidents) * 0.01
+ // Each critical incident reduces by additional 3%
+ adj -= float64(criticalIncidents) * 0.03
+ return math.Max(0.8, adj) // minimum 80% (max 20% reduction)
+}
+
+func determineMaturityLevel(score float64) string {
+ switch {
+ case score >= 90:
+ return MaturityOptimized
+ case score >= 75:
+ return MaturityManaged
+ case score >= 60:
+ return MaturityDefined
+ case score >= 40:
+ return MaturityReactive
+ default:
+ return MaturityHighRisk
+ }
+}
diff --git a/ai-compliance-sdk/internal/gci/iso_gap_analysis.go b/ai-compliance-sdk/internal/gci/iso_gap_analysis.go
new file mode 100644
index 0000000..9032f45
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/iso_gap_analysis.go
@@ -0,0 +1,188 @@
+package gci
+
+import "math"
+
+// ISOGapAnalysis represents the complete ISO 27001 gap analysis
+type ISOGapAnalysis struct {
+ TenantID string `json:"tenant_id"`
+ TotalControls int `json:"total_controls"`
+ CoveredFull int `json:"covered_full"`
+ CoveredPartial int `json:"covered_partial"`
+ NotCovered int `json:"not_covered"`
+ CoveragePercent float64 `json:"coverage_percent"`
+ CategorySummaries []ISOCategorySummary `json:"category_summaries"`
+ ControlDetails []ISOControlDetail `json:"control_details"`
+ Gaps []ISOGap `json:"gaps"`
+}
+
+// ISOControlDetail shows coverage status for a single control
+type ISOControlDetail struct {
+ Control ISOControl `json:"control"`
+ CoverageLevel string `json:"coverage_level"` // full, partial, none
+ CoveredBy []string `json:"covered_by"` // module IDs
+ Score float64 `json:"score"` // 0-100
+}
+
+// ISOGap represents an identified gap in ISO coverage
+type ISOGap struct {
+ ControlID string `json:"control_id"`
+ ControlName string `json:"control_name"`
+ Category string `json:"category"`
+ Priority string `json:"priority"` // high, medium, low
+ Recommendation string `json:"recommendation"`
+}
+
+// CalculateISOGapAnalysis performs the ISO 27001 gap analysis
+func CalculateISOGapAnalysis(tenantID string) *ISOGapAnalysis {
+ modules := MockModuleData(tenantID)
+ moduleMap := map[string]ModuleScore{}
+ for _, m := range modules {
+ moduleMap[m.ModuleID] = m
+ }
+
+ // Build reverse mapping: control -> modules covering it
+ controlCoverage := map[string][]string{}
+ controlCoverageLevel := map[string]string{}
+ for _, mapping := range DefaultISOModuleMappings {
+ for _, controlID := range mapping.ISOControls {
+ controlCoverage[controlID] = append(controlCoverage[controlID], mapping.ModuleID)
+ // Use the highest coverage level
+ existingLevel := controlCoverageLevel[controlID]
+ if mapping.CoverageLevel == "full" || existingLevel == "" {
+ controlCoverageLevel[controlID] = mapping.CoverageLevel
+ }
+ }
+ }
+
+ // Analyze each control
+ details := []ISOControlDetail{}
+ gaps := []ISOGap{}
+ coveredFull := 0
+ coveredPartial := 0
+ notCovered := 0
+
+ categoryCounts := map[string]*ISOCategorySummary{
+ "A.5": {CategoryID: "A.5", CategoryName: "Organisatorische Massnahmen"},
+ "A.6": {CategoryID: "A.6", CategoryName: "Personelle Massnahmen"},
+ "A.7": {CategoryID: "A.7", CategoryName: "Physische Massnahmen"},
+ "A.8": {CategoryID: "A.8", CategoryName: "Technologische Massnahmen"},
+ }
+
+ for _, control := range ISOControls {
+ coveredBy := controlCoverage[control.ID]
+ level := controlCoverageLevel[control.ID]
+
+ if len(coveredBy) == 0 {
+ level = "none"
+ }
+
+ // Calculate score based on module completion
+ score := 0.0
+ if len(coveredBy) > 0 {
+ scoreSum := 0.0
+ count := 0
+ for _, modID := range coveredBy {
+ if m, ok := moduleMap[modID]; ok && m.Assigned > 0 {
+ scoreSum += float64(m.Completed) / float64(m.Assigned) * 100
+ count++
+ }
+ }
+ if count > 0 {
+ score = scoreSum / float64(count)
+ }
+ // Adjust for coverage level
+ if level == "partial" {
+ score *= 0.7 // partial coverage reduces effective score
+ }
+ }
+
+ detail := ISOControlDetail{
+ Control: control,
+ CoverageLevel: level,
+ CoveredBy: coveredBy,
+ Score: math.Round(score*10) / 10,
+ }
+ details = append(details, detail)
+
+ // Count by category
+ cat := categoryCounts[control.CategoryID]
+ if cat != nil {
+ cat.TotalControls++
+ switch level {
+ case "full":
+ coveredFull++
+ cat.CoveredFull++
+ case "partial":
+ coveredPartial++
+ cat.CoveredPartial++
+ default:
+ notCovered++
+ cat.NotCovered++
+ // Generate gap recommendation
+ gap := ISOGap{
+ ControlID: control.ID,
+ ControlName: control.Name,
+ Category: control.Category,
+ Priority: determineGapPriority(control),
+ Recommendation: generateGapRecommendation(control),
+ }
+ gaps = append(gaps, gap)
+ }
+ }
+ }
+
+ totalControls := len(ISOControls)
+ coveragePercent := 0.0
+ if totalControls > 0 {
+ coveragePercent = math.Round(float64(coveredFull+coveredPartial)/float64(totalControls)*100*10) / 10
+ }
+
+ summaries := []ISOCategorySummary{}
+ for _, catID := range []string{"A.5", "A.6", "A.7", "A.8"} {
+ if cat, ok := categoryCounts[catID]; ok {
+ summaries = append(summaries, *cat)
+ }
+ }
+
+ return &ISOGapAnalysis{
+ TenantID: tenantID,
+ TotalControls: totalControls,
+ CoveredFull: coveredFull,
+ CoveredPartial: coveredPartial,
+ NotCovered: notCovered,
+ CoveragePercent: coveragePercent,
+ CategorySummaries: summaries,
+ ControlDetails: details,
+ Gaps: gaps,
+ }
+}
+
+func determineGapPriority(control ISOControl) string {
+ // High priority for access, incident, and data protection controls
+ highPriority := map[string]bool{
+ "A.5.15": true, "A.5.17": true, "A.5.24": true, "A.5.26": true,
+ "A.5.34": true, "A.8.2": true, "A.8.5": true, "A.8.7": true,
+ "A.8.10": true, "A.8.20": true,
+ }
+ if highPriority[control.ID] {
+ return "high"
+ }
+ // Medium for organizational and people controls
+ if control.CategoryID == "A.5" || control.CategoryID == "A.6" {
+ return "medium"
+ }
+ return "low"
+}
+
+func generateGapRecommendation(control ISOControl) string {
+ recommendations := map[string]string{
+ "organizational": "Erstellen Sie eine Richtlinie und weisen Sie Verantwortlichkeiten zu fuer: " + control.Name,
+ "people": "Implementieren Sie Schulungen und Prozesse fuer: " + control.Name,
+ "physical": "Definieren Sie physische Sicherheitsmassnahmen fuer: " + control.Name,
+ "technological": "Implementieren Sie technische Kontrollen fuer: " + control.Name,
+ }
+ if rec, ok := recommendations[control.Category]; ok {
+ return rec
+ }
+ return "Massnahmen implementieren fuer: " + control.Name
+}
diff --git a/ai-compliance-sdk/internal/gci/iso_mapping.go b/ai-compliance-sdk/internal/gci/iso_mapping.go
new file mode 100644
index 0000000..8f1a8fa
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/iso_mapping.go
@@ -0,0 +1,207 @@
+package gci
+
+// ISOControl represents an ISO 27001:2022 Annex A control
+type ISOControl struct {
+ ID string `json:"id"` // e.g. "A.5.1"
+ Name string `json:"name"`
+ Category string `json:"category"` // organizational, people, physical, technological
+ CategoryID string `json:"category_id"` // A.5, A.6, A.7, A.8
+ Description string `json:"description"`
+}
+
+// ISOModuleMapping maps a course/module to ISO controls
+type ISOModuleMapping struct {
+ ModuleID string `json:"module_id"`
+ ModuleName string `json:"module_name"`
+ ISOControls []string `json:"iso_controls"` // control IDs
+ CoverageLevel string `json:"coverage_level"` // full, partial, none
+}
+
+// ISO 27001:2022 Annex A controls (representative selection)
+var ISOControls = []ISOControl{
+ // A.5 Organizational Controls (37 controls, showing key ones)
+ {ID: "A.5.1", Name: "Informationssicherheitsrichtlinien", Category: "organizational", CategoryID: "A.5", Description: "Informationssicherheitsleitlinie und themenspezifische Richtlinien"},
+ {ID: "A.5.2", Name: "Rollen und Verantwortlichkeiten", Category: "organizational", CategoryID: "A.5", Description: "Definition und Zuweisung von Informationssicherheitsrollen"},
+ {ID: "A.5.3", Name: "Aufgabentrennung", Category: "organizational", CategoryID: "A.5", Description: "Trennung von konfligierenden Aufgaben und Verantwortlichkeiten"},
+ {ID: "A.5.4", Name: "Managementverantwortung", Category: "organizational", CategoryID: "A.5", Description: "Fuehrungskraefte muessen Sicherheitsrichtlinien einhalten und durchsetzen"},
+ {ID: "A.5.5", Name: "Kontakt mit Behoerden", Category: "organizational", CategoryID: "A.5", Description: "Pflege von Kontakten zu relevanten Aufsichtsbehoerden"},
+ {ID: "A.5.6", Name: "Kontakt mit Interessengruppen", Category: "organizational", CategoryID: "A.5", Description: "Kontakt zu Fachgruppen und Sicherheitsforen"},
+ {ID: "A.5.7", Name: "Bedrohungsintelligenz", Category: "organizational", CategoryID: "A.5", Description: "Sammlung und Analyse von Bedrohungsinformationen"},
+ {ID: "A.5.8", Name: "Informationssicherheit im Projektmanagement", Category: "organizational", CategoryID: "A.5", Description: "Integration von Sicherheit in Projektmanagement"},
+ {ID: "A.5.9", Name: "Inventar der Informationswerte", Category: "organizational", CategoryID: "A.5", Description: "Inventarisierung und Verwaltung von Informationswerten"},
+ {ID: "A.5.10", Name: "Zuleassige Nutzung", Category: "organizational", CategoryID: "A.5", Description: "Regeln fuer die zuleassige Nutzung von Informationswerten"},
+ {ID: "A.5.11", Name: "Rueckgabe von Werten", Category: "organizational", CategoryID: "A.5", Description: "Rueckgabe von Werten bei Beendigung"},
+ {ID: "A.5.12", Name: "Klassifizierung von Informationen", Category: "organizational", CategoryID: "A.5", Description: "Klassifizierungsschema fuer Informationen"},
+ {ID: "A.5.13", Name: "Kennzeichnung von Informationen", Category: "organizational", CategoryID: "A.5", Description: "Kennzeichnung gemaess Klassifizierung"},
+ {ID: "A.5.14", Name: "Informationsuebertragung", Category: "organizational", CategoryID: "A.5", Description: "Regeln fuer sichere Informationsuebertragung"},
+ {ID: "A.5.15", Name: "Zugangssteuerung", Category: "organizational", CategoryID: "A.5", Description: "Zugangssteuerungsrichtlinie"},
+ {ID: "A.5.16", Name: "Identitaetsmanagement", Category: "organizational", CategoryID: "A.5", Description: "Verwaltung des Lebenszyklus von Identitaeten"},
+ {ID: "A.5.17", Name: "Authentifizierungsinformationen", Category: "organizational", CategoryID: "A.5", Description: "Verwaltung von Authentifizierungsinformationen"},
+ {ID: "A.5.18", Name: "Zugriffsrechte", Category: "organizational", CategoryID: "A.5", Description: "Vergabe, Pruefung und Entzug von Zugriffsrechten"},
+ {ID: "A.5.19", Name: "Informationssicherheit in Lieferantenbeziehungen", Category: "organizational", CategoryID: "A.5", Description: "Sicherheitsanforderungen an Lieferanten"},
+ {ID: "A.5.20", Name: "Informationssicherheit in Lieferantenvereinbarungen", Category: "organizational", CategoryID: "A.5", Description: "Sicherheitsklauseln in Vertraegen"},
+ {ID: "A.5.21", Name: "IKT-Lieferkette", Category: "organizational", CategoryID: "A.5", Description: "Management der IKT-Lieferkette"},
+ {ID: "A.5.22", Name: "Ueberwachung von Lieferantenservices", Category: "organizational", CategoryID: "A.5", Description: "Ueberwachung und Pruefung von Lieferantenservices"},
+ {ID: "A.5.23", Name: "Cloud-Sicherheit", Category: "organizational", CategoryID: "A.5", Description: "Informationssicherheit fuer Cloud-Dienste"},
+ {ID: "A.5.24", Name: "Vorfallsmanagement - Planung", Category: "organizational", CategoryID: "A.5", Description: "Planung und Vorbereitung des Vorfallsmanagements"},
+ {ID: "A.5.25", Name: "Vorfallsbeurteilung", Category: "organizational", CategoryID: "A.5", Description: "Beurteilung und Entscheidung ueber Sicherheitsereignisse"},
+ {ID: "A.5.26", Name: "Vorfallsreaktion", Category: "organizational", CategoryID: "A.5", Description: "Reaktion auf Sicherheitsvorfaelle"},
+ {ID: "A.5.27", Name: "Aus Vorfaellen lernen", Category: "organizational", CategoryID: "A.5", Description: "Lessons Learned aus Sicherheitsvorfaellen"},
+ {ID: "A.5.28", Name: "Beweissicherung", Category: "organizational", CategoryID: "A.5", Description: "Identifikation und Sicherung von Beweisen"},
+ {ID: "A.5.29", Name: "Informationssicherheit bei Stoerungen", Category: "organizational", CategoryID: "A.5", Description: "Sicherheit waehrend Stoerungen und Krisen"},
+ {ID: "A.5.30", Name: "IKT-Bereitschaft fuer Business Continuity", Category: "organizational", CategoryID: "A.5", Description: "IKT-Bereitschaft zur Unterstuetzung der Geschaeftskontinuitaet"},
+ {ID: "A.5.31", Name: "Rechtliche Anforderungen", Category: "organizational", CategoryID: "A.5", Description: "Einhaltung rechtlicher und vertraglicher Anforderungen"},
+ {ID: "A.5.32", Name: "Geistige Eigentumsrechte", Category: "organizational", CategoryID: "A.5", Description: "Schutz geistigen Eigentums"},
+ {ID: "A.5.33", Name: "Schutz von Aufzeichnungen", Category: "organizational", CategoryID: "A.5", Description: "Schutz von Aufzeichnungen vor Verlust und Manipulation"},
+ {ID: "A.5.34", Name: "Datenschutz und PII", Category: "organizational", CategoryID: "A.5", Description: "Datenschutz und Schutz personenbezogener Daten"},
+ {ID: "A.5.35", Name: "Unabhaengige Ueberpruefung", Category: "organizational", CategoryID: "A.5", Description: "Unabhaengige Ueberpruefung der Informationssicherheit"},
+ {ID: "A.5.36", Name: "Richtlinienkonformitaet", Category: "organizational", CategoryID: "A.5", Description: "Einhaltung von Richtlinien und Standards"},
+ {ID: "A.5.37", Name: "Dokumentierte Betriebsverfahren", Category: "organizational", CategoryID: "A.5", Description: "Dokumentation von Betriebsverfahren"},
+
+ // A.6 People Controls (8 controls)
+ {ID: "A.6.1", Name: "Ueberpruefen", Category: "people", CategoryID: "A.6", Description: "Hintergrundpruefungen vor der Einstellung"},
+ {ID: "A.6.2", Name: "Beschaeftigungsbedingungen", Category: "people", CategoryID: "A.6", Description: "Sicherheitsanforderungen in Arbeitsvertraegen"},
+ {ID: "A.6.3", Name: "Sensibilisierung und Schulung", Category: "people", CategoryID: "A.6", Description: "Awareness-Programme und Schulungen"},
+ {ID: "A.6.4", Name: "Disziplinarverfahren", Category: "people", CategoryID: "A.6", Description: "Formales Disziplinarverfahren"},
+ {ID: "A.6.5", Name: "Verantwortlichkeiten nach Beendigung", Category: "people", CategoryID: "A.6", Description: "Sicherheitspflichten nach Beendigung des Beschaeftigungsverhaeltnisses"},
+ {ID: "A.6.6", Name: "Vertraulichkeitsvereinbarungen", Category: "people", CategoryID: "A.6", Description: "Vertraulichkeits- und Geheimhaltungsvereinbarungen"},
+ {ID: "A.6.7", Name: "Remote-Arbeit", Category: "people", CategoryID: "A.6", Description: "Sicherheitsmassnahmen fuer Remote-Arbeit"},
+ {ID: "A.6.8", Name: "Meldung von Sicherheitsereignissen", Category: "people", CategoryID: "A.6", Description: "Mechanismen zur Meldung von Sicherheitsereignissen"},
+
+ // A.7 Physical Controls (14 controls, showing key ones)
+ {ID: "A.7.1", Name: "Physische Sicherheitsperimeter", Category: "physical", CategoryID: "A.7", Description: "Definition physischer Sicherheitszonen"},
+ {ID: "A.7.2", Name: "Physischer Zutritt", Category: "physical", CategoryID: "A.7", Description: "Zutrittskontrolle zu Sicherheitszonen"},
+ {ID: "A.7.3", Name: "Sicherung von Bueros und Raeumen", Category: "physical", CategoryID: "A.7", Description: "Physische Sicherheit fuer Bueros und Raeume"},
+ {ID: "A.7.4", Name: "Physische Sicherheitsueberwachung", Category: "physical", CategoryID: "A.7", Description: "Ueberwachung physischer Sicherheit"},
+ {ID: "A.7.5", Name: "Schutz vor Umweltgefahren", Category: "physical", CategoryID: "A.7", Description: "Schutz gegen natuerliche und menschgemachte Gefahren"},
+ {ID: "A.7.6", Name: "Arbeit in Sicherheitszonen", Category: "physical", CategoryID: "A.7", Description: "Regeln fuer das Arbeiten in Sicherheitszonen"},
+ {ID: "A.7.7", Name: "Aufgeraemter Schreibtisch", Category: "physical", CategoryID: "A.7", Description: "Clean-Desk und Clear-Screen Richtlinie"},
+ {ID: "A.7.8", Name: "Geraeteplatzierung", Category: "physical", CategoryID: "A.7", Description: "Platzierung und Schutz von Geraeten"},
+ {ID: "A.7.9", Name: "Sicherheit von Geraeten ausserhalb", Category: "physical", CategoryID: "A.7", Description: "Sicherheit von Geraeten ausserhalb der Raeumlichkeiten"},
+ {ID: "A.7.10", Name: "Speichermedien", Category: "physical", CategoryID: "A.7", Description: "Verwaltung von Speichermedien"},
+ {ID: "A.7.11", Name: "Versorgungseinrichtungen", Category: "physical", CategoryID: "A.7", Description: "Schutz vor Ausfaellen der Versorgungseinrichtungen"},
+ {ID: "A.7.12", Name: "Verkabelungssicherheit", Category: "physical", CategoryID: "A.7", Description: "Schutz der Verkabelung"},
+ {ID: "A.7.13", Name: "Instandhaltung von Geraeten", Category: "physical", CategoryID: "A.7", Description: "Korrekte Instandhaltung von Geraeten"},
+ {ID: "A.7.14", Name: "Sichere Entsorgung", Category: "physical", CategoryID: "A.7", Description: "Sichere Entsorgung oder Wiederverwendung"},
+
+ // A.8 Technological Controls (34 controls, showing key ones)
+ {ID: "A.8.1", Name: "Endbenutzergeraete", Category: "technological", CategoryID: "A.8", Description: "Sicherheit von Endbenutzergeraeten"},
+ {ID: "A.8.2", Name: "Privilegierte Zugriffsrechte", Category: "technological", CategoryID: "A.8", Description: "Verwaltung privilegierter Zugriffsrechte"},
+ {ID: "A.8.3", Name: "Informationszugangsbeschraenkung", Category: "technological", CategoryID: "A.8", Description: "Beschraenkung des Zugangs zu Informationen"},
+ {ID: "A.8.4", Name: "Zugang zu Quellcode", Category: "technological", CategoryID: "A.8", Description: "Sicherer Zugang zu Quellcode"},
+ {ID: "A.8.5", Name: "Sichere Authentifizierung", Category: "technological", CategoryID: "A.8", Description: "Sichere Authentifizierungstechnologien"},
+ {ID: "A.8.6", Name: "Kapazitaetsmanagement", Category: "technological", CategoryID: "A.8", Description: "Ueberwachung und Anpassung der Kapazitaet"},
+ {ID: "A.8.7", Name: "Schutz gegen Malware", Category: "technological", CategoryID: "A.8", Description: "Schutz vor Schadprogrammen"},
+ {ID: "A.8.8", Name: "Management technischer Schwachstellen", Category: "technological", CategoryID: "A.8", Description: "Identifikation und Behebung von Schwachstellen"},
+ {ID: "A.8.9", Name: "Konfigurationsmanagement", Category: "technological", CategoryID: "A.8", Description: "Sichere Konfiguration von Systemen"},
+ {ID: "A.8.10", Name: "Datensicherung", Category: "technological", CategoryID: "A.8", Description: "Erstellen und Testen von Datensicherungen"},
+ {ID: "A.8.11", Name: "Datenredundanz", Category: "technological", CategoryID: "A.8", Description: "Redundanz von Informationsverarbeitungseinrichtungen"},
+ {ID: "A.8.12", Name: "Protokollierung", Category: "technological", CategoryID: "A.8", Description: "Aufzeichnung und Ueberwachung von Aktivitaeten"},
+ {ID: "A.8.13", Name: "Ueberwachung von Aktivitaeten", Category: "technological", CategoryID: "A.8", Description: "Ueberwachung von Netzwerken und Systemen"},
+ {ID: "A.8.14", Name: "Zeitsynchronisation", Category: "technological", CategoryID: "A.8", Description: "Synchronisation von Uhren"},
+ {ID: "A.8.15", Name: "Nutzung privilegierter Hilfsprogramme", Category: "technological", CategoryID: "A.8", Description: "Einschraenkung privilegierter Hilfsprogramme"},
+ {ID: "A.8.16", Name: "Softwareinstallation", Category: "technological", CategoryID: "A.8", Description: "Kontrolle der Softwareinstallation"},
+ {ID: "A.8.17", Name: "Netzwerksicherheit", Category: "technological", CategoryID: "A.8", Description: "Sicherheit von Netzwerken"},
+ {ID: "A.8.18", Name: "Netzwerksegmentierung", Category: "technological", CategoryID: "A.8", Description: "Segmentierung von Netzwerken"},
+ {ID: "A.8.19", Name: "Webfilterung", Category: "technological", CategoryID: "A.8", Description: "Filterung des Webzugangs"},
+ {ID: "A.8.20", Name: "Kryptografie", Category: "technological", CategoryID: "A.8", Description: "Einsatz kryptografischer Massnahmen"},
+ {ID: "A.8.21", Name: "Sichere Entwicklung", Category: "technological", CategoryID: "A.8", Description: "Sichere Entwicklungslebenszyklus"},
+ {ID: "A.8.22", Name: "Sicherheitsanforderungen bei Applikationen", Category: "technological", CategoryID: "A.8", Description: "Sicherheitsanforderungen bei Anwendungen"},
+ {ID: "A.8.23", Name: "Sichere Systemarchitektur", Category: "technological", CategoryID: "A.8", Description: "Sicherheitsprinzipien in der Systemarchitektur"},
+ {ID: "A.8.24", Name: "Sicheres Programmieren", Category: "technological", CategoryID: "A.8", Description: "Sichere Programmierpraktiken"},
+ {ID: "A.8.25", Name: "Sicherheitstests", Category: "technological", CategoryID: "A.8", Description: "Sicherheitstests in der Entwicklung und Abnahme"},
+ {ID: "A.8.26", Name: "Auslagerung der Entwicklung", Category: "technological", CategoryID: "A.8", Description: "Ueberwachung ausgelagerter Entwicklung"},
+ {ID: "A.8.27", Name: "Trennung von Umgebungen", Category: "technological", CategoryID: "A.8", Description: "Trennung von Entwicklungs-, Test- und Produktionsumgebungen"},
+ {ID: "A.8.28", Name: "Aenderungsmanagement", Category: "technological", CategoryID: "A.8", Description: "Formales Aenderungsmanagement"},
+ {ID: "A.8.29", Name: "Sicherheitstests in der Abnahme", Category: "technological", CategoryID: "A.8", Description: "Durchfuehrung von Sicherheitstests vor Abnahme"},
+ {ID: "A.8.30", Name: "Datenloeschung", Category: "technological", CategoryID: "A.8", Description: "Sichere Datenloeschung"},
+ {ID: "A.8.31", Name: "Datenmaskierung", Category: "technological", CategoryID: "A.8", Description: "Techniken zur Datenmaskierung"},
+ {ID: "A.8.32", Name: "Verhinderung von Datenverlust", Category: "technological", CategoryID: "A.8", Description: "DLP-Massnahmen"},
+ {ID: "A.8.33", Name: "Testinformationen", Category: "technological", CategoryID: "A.8", Description: "Schutz von Testinformationen"},
+ {ID: "A.8.34", Name: "Audit-Informationssysteme", Category: "technological", CategoryID: "A.8", Description: "Schutz von Audit-Tools und -systemen"},
+}
+
+// Default mappings: which modules cover which ISO controls
+var DefaultISOModuleMappings = []ISOModuleMapping{
+ {
+ ModuleID: "iso-isms", ModuleName: "ISMS Grundlagen",
+ ISOControls: []string{"A.5.1", "A.5.2", "A.5.3", "A.5.4", "A.5.35", "A.5.36"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "iso-risikobewertung", ModuleName: "Risikobewertung",
+ ISOControls: []string{"A.5.7", "A.5.8", "A.5.9", "A.5.10", "A.5.12", "A.5.13"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "iso-zugangssteuerung", ModuleName: "Zugangssteuerung",
+ ISOControls: []string{"A.5.15", "A.5.16", "A.5.17", "A.5.18", "A.8.2", "A.8.3", "A.8.5"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "iso-kryptografie", ModuleName: "Kryptografie",
+ ISOControls: []string{"A.8.20", "A.8.21", "A.8.24"},
+ CoverageLevel: "partial",
+ },
+ {
+ ModuleID: "iso-physisch", ModuleName: "Physische Sicherheit",
+ ISOControls: []string{"A.7.1", "A.7.2", "A.7.3", "A.7.4", "A.7.5", "A.7.7", "A.7.8"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "dsgvo-tom", ModuleName: "Technisch-Organisatorische Massnahmen",
+ ISOControls: []string{"A.5.34", "A.8.10", "A.8.12", "A.8.30", "A.8.31"},
+ CoverageLevel: "partial",
+ },
+ {
+ ModuleID: "nis2-incident-response", ModuleName: "NIS2 Incident Response",
+ ISOControls: []string{"A.5.24", "A.5.25", "A.5.26", "A.5.27", "A.5.28", "A.6.8"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "nis2-supply-chain", ModuleName: "NIS2 Lieferkettensicherheit",
+ ISOControls: []string{"A.5.19", "A.5.20", "A.5.21", "A.5.22", "A.5.23"},
+ CoverageLevel: "full",
+ },
+ {
+ ModuleID: "nis2-risikomanagement", ModuleName: "NIS2 Risikomanagement",
+ ISOControls: []string{"A.5.29", "A.5.30", "A.8.6", "A.8.7", "A.8.8", "A.8.9"},
+ CoverageLevel: "partial",
+ },
+ {
+ ModuleID: "dsgvo-grundlagen", ModuleName: "DSGVO Grundlagen",
+ ISOControls: []string{"A.5.31", "A.5.34", "A.6.2", "A.6.3"},
+ CoverageLevel: "partial",
+ },
+}
+
+// GetISOControlByID returns a control by its ID
+func GetISOControlByID(id string) (ISOControl, bool) {
+ for _, c := range ISOControls {
+ if c.ID == id {
+ return c, true
+ }
+ }
+ return ISOControl{}, false
+}
+
+// GetISOControlsByCategory returns all controls in a category
+func GetISOControlsByCategory(categoryID string) []ISOControl {
+ var result []ISOControl
+ for _, c := range ISOControls {
+ if c.CategoryID == categoryID {
+ result = append(result, c)
+ }
+ }
+ return result
+}
+
+// ISOCategorySummary provides a summary per ISO category
+type ISOCategorySummary struct {
+ CategoryID string `json:"category_id"`
+ CategoryName string `json:"category_name"`
+ TotalControls int `json:"total_controls"`
+ CoveredFull int `json:"covered_full"`
+ CoveredPartial int `json:"covered_partial"`
+ NotCovered int `json:"not_covered"`
+}
diff --git a/ai-compliance-sdk/internal/gci/mock_data.go b/ai-compliance-sdk/internal/gci/mock_data.go
new file mode 100644
index 0000000..bb8c074
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/mock_data.go
@@ -0,0 +1,74 @@
+package gci
+
+import "time"
+
+// MockModuleData provides fallback data when academy store is empty
+func MockModuleData(tenantID string) []ModuleScore {
+ return []ModuleScore{
+ // DSGVO modules
+ {ModuleID: "dsgvo-grundlagen", ModuleName: "DSGVO Grundlagen", Assigned: 25, Completed: 22, Category: "dsgvo", RiskWeight: 2.0},
+ {ModuleID: "dsgvo-betroffenenrechte", ModuleName: "Betroffenenrechte", Assigned: 25, Completed: 18, Category: "dsgvo", RiskWeight: 2.5},
+ {ModuleID: "dsgvo-tom", ModuleName: "Technisch-Organisatorische Massnahmen", Assigned: 20, Completed: 17, Category: "dsgvo", RiskWeight: 2.5},
+ {ModuleID: "dsgvo-dsfa", ModuleName: "Datenschutz-Folgenabschaetzung", Assigned: 15, Completed: 10, Category: "dsgvo", RiskWeight: 2.0},
+ {ModuleID: "dsgvo-auftragsverarbeitung", ModuleName: "Auftragsverarbeitung", Assigned: 20, Completed: 16, Category: "dsgvo", RiskWeight: 2.0},
+
+ // NIS2 modules
+ {ModuleID: "nis2-risikomanagement", ModuleName: "NIS2 Risikomanagement", Assigned: 15, Completed: 11, Category: "nis2", RiskWeight: 3.0},
+ {ModuleID: "nis2-incident-response", ModuleName: "NIS2 Incident Response", Assigned: 15, Completed: 9, Category: "nis2", RiskWeight: 3.0},
+ {ModuleID: "nis2-supply-chain", ModuleName: "NIS2 Lieferkettensicherheit", Assigned: 10, Completed: 6, Category: "nis2", RiskWeight: 2.0},
+ {ModuleID: "nis2-management", ModuleName: "NIS2 Geschaeftsleitungspflicht", Assigned: 10, Completed: 8, Category: "nis2", RiskWeight: 3.0},
+
+ // ISO 27001 modules
+ {ModuleID: "iso-isms", ModuleName: "ISMS Grundlagen", Assigned: 20, Completed: 16, Category: "iso27001", RiskWeight: 2.0},
+ {ModuleID: "iso-risikobewertung", ModuleName: "Risikobewertung", Assigned: 15, Completed: 12, Category: "iso27001", RiskWeight: 2.0},
+ {ModuleID: "iso-zugangssteuerung", ModuleName: "Zugangssteuerung", Assigned: 20, Completed: 18, Category: "iso27001", RiskWeight: 2.0},
+ {ModuleID: "iso-kryptografie", ModuleName: "Kryptografie", Assigned: 10, Completed: 7, Category: "iso27001", RiskWeight: 1.5},
+ {ModuleID: "iso-physisch", ModuleName: "Physische Sicherheit", Assigned: 10, Completed: 9, Category: "iso27001", RiskWeight: 1.0},
+
+ // AI Act modules
+ {ModuleID: "ai-risikokategorien", ModuleName: "KI-Risikokategorien", Assigned: 15, Completed: 12, Category: "ai_act", RiskWeight: 2.5},
+ {ModuleID: "ai-transparenz", ModuleName: "KI-Transparenzpflichten", Assigned: 15, Completed: 10, Category: "ai_act", RiskWeight: 2.0},
+ {ModuleID: "ai-hochrisiko", ModuleName: "Hochrisiko-KI-Systeme", Assigned: 10, Completed: 6, Category: "ai_act", RiskWeight: 2.5},
+ {ModuleID: "ai-governance", ModuleName: "KI-Governance", Assigned: 10, Completed: 7, Category: "ai_act", RiskWeight: 2.0},
+ }
+}
+
+// MockCertificateData provides mock certificate validity dates
+func MockCertificateData() map[string]time.Time {
+ now := time.Now()
+ return map[string]time.Time{
+ "dsgvo-grundlagen": now.AddDate(0, 8, 0), // valid 8 months
+ "dsgvo-betroffenenrechte": now.AddDate(0, 3, 0), // expiring in 3 months
+ "dsgvo-tom": now.AddDate(0, 10, 0), // valid
+ "dsgvo-dsfa": now.AddDate(0, -1, 0), // expired 1 month ago
+ "dsgvo-auftragsverarbeitung": now.AddDate(0, 6, 0),
+ "nis2-risikomanagement": now.AddDate(0, 5, 0),
+ "nis2-incident-response": now.AddDate(0, 2, 0), // expiring soon
+ "nis2-supply-chain": now.AddDate(0, -2, 0), // expired 2 months
+ "nis2-management": now.AddDate(0, 9, 0),
+ "iso-isms": now.AddDate(1, 0, 0),
+ "iso-risikobewertung": now.AddDate(0, 4, 0),
+ "iso-zugangssteuerung": now.AddDate(0, 11, 0),
+ "iso-kryptografie": now.AddDate(0, 1, 0), // expiring in 1 month
+ "iso-physisch": now.AddDate(0, 7, 0),
+ "ai-risikokategorien": now.AddDate(0, 6, 0),
+ "ai-transparenz": now.AddDate(0, 3, 0),
+ "ai-hochrisiko": now.AddDate(0, -3, 0), // expired 3 months
+ "ai-governance": now.AddDate(0, 5, 0),
+ }
+}
+
+// MockIncidentData returns mock incident counts for adjustment
+func MockIncidentData() (openIncidents int, criticalIncidents int) {
+ return 3, 1
+}
+
+// MockGCIHistory returns mock historical GCI snapshots
+func MockGCIHistory(tenantID string) []GCISnapshot {
+ now := time.Now()
+ return []GCISnapshot{
+ {TenantID: tenantID, Score: 58.2, MaturityLevel: MaturityReactive, AreaScores: map[string]float64{"dsgvo": 62, "nis2": 48, "iso27001": 60, "ai_act": 55}, CalculatedAt: now.AddDate(0, -3, 0)},
+ {TenantID: tenantID, Score: 62.5, MaturityLevel: MaturityDefined, AreaScores: map[string]float64{"dsgvo": 65, "nis2": 55, "iso27001": 63, "ai_act": 58}, CalculatedAt: now.AddDate(0, -2, 0)},
+ {TenantID: tenantID, Score: 67.8, MaturityLevel: MaturityDefined, AreaScores: map[string]float64{"dsgvo": 70, "nis2": 60, "iso27001": 68, "ai_act": 62}, CalculatedAt: now.AddDate(0, -1, 0)},
+ }
+}
diff --git a/ai-compliance-sdk/internal/gci/models.go b/ai-compliance-sdk/internal/gci/models.go
new file mode 100644
index 0000000..0f75779
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/models.go
@@ -0,0 +1,104 @@
+package gci
+
+import "time"
+
+// Level 1: Module Score
+type ModuleScore struct {
+ ModuleID string `json:"module_id"`
+ ModuleName string `json:"module_name"`
+ Assigned int `json:"assigned"`
+ Completed int `json:"completed"`
+ RawScore float64 `json:"raw_score"` // completions/assigned
+ ValidityFactor float64 `json:"validity_factor"` // 0.0-1.0
+ FinalScore float64 `json:"final_score"` // RawScore * ValidityFactor
+ RiskWeight float64 `json:"risk_weight"` // module criticality weight
+ Category string `json:"category"` // dsgvo, nis2, iso27001, ai_act
+}
+
+// Level 2: Risk-weighted Module Score per regulation area
+type RiskWeightedScore struct {
+ AreaID string `json:"area_id"`
+ AreaName string `json:"area_name"`
+ Modules []ModuleScore `json:"modules"`
+ WeightedSum float64 `json:"weighted_sum"`
+ TotalWeight float64 `json:"total_weight"`
+ AreaScore float64 `json:"area_score"` // WeightedSum / TotalWeight
+}
+
+// Level 3: Regulation Area Score
+type RegulationAreaScore struct {
+ RegulationID string `json:"regulation_id"` // dsgvo, nis2, iso27001, ai_act
+ RegulationName string `json:"regulation_name"` // Display name
+ Score float64 `json:"score"` // 0-100
+ Weight float64 `json:"weight"` // regulation weight in GCI
+ WeightedScore float64 `json:"weighted_score"` // Score * Weight
+ ModuleCount int `json:"module_count"`
+ CompletedCount int `json:"completed_count"`
+}
+
+// Level 4: GCI Result
+type GCIResult struct {
+ TenantID string `json:"tenant_id"`
+ GCIScore float64 `json:"gci_score"` // 0-100
+ MaturityLevel string `json:"maturity_level"` // Optimized, Managed, Defined, Reactive, HighRisk
+ MaturityLabel string `json:"maturity_label"` // German label
+ CalculatedAt time.Time `json:"calculated_at"`
+ Profile string `json:"profile"` // default, nis2_relevant, ki_nutzer
+ AreaScores []RegulationAreaScore `json:"area_scores"`
+ CriticalityMult float64 `json:"criticality_multiplier"`
+ IncidentAdj float64 `json:"incident_adjustment"`
+ AuditTrail []AuditEntry `json:"audit_trail"`
+}
+
+// GCI Breakdown with all 4 levels
+type GCIBreakdown struct {
+ GCIResult
+ Level1Modules []ModuleScore `json:"level1_modules"`
+ Level2Areas []RiskWeightedScore `json:"level2_areas"`
+}
+
+// MaturityLevel constants
+const (
+ MaturityOptimized = "OPTIMIZED"
+ MaturityManaged = "MANAGED"
+ MaturityDefined = "DEFINED"
+ MaturityReactive = "REACTIVE"
+ MaturityHighRisk = "HIGH_RISK"
+)
+
+// Maturity level labels (German)
+var MaturityLabels = map[string]string{
+ MaturityOptimized: "Optimiert",
+ MaturityManaged: "Gesteuert",
+ MaturityDefined: "Definiert",
+ MaturityReactive: "Reaktiv",
+ MaturityHighRisk: "Hohes Risiko",
+}
+
+// AuditEntry for score transparency
+type AuditEntry struct {
+ Timestamp time.Time `json:"timestamp"`
+ Factor string `json:"factor"`
+ Description string `json:"description"`
+ Value float64 `json:"value"`
+ Impact string `json:"impact"` // positive, negative, neutral
+}
+
+// ComplianceMatrixEntry maps roles to regulations
+type ComplianceMatrixEntry struct {
+ Role string `json:"role"`
+ RoleName string `json:"role_name"`
+ Regulations map[string]float64 `json:"regulations"` // regulation_id -> score
+ OverallScore float64 `json:"overall_score"`
+ RequiredModules int `json:"required_modules"`
+ CompletedModules int `json:"completed_modules"`
+}
+
+// GCI History snapshot
+type GCISnapshot struct {
+ TenantID string `json:"tenant_id"`
+ Score float64 `json:"score"`
+ MaturityLevel string `json:"maturity_level"`
+ AreaScores map[string]float64 `json:"area_scores"`
+ CalculatedAt time.Time `json:"calculated_at"`
+}
diff --git a/ai-compliance-sdk/internal/gci/nis2_roles.go b/ai-compliance-sdk/internal/gci/nis2_roles.go
new file mode 100644
index 0000000..c75d134
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/nis2_roles.go
@@ -0,0 +1,118 @@
+package gci
+
+// NIS2Role defines a NIS2 role classification
+type NIS2Role struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ MandatoryModules []string `json:"mandatory_modules"`
+ Priority int `json:"priority"` // 1=highest
+}
+
+// NIS2RoleAssignment represents a user's NIS2 role
+type NIS2RoleAssignment struct {
+ TenantID string `json:"tenant_id"`
+ UserID string `json:"user_id"`
+ UserName string `json:"user_name"`
+ RoleID string `json:"role_id"`
+ RoleName string `json:"role_name"`
+ AssignedAt string `json:"assigned_at"`
+}
+
+// NIS2 role definitions
+var NIS2Roles = map[string]NIS2Role{
+ "N1": {
+ ID: "N1",
+ Name: "Geschaeftsleitung",
+ Description: "Leitungsorgane mit persoenlicher Haftung gemaess NIS2 Art. 20",
+ Priority: 1,
+ MandatoryModules: []string{
+ "nis2-management",
+ "nis2-risikomanagement",
+ "dsgvo-grundlagen",
+ "iso-isms",
+ },
+ },
+ "N2": {
+ ID: "N2",
+ Name: "IT-Sicherheit / CISO",
+ Description: "Verantwortliche fuer IT-Sicherheit und Cybersecurity",
+ Priority: 2,
+ MandatoryModules: []string{
+ "nis2-risikomanagement",
+ "nis2-incident-response",
+ "nis2-supply-chain",
+ "iso-zugangssteuerung",
+ "iso-kryptografie",
+ },
+ },
+ "N3": {
+ ID: "N3",
+ Name: "Kritische Funktionen",
+ Description: "Mitarbeiter in kritischen Geschaeftsprozessen",
+ Priority: 3,
+ MandatoryModules: []string{
+ "nis2-risikomanagement",
+ "nis2-incident-response",
+ "dsgvo-tom",
+ "iso-zugangssteuerung",
+ },
+ },
+ "N4": {
+ ID: "N4",
+ Name: "Allgemeine Mitarbeiter",
+ Description: "Alle Mitarbeiter mit IT-Zugang",
+ Priority: 4,
+ MandatoryModules: []string{
+ "nis2-risikomanagement",
+ "dsgvo-grundlagen",
+ "iso-isms",
+ },
+ },
+ "N5": {
+ ID: "N5",
+ Name: "Incident Response Team",
+ Description: "Mitglieder des IRT/CSIRT gemaess NIS2 Art. 21",
+ Priority: 2,
+ MandatoryModules: []string{
+ "nis2-incident-response",
+ "nis2-risikomanagement",
+ "nis2-supply-chain",
+ "iso-zugangssteuerung",
+ "iso-kryptografie",
+ "iso-isms",
+ },
+ },
+}
+
+// GetNIS2Role returns a NIS2 role by ID
+func GetNIS2Role(roleID string) (NIS2Role, bool) {
+ r, ok := NIS2Roles[roleID]
+ return r, ok
+}
+
+// ListNIS2Roles returns all NIS2 roles sorted by priority
+func ListNIS2Roles() []NIS2Role {
+ roles := []NIS2Role{}
+ // Return in priority order
+ order := []string{"N1", "N2", "N5", "N3", "N4"}
+ for _, id := range order {
+ if r, ok := NIS2Roles[id]; ok {
+ roles = append(roles, r)
+ }
+ }
+ return roles
+}
+
+// MockNIS2RoleAssignments returns mock role assignments
+func MockNIS2RoleAssignments(tenantID string) []NIS2RoleAssignment {
+ return []NIS2RoleAssignment{
+ {TenantID: tenantID, UserID: "user-001", UserName: "Dr. Schmidt", RoleID: "N1", RoleName: "Geschaeftsleitung", AssignedAt: "2025-06-01"},
+ {TenantID: tenantID, UserID: "user-002", UserName: "M. Weber", RoleID: "N2", RoleName: "IT-Sicherheit / CISO", AssignedAt: "2025-06-01"},
+ {TenantID: tenantID, UserID: "user-003", UserName: "S. Mueller", RoleID: "N5", RoleName: "Incident Response Team", AssignedAt: "2025-07-15"},
+ {TenantID: tenantID, UserID: "user-004", UserName: "K. Fischer", RoleID: "N3", RoleName: "Kritische Funktionen", AssignedAt: "2025-08-01"},
+ {TenantID: tenantID, UserID: "user-005", UserName: "L. Braun", RoleID: "N3", RoleName: "Kritische Funktionen", AssignedAt: "2025-08-01"},
+ {TenantID: tenantID, UserID: "user-006", UserName: "A. Schwarz", RoleID: "N4", RoleName: "Allgemeine Mitarbeiter", AssignedAt: "2025-09-01"},
+ {TenantID: tenantID, UserID: "user-007", UserName: "T. Wagner", RoleID: "N4", RoleName: "Allgemeine Mitarbeiter", AssignedAt: "2025-09-01"},
+ }
+}
diff --git a/ai-compliance-sdk/internal/gci/nis2_scoring.go b/ai-compliance-sdk/internal/gci/nis2_scoring.go
new file mode 100644
index 0000000..57b7468
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/nis2_scoring.go
@@ -0,0 +1,147 @@
+package gci
+
+import "math"
+
+// NIS2Score represents the NIS2-specific compliance score
+type NIS2Score struct {
+ TenantID string `json:"tenant_id"`
+ OverallScore float64 `json:"overall_score"`
+ MaturityLevel string `json:"maturity_level"`
+ MaturityLabel string `json:"maturity_label"`
+ AreaScores []NIS2AreaScore `json:"area_scores"`
+ RoleCompliance []NIS2RoleScore `json:"role_compliance"`
+}
+
+// NIS2AreaScore represents a NIS2 compliance area
+type NIS2AreaScore struct {
+ AreaID string `json:"area_id"`
+ AreaName string `json:"area_name"`
+ Score float64 `json:"score"`
+ Weight float64 `json:"weight"`
+ ModuleIDs []string `json:"module_ids"`
+}
+
+// NIS2RoleScore represents completion per NIS2 role
+type NIS2RoleScore struct {
+ RoleID string `json:"role_id"`
+ RoleName string `json:"role_name"`
+ AssignedUsers int `json:"assigned_users"`
+ CompletionRate float64 `json:"completion_rate"`
+ MandatoryTotal int `json:"mandatory_total"`
+ MandatoryDone int `json:"mandatory_done"`
+}
+
+// NIS2 scoring areas with weights
+// NIS2Score = 25% Management + 25% Incident + 30% IT Security + 20% Supply Chain
+var nis2Areas = []struct {
+ ID string
+ Name string
+ Weight float64
+ ModuleIDs []string
+}{
+ {
+ ID: "management", Name: "Management & Governance", Weight: 0.25,
+ ModuleIDs: []string{"nis2-management", "dsgvo-grundlagen", "iso-isms"},
+ },
+ {
+ ID: "incident", Name: "Vorfallsbehandlung", Weight: 0.25,
+ ModuleIDs: []string{"nis2-incident-response"},
+ },
+ {
+ ID: "it_security", Name: "IT-Sicherheit", Weight: 0.30,
+ ModuleIDs: []string{"nis2-risikomanagement", "iso-zugangssteuerung", "iso-kryptografie"},
+ },
+ {
+ ID: "supply_chain", Name: "Lieferkettensicherheit", Weight: 0.20,
+ ModuleIDs: []string{"nis2-supply-chain", "dsgvo-auftragsverarbeitung"},
+ },
+}
+
+// CalculateNIS2Score computes the NIS2-specific compliance score
+func CalculateNIS2Score(tenantID string) *NIS2Score {
+ modules := MockModuleData(tenantID)
+ moduleMap := map[string]ModuleScore{}
+ for _, m := range modules {
+ moduleMap[m.ModuleID] = m
+ }
+
+ areaScores := []NIS2AreaScore{}
+ totalWeighted := 0.0
+
+ for _, area := range nis2Areas {
+ areaScore := NIS2AreaScore{
+ AreaID: area.ID,
+ AreaName: area.Name,
+ Weight: area.Weight,
+ ModuleIDs: area.ModuleIDs,
+ }
+
+ scoreSum := 0.0
+ count := 0
+ for _, modID := range area.ModuleIDs {
+ if m, ok := moduleMap[modID]; ok {
+ if m.Assigned > 0 {
+ scoreSum += float64(m.Completed) / float64(m.Assigned) * 100
+ }
+ count++
+ }
+ }
+ if count > 0 {
+ areaScore.Score = math.Round(scoreSum/float64(count)*10) / 10
+ }
+ totalWeighted += areaScore.Score * areaScore.Weight
+ areaScores = append(areaScores, areaScore)
+ }
+
+ overallScore := math.Round(totalWeighted*10) / 10
+
+ // Calculate role compliance
+ roleAssignments := MockNIS2RoleAssignments(tenantID)
+ roleScores := calculateNIS2RoleScores(roleAssignments, moduleMap)
+
+ return &NIS2Score{
+ TenantID: tenantID,
+ OverallScore: overallScore,
+ MaturityLevel: determineMaturityLevel(overallScore),
+ MaturityLabel: MaturityLabels[determineMaturityLevel(overallScore)],
+ AreaScores: areaScores,
+ RoleCompliance: roleScores,
+ }
+}
+
+func calculateNIS2RoleScores(assignments []NIS2RoleAssignment, moduleMap map[string]ModuleScore) []NIS2RoleScore {
+ // Count users per role
+ roleCounts := map[string]int{}
+ for _, a := range assignments {
+ roleCounts[a.RoleID]++
+ }
+
+ scores := []NIS2RoleScore{}
+ for roleID, role := range NIS2Roles {
+ rs := NIS2RoleScore{
+ RoleID: roleID,
+ RoleName: role.Name,
+ AssignedUsers: roleCounts[roleID],
+ MandatoryTotal: len(role.MandatoryModules),
+ }
+
+ completionSum := 0.0
+ for _, modID := range role.MandatoryModules {
+ if m, ok := moduleMap[modID]; ok {
+ if m.Assigned > 0 {
+ rate := float64(m.Completed) / float64(m.Assigned)
+ completionSum += rate
+ if rate >= 0.8 { // 80%+ = considered done
+ rs.MandatoryDone++
+ }
+ }
+ }
+ }
+ if rs.MandatoryTotal > 0 {
+ rs.CompletionRate = math.Round(completionSum/float64(rs.MandatoryTotal)*100*10) / 10
+ }
+ scores = append(scores, rs)
+ }
+
+ return scores
+}
diff --git a/ai-compliance-sdk/internal/gci/validity.go b/ai-compliance-sdk/internal/gci/validity.go
new file mode 100644
index 0000000..5578f3d
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/validity.go
@@ -0,0 +1,59 @@
+package gci
+
+import (
+ "math"
+ "time"
+)
+
+const (
+ // GracePeriodDays is the number of days after expiry during which
+ // the certificate still contributes (with declining factor)
+ GracePeriodDays = 180
+
+ // DecayStartDays is how many days before expiry the linear decay begins
+ DecayStartDays = 180
+)
+
+// CalculateValidityFactor computes the validity factor for a certificate
+// based on its expiry date.
+//
+// Rules:
+// - Certificate not yet expiring (>6 months): factor = 1.0
+// - Certificate expiring within 6 months: linear decay from 1.0 to 0.5
+// - Certificate expired: linear decay from 0.5 to 0.0 over grace period
+// - Certificate expired beyond grace period: factor = 0.0
+func CalculateValidityFactor(validUntil time.Time, now time.Time) float64 {
+ daysUntilExpiry := validUntil.Sub(now).Hours() / 24.0
+
+ if daysUntilExpiry > float64(DecayStartDays) {
+ // Not yet in decay window
+ return 1.0
+ }
+
+ if daysUntilExpiry > 0 {
+ // In pre-expiry decay window: linear from 1.0 to 0.5
+ fraction := daysUntilExpiry / float64(DecayStartDays)
+ return 0.5 + 0.5*fraction
+ }
+
+ // Certificate is expired
+ daysExpired := -daysUntilExpiry
+ if daysExpired > float64(GracePeriodDays) {
+ return 0.0
+ }
+
+ // In grace period: linear from 0.5 to 0.0
+ fraction := 1.0 - (daysExpired / float64(GracePeriodDays))
+ return math.Max(0, 0.5*fraction)
+}
+
+// IsExpired returns true if the certificate is past its validity date
+func IsExpired(validUntil time.Time, now time.Time) bool {
+ return now.After(validUntil)
+}
+
+// IsExpiringSoon returns true if the certificate expires within the decay window
+func IsExpiringSoon(validUntil time.Time, now time.Time) bool {
+ daysUntil := validUntil.Sub(now).Hours() / 24.0
+ return daysUntil > 0 && daysUntil <= float64(DecayStartDays)
+}
diff --git a/ai-compliance-sdk/internal/gci/weights.go b/ai-compliance-sdk/internal/gci/weights.go
new file mode 100644
index 0000000..7c50742
--- /dev/null
+++ b/ai-compliance-sdk/internal/gci/weights.go
@@ -0,0 +1,78 @@
+package gci
+
+// WeightProfile defines regulation weights for different compliance profiles
+type WeightProfile struct {
+ ID string `json:"id"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Weights map[string]float64 `json:"weights"` // regulation_id -> weight (0.0-1.0)
+}
+
+// Default weight profiles
+var DefaultProfiles = map[string]WeightProfile{
+ "default": {
+ ID: "default",
+ Name: "Standard",
+ Description: "Ausgewogenes Profil fuer allgemeine Compliance",
+ Weights: map[string]float64{
+ "dsgvo": 0.30,
+ "nis2": 0.25,
+ "iso27001": 0.25,
+ "ai_act": 0.20,
+ },
+ },
+ "nis2_relevant": {
+ ID: "nis2_relevant",
+ Name: "NIS2-relevant",
+ Description: "Fuer Betreiber kritischer Infrastrukturen",
+ Weights: map[string]float64{
+ "dsgvo": 0.25,
+ "nis2": 0.35,
+ "iso27001": 0.25,
+ "ai_act": 0.15,
+ },
+ },
+ "ki_nutzer": {
+ ID: "ki_nutzer",
+ Name: "KI-Nutzer",
+ Description: "Fuer Organisationen mit KI-Einsatz",
+ Weights: map[string]float64{
+ "dsgvo": 0.25,
+ "nis2": 0.25,
+ "iso27001": 0.20,
+ "ai_act": 0.30,
+ },
+ },
+}
+
+// ModuleRiskWeights defines risk criticality per module type
+var ModuleRiskWeights = map[string]float64{
+ "incident_response": 3.0,
+ "management_awareness": 3.0,
+ "data_protection": 2.5,
+ "it_security": 2.5,
+ "supply_chain": 2.0,
+ "risk_assessment": 2.0,
+ "access_control": 2.0,
+ "business_continuity": 2.0,
+ "employee_training": 1.5,
+ "documentation": 1.5,
+ "physical_security": 1.0,
+ "general": 1.0,
+}
+
+// GetProfile returns a weight profile by ID, defaulting to "default"
+func GetProfile(profileID string) WeightProfile {
+ if p, ok := DefaultProfiles[profileID]; ok {
+ return p
+ }
+ return DefaultProfiles["default"]
+}
+
+// GetModuleRiskWeight returns the risk weight for a module category
+func GetModuleRiskWeight(category string) float64 {
+ if w, ok := ModuleRiskWeights[category]; ok {
+ return w
+ }
+ return 1.0
+}