All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 32s
CI/CD / test-python-backend-compliance (push) Successful in 34s
CI/CD / test-python-document-crawler (push) Successful in 23s
CI/CD / test-python-dsms-gateway (push) Successful in 21s
CI/CD / validate-canonical-controls (push) Successful in 11s
CI/CD / Deploy (push) Successful in 2s
Module 2: Extended Compliance Dashboard with roadmap, module-status, next-actions, snapshots, score-history Module 3: 7 German security document templates (IT-Sicherheitskonzept, Datenschutz, Backup, Logging, Incident-Response, Zugriff, Risikomanagement) Module 4: Compliance Process Manager with CRUD, complete/skip/seed, ~50 seed tasks, 3-tab UI Module 5: Evidence Collector Extended with automated checks, control-mapping, coverage report, 4-tab UI Also includes: canonical control library enhancements (verification method, categories, dedup), control generator improvements, RAG client extensions 52 tests pass, frontend builds clean. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
235 lines
8.4 KiB
TypeScript
235 lines
8.4 KiB
TypeScript
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<string, string> | null
|
|
customer_visible?: boolean
|
|
verification_method: string | null
|
|
category: string | null
|
|
target_audience: string | null
|
|
generation_metadata?: Record<string, unknown> | 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<string, { bg: string; label: string; icon: React.ComponentType<{ className?: string }> }> = {
|
|
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<string, string> = {
|
|
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<string, { bg: string; label: string }> = {
|
|
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<string, { bg: string; label: string }> = {
|
|
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' },
|
|
}
|
|
|
|
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 (
|
|
<span className={`inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium ${config.bg}`}>
|
|
<Icon className="w-3 h-3" />
|
|
{config.label}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
export function StateBadge({ state }: { state: string }) {
|
|
const config: Record<string, string> = {
|
|
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<string, string> = {
|
|
needs_review: 'Review noetig',
|
|
too_close: 'Zu aehnlich',
|
|
duplicate: 'Duplikat',
|
|
}
|
|
return (
|
|
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config[state] || config.draft}`}>
|
|
{labels[state] || state}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
export function LicenseRuleBadge({ rule }: { rule: number | null | undefined }) {
|
|
if (!rule) return null
|
|
const config: Record<number, { bg: string; label: string }> = {
|
|
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 <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${c.bg}`}>{c.label}</span>
|
|
}
|
|
|
|
export function VerificationMethodBadge({ method }: { method: string | null }) {
|
|
if (!method) return null
|
|
const config = VERIFICATION_METHODS[method]
|
|
if (!config) return null
|
|
return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.bg}`}>{config.label}</span>
|
|
}
|
|
|
|
export function CategoryBadge({ category }: { category: string | null }) {
|
|
if (!category) return null
|
|
const opt = CATEGORY_OPTIONS.find(c => c.value === category)
|
|
return (
|
|
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-indigo-50 text-indigo-700">
|
|
{opt?.label || category}
|
|
</span>
|
|
)
|
|
}
|
|
|
|
export function TargetAudienceBadge({ audience }: { audience: string | null }) {
|
|
if (!audience) return null
|
|
const config = TARGET_AUDIENCE_OPTIONS[audience]
|
|
if (!config) return null
|
|
return <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${config.bg}`}>{config.label}</span>
|
|
}
|
|
|
|
export function getDomain(controlId: string): string {
|
|
return controlId.split('-')[0] || ''
|
|
}
|