This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/website/app/admin/security/wizard/page.tsx
Benjamin Admin 21a844cb8a fix: Restore all files lost during destructive rebase
A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.

This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).

Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 09:51:32 +01:00

408 lines
13 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState } from 'react'
import AdminLayout from '@/components/admin/AdminLayout'
import {
WizardStepper,
WizardNavigation,
EducationCard,
ArchitectureContext,
TestRunner,
TestSummary,
type WizardStep,
type TestCategoryResult,
type FullTestResults,
type EducationContent,
type ArchitectureContextType,
} from '@/components/wizard'
// ==============================================
// Constants
// ==============================================
const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || 'http://localhost:8000'
const STEPS: WizardStep[] = [
{ id: 'welcome', name: 'Willkommen', icon: '👋', status: 'pending' },
{ id: 'api-health', name: 'API Status', icon: '💚', status: 'pending', category: 'api-health' },
{ id: 'sast', name: 'SAST', icon: '🔍', status: 'pending', category: 'sast' },
{ id: 'sca', name: 'SCA', icon: '📦', status: 'pending', category: 'sca' },
{ id: 'secrets', name: 'Secrets', icon: '🔑', status: 'pending', category: 'secrets' },
{ id: 'sbom', name: 'SBOM', icon: '📋', status: 'pending', category: 'sbom' },
{ id: 'summary', name: 'Zusammenfassung', icon: '📊', status: 'pending' },
]
const EDUCATION_CONTENT: Record<string, EducationContent> = {
'welcome': {
title: 'Willkommen zum Security Wizard',
content: [
'DevSecOps integriert Sicherheit in den gesamten Entwicklungsprozess.',
'',
'In diesem Wizard lernen Sie:',
'• SAST: Statische Code-Analyse mit Semgrep',
'• SCA: Abhaengigkeits-Scans mit Trivy/Grype',
'• Secrets: Erkennung von Credentials im Code',
'• SBOM: Software Bill of Materials',
'',
'Shift Left Security: Fehler frueh finden, nicht in Produktion!',
],
},
'api-health': {
title: 'Security API - DevSecOps Dashboard',
content: [
'Die Security API steuert alle Sicherheitsscans.',
'',
'Endpunkte:',
'• /api/security/scan - Scan starten',
'• /api/security/findings - Ergebnisse abrufen',
'• /api/security/sbom - SBOM generieren',
'• /api/security/dashboard - Uebersicht',
'',
'Verfuegbarkeit kritisch fuer:',
'• CI/CD Pipeline Integration',
'• Automatisierte Security Gates',
'• Compliance Reporting',
],
},
'sast': {
title: 'SAST - Static Application Security Testing',
content: [
'Semgrep analysiert Quellcode OHNE ihn auszufuehren.',
'',
'Findet:',
'• SQL Injection Patterns',
'• XSS Vulnerabilities',
'• Hardcoded Credentials',
'• Insecure Crypto Usage',
'• Path Traversal Risks',
'',
'Vorteile:',
'• Schnell (Minuten, nicht Stunden)',
'• Findet Fehler VOR dem Deployment',
'• Keine laufende Anwendung noetig',
],
},
'sca': {
title: 'SCA - Software Composition Analysis',
content: [
'Prueft Abhaengigkeiten auf bekannte Schwachstellen (CVEs).',
'',
'Gescannt werden:',
'• Python packages (requirements.txt)',
'• Node modules (package.json)',
'• Go modules (go.mod)',
'• Container Images',
'',
'Bekannte Angriffe:',
'• Log4Shell (CVE-2021-44228)',
'• NPM Supply Chain Attacks',
'• PyPI Typosquatting',
],
},
'secrets': {
title: 'Secret Detection mit Gitleaks',
content: [
'Findet versehentlich eingecheckte Secrets:',
'',
'• AWS/GCP/Azure Credentials',
'• Database Passwords',
'• Private SSH Keys',
'• OAuth Tokens',
'• JWT Secrets',
'',
'Risiken ohne Detection:',
'• Kompromittierte Cloud-Accounts',
'• Datenlecks durch DB-Zugriff',
'• Git History: Einmal gepusht, schwer zu entfernen!',
],
},
'sbom': {
title: 'SBOM - Software Bill of Materials',
content: [
'Vollstaendige Inventarliste aller Software-Komponenten.',
'',
'Rechtliche Anforderungen:',
'• US Executive Order 14028 (2021)',
'• EU Cyber Resilience Act',
'• Supply Chain Transparency',
'',
'Inhalt:',
'• Alle Abhaengigkeiten mit Versionen',
'• Lizenzen (GPL, MIT, Apache, etc.)',
'• Bekannte Vulnerabilities',
'',
'Bei Zero-Day: Schnell pruefen wer betroffen ist',
],
},
'summary': {
title: 'Test-Zusammenfassung',
content: [
'Hier sehen Sie eine Uebersicht aller durchgefuehrten Tests:',
'• Anzahl bestandener Tests',
'• Fehlgeschlagene Tests mit Details',
'• Empfehlungen zur Behebung',
],
},
}
const ARCHITECTURE_CONTEXTS: Record<string, ArchitectureContextType> = {
'api-health': {
layer: 'api',
services: ['backend'],
dependencies: ['PostgreSQL', 'Scanner Tools'],
dataFlow: ['API Request', 'Scanner Dispatch', 'Result Storage'],
},
'sast': {
layer: 'service',
services: ['backend'],
dependencies: ['semgrep', 'Git Repository'],
dataFlow: ['Source Code', 'Semgrep Scanner', 'Findings', 'Dashboard'],
},
'sca': {
layer: 'service',
services: ['backend'],
dependencies: ['trivy', 'grype', 'CVE Database'],
dataFlow: ['Dependencies', 'Scanner', 'CVE Lookup', 'Report'],
},
'secrets': {
layer: 'service',
services: ['backend'],
dependencies: ['gitleaks', 'Git Repository'],
dataFlow: ['Git History', 'Pattern Matching', 'Findings'],
},
'sbom': {
layer: 'service',
services: ['backend'],
dependencies: ['syft', 'cyclonedx'],
dataFlow: ['Container/Code', 'Syft Analysis', 'CycloneDX SBOM'],
},
}
// ==============================================
// Main Component
// ==============================================
export default function SecurityWizardPage() {
const [currentStep, setCurrentStep] = useState(0)
const [steps, setSteps] = useState<WizardStep[]>(STEPS)
const [categoryResults, setCategoryResults] = useState<Record<string, TestCategoryResult>>({})
const [fullResults, setFullResults] = useState<FullTestResults | null>(null)
const [isLoading, setIsLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const currentStepData = steps[currentStep]
const isTestStep = currentStepData?.category !== undefined
const isWelcome = currentStepData?.id === 'welcome'
const isSummary = currentStepData?.id === 'summary'
const runCategoryTest = async (category: string) => {
setIsLoading(true)
setError(null)
try {
const response = await fetch(`${BACKEND_URL}/api/admin/security-tests/${category}`, {
method: 'POST',
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const result: TestCategoryResult = await response.json()
setCategoryResults((prev) => ({ ...prev, [category]: result }))
setSteps((prev) =>
prev.map((step) =>
step.category === category
? { ...step, status: result.failed === 0 ? 'completed' : 'failed' }
: step
)
)
} catch (err) {
setError(err instanceof Error ? err.message : 'Unbekannter Fehler')
} finally {
setIsLoading(false)
}
}
const runAllTests = async () => {
setIsLoading(true)
setError(null)
try {
const response = await fetch(`${BACKEND_URL}/api/admin/security-tests/run-all`, {
method: 'POST',
})
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
}
const results: FullTestResults = await response.json()
setFullResults(results)
setSteps((prev) =>
prev.map((step) => {
if (step.category) {
const catResult = results.categories.find((c) => c.category === step.category)
if (catResult) {
return { ...step, status: catResult.failed === 0 ? 'completed' : 'failed' }
}
}
return step
})
)
const newCategoryResults: Record<string, TestCategoryResult> = {}
results.categories.forEach((cat) => {
newCategoryResults[cat.category] = cat
})
setCategoryResults(newCategoryResults)
} catch (err) {
setError(err instanceof Error ? err.message : 'Unbekannter Fehler')
} finally {
setIsLoading(false)
}
}
const goToNext = () => {
if (currentStep < steps.length - 1) {
setSteps((prev) =>
prev.map((step, idx) =>
idx === currentStep && step.status === 'pending'
? { ...step, status: 'completed' }
: step
)
)
setCurrentStep((prev) => prev + 1)
}
}
const goToPrev = () => {
if (currentStep > 0) {
setCurrentStep((prev) => prev - 1)
}
}
const handleStepClick = (index: number) => {
if (index <= currentStep || steps[index - 1]?.status !== 'pending') {
setCurrentStep(index)
}
}
return (
<AdminLayout
title="Security Wizard"
description="Interaktives Lernen und Testen der DevSecOps Pipeline"
>
{/* Header */}
<div className="bg-white rounded-lg shadow p-4 mb-6 flex items-center justify-between">
<div className="flex items-center">
<span className="text-3xl mr-3">🛡</span>
<div>
<h2 className="text-lg font-bold text-gray-800">DevSecOps Test Wizard</h2>
<p className="text-sm text-gray-600">SAST, SCA, Secrets, SBOM</p>
</div>
</div>
<a href="/admin/security" className="text-blue-600 hover:text-blue-800 text-sm">
Zurueck zum Security Dashboard
</a>
</div>
{/* Stepper */}
<div className="bg-white rounded-lg shadow p-6 mb-6">
<WizardStepper steps={steps} currentStep={currentStep} onStepClick={handleStepClick} />
</div>
{/* Content */}
<div className="bg-white rounded-lg shadow p-6">
<div className="flex items-center mb-6">
<span className="text-3xl mr-3">{currentStepData?.icon}</span>
<div>
<h2 className="text-xl font-bold text-gray-800">
Schritt {currentStep + 1}: {currentStepData?.name}
</h2>
<p className="text-gray-500 text-sm">
{currentStep + 1} von {steps.length}
</p>
</div>
</div>
<EducationCard content={EDUCATION_CONTENT[currentStepData?.id || '']} />
{isTestStep && currentStepData?.category && ARCHITECTURE_CONTEXTS[currentStepData.category] && (
<ArchitectureContext
context={ARCHITECTURE_CONTEXTS[currentStepData.category]}
currentStep={currentStepData.name}
/>
)}
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 rounded-lg p-4 mb-6">
<strong>Fehler:</strong> {error}
</div>
)}
{isWelcome && (
<div className="text-center py-8">
<button
onClick={goToNext}
className="bg-blue-600 text-white px-8 py-3 rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
Wizard starten
</button>
</div>
)}
{isTestStep && currentStepData?.category && (
<TestRunner
category={currentStepData.category}
categoryResult={categoryResults[currentStepData.category]}
isLoading={isLoading}
onRunTests={() => runCategoryTest(currentStepData.category!)}
/>
)}
{isSummary && (
<div>
{!fullResults ? (
<div className="text-center py-8">
<p className="text-gray-600 mb-4">
Fuehren Sie alle Tests aus um eine Zusammenfassung zu sehen.
</p>
<button
onClick={runAllTests}
disabled={isLoading}
className={`px-6 py-3 rounded-lg font-medium transition-colors ${
isLoading
? 'bg-gray-400 cursor-not-allowed'
: 'bg-blue-600 text-white hover:bg-blue-700'
}`}
>
{isLoading ? 'Alle Tests laufen...' : 'Alle Tests ausfuehren'}
</button>
</div>
) : (
<TestSummary results={fullResults} />
)}
</div>
)}
<WizardNavigation
currentStep={currentStep}
totalSteps={steps.length}
onPrev={goToPrev}
onNext={goToNext}
showNext={!isSummary}
isLoading={isLoading}
/>
</div>
<div className="text-center text-gray-500 text-sm mt-6">
Diese Tests pruefen die DevSecOps Security-Konfiguration.
Bei Fragen wenden Sie sich an das Security-Team.
</div>
</AdminLayout>
)
}