feat: Vorbereitung-Module auf 100% — Persistenz, Backend-Services, UCCA Frontend
All checks were successful
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) Successful in 37s
CI / test-python-backend-compliance (push) Successful in 32s
CI / test-python-document-crawler (push) Successful in 22s
CI / test-python-dsms-gateway (push) Successful in 18s

Phase A: PostgreSQL State Store (sdk_states Tabelle, InMemory-Fallback)
Phase B: Modules dynamisch vom Backend, Scope DB-Persistenz, Source Policy State
Phase C: UCCA Frontend (3 Seiten, Wizard, RiskScoreGauge), Obligations Live-Daten
Phase D: Document Import (PDF/LLM/Gap-Analyse), System Screening (SBOM/OSV.dev)
Phase E: Company Profile CRUD mit Audit-Logging
Phase F: Tests (Python + TypeScript), flow-data.ts DB-Tabellen aktualisiert

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-03-02 11:04:31 +01:00
parent cd15ab0932
commit e6d666b89b
38 changed files with 4195 additions and 420 deletions

View File

@@ -1,85 +1,8 @@
'use client'
import React, { useState } from 'react'
import React, { useState, useRef } from 'react'
import { useSDK, ScreeningResult, SecurityIssue, SBOMComponent } from '@/lib/sdk'
// =============================================================================
// MOCK DATA
// =============================================================================
const mockSBOMComponents: SBOMComponent[] = [
{
name: 'react',
version: '18.3.0',
type: 'library',
purl: 'pkg:npm/react@18.3.0',
licenses: ['MIT'],
vulnerabilities: [],
},
{
name: 'next',
version: '15.1.0',
type: 'framework',
purl: 'pkg:npm/next@15.1.0',
licenses: ['MIT'],
vulnerabilities: [],
},
{
name: 'lodash',
version: '4.17.21',
type: 'library',
purl: 'pkg:npm/lodash@4.17.21',
licenses: ['MIT'],
vulnerabilities: [
{
id: 'CVE-2021-23337',
cve: 'CVE-2021-23337',
severity: 'HIGH',
title: 'Prototype Pollution',
description: 'Lodash versions prior to 4.17.21 are vulnerable to Command Injection via the template function.',
cvss: 7.2,
fixedIn: '4.17.21',
},
],
},
]
const mockSecurityIssues: SecurityIssue[] = [
{
id: 'issue-1',
severity: 'CRITICAL',
title: 'SQL Injection Vulnerability',
description: 'Unvalidated user input in database queries',
cve: 'CVE-2024-12345',
cvss: 9.8,
affectedComponent: 'database-connector',
remediation: 'Use parameterized queries',
status: 'OPEN',
},
{
id: 'issue-2',
severity: 'HIGH',
title: 'Cross-Site Scripting (XSS)',
description: 'Reflected XSS in search functionality',
cve: 'CVE-2024-12346',
cvss: 7.5,
affectedComponent: 'search-module',
remediation: 'Sanitize and encode user input',
status: 'IN_PROGRESS',
},
{
id: 'issue-3',
severity: 'MEDIUM',
title: 'Insecure Cookie Configuration',
description: 'Session cookies missing Secure and HttpOnly flags',
cve: null,
cvss: 5.3,
affectedComponent: 'auth-service',
remediation: 'Set Secure and HttpOnly flags on cookies',
status: 'OPEN',
},
]
// =============================================================================
// COMPONENTS
// =============================================================================
@@ -243,62 +166,120 @@ export default function ScreeningPage() {
const [isScanning, setIsScanning] = useState(false)
const [scanProgress, setScanProgress] = useState(0)
const [scanStatus, setScanStatus] = useState('')
const [repositoryUrl, setRepositoryUrl] = useState('')
const startScan = async () => {
if (!repositoryUrl) return
const [scanError, setScanError] = useState<string | null>(null)
const fileInputRef = useRef<HTMLInputElement>(null)
const startScan = async (file: File) => {
setIsScanning(true)
setScanProgress(0)
setScanStatus('Initialisierung...')
setScanError(null)
// Simulate scan progress
const steps = [
{ progress: 10, status: 'Repository wird geklont...' },
{ progress: 25, status: 'Abhängigkeiten werden analysiert...' },
{ progress: 40, status: 'SBOM wird generiert...' },
{ progress: 60, status: 'Schwachstellenscan läuft...' },
{ progress: 80, status: 'Lizenzprüfung...' },
{ progress: 95, status: 'Bericht wird erstellt...' },
{ progress: 100, status: 'Abgeschlossen!' },
]
// Show progress steps while API processes
const progressInterval = setInterval(() => {
setScanProgress(prev => {
if (prev >= 90) return prev
const step = Math.random() * 15 + 5
const next = Math.min(prev + step, 90)
const statuses = [
'Abhaengigkeiten werden analysiert...',
'SBOM wird generiert...',
'Schwachstellenscan laeuft...',
'OSV.dev Datenbank wird abgefragt...',
'Lizenzpruefung...',
]
setScanStatus(statuses[Math.min(Math.floor(next / 20), statuses.length - 1)])
return next
})
}, 600)
for (const step of steps) {
await new Promise(r => setTimeout(r, 800))
setScanProgress(step.progress)
setScanStatus(step.status)
try {
const formData = new FormData()
formData.append('file', file)
formData.append('tenant_id', 'default')
const response = await fetch('/api/sdk/v1/screening/scan', {
method: 'POST',
body: formData,
})
clearInterval(progressInterval)
if (!response.ok) {
const err = await response.json().catch(() => ({ error: 'Unknown error' }))
throw new Error(err.details || err.error || `HTTP ${response.status}`)
}
const data = await response.json()
setScanProgress(100)
setScanStatus('Abgeschlossen!')
// Map backend response to ScreeningResult
const issues: SecurityIssue[] = (data.issues || []).map((i: any) => ({
id: i.id,
severity: i.severity,
title: i.title,
description: i.description,
cve: i.cve || null,
cvss: i.cvss || null,
affectedComponent: i.affected_component,
remediation: i.remediation,
status: i.status || 'OPEN',
}))
const components: SBOMComponent[] = (data.components || []).map((c: any) => ({
name: c.name,
version: c.version,
type: c.type,
purl: c.purl,
licenses: c.licenses || [],
vulnerabilities: c.vulnerabilities || [],
}))
const result: ScreeningResult = {
id: data.id,
status: 'COMPLETED',
startedAt: data.started_at ? new Date(data.started_at) : new Date(),
completedAt: data.completed_at ? new Date(data.completed_at) : new Date(),
sbom: {
format: data.sbom_format || 'CycloneDX',
version: data.sbom_version || '1.5',
components,
dependencies: [],
generatedAt: new Date(),
},
securityScan: {
totalIssues: data.total_issues || issues.length,
critical: data.critical_issues || 0,
high: data.high_issues || 0,
medium: data.medium_issues || 0,
low: data.low_issues || 0,
issues,
},
error: null,
}
dispatch({ type: 'SET_SCREENING', payload: result })
issues.forEach(issue => {
dispatch({ type: 'ADD_SECURITY_ISSUE', payload: issue })
})
} catch (error: any) {
clearInterval(progressInterval)
console.error('Screening scan failed:', error)
setScanError(error.message || 'Scan fehlgeschlagen')
setScanProgress(0)
setScanStatus('')
} finally {
setIsScanning(false)
}
}
// Set mock results
const result: ScreeningResult = {
id: `scan-${Date.now()}`,
status: 'COMPLETED',
startedAt: new Date(Date.now() - 30000),
completedAt: new Date(),
sbom: {
format: 'CycloneDX',
version: '1.5',
components: mockSBOMComponents,
dependencies: [],
generatedAt: new Date(),
},
securityScan: {
totalIssues: mockSecurityIssues.length,
critical: mockSecurityIssues.filter(i => i.severity === 'CRITICAL').length,
high: mockSecurityIssues.filter(i => i.severity === 'HIGH').length,
medium: mockSecurityIssues.filter(i => i.severity === 'MEDIUM').length,
low: mockSecurityIssues.filter(i => i.severity === 'LOW').length,
issues: mockSecurityIssues,
},
error: null,
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (file) {
startScan(file)
}
dispatch({ type: 'SET_SCREENING', payload: result })
mockSecurityIssues.forEach(issue => {
dispatch({ type: 'ADD_SECURITY_ISSUE', payload: issue })
})
setIsScanning(false)
}
return (
@@ -314,30 +295,33 @@ export default function ScreeningPage() {
{/* Scan Input */}
{!state.screening && !isScanning && (
<div className="bg-white rounded-xl border border-gray-200 p-6">
<h3 className="font-semibold text-gray-900 mb-4">Repository scannen</h3>
<h3 className="font-semibold text-gray-900 mb-4">Abhaengigkeiten scannen</h3>
<p className="text-sm text-gray-500 mb-4">
Laden Sie eine Abhaengigkeitsdatei hoch, um ein SBOM zu generieren und Schwachstellen zu erkennen.
</p>
<input
ref={fileInputRef}
type="file"
accept=".json,.txt,.lock"
onChange={handleFileSelect}
className="hidden"
/>
<div className="flex gap-4">
<input
type="text"
value={repositoryUrl}
onChange={e => setRepositoryUrl(e.target.value)}
placeholder="https://github.com/organization/repository"
className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent"
/>
<button
onClick={startScan}
disabled={!repositoryUrl}
className={`px-6 py-2 rounded-lg font-medium transition-colors ${
repositoryUrl
? 'bg-purple-600 text-white hover:bg-purple-700'
: 'bg-gray-200 text-gray-400 cursor-not-allowed'
}`}
onClick={() => fileInputRef.current?.click()}
className="px-6 py-2 bg-purple-600 text-white rounded-lg font-medium hover:bg-purple-700 transition-colors"
>
Scan starten
Datei auswaehlen & scannen
</button>
</div>
<p className="mt-2 text-sm text-gray-500">
Unterstützte Formate: Git URL, GitHub, GitLab, Bitbucket
Unterstuetzte Formate: package-lock.json, requirements.txt, yarn.lock
</p>
{scanError && (
<div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
{scanError}
</div>
)}
</div>
)}