Files
breakpilot-compliance/ai-compliance-sdk/internal/ucca/dsgvo_module.go
Sharang Parnerkar c293d76e6b refactor(go/ucca): split policy_engine, legal_rag, ai_act, nis2, financial_policy, dsgvo_module
Split 6 oversized files (719–882 LOC each) into focused files under 500 LOC:
- policy_engine.go → types, loader, eval, gen (4 files)
- legal_rag.go     → types, client, http, context, scroll (5 files)
- ai_act_module.go → module, yaml, obligations (3 files)
- nis2_module.go   → module, yaml, obligations + shared obligation_yaml_types.go (3+1 files)
- financial_policy.go → types, engine (2 files)
- dsgvo_module.go  → module, yaml, obligations (3 files)

All in package ucca, zero exported symbol renames, go test ./internal/ucca/... passes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 09:48:41 +02:00

268 lines
8.1 KiB
Go

package ucca
// ============================================================================
// DSGVO Module
// ============================================================================
//
// This module implements the GDPR (DSGVO - Datenschutz-Grundverordnung) obligations.
//
// Split into:
// - dsgvo_module.go — struct, constants, classification, derive methods, decision tree
// - dsgvo_yaml.go — YAML loading and conversion helpers
// - dsgvo_obligations.go — hardcoded fallback obligations/controls/deadlines
//
// ============================================================================
// DSGVOModule implements the RegulationModule interface for DSGVO
type DSGVOModule struct {
obligations []Obligation
controls []ObligationControl
incidentDeadlines []IncidentDeadline
decisionTree *DecisionTree
loaded bool
}
var (
// DSGVOSpecialCategories contains Article 9 special categories of personal data
DSGVOSpecialCategories = map[string]bool{
"racial_ethnic_origin": true,
"political_opinions": true,
"religious_beliefs": true,
"trade_union_membership": true,
"genetic_data": true,
"biometric_data": true,
"health_data": true,
"sexual_orientation": true,
}
// DSGVOHighRiskProcessing contains high risk processing activities (Art. 35)
DSGVOHighRiskProcessing = map[string]bool{
"systematic_monitoring": true,
"automated_decisions": true,
"large_scale_special": true,
"public_area_monitoring": true,
"profiling": true,
"vulnerable_persons": true,
}
)
// NewDSGVOModule creates a new DSGVO module, loading obligations from YAML
func NewDSGVOModule() (*DSGVOModule, error) {
m := &DSGVOModule{
obligations: []Obligation{},
controls: []ObligationControl{},
incidentDeadlines: []IncidentDeadline{},
}
if err := m.loadFromYAML(); err != nil {
m.loadHardcodedObligations()
}
m.buildDecisionTree()
m.loaded = true
return m, nil
}
// ID returns the module identifier
func (m *DSGVOModule) ID() string { return "dsgvo" }
// Name returns the human-readable name
func (m *DSGVOModule) Name() string { return "DSGVO (Datenschutz-Grundverordnung)" }
// Description returns a brief description
func (m *DSGVOModule) Description() string {
return "EU-Datenschutz-Grundverordnung (Verordnung (EU) 2016/679) - Schutz personenbezogener Daten"
}
// IsApplicable checks if DSGVO applies to the organization
func (m *DSGVOModule) IsApplicable(facts *UnifiedFacts) bool {
if !facts.DataProtection.ProcessesPersonalData {
return false
}
if facts.Organization.EUMember || facts.DataProtection.OffersToEU || facts.DataProtection.MonitorsEUIndividuals {
return true
}
return facts.DataProtection.ProcessesPersonalData
}
// GetClassification returns the DSGVO classification as string
func (m *DSGVOModule) GetClassification(facts *UnifiedFacts) string {
if !m.IsApplicable(facts) {
return "nicht_anwendbar"
}
if m.hasHighRiskProcessing(facts) {
if facts.DataProtection.IsController {
return "verantwortlicher_hohes_risiko"
}
return "auftragsverarbeiter_hohes_risiko"
}
if facts.DataProtection.IsController {
return "verantwortlicher"
}
return "auftragsverarbeiter"
}
func (m *DSGVOModule) hasHighRiskProcessing(facts *UnifiedFacts) bool {
for _, cat := range facts.DataProtection.SpecialCategories {
if DSGVOSpecialCategories[cat] {
return true
}
}
for _, activity := range facts.DataProtection.HighRiskActivities {
if DSGVOHighRiskProcessing[activity] {
return true
}
}
if facts.DataProtection.DataSubjectCount > 10000 {
return true
}
if facts.DataProtection.SystematicMonitoring {
return true
}
if facts.DataProtection.AutomatedDecisions && facts.DataProtection.LegalEffects {
return true
}
return false
}
func (m *DSGVOModule) requiresDPO(facts *UnifiedFacts) bool {
if facts.Organization.IsPublicAuthority {
return true
}
if facts.DataProtection.SystematicMonitoring && facts.DataProtection.DataSubjectCount > 10000 {
return true
}
if len(facts.DataProtection.SpecialCategories) > 0 && facts.DataProtection.DataSubjectCount > 10000 {
return true
}
if facts.Organization.Country == "DE" && facts.Organization.EmployeeCount >= 20 {
return true
}
return false
}
// DeriveObligations derives all applicable DSGVO obligations
func (m *DSGVOModule) DeriveObligations(facts *UnifiedFacts) []Obligation {
if !m.IsApplicable(facts) {
return []Obligation{}
}
var result []Obligation
isHighRisk := m.hasHighRiskProcessing(facts)
needsDPO := m.requiresDPO(facts)
isController := facts.DataProtection.IsController
usesProcessors := facts.DataProtection.UsesExternalProcessor
for _, obl := range m.obligations {
if m.obligationApplies(obl, isController, isHighRisk, needsDPO, usesProcessors, facts) {
customized := obl
customized.RegulationID = m.ID()
result = append(result, customized)
}
}
return result
}
func (m *DSGVOModule) obligationApplies(obl Obligation, isController, isHighRisk, needsDPO, usesProcessors bool, facts *UnifiedFacts) bool {
switch obl.AppliesWhen {
case "always":
return true
case "controller":
return isController
case "processor":
return !isController
case "high_risk":
return isHighRisk
case "needs_dpo":
return needsDPO
case "uses_processors":
return usesProcessors
case "controller_or_processor":
return true
case "special_categories":
return len(facts.DataProtection.SpecialCategories) > 0
case "cross_border":
return facts.DataProtection.CrossBorderProcessing
case "":
return true
default:
return true
}
}
// DeriveControls derives all applicable DSGVO controls
func (m *DSGVOModule) DeriveControls(facts *UnifiedFacts) []ObligationControl {
if !m.IsApplicable(facts) {
return []ObligationControl{}
}
var result []ObligationControl
for _, ctrl := range m.controls {
ctrl.RegulationID = m.ID()
result = append(result, ctrl)
}
return result
}
// GetDecisionTree returns the DSGVO applicability decision tree
func (m *DSGVOModule) GetDecisionTree() *DecisionTree { return m.decisionTree }
// GetIncidentDeadlines returns DSGVO breach notification deadlines
func (m *DSGVOModule) GetIncidentDeadlines(facts *UnifiedFacts) []IncidentDeadline {
if !m.IsApplicable(facts) {
return []IncidentDeadline{}
}
return m.incidentDeadlines
}
func (m *DSGVOModule) buildDecisionTree() {
m.decisionTree = &DecisionTree{
ID: "dsgvo_applicability",
Name: "DSGVO Anwendbarkeits-Entscheidungsbaum",
RootNode: &DecisionNode{
ID: "root",
Question: "Verarbeitet Ihre Organisation personenbezogene Daten?",
YesNode: &DecisionNode{
ID: "eu_check",
Question: "Ist Ihre Organisation in der EU/EWR ansässig?",
YesNode: &DecisionNode{
ID: "eu_established",
Result: "DSGVO anwendbar",
Explanation: "Die DSGVO gilt für alle in der EU ansässigen Organisationen, die personenbezogene Daten verarbeiten.",
},
NoNode: &DecisionNode{
ID: "offering_check",
Question: "Bieten Sie Waren oder Dienstleistungen an Personen in der EU an?",
YesNode: &DecisionNode{
ID: "offering_to_eu",
Result: "DSGVO anwendbar",
Explanation: "Die DSGVO gilt für Organisationen außerhalb der EU, die Waren/Dienstleistungen an EU-Bürger anbieten.",
},
NoNode: &DecisionNode{
ID: "monitoring_check",
Question: "Beobachten Sie das Verhalten von Personen in der EU?",
YesNode: &DecisionNode{
ID: "monitoring_eu",
Result: "DSGVO anwendbar",
Explanation: "Die DSGVO gilt für Organisationen, die das Verhalten von Personen in der EU beobachten (z.B. Tracking, Profiling).",
},
NoNode: &DecisionNode{
ID: "not_applicable",
Result: "DSGVO nicht anwendbar",
Explanation: "Die DSGVO ist nicht anwendbar, da keine der Anknüpfungskriterien erfüllt ist.",
},
},
},
},
NoNode: &DecisionNode{
ID: "no_personal_data",
Result: "DSGVO nicht anwendbar",
Explanation: "Die DSGVO gilt nur für die Verarbeitung personenbezogener Daten.",
},
},
}
}