refactor: Remove Compliance SDK from admin-v2 sidebar, add new SDK modules
Remove Compliance SDK category from sidebar navigation as it is now handled exclusively in the Compliance Admin. Add new SDK modules (DSB Portal, Industry Templates, Multi-Tenant, Reporting, SSO) and GCI engine components. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
188
admin-v2/ai-compliance-sdk/internal/gci/iso_gap_analysis.go
Normal file
188
admin-v2/ai-compliance-sdk/internal/gci/iso_gap_analysis.go
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user