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>
189 lines
5.8 KiB
Go
189 lines
5.8 KiB
Go
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
|
|
}
|