fix+test+docs: Archivierte Projekte, Vitest-Tests & Regulations-Doku
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 38s
CI / test-python-backend-compliance (push) Successful in 39s
CI / test-python-document-crawler (push) Successful in 26s
CI / test-python-dsms-gateway (push) Successful in 21s
Some checks failed
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-ai-compliance (push) Failing after 38s
CI / test-python-backend-compliance (push) Successful in 39s
CI / test-python-document-crawler (push) Successful in 26s
CI / test-python-dsms-gateway (push) Successful in 21s
- fix(ProjectSelector): Archivierte Projekte anklickbar machen, doppelten "Neues Projekt" Button entfernen - test: 32 Vitest-Tests fuer scope-to-facts und supervisory-authority-resolver - docs(flow-data): Scope-Step outputs + Obligations inputs erweitert - docs(developer-portal): Feature-Highlight "Automatische Regulierungs-Ableitung" - docs(mkdocs): Neuer Abschnitt Regulierungs-Ableitung in obligations.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -123,9 +123,9 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
checkpointType: 'REQUIRED',
|
checkpointType: 'REQUIRED',
|
||||||
checkpointReviewer: 'NONE',
|
checkpointReviewer: 'NONE',
|
||||||
description: 'Bestimmung der Compliance-Tiefe und des Umfangs basierend auf Unternehmensprofil.',
|
description: 'Bestimmung der Compliance-Tiefe und des Umfangs basierend auf Unternehmensprofil.',
|
||||||
descriptionLong: 'Basierend auf dem Unternehmensprofil wird automatisch ermittelt, wie tiefgehend die Compliance-Analyse sein muss. Kleine Unternehmen mit wenig Datenverarbeitung erhalten eine "BASIS"-Tiefe, waehrend grosse Unternehmen mit sensiblen Daten oder KI-Systemen eine "ERWEITERT" oder "VOLLSTAENDIG"-Tiefe erhalten. Der Compliance-Scope bestimmt, welche Module aktiviert werden und wie detailliert die Dokumentation sein muss.',
|
descriptionLong: 'Basierend auf dem Unternehmensprofil wird automatisch ermittelt, wie tiefgehend die Compliance-Analyse sein muss. Kleine Unternehmen mit wenig Datenverarbeitung erhalten eine "BASIS"-Tiefe, waehrend grosse Unternehmen mit sensiblen Daten oder KI-Systemen eine "ERWEITERT" oder "VOLLSTAENDIG"-Tiefe erhalten. Der Compliance-Scope bestimmt, welche Module aktiviert werden und wie detailliert die Dokumentation sein muss. Zusaetzlich werden anwendbare Regulierungen (DSGVO, AI Act, NIS2 etc.) und zustaendige Aufsichtsbehoerden automatisch abgeleitet.',
|
||||||
inputs: ['companyProfile'],
|
inputs: ['companyProfile'],
|
||||||
outputs: ['complianceDepthLevel'],
|
outputs: ['complianceDepthLevel', 'applicableRegulations', 'supervisoryAuthorities'],
|
||||||
prerequisiteSteps: ['company-profile'],
|
prerequisiteSteps: ['company-profile'],
|
||||||
dbTables: ['sdk_states'],
|
dbTables: ['sdk_states'],
|
||||||
dbMode: 'read/write',
|
dbMode: 'read/write',
|
||||||
@@ -399,9 +399,9 @@ export const SDK_FLOW_STEPS: SDKFlowStep[] = [
|
|||||||
checkpointType: 'REQUIRED',
|
checkpointType: 'REQUIRED',
|
||||||
checkpointReviewer: 'NONE',
|
checkpointReviewer: 'NONE',
|
||||||
description: 'Zusammenfassung aller gesetzlichen Pflichten aus DSGVO, AI Act, NIS2.',
|
description: 'Zusammenfassung aller gesetzlichen Pflichten aus DSGVO, AI Act, NIS2.',
|
||||||
descriptionLong: 'Die Pflichtenuebersicht konsolidiert alle gesetzlichen Pflichten, die sich aus den Requirements, der AI-Act-Klassifizierung und den aktivierten Modulen ergeben. Fuer jede Pflicht wird angegeben: Welches Gesetz (DSGVO, AI Act, NIS2), welcher Artikel, welche Frist, wer verantwortlich ist und welche Massnahmen erforderlich sind. Die RAG-Collection bp_compliance_recht liefert aktuelle Pflichtentexte und Auslegungshinweise.',
|
descriptionLong: 'Die Pflichtenuebersicht konsolidiert alle gesetzlichen Pflichten, die sich aus den Requirements, der AI-Act-Klassifizierung und den aktivierten Modulen ergeben. Wenn applicableRegulations aus dem Scope-Profiling vorliegen, werden diese direkt als Vorfilter verwendet. Fuer jede Pflicht wird angegeben: Welches Gesetz (DSGVO, AI Act, NIS2), welcher Artikel, welche Frist, wer verantwortlich ist und welche Massnahmen erforderlich sind. Die RAG-Collection bp_compliance_recht liefert aktuelle Pflichtentexte und Auslegungshinweise.',
|
||||||
legalBasis: 'Art. 5 Abs. 2 DSGVO, Art. 9 AI Act',
|
legalBasis: 'Art. 5 Abs. 2 DSGVO, Art. 9 AI Act',
|
||||||
inputs: ['requirements', 'aiActClassification', 'modules'],
|
inputs: ['requirements', 'aiActClassification', 'modules', 'applicableRegulations'],
|
||||||
outputs: ['obligationsOverview'],
|
outputs: ['obligationsOverview'],
|
||||||
prerequisiteSteps: ['audit-report'],
|
prerequisiteSteps: ['audit-report'],
|
||||||
dbTables: ['compliance_obligations'],
|
dbTables: ['compliance_obligations'],
|
||||||
|
|||||||
@@ -482,7 +482,6 @@ export function ProjectSelector() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleProjectClick = (project: ProjectInfo) => {
|
const handleProjectClick = (project: ProjectInfo) => {
|
||||||
if (project.status === 'archived') return // archived projects are read-only in list
|
|
||||||
router.push(`/sdk?project=${project.id}`)
|
router.push(`/sdk?project=${project.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -598,26 +597,6 @@ export function ProjectSelector() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* No active but has archived */}
|
|
||||||
{!loading && projects.length === 0 && archivedProjects.length > 0 && (
|
|
||||||
<div className="text-center py-12 mb-6">
|
|
||||||
<h2 className="text-lg font-semibold text-gray-900">Keine aktiven Projekte</h2>
|
|
||||||
<p className="mt-2 text-gray-500">
|
|
||||||
Sie haben {archivedProjects.length} archivierte{archivedProjects.length === 1 ? 's' : ''} Projekt{archivedProjects.length === 1 ? '' : 'e'}.
|
|
||||||
Stellen Sie ein Projekt wieder her oder erstellen Sie ein neues.
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowCreateDialog(true)}
|
|
||||||
className="mt-4 inline-flex items-center gap-2 px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-colors font-medium"
|
|
||||||
>
|
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
|
|
||||||
</svg>
|
|
||||||
Neues Projekt erstellen
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Archived Projects Section */}
|
{/* Archived Projects Section */}
|
||||||
{!loading && archivedProjects.length > 0 && (
|
{!loading && archivedProjects.length > 0 && (
|
||||||
<div className="mt-8">
|
<div className="mt-8">
|
||||||
|
|||||||
193
admin-compliance/lib/sdk/__tests__/scope-to-facts.test.ts
Normal file
193
admin-compliance/lib/sdk/__tests__/scope-to-facts.test.ts
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import {
|
||||||
|
parseEmployeeRange,
|
||||||
|
parseRevenueRange,
|
||||||
|
buildAssessmentPayload,
|
||||||
|
} from '../scope-to-facts'
|
||||||
|
import type { CompanyProfile } from '../types'
|
||||||
|
import type { ScopeProfilingAnswer, ScopeDecision } from '../compliance-scope-types'
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// parseEmployeeRange
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('parseEmployeeRange', () => {
|
||||||
|
it('returns 5 for "1-9"', () => {
|
||||||
|
expect(parseEmployeeRange('1-9')).toBe(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 30 for "10-49"', () => {
|
||||||
|
expect(parseEmployeeRange('10-49')).toBe(30)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 150 for "50-249"', () => {
|
||||||
|
expect(parseEmployeeRange('50-249')).toBe(150)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 625 for "250-999"', () => {
|
||||||
|
expect(parseEmployeeRange('250-999')).toBe(625)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 1500 for "1000+"', () => {
|
||||||
|
expect(parseEmployeeRange('1000+')).toBe(1500)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 10 for null', () => {
|
||||||
|
expect(parseEmployeeRange(null)).toBe(10)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 10 for undefined', () => {
|
||||||
|
expect(parseEmployeeRange(undefined)).toBe(10)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// parseRevenueRange
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('parseRevenueRange', () => {
|
||||||
|
it('returns 1000000 for "< 2 Mio"', () => {
|
||||||
|
expect(parseRevenueRange('< 2 Mio')).toBe(1000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 6000000 for "2-10 Mio"', () => {
|
||||||
|
expect(parseRevenueRange('2-10 Mio')).toBe(6000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 30000000 for "10-50 Mio"', () => {
|
||||||
|
expect(parseRevenueRange('10-50 Mio')).toBe(30000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 75000000 for "> 50 Mio"', () => {
|
||||||
|
expect(parseRevenueRange('> 50 Mio')).toBe(75000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 1000000 for null', () => {
|
||||||
|
expect(parseRevenueRange(null)).toBe(1000000)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns 1000000 for undefined', () => {
|
||||||
|
expect(parseRevenueRange(undefined)).toBe(1000000)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// buildAssessmentPayload
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('buildAssessmentPayload', () => {
|
||||||
|
const baseProfile: CompanyProfile = {
|
||||||
|
companyName: 'Test GmbH',
|
||||||
|
legalForm: 'GmbH',
|
||||||
|
industry: ['IT', 'Software'],
|
||||||
|
employeeCount: '50-249',
|
||||||
|
annualRevenue: '10-50 Mio',
|
||||||
|
headquartersCountry: 'DE',
|
||||||
|
headquartersState: 'BW',
|
||||||
|
isDataController: true,
|
||||||
|
isDataProcessor: false,
|
||||||
|
offerings: ['software_saas'],
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseAnswers: ScopeProfilingAnswer[] = [
|
||||||
|
{ questionId: 'data_art9', value: false, blockId: 'data' },
|
||||||
|
{ questionId: 'data_minors', value: false, blockId: 'data' },
|
||||||
|
{ questionId: 'data_hr', value: true, blockId: 'data' },
|
||||||
|
{ questionId: 'data_financial', value: false, blockId: 'data' },
|
||||||
|
{ questionId: 'tech_third_country', value: true, blockId: 'tech' },
|
||||||
|
{ questionId: 'tech_subprocessors', value: true, blockId: 'tech' },
|
||||||
|
{ questionId: 'proc_adm_scoring', value: false, blockId: 'processing' },
|
||||||
|
{ questionId: 'proc_employee_monitoring', value: false, blockId: 'processing' },
|
||||||
|
{ questionId: 'proc_video_surveillance', value: false, blockId: 'processing' },
|
||||||
|
{ questionId: 'proc_tracking', value: false, blockId: 'processing' },
|
||||||
|
{ questionId: 'prod_cookies_consent', value: true, blockId: 'product' },
|
||||||
|
{ questionId: 'data_volume', value: false, blockId: 'data' },
|
||||||
|
{ questionId: 'ai_uses_ai', value: true, blockId: 'ai' },
|
||||||
|
{ questionId: 'ai_categories', value: ['ai_provider'], blockId: 'ai' },
|
||||||
|
{ questionId: 'ai_risk_assessment', value: 'limited', blockId: 'ai' },
|
||||||
|
{ questionId: 'org_cert_target', value: 'iso27001', blockId: 'organisation' },
|
||||||
|
]
|
||||||
|
|
||||||
|
it('maps a full profile correctly', () => {
|
||||||
|
const payload = buildAssessmentPayload(baseProfile, baseAnswers, null)
|
||||||
|
|
||||||
|
expect(payload.employee_count).toBe(150)
|
||||||
|
expect(payload.annual_revenue).toBe(30000000)
|
||||||
|
expect(payload.country).toBe('DE')
|
||||||
|
expect(payload.industry).toBe('IT, Software')
|
||||||
|
expect(payload.legal_form).toBe('GmbH')
|
||||||
|
expect(payload.is_controller).toBe(true)
|
||||||
|
expect(payload.is_processor).toBe(false)
|
||||||
|
expect(payload.cross_border_transfer).toBe(true)
|
||||||
|
expect(payload.uses_processors).toBe(true)
|
||||||
|
expect(payload.uses_cookies).toBe(true)
|
||||||
|
expect(payload.processes_employee_data).toBe(true)
|
||||||
|
expect(payload.operates_platform).toBe(true)
|
||||||
|
expect(payload.proc_ai_usage).toBe(true)
|
||||||
|
expect(payload.cert_target).toBe('iso27001')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses defaults for null/undefined profile fields', () => {
|
||||||
|
const emptyProfile: CompanyProfile = {
|
||||||
|
companyName: 'Minimal',
|
||||||
|
}
|
||||||
|
const payload = buildAssessmentPayload(emptyProfile, [], null)
|
||||||
|
|
||||||
|
expect(payload.employee_count).toBe(10) // parseEmployeeRange(undefined)
|
||||||
|
expect(payload.annual_revenue).toBe(1000000)
|
||||||
|
expect(payload.country).toBe('DE') // default
|
||||||
|
expect(payload.industry).toBe('')
|
||||||
|
expect(payload.legal_form).toBe('')
|
||||||
|
expect(payload.is_controller).toBe(true) // default
|
||||||
|
expect(payload.is_processor).toBe(false) // default
|
||||||
|
expect(payload.determined_level).toBe('L2') // default
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects AI provider from ai_categories', () => {
|
||||||
|
const payload = buildAssessmentPayload(baseProfile, baseAnswers, null)
|
||||||
|
|
||||||
|
expect(payload.is_ai_provider).toBe(true)
|
||||||
|
expect(payload.is_ai_deployer).toBe(false)
|
||||||
|
expect(payload.limited_risk_ai).toBe(true)
|
||||||
|
expect(payload.high_risk_ai).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects AI deployer from ai_categories', () => {
|
||||||
|
const deployerAnswers = baseAnswers.map(a =>
|
||||||
|
a.questionId === 'ai_categories'
|
||||||
|
? { ...a, value: ['ai_deployer'] }
|
||||||
|
: a
|
||||||
|
)
|
||||||
|
const payload = buildAssessmentPayload(baseProfile, deployerAnswers, null)
|
||||||
|
|
||||||
|
expect(payload.is_ai_provider).toBe(false)
|
||||||
|
expect(payload.is_ai_deployer).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('detects financial institution from industry', () => {
|
||||||
|
const finProfile: CompanyProfile = {
|
||||||
|
...baseProfile,
|
||||||
|
industry: ['Finanzdienstleistungen', 'Banking'],
|
||||||
|
}
|
||||||
|
const payload = buildAssessmentPayload(finProfile, baseAnswers, null)
|
||||||
|
|
||||||
|
expect(payload.is_financial_institution).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes decision data when provided', () => {
|
||||||
|
const decision: ScopeDecision = {
|
||||||
|
determinedLevel: 'L3',
|
||||||
|
triggeredHardTriggers: [
|
||||||
|
{ rule: { id: 'rule-1', name: 'Test Rule', description: '', targetLevel: 'L3', trigger: { field: '', op: 'eq', value: true } }, factValue: true },
|
||||||
|
],
|
||||||
|
requiredDocuments: [
|
||||||
|
{ documentType: 'dsfa', reason: 'test', regulation: 'dsgvo' },
|
||||||
|
],
|
||||||
|
} as any
|
||||||
|
const payload = buildAssessmentPayload(baseProfile, baseAnswers, decision)
|
||||||
|
|
||||||
|
expect(payload.determined_level).toBe('L3')
|
||||||
|
expect(payload.triggered_rules).toEqual(['rule-1'])
|
||||||
|
expect(payload.required_documents).toEqual(['dsfa'])
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import { resolveAuthorities } from '../supervisory-authority-resolver'
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Datenschutz-Aufsichtsbehoerde (DSGVO)
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('resolveAuthorities — Datenschutz', () => {
|
||||||
|
it('resolves DE + BW to LfDI BW', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', ['dsgvo'])
|
||||||
|
const dp = results.find(r => r.domain === 'Datenschutz')
|
||||||
|
expect(dp).toBeDefined()
|
||||||
|
expect(dp!.authority.abbreviation).toBe('LfDI BW')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolves DE + BY to BayLDA', () => {
|
||||||
|
const results = resolveAuthorities('BY', 'DE', ['dsgvo'])
|
||||||
|
const dp = results.find(r => r.domain === 'Datenschutz')
|
||||||
|
expect(dp).toBeDefined()
|
||||||
|
expect(dp!.authority.abbreviation).toBe('BayLDA')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolves DE without state to BfDI', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'DE', ['dsgvo'])
|
||||||
|
const dp = results.find(r => r.domain === 'Datenschutz')
|
||||||
|
expect(dp).toBeDefined()
|
||||||
|
expect(dp!.authority.abbreviation).toBe('BfDI')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolves AT to DSB AT', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'AT', ['dsgvo'])
|
||||||
|
const dp = results.find(r => r.domain === 'Datenschutz')
|
||||||
|
expect(dp).toBeDefined()
|
||||||
|
expect(dp!.authority.abbreviation).toBe('DSB AT')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('resolves CH to EDOEB', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'CH', ['dsgvo'])
|
||||||
|
const dp = results.find(r => r.domain === 'Datenschutz')
|
||||||
|
expect(dp).toBeDefined()
|
||||||
|
expect(dp!.authority.abbreviation).toBe('EDOEB')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Regulierungs-spezifische Behoerden
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('resolveAuthorities — regulation-specific', () => {
|
||||||
|
it('includes BSI for NIS2 in DE', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', ['dsgvo', 'nis2'])
|
||||||
|
const nis2 = results.find(r => r.domain.includes('NIS2'))
|
||||||
|
expect(nis2).toBeDefined()
|
||||||
|
expect(nis2!.authority.abbreviation).toBe('BSI')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes BaFin for financial_policy in DE', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', ['dsgvo', 'financial_policy'])
|
||||||
|
const fin = results.find(r => r.domain.includes('Finanzaufsicht'))
|
||||||
|
expect(fin).toBeDefined()
|
||||||
|
expect(fin!.authority.abbreviation).toBe('BaFin')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes BNetzA for ai_act in DE', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', ['dsgvo', 'ai_act'])
|
||||||
|
const ai = results.find(r => r.domain.includes('KI-Aufsicht'))
|
||||||
|
expect(ai).toBeDefined()
|
||||||
|
expect(ai!.authority.abbreviation).toBe('BNetzA')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('includes NCSA for NIS2 outside DE', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'AT', ['dsgvo', 'nis2'])
|
||||||
|
const nis2 = results.find(r => r.domain.includes('NIS2'))
|
||||||
|
expect(nis2).toBeDefined()
|
||||||
|
expect(nis2!.authority.abbreviation).toBe('NCSA')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Edge Cases
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
describe('resolveAuthorities — edge cases', () => {
|
||||||
|
it('returns empty array when no regulations', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', [])
|
||||||
|
expect(results).toEqual([])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns multiple authorities for multiple regulations', () => {
|
||||||
|
const results = resolveAuthorities('BW', 'DE', ['dsgvo', 'nis2', 'ai_act', 'financial_policy'])
|
||||||
|
expect(results.length).toBe(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not include BaFin for non-DE financial_policy', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'AT', ['financial_policy'])
|
||||||
|
const fin = results.find(r => r.domain.includes('Finanzaufsicht'))
|
||||||
|
expect(fin).toBeUndefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not include BNetzA for non-DE ai_act', () => {
|
||||||
|
const results = resolveAuthorities(undefined, 'AT', ['ai_act'])
|
||||||
|
const ai = results.find(r => r.domain.includes('KI-Aufsicht'))
|
||||||
|
expect(ai).toBeUndefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -228,6 +228,12 @@ function ComplianceDashboard() {
|
|||||||
PDF, JSON, ZIP-Export fuer Audits und Dokumentation
|
PDF, JSON, ZIP-Export fuer Audits und Dokumentation
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="p-4 border border-gray-200 rounded-lg">
|
||||||
|
<h4 className="font-medium text-gray-900 mb-2">Automatische Regulierungs-Ableitung</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Aus CompanyProfile und Scope-Profiling werden anwendbare Gesetze (DSGVO, AI Act, NIS2, DORA etc.) und zustaendige Aufsichtsbehoerden automatisch ermittelt
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Next Steps */}
|
{/* Next Steps */}
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ Obligation DSGVO-ART-32 → Controls [TOM-001, TOM-042, TOM-097]
|
|||||||
| `GET` | `/sdk/v1/ucca/obligations/regulations` | Alle verfügbaren Regulierungen auflisten |
|
| `GET` | `/sdk/v1/ucca/obligations/regulations` | Alle verfügbaren Regulierungen auflisten |
|
||||||
| `GET` | `/sdk/v1/ucca/obligations/regulations/:id/decision-tree` | Entscheidungsbaum für eine Regulierung |
|
| `GET` | `/sdk/v1/ucca/obligations/regulations/:id/decision-tree` | Entscheidungsbaum für eine Regulierung |
|
||||||
|
|
||||||
### Schnellprüfung
|
### Schnellprüfung & Scope-Assessment
|
||||||
|
|
||||||
| Methode | Pfad | Beschreibung |
|
| Methode | Pfad | Beschreibung |
|
||||||
|---------|------|--------------|
|
|---------|------|--------------|
|
||||||
@@ -205,6 +205,60 @@ Obligation DSGVO-ART-32 → Controls [TOM-001, TOM-042, TOM-097]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Automatische Regulierungs-Ableitung
|
||||||
|
|
||||||
|
Seit v2 kann das Obligations-Framework anwendbare Regulierungen und Aufsichtsbehörden direkt aus dem Scope-Profiling ableiten.
|
||||||
|
|
||||||
|
### Datenfluss
|
||||||
|
|
||||||
|
```
|
||||||
|
CompanyProfile + ScopeProfilingAnswers
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
scope-to-facts.ts ← Konvertiert Profil + Scope-Antworten in ScopeDecisionPayload
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
POST /assess-from-scope ← Go AI SDK bewertet Payload gegen Condition Engine
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
ApplicableRegulations ← Liste anwendbarer Gesetze (DSGVO, AI Act, NIS2, etc.)
|
||||||
|
+
|
||||||
|
supervisory-authority- ← Zuständige Aufsichtsbehörden (LfDI, BSI, BaFin, BNetzA)
|
||||||
|
resolver.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
### Neue Dateien
|
||||||
|
|
||||||
|
| Datei | Beschreibung |
|
||||||
|
|-------|--------------|
|
||||||
|
| `admin-compliance/lib/sdk/scope-to-facts.ts` | Mapper: CompanyProfile + ScopeAnswers → `ScopeDecisionPayload` für Go SDK |
|
||||||
|
| `admin-compliance/lib/sdk/supervisory-authority-resolver.ts` | Ermittelt Aufsichtsbehörden aus Bundesland/Land + Regulierungen |
|
||||||
|
|
||||||
|
### scope-to-facts.ts
|
||||||
|
|
||||||
|
Exportierte Funktionen:
|
||||||
|
|
||||||
|
- `buildAssessmentPayload(profile, scopeAnswers, decision)` → `ScopeDecisionPayload`
|
||||||
|
- `parseEmployeeRange(range)` → Mittelwert als Zahl (z.B. "50-249" → 150)
|
||||||
|
- `parseRevenueRange(range)` → Umsatz als Zahl (z.B. "10-50 Mio" → 30.000.000)
|
||||||
|
|
||||||
|
### supervisory-authority-resolver.ts
|
||||||
|
|
||||||
|
Exportierte Funktion:
|
||||||
|
|
||||||
|
- `resolveAuthorities(state, country, regulationIds)` → `SupervisoryAuthorityResult[]`
|
||||||
|
|
||||||
|
Abgedeckte Regulierungen → Behörden:
|
||||||
|
|
||||||
|
| Regulierung | Behörde (DE) | Behörde (Andere) |
|
||||||
|
|-------------|-------------|-----------------|
|
||||||
|
| `dsgvo` | Landes-Datenschutzbehörde (16 Bundesländer) | Nationale DSB (AT, CH, FR, NL, etc.) |
|
||||||
|
| `nis2` | BSI | NCSA |
|
||||||
|
| `financial_policy` | BaFin | — |
|
||||||
|
| `ai_act` | BNetzA | — |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Gap-Analyse
|
## Gap-Analyse
|
||||||
|
|
||||||
Die Gap-Analyse vergleicht die **geforderten TOM-Controls** (aus Obligations) mit den **implementierten Controls** (aus `compliance_controls`):
|
Die Gap-Analyse vergleicht die **geforderten TOM-Controls** (aus Obligations) mit den **implementierten Controls** (aus `compliance_controls`):
|
||||||
|
|||||||
Reference in New Issue
Block a user