import { AlertTriangle, CheckCircle2, Info } from 'lucide-react' import React from 'react' // ============================================================================= // TYPES // ============================================================================= export interface OpenAnchor { framework: string ref: string url: string } export interface EvidenceItem { type: string description: string } export interface CanonicalControl { id: string framework_id: string control_id: string title: string objective: string rationale: string scope: { platforms?: string[] components?: string[] data_classes?: string[] } requirements: string[] test_procedure: string[] evidence: EvidenceItem[] severity: string risk_score: number | null implementation_effort: string | null evidence_confidence: number | null open_anchors: OpenAnchor[] release_state: string tags: string[] license_rule?: number | null source_original_text?: string | null source_citation?: Record | null customer_visible?: boolean verification_method: string | null category: string | null target_audience: string | string[] | null generation_metadata?: Record | null generation_strategy?: string | null created_at: string updated_at: string } export interface Framework { id: string framework_id: string name: string version: string description: string release_state: string } // ============================================================================= // CONSTANTS // ============================================================================= export const BACKEND_URL = '/api/sdk/v1/canonical' export const SEVERITY_CONFIG: Record }> = { critical: { bg: 'bg-red-100 text-red-800', label: 'Kritisch', icon: AlertTriangle }, high: { bg: 'bg-orange-100 text-orange-800', label: 'Hoch', icon: AlertTriangle }, medium: { bg: 'bg-yellow-100 text-yellow-800', label: 'Mittel', icon: Info }, low: { bg: 'bg-green-100 text-green-800', label: 'Niedrig', icon: CheckCircle2 }, } export const EFFORT_LABELS: Record = { s: 'Klein (S)', m: 'Mittel (M)', l: 'Gross (L)', xl: 'Sehr gross (XL)', } export const EMPTY_CONTROL = { framework_id: 'bp_security_v1', control_id: '', title: '', objective: '', rationale: '', scope: { platforms: [] as string[], components: [] as string[], data_classes: [] as string[] }, requirements: [''], test_procedure: [''], evidence: [{ type: '', description: '' }], severity: 'medium', risk_score: null as number | null, implementation_effort: 'm' as string | null, open_anchors: [{ framework: '', ref: '', url: '' }], release_state: 'draft', tags: [] as string[], verification_method: null as string | null, category: null as string | null, target_audience: null as string | null, } export const DOMAIN_OPTIONS = [ { value: 'AUTH', label: 'AUTH — Authentifizierung' }, { value: 'CRYPT', label: 'CRYPT — Kryptographie' }, { value: 'NET', label: 'NET — Netzwerk' }, { value: 'DATA', label: 'DATA — Datenschutz' }, { value: 'LOG', label: 'LOG — Logging' }, { value: 'ACC', label: 'ACC — Zugriffskontrolle' }, { value: 'SEC', label: 'SEC — Sicherheit' }, { value: 'INC', label: 'INC — Incident Response' }, { value: 'AI', label: 'AI — Kuenstliche Intelligenz' }, { value: 'COMP', label: 'COMP — Compliance' }, ] export const VERIFICATION_METHODS: Record = { code_review: { bg: 'bg-blue-100 text-blue-700', label: 'Code Review' }, document: { bg: 'bg-amber-100 text-amber-700', label: 'Dokument' }, tool: { bg: 'bg-teal-100 text-teal-700', label: 'Tool' }, hybrid: { bg: 'bg-purple-100 text-purple-700', label: 'Hybrid' }, } export const CATEGORY_OPTIONS = [ { value: 'encryption', label: 'Verschluesselung & Kryptographie' }, { value: 'authentication', label: 'Authentisierung & Zugriffskontrolle' }, { value: 'network', label: 'Netzwerksicherheit' }, { value: 'data_protection', label: 'Datenschutz & Datensicherheit' }, { value: 'logging', label: 'Logging & Monitoring' }, { value: 'incident', label: 'Vorfallmanagement' }, { value: 'continuity', label: 'Notfall & Wiederherstellung' }, { value: 'compliance', label: 'Compliance & Audit' }, { value: 'supply_chain', label: 'Lieferkettenmanagement' }, { value: 'physical', label: 'Physische Sicherheit' }, { value: 'personnel', label: 'Personal & Schulung' }, { value: 'application', label: 'Anwendungssicherheit' }, { value: 'system', label: 'Systemhaertung & -betrieb' }, { value: 'risk', label: 'Risikomanagement' }, { value: 'governance', label: 'Sicherheitsorganisation' }, { value: 'hardware', label: 'Hardware & Plattformsicherheit' }, { value: 'identity', label: 'Identitaetsmanagement' }, ] export const TARGET_AUDIENCE_OPTIONS: Record = { // Legacy English keys enterprise: { bg: 'bg-cyan-100 text-cyan-700', label: 'Unternehmen' }, authority: { bg: 'bg-rose-100 text-rose-700', label: 'Behoerden' }, provider: { bg: 'bg-violet-100 text-violet-700', label: 'Anbieter' }, all: { bg: 'bg-gray-100 text-gray-700', label: 'Alle' }, // German keys from LLM generation unternehmen: { bg: 'bg-cyan-100 text-cyan-700', label: 'Unternehmen' }, behoerden: { bg: 'bg-rose-100 text-rose-700', label: 'Behoerden' }, entwickler: { bg: 'bg-sky-100 text-sky-700', label: 'Entwickler' }, datenschutzbeauftragte: { bg: 'bg-purple-100 text-purple-700', label: 'DSB' }, geschaeftsfuehrung: { bg: 'bg-amber-100 text-amber-700', label: 'GF' }, 'it-abteilung': { bg: 'bg-blue-100 text-blue-700', label: 'IT' }, rechtsabteilung: { bg: 'bg-fuchsia-100 text-fuchsia-700', label: 'Recht' }, 'compliance-officer': { bg: 'bg-indigo-100 text-indigo-700', label: 'Compliance' }, personalwesen: { bg: 'bg-pink-100 text-pink-700', label: 'Personal' }, einkauf: { bg: 'bg-lime-100 text-lime-700', label: 'Einkauf' }, produktion: { bg: 'bg-orange-100 text-orange-700', label: 'Produktion' }, vertrieb: { bg: 'bg-teal-100 text-teal-700', label: 'Vertrieb' }, gesundheitswesen: { bg: 'bg-red-100 text-red-700', label: 'Gesundheit' }, finanzwesen: { bg: 'bg-emerald-100 text-emerald-700', label: 'Finanzen' }, oeffentlicher_dienst: { bg: 'bg-rose-100 text-rose-700', label: 'Oeffentl. Dienst' }, } export const COLLECTION_OPTIONS = [ { value: 'bp_compliance_ce', label: 'CE (OWASP, ENISA, BSI)' }, { value: 'bp_compliance_gesetze', label: 'Gesetze (EU, DE, BSI)' }, { value: 'bp_compliance_datenschutz', label: 'Datenschutz' }, { value: 'bp_compliance_recht', label: 'Recht' }, { value: 'bp_dsfa_corpus', label: 'DSFA Corpus' }, { value: 'bp_legal_templates', label: 'Legal Templates' }, ] // ============================================================================= // BADGE COMPONENTS // ============================================================================= export function SeverityBadge({ severity }: { severity: string }) { const config = SEVERITY_CONFIG[severity] || SEVERITY_CONFIG.medium const Icon = config.icon return ( {config.label} ) } export function StateBadge({ state }: { state: string }) { const config: Record = { draft: 'bg-gray-100 text-gray-600', review: 'bg-blue-100 text-blue-700', approved: 'bg-green-100 text-green-700', deprecated: 'bg-red-100 text-red-600', needs_review: 'bg-yellow-100 text-yellow-800', too_close: 'bg-red-100 text-red-700', duplicate: 'bg-orange-100 text-orange-700', } const labels: Record = { needs_review: 'Review noetig', too_close: 'Zu aehnlich', duplicate: 'Duplikat', } return ( {labels[state] || state} ) } export function LicenseRuleBadge({ rule }: { rule: number | null | undefined }) { if (!rule) return null const config: Record = { 1: { bg: 'bg-green-100 text-green-700', label: 'Free Use' }, 2: { bg: 'bg-blue-100 text-blue-700', label: 'Zitation' }, 3: { bg: 'bg-amber-100 text-amber-700', label: 'Reformuliert' }, } const c = config[rule] if (!c) return null return {c.label} } export function VerificationMethodBadge({ method }: { method: string | null }) { if (!method) return null const config = VERIFICATION_METHODS[method] if (!config) return null return {config.label} } export function CategoryBadge({ category }: { category: string | null }) { if (!category) return null const opt = CATEGORY_OPTIONS.find(c => c.value === category) return ( {opt?.label || category} ) } export function TargetAudienceBadge({ audience }: { audience: string | string[] | null }) { if (!audience) return null // Parse JSON array string from DB (e.g. '["unternehmen", "einkauf"]') let items: string[] = [] if (typeof audience === 'string') { if (audience.startsWith('[')) { try { items = JSON.parse(audience) } catch { items = [audience] } } else { items = [audience] } } else if (Array.isArray(audience)) { items = audience } if (items.length === 0) return null return ( {items.map((item, i) => { const config = TARGET_AUDIENCE_OPTIONS[item] if (!config) return {item} return {config.label} })} ) } export function GenerationStrategyBadge({ strategy }: { strategy: string | null | undefined }) { if (!strategy || strategy === 'ungrouped') { return v1 } if (strategy === 'document_grouped') { return v2 } return null } export function getDomain(controlId: string): string { return controlId.split('-')[0] || '' }