'use client' import React, { useState, useRef, useEffect } from 'react' import { useRouter } from 'next/navigation' import { useSDK, ScreeningResult, SecurityIssue, SBOMComponent, BacklogItem } from '@/lib/sdk' // ============================================================================= // COMPONENTS // ============================================================================= function ScanProgress({ status }: { status: string }) { return (

Scan läuft...

{status}

) } function SBOMViewer({ components }: { components: SBOMComponent[] }) { return (

Software Bill of Materials (SBOM)

{components.length} Komponenten gefunden

{components.map(component => ( ))}
Name Version Typ Lizenz Vulnerabilities
{component.name}
{component.purl}
{component.version} {component.type} {component.licenses.join(', ')} {component.vulnerabilities.length > 0 ? ( {component.vulnerabilities.length} gefunden ) : ( Keine )}
) } function SecurityIssueCard({ issue }: { issue: SecurityIssue }) { const severityColors = { CRITICAL: 'bg-red-100 text-red-700 border-red-200', HIGH: 'bg-orange-100 text-orange-700 border-orange-200', MEDIUM: 'bg-yellow-100 text-yellow-700 border-yellow-200', LOW: 'bg-blue-100 text-blue-700 border-blue-200', } const statusColors = { OPEN: 'bg-red-50 text-red-700', IN_PROGRESS: 'bg-yellow-50 text-yellow-700', RESOLVED: 'bg-green-50 text-green-700', ACCEPTED: 'bg-gray-50 text-gray-700', } return (

{issue.title}

{issue.description}

{issue.severity} {issue.cve && ( {issue.cve} )} {issue.cvss && ( CVSS: {issue.cvss} )}
{issue.status}

Betroffene Komponente: {issue.affectedComponent}

Empfehlung: {issue.remediation}

) } // ============================================================================= // MAIN PAGE // ============================================================================= export default function ScreeningPage() { const { state, dispatch } = useSDK() const router = useRouter() const [isScanning, setIsScanning] = useState(false) const [scanStatus, setScanStatus] = useState('') const [scanError, setScanError] = useState(null) const [scanHistory, setScanHistory] = useState([]) const [historyLoading, setHistoryLoading] = useState(false) const fileInputRef = useRef(null) // 5.2: Load scan history useEffect(() => { const loadHistory = async () => { setHistoryLoading(true) try { const response = await fetch('/api/sdk/v1/screening?tenant_id=default') if (response.ok) { const data = await response.json() setScanHistory(Array.isArray(data) ? data : data.items || []) } } catch (err) { console.error('Failed to load scan history:', err) } finally { setHistoryLoading(false) } } loadHistory() }, [state.screening]) // 5.1: Add issues to security backlog const addToSecurityBacklog = () => { const issues = state.screening?.securityScan?.issues || [] issues.forEach(issue => { const backlogItem: BacklogItem = { id: `backlog-${issue.id}`, title: issue.title, description: `${issue.description}\n\nBetroffene Komponente: ${issue.affectedComponent}\nEmpfehlung: ${issue.remediation}`, severity: issue.severity, securityIssueId: issue.id, status: 'OPEN', assignee: null, dueDate: null, createdAt: new Date(), } dispatch({ type: 'ADD_BACKLOG_ITEM', payload: backlogItem }) }) router.push('/sdk/security-backlog') } const startScan = async (file: File) => { setIsScanning(true) setScanStatus('Abhängigkeiten werden analysiert...') setScanError(null) // Rotate through honest status messages while backend processes const statusMessages = [ 'Abhängigkeiten werden analysiert...', 'SBOM wird generiert...', 'Schwachstellenscan läuft...', 'OSV.dev Datenbank wird abgefragt...', 'Lizenzprüfung...', ] let statusIdx = 0 const statusInterval = setInterval(() => { statusIdx = (statusIdx + 1) % statusMessages.length setScanStatus(statusMessages[statusIdx]) }, 2000) 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(statusInterval) 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() 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(statusInterval) console.error('Screening scan failed:', error) setScanError(error.message || 'Scan fehlgeschlagen') setScanStatus('') } finally { setIsScanning(false) } } const handleFileSelect = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) { startScan(file) } } return (
{/* Header */}

System Screening

Generieren Sie ein SBOM und scannen Sie Ihr System auf Sicherheitslücken

{/* Scan Input */} {!state.screening && !isScanning && (

Abhaengigkeiten scannen

Laden Sie eine Abhaengigkeitsdatei hoch, um ein SBOM zu generieren und Schwachstellen zu erkennen.

Unterstuetzte Formate: package-lock.json, requirements.txt, yarn.lock

{scanError && (
{scanError}
)}
)} {/* Scan Progress */} {isScanning && } {/* Results */} {state.screening && state.screening.status === 'COMPLETED' && ( <> {/* Summary */}
Komponenten
{state.screening.sbom?.components.length || 0}
Kritisch
{state.screening.securityScan?.critical || 0}
Hoch
{state.screening.securityScan?.high || 0}
Mittel
{state.screening.securityScan?.medium || 0}
{/* SBOM */} {state.screening.sbom && } {/* Security Issues */}

Sicherheitsprobleme

{state.screening.securityScan?.issues.map(issue => ( ))}
{/* Actions */}
)} {/* Scan-Verlauf */} {scanHistory.length > 0 && (

Scan-Verlauf

{scanHistory.length} fruehere Scans

{scanHistory.map((scan: any, idx: number) => (

Scan #{scan.id?.slice(0, 8) || idx + 1}

{scan.completed_at ? new Date(scan.completed_at).toLocaleString('de-DE') : 'Unbekannt'}

{scan.total_issues || 0} Issues 0 ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700' }`}> {(scan.critical_issues || 0) > 0 ? `${scan.critical_issues} Kritisch` : 'Keine kritischen'}
))}
)} {historyLoading && (
Scan-Verlauf wird geladen...
)}
) }