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 }