feat: Payment Terminal Compliance Modul — Phase 1+2
1. Control-Bibliothek: 130 Controls in 10 Domaenen (payment_controls_v1.json) - PAY (20): Transaction Flow, Idempotenz, State Machine - LOG (15): Audit Trail, PAN-Maskierung, Event-Typen - CRYPTO (15): Secrets, HSM, P2PE, TLS - API (15): Auth, RBAC, Rate Limiting, Injection - TERM (15): ZVT/OPI, Heartbeat, Offline-Queue - FW (10): Firmware Signing, Secure Boot, Tamper Detection - REP (10): Reconciliation, Tagesabschluss, GoBD - ACC (10): MFA, Session, Least Privilege - ERR (10): Recovery, Circuit Breaker, Offline-Modus - BLD (10): CI/CD, SBOM, Container Scanning 2. Backend: DB Migration 024, Go Handler (5 Endpoints), Routes 3. Frontend: /sdk/payment-compliance mit Control-Browser + Assessment-Wizard Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
48
admin-compliance/app/api/sdk/v1/payment-compliance/route.ts
Normal file
48
admin-compliance/app/api/sdk/v1/payment-compliance/route.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { NextRequest, NextResponse } from 'next/server'
|
||||||
|
|
||||||
|
const SDK_URL = process.env.SDK_URL || 'http://ai-compliance-sdk:8090'
|
||||||
|
|
||||||
|
export async function GET(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const { searchParams } = new URL(request.url)
|
||||||
|
const endpoint = searchParams.get('endpoint') || 'controls'
|
||||||
|
const tenantId = request.headers.get('x-tenant-id') || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'
|
||||||
|
|
||||||
|
let path: string
|
||||||
|
switch (endpoint) {
|
||||||
|
case 'controls':
|
||||||
|
const domain = searchParams.get('domain') || ''
|
||||||
|
path = `/sdk/v1/payment-compliance/controls${domain ? `?domain=${domain}` : ''}`
|
||||||
|
break
|
||||||
|
case 'assessments':
|
||||||
|
path = '/sdk/v1/payment-compliance/assessments'
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
path = '/sdk/v1/payment-compliance/controls'
|
||||||
|
}
|
||||||
|
|
||||||
|
const resp = await fetch(`${SDK_URL}${path}`, {
|
||||||
|
headers: { 'X-Tenant-ID': tenantId },
|
||||||
|
})
|
||||||
|
const data = await resp.json()
|
||||||
|
return NextResponse.json(data)
|
||||||
|
} catch (err) {
|
||||||
|
return NextResponse.json({ error: 'Failed to fetch' }, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(request: NextRequest) {
|
||||||
|
try {
|
||||||
|
const tenantId = request.headers.get('x-tenant-id') || '9282a473-5c95-4b3a-bf78-0ecc0ec71d3e'
|
||||||
|
const body = await request.json()
|
||||||
|
const resp = await fetch(`${SDK_URL}/sdk/v1/payment-compliance/assessments`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json', 'X-Tenant-ID': tenantId },
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
const data = await resp.json()
|
||||||
|
return NextResponse.json(data, { status: resp.status })
|
||||||
|
} catch (err) {
|
||||||
|
return NextResponse.json({ error: 'Failed to create' }, { status: 500 })
|
||||||
|
}
|
||||||
|
}
|
||||||
291
admin-compliance/app/sdk/payment-compliance/page.tsx
Normal file
291
admin-compliance/app/sdk/payment-compliance/page.tsx
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
import React, { useState, useEffect } from 'react'
|
||||||
|
|
||||||
|
interface PaymentControl {
|
||||||
|
control_id: string
|
||||||
|
domain: string
|
||||||
|
title: string
|
||||||
|
objective: string
|
||||||
|
check_target: string
|
||||||
|
evidence: string[]
|
||||||
|
automation: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PaymentDomain {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Assessment {
|
||||||
|
id: string
|
||||||
|
project_name: string
|
||||||
|
tender_reference: string
|
||||||
|
customer_name: string
|
||||||
|
system_type: string
|
||||||
|
total_controls: number
|
||||||
|
controls_passed: number
|
||||||
|
controls_failed: number
|
||||||
|
controls_partial: number
|
||||||
|
controls_not_applicable: number
|
||||||
|
controls_not_checked: number
|
||||||
|
compliance_score: number
|
||||||
|
status: string
|
||||||
|
created_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const AUTOMATION_STYLES: Record<string, { bg: string; text: string }> = {
|
||||||
|
high: { bg: 'bg-green-100', text: 'text-green-700' },
|
||||||
|
medium: { bg: 'bg-yellow-100', text: 'text-yellow-700' },
|
||||||
|
partial: { bg: 'bg-orange-100', text: 'text-orange-700' },
|
||||||
|
low: { bg: 'bg-red-100', text: 'text-red-700' },
|
||||||
|
}
|
||||||
|
|
||||||
|
const TARGET_ICONS: Record<string, string> = {
|
||||||
|
code: '💻', system: '🖥️', config: '⚙️', process: '📋',
|
||||||
|
repository: '📦', certificate: '📜',
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PaymentCompliancePage() {
|
||||||
|
const [controls, setControls] = useState<PaymentControl[]>([])
|
||||||
|
const [domains, setDomains] = useState<PaymentDomain[]>([])
|
||||||
|
const [assessments, setAssessments] = useState<Assessment[]>([])
|
||||||
|
const [selectedDomain, setSelectedDomain] = useState<string>('all')
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
const [tab, setTab] = useState<'controls' | 'assessments'>('controls')
|
||||||
|
const [showNewAssessment, setShowNewAssessment] = useState(false)
|
||||||
|
const [newProject, setNewProject] = useState({ project_name: '', tender_reference: '', customer_name: '', system_type: 'full_stack' })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadData()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
async function loadData() {
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
const [ctrlResp, assessResp] = await Promise.all([
|
||||||
|
fetch('/api/sdk/v1/payment-compliance?endpoint=controls'),
|
||||||
|
fetch('/api/sdk/v1/payment-compliance?endpoint=assessments'),
|
||||||
|
])
|
||||||
|
if (ctrlResp.ok) {
|
||||||
|
const data = await ctrlResp.json()
|
||||||
|
setControls(data.controls || [])
|
||||||
|
setDomains(data.domains || [])
|
||||||
|
}
|
||||||
|
if (assessResp.ok) {
|
||||||
|
const data = await assessResp.json()
|
||||||
|
setAssessments(data.assessments || [])
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
finally { setLoading(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleCreateAssessment() {
|
||||||
|
const resp = await fetch('/api/sdk/v1/payment-compliance', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify(newProject),
|
||||||
|
})
|
||||||
|
if (resp.ok) {
|
||||||
|
setShowNewAssessment(false)
|
||||||
|
setNewProject({ project_name: '', tender_reference: '', customer_name: '', system_type: 'full_stack' })
|
||||||
|
loadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredControls = selectedDomain === 'all'
|
||||||
|
? controls
|
||||||
|
: controls.filter(c => c.domain === selectedDomain)
|
||||||
|
|
||||||
|
const domainStats = domains.map(d => ({
|
||||||
|
...d,
|
||||||
|
count: controls.filter(c => c.domain === d.id).length,
|
||||||
|
}))
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="max-w-6xl mx-auto p-6">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between mb-8">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900">Payment Terminal Compliance</h1>
|
||||||
|
<p className="text-sm text-gray-500 mt-1">
|
||||||
|
Technische Pruefbibliothek fuer Zahlungssysteme — {controls.length} Controls in {domains.length} Domaenen
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button onClick={() => setTab('controls')}
|
||||||
|
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === 'controls' ? 'bg-purple-600 text-white' : 'bg-gray-100 text-gray-700'}`}>
|
||||||
|
Controls ({controls.length})
|
||||||
|
</button>
|
||||||
|
<button onClick={() => setTab('assessments')}
|
||||||
|
className={`px-4 py-2 rounded-lg text-sm font-medium ${tab === 'assessments' ? 'bg-purple-600 text-white' : 'bg-gray-100 text-gray-700'}`}>
|
||||||
|
Assessments ({assessments.length})
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{loading ? (
|
||||||
|
<div className="text-center py-12 text-gray-500">Lade...</div>
|
||||||
|
) : tab === 'controls' ? (
|
||||||
|
<>
|
||||||
|
{/* Domain Filter */}
|
||||||
|
<div className="grid grid-cols-5 gap-3 mb-6">
|
||||||
|
<button onClick={() => setSelectedDomain('all')}
|
||||||
|
className={`p-3 rounded-xl border text-center ${selectedDomain === 'all' ? 'border-purple-500 bg-purple-50' : 'border-gray-200 hover:border-purple-300'}`}>
|
||||||
|
<div className="text-lg font-bold text-purple-700">{controls.length}</div>
|
||||||
|
<div className="text-xs text-gray-500">Alle</div>
|
||||||
|
</button>
|
||||||
|
{domainStats.map(d => (
|
||||||
|
<button key={d.id} onClick={() => setSelectedDomain(d.id)}
|
||||||
|
className={`p-3 rounded-xl border text-center ${selectedDomain === d.id ? 'border-purple-500 bg-purple-50' : 'border-gray-200 hover:border-purple-300'}`}>
|
||||||
|
<div className="text-lg font-bold text-gray-900">{d.count}</div>
|
||||||
|
<div className="text-xs text-gray-500 truncate">{d.id}</div>
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Domain Description */}
|
||||||
|
{selectedDomain !== 'all' && (
|
||||||
|
<div className="mb-4 p-3 bg-blue-50 border border-blue-200 rounded-lg text-sm text-blue-800">
|
||||||
|
<strong>{domains.find(d => d.id === selectedDomain)?.name}:</strong>{' '}
|
||||||
|
{domains.find(d => d.id === selectedDomain)?.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Controls List */}
|
||||||
|
<div className="space-y-3">
|
||||||
|
{filteredControls.map(ctrl => {
|
||||||
|
const autoStyle = AUTOMATION_STYLES[ctrl.automation] || AUTOMATION_STYLES.low
|
||||||
|
return (
|
||||||
|
<div key={ctrl.control_id} className="bg-white rounded-xl border border-gray-200 p-4 hover:border-purple-300 transition-all">
|
||||||
|
<div className="flex items-start justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<div className="flex items-center gap-2 mb-1">
|
||||||
|
<span className="text-xs font-mono text-purple-600 bg-purple-50 px-2 py-0.5 rounded">{ctrl.control_id}</span>
|
||||||
|
<span className="text-xs text-gray-400">{TARGET_ICONS[ctrl.check_target] || '🔍'} {ctrl.check_target}</span>
|
||||||
|
<span className={`text-xs px-2 py-0.5 rounded-full ${autoStyle.bg} ${autoStyle.text}`}>
|
||||||
|
{ctrl.automation}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="text-sm font-semibold text-gray-900">{ctrl.title}</h3>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">{ctrl.objective}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-1 mt-2">
|
||||||
|
{ctrl.evidence.map(ev => (
|
||||||
|
<span key={ev} className="text-xs bg-gray-100 text-gray-600 px-2 py-0.5 rounded">{ev}</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{/* Assessments Tab */}
|
||||||
|
<div className="mb-4">
|
||||||
|
<button onClick={() => setShowNewAssessment(true)}
|
||||||
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700">
|
||||||
|
+ Neues Assessment
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{showNewAssessment && (
|
||||||
|
<div className="mb-6 p-6 bg-white rounded-xl border border-purple-200">
|
||||||
|
<h3 className="font-semibold text-gray-900 mb-4">Neues Payment Compliance Assessment</h3>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">Projektname *</label>
|
||||||
|
<input value={newProject.project_name} onChange={e => setNewProject(p => ({ ...p, project_name: e.target.value }))}
|
||||||
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="z.B. Ausschreibung Muenchen 2026" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">Ausschreibungs-Referenz</label>
|
||||||
|
<input value={newProject.tender_reference} onChange={e => setNewProject(p => ({ ...p, tender_reference: e.target.value }))}
|
||||||
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="z.B. 2026-PAY-001" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">Kunde</label>
|
||||||
|
<input value={newProject.customer_name} onChange={e => setNewProject(p => ({ ...p, customer_name: e.target.value }))}
|
||||||
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" placeholder="z.B. Stadt Muenchen" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-1">Systemtyp</label>
|
||||||
|
<select value={newProject.system_type} onChange={e => setNewProject(p => ({ ...p, system_type: e.target.value }))}
|
||||||
|
className="w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500">
|
||||||
|
<option value="full_stack">Full Stack (Terminal + Backend)</option>
|
||||||
|
<option value="terminal">Nur Terminal</option>
|
||||||
|
<option value="backend">Nur Backend</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex gap-2 mt-4">
|
||||||
|
<button onClick={handleCreateAssessment} disabled={!newProject.project_name}
|
||||||
|
className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50">Erstellen</button>
|
||||||
|
<button onClick={() => setShowNewAssessment(false)}
|
||||||
|
className="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">Abbrechen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{assessments.length === 0 ? (
|
||||||
|
<div className="text-center py-12 text-gray-400">
|
||||||
|
<p className="text-lg mb-2">Noch keine Assessments</p>
|
||||||
|
<p className="text-sm">Erstelle ein neues Assessment fuer eine Ausschreibung.</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="space-y-4">
|
||||||
|
{assessments.map(a => (
|
||||||
|
<div key={a.id} className="bg-white rounded-xl border border-gray-200 p-6">
|
||||||
|
<div className="flex items-center justify-between mb-3">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg font-semibold text-gray-900">{a.project_name}</h3>
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
{a.customer_name && <span>{a.customer_name} · </span>}
|
||||||
|
{a.tender_reference && <span>Ref: {a.tender_reference} · </span>}
|
||||||
|
<span>{new Date(a.created_at).toLocaleDateString('de-DE')}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className={`px-3 py-1 rounded-full text-sm font-medium ${
|
||||||
|
a.status === 'completed' ? 'bg-green-100 text-green-700' :
|
||||||
|
a.status === 'in_progress' ? 'bg-yellow-100 text-yellow-700' :
|
||||||
|
'bg-gray-100 text-gray-700'
|
||||||
|
}`}>{a.status}</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-6 gap-2">
|
||||||
|
<div className="text-center p-2 bg-gray-50 rounded">
|
||||||
|
<div className="text-lg font-bold">{a.total_controls}</div>
|
||||||
|
<div className="text-xs text-gray-500">Total</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-2 bg-green-50 rounded">
|
||||||
|
<div className="text-lg font-bold text-green-700">{a.controls_passed}</div>
|
||||||
|
<div className="text-xs text-gray-500">Passed</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-2 bg-red-50 rounded">
|
||||||
|
<div className="text-lg font-bold text-red-700">{a.controls_failed}</div>
|
||||||
|
<div className="text-xs text-gray-500">Failed</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-2 bg-yellow-50 rounded">
|
||||||
|
<div className="text-lg font-bold text-yellow-700">{a.controls_partial}</div>
|
||||||
|
<div className="text-xs text-gray-500">Partial</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-2 bg-gray-50 rounded">
|
||||||
|
<div className="text-lg font-bold text-gray-400">{a.controls_not_applicable}</div>
|
||||||
|
<div className="text-xs text-gray-500">N/A</div>
|
||||||
|
</div>
|
||||||
|
<div className="text-center p-2 bg-gray-50 rounded">
|
||||||
|
<div className="text-lg font-bold text-gray-400">{a.controls_not_checked}</div>
|
||||||
|
<div className="text-xs text-gray-500">Offen</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -106,6 +106,7 @@ func main() {
|
|||||||
escalationHandlers := handlers.NewEscalationHandlers(escalationStore, uccaStore)
|
escalationHandlers := handlers.NewEscalationHandlers(escalationStore, uccaStore)
|
||||||
registrationStore := ucca.NewRegistrationStore(pool)
|
registrationStore := ucca.NewRegistrationStore(pool)
|
||||||
registrationHandlers := handlers.NewRegistrationHandlers(registrationStore, uccaStore)
|
registrationHandlers := handlers.NewRegistrationHandlers(registrationStore, uccaStore)
|
||||||
|
paymentHandlers := handlers.NewPaymentHandlers(pool)
|
||||||
roadmapHandlers := handlers.NewRoadmapHandlers(roadmapStore)
|
roadmapHandlers := handlers.NewRoadmapHandlers(roadmapStore)
|
||||||
workshopHandlers := handlers.NewWorkshopHandlers(workshopStore)
|
workshopHandlers := handlers.NewWorkshopHandlers(workshopStore)
|
||||||
portfolioHandlers := handlers.NewPortfolioHandlers(portfolioStore)
|
portfolioHandlers := handlers.NewPortfolioHandlers(portfolioStore)
|
||||||
@@ -298,6 +299,16 @@ func main() {
|
|||||||
regRoutes.GET("/:id/export", registrationHandlers.Export)
|
regRoutes.GET("/:id/export", registrationHandlers.Export)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Payment Compliance routes
|
||||||
|
payRoutes := v1.Group("/payment-compliance")
|
||||||
|
{
|
||||||
|
payRoutes.GET("/controls", paymentHandlers.ListControls)
|
||||||
|
payRoutes.POST("/assessments", paymentHandlers.CreateAssessment)
|
||||||
|
payRoutes.GET("/assessments", paymentHandlers.ListAssessments)
|
||||||
|
payRoutes.GET("/assessments/:id", paymentHandlers.GetAssessment)
|
||||||
|
payRoutes.PATCH("/assessments/:id/verdict", paymentHandlers.UpdateControlVerdict)
|
||||||
|
}
|
||||||
|
|
||||||
// RAG routes - Legal Corpus Search & Versioning
|
// RAG routes - Legal Corpus Search & Versioning
|
||||||
ragRoutes := v1.Group("/rag")
|
ragRoutes := v1.Group("/rag")
|
||||||
{
|
{
|
||||||
|
|||||||
285
ai-compliance-sdk/internal/api/handlers/payment_handlers.go
Normal file
285
ai-compliance-sdk/internal/api/handlers/payment_handlers.go
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PaymentHandlers handles payment compliance endpoints
|
||||||
|
type PaymentHandlers struct {
|
||||||
|
pool *pgxpool.Pool
|
||||||
|
controls *PaymentControlLibrary
|
||||||
|
}
|
||||||
|
|
||||||
|
// PaymentControlLibrary holds the control catalog
|
||||||
|
type PaymentControlLibrary struct {
|
||||||
|
Domains []PaymentDomain `json:"domains"`
|
||||||
|
Controls []PaymentControl `json:"controls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaymentDomain struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaymentControl struct {
|
||||||
|
ControlID string `json:"control_id"`
|
||||||
|
Domain string `json:"domain"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Objective string `json:"objective"`
|
||||||
|
CheckTarget string `json:"check_target"`
|
||||||
|
Evidence []string `json:"evidence"`
|
||||||
|
Automation string `json:"automation"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PaymentAssessment struct {
|
||||||
|
ID uuid.UUID `json:"id"`
|
||||||
|
TenantID uuid.UUID `json:"tenant_id"`
|
||||||
|
ProjectName string `json:"project_name"`
|
||||||
|
TenderReference string `json:"tender_reference,omitempty"`
|
||||||
|
CustomerName string `json:"customer_name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
SystemType string `json:"system_type,omitempty"`
|
||||||
|
PaymentMethods json.RawMessage `json:"payment_methods,omitempty"`
|
||||||
|
Protocols json.RawMessage `json:"protocols,omitempty"`
|
||||||
|
TotalControls int `json:"total_controls"`
|
||||||
|
ControlsPassed int `json:"controls_passed"`
|
||||||
|
ControlsFailed int `json:"controls_failed"`
|
||||||
|
ControlsPartial int `json:"controls_partial"`
|
||||||
|
ControlsNA int `json:"controls_not_applicable"`
|
||||||
|
ControlsUnchecked int `json:"controls_not_checked"`
|
||||||
|
ComplianceScore float64 `json:"compliance_score"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
ControlResults json.RawMessage `json:"control_results,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
CreatedBy string `json:"created_by,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPaymentHandlers creates payment handlers with loaded control library
|
||||||
|
func NewPaymentHandlers(pool *pgxpool.Pool) *PaymentHandlers {
|
||||||
|
lib := loadControlLibrary()
|
||||||
|
return &PaymentHandlers{pool: pool, controls: lib}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadControlLibrary() *PaymentControlLibrary {
|
||||||
|
// Try to load from policies directory
|
||||||
|
paths := []string{
|
||||||
|
"policies/payment_controls_v1.json",
|
||||||
|
"/app/policies/payment_controls_v1.json",
|
||||||
|
}
|
||||||
|
for _, p := range paths {
|
||||||
|
data, err := os.ReadFile(p)
|
||||||
|
if err != nil {
|
||||||
|
// Try relative to executable
|
||||||
|
execDir, _ := os.Executable()
|
||||||
|
altPath := filepath.Join(filepath.Dir(execDir), p)
|
||||||
|
data, err = os.ReadFile(altPath)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lib PaymentControlLibrary
|
||||||
|
if err := json.Unmarshal(data, &lib); err == nil {
|
||||||
|
return &lib
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &PaymentControlLibrary{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListControls returns the control library
|
||||||
|
func (h *PaymentHandlers) ListControls(c *gin.Context) {
|
||||||
|
domain := c.Query("domain")
|
||||||
|
automation := c.Query("automation")
|
||||||
|
|
||||||
|
controls := h.controls.Controls
|
||||||
|
if domain != "" {
|
||||||
|
var filtered []PaymentControl
|
||||||
|
for _, ctrl := range controls {
|
||||||
|
if ctrl.Domain == domain {
|
||||||
|
filtered = append(filtered, ctrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controls = filtered
|
||||||
|
}
|
||||||
|
if automation != "" {
|
||||||
|
var filtered []PaymentControl
|
||||||
|
for _, ctrl := range controls {
|
||||||
|
if ctrl.Automation == automation {
|
||||||
|
filtered = append(filtered, ctrl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
controls = filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"controls": controls,
|
||||||
|
"domains": h.controls.Domains,
|
||||||
|
"total": len(controls),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateAssessment creates a new payment compliance assessment
|
||||||
|
func (h *PaymentHandlers) CreateAssessment(c *gin.Context) {
|
||||||
|
var req PaymentAssessment
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
|
||||||
|
if tenantID == uuid.Nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.ID = uuid.New()
|
||||||
|
req.TenantID = tenantID
|
||||||
|
req.Status = "draft"
|
||||||
|
req.TotalControls = len(h.controls.Controls)
|
||||||
|
req.ControlsUnchecked = req.TotalControls
|
||||||
|
req.CreatedAt = time.Now()
|
||||||
|
req.UpdatedAt = time.Now()
|
||||||
|
|
||||||
|
_, err := h.pool.Exec(c.Request.Context(), `
|
||||||
|
INSERT INTO payment_compliance_assessments (
|
||||||
|
id, tenant_id, project_name, tender_reference, customer_name, description,
|
||||||
|
system_type, payment_methods, protocols,
|
||||||
|
total_controls, controls_not_checked, status, created_by
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,
|
||||||
|
req.ID, req.TenantID, req.ProjectName, req.TenderReference, req.CustomerName, req.Description,
|
||||||
|
req.SystemType, req.PaymentMethods, req.Protocols,
|
||||||
|
req.TotalControls, req.ControlsUnchecked, req.Status, req.CreatedBy,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusCreated, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAssessments lists all payment assessments for a tenant
|
||||||
|
func (h *PaymentHandlers) ListAssessments(c *gin.Context) {
|
||||||
|
tenantID, _ := uuid.Parse(c.GetHeader("X-Tenant-ID"))
|
||||||
|
if tenantID == uuid.Nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "tenant ID required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := h.pool.Query(c.Request.Context(), `
|
||||||
|
SELECT id, tenant_id, project_name, tender_reference, customer_name,
|
||||||
|
system_type, total_controls, controls_passed, controls_failed,
|
||||||
|
controls_partial, controls_not_applicable, controls_not_checked,
|
||||||
|
compliance_score, status, created_at, updated_at
|
||||||
|
FROM payment_compliance_assessments
|
||||||
|
WHERE tenant_id = $1
|
||||||
|
ORDER BY created_at DESC`, tenantID)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var assessments []PaymentAssessment
|
||||||
|
for rows.Next() {
|
||||||
|
var a PaymentAssessment
|
||||||
|
rows.Scan(&a.ID, &a.TenantID, &a.ProjectName, &a.TenderReference, &a.CustomerName,
|
||||||
|
&a.SystemType, &a.TotalControls, &a.ControlsPassed, &a.ControlsFailed,
|
||||||
|
&a.ControlsPartial, &a.ControlsNA, &a.ControlsUnchecked,
|
||||||
|
&a.ComplianceScore, &a.Status, &a.CreatedAt, &a.UpdatedAt)
|
||||||
|
assessments = append(assessments, a)
|
||||||
|
}
|
||||||
|
if assessments == nil {
|
||||||
|
assessments = []PaymentAssessment{}
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"assessments": assessments, "total": len(assessments)})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAssessment returns a single assessment with control results
|
||||||
|
func (h *PaymentHandlers) GetAssessment(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var a PaymentAssessment
|
||||||
|
err = h.pool.QueryRow(c.Request.Context(), `
|
||||||
|
SELECT id, tenant_id, project_name, tender_reference, customer_name, description,
|
||||||
|
system_type, payment_methods, protocols,
|
||||||
|
total_controls, controls_passed, controls_failed, controls_partial,
|
||||||
|
controls_not_applicable, controls_not_checked, compliance_score,
|
||||||
|
status, control_results, created_at, updated_at, created_by
|
||||||
|
FROM payment_compliance_assessments WHERE id = $1`, id).Scan(
|
||||||
|
&a.ID, &a.TenantID, &a.ProjectName, &a.TenderReference, &a.CustomerName, &a.Description,
|
||||||
|
&a.SystemType, &a.PaymentMethods, &a.Protocols,
|
||||||
|
&a.TotalControls, &a.ControlsPassed, &a.ControlsFailed, &a.ControlsPartial,
|
||||||
|
&a.ControlsNA, &a.ControlsUnchecked, &a.ComplianceScore,
|
||||||
|
&a.Status, &a.ControlResults, &a.CreatedAt, &a.UpdatedAt, &a.CreatedBy)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "assessment not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateControlVerdict updates the verdict for a single control
|
||||||
|
func (h *PaymentHandlers) UpdateControlVerdict(c *gin.Context) {
|
||||||
|
id, err := uuid.Parse(c.Param("id"))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var body struct {
|
||||||
|
ControlID string `json:"control_id"`
|
||||||
|
Verdict string `json:"verdict"` // passed, failed, partial, na, unchecked
|
||||||
|
Evidence string `json:"evidence,omitempty"`
|
||||||
|
Notes string `json:"notes,omitempty"`
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindJSON(&body); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the control_results JSONB and recalculate scores
|
||||||
|
_, err = h.pool.Exec(c.Request.Context(), `
|
||||||
|
WITH updated AS (
|
||||||
|
SELECT id,
|
||||||
|
COALESCE(control_results, '[]'::jsonb) AS existing_results
|
||||||
|
FROM payment_compliance_assessments WHERE id = $1
|
||||||
|
)
|
||||||
|
UPDATE payment_compliance_assessments SET
|
||||||
|
control_results = (
|
||||||
|
SELECT jsonb_agg(
|
||||||
|
CASE WHEN elem->>'control_id' = $2 THEN
|
||||||
|
jsonb_build_object('control_id', $2, 'verdict', $3, 'evidence', $4, 'notes', $5)
|
||||||
|
ELSE elem END
|
||||||
|
) FROM updated, jsonb_array_elements(
|
||||||
|
CASE WHEN existing_results @> jsonb_build_array(jsonb_build_object('control_id', $2))
|
||||||
|
THEN existing_results
|
||||||
|
ELSE existing_results || jsonb_build_array(jsonb_build_object('control_id', $2, 'verdict', $3, 'evidence', $4, 'notes', $5))
|
||||||
|
END
|
||||||
|
) AS elem
|
||||||
|
),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE id = $1`,
|
||||||
|
id, body.ControlID, body.Verdict, body.Evidence, body.Notes)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"status": "updated", "control_id": body.ControlID, "verdict": body.Verdict})
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
-- Migration 024: Payment Compliance Schema
|
||||||
|
-- Tracks payment terminal compliance assessments against control library
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS payment_compliance_assessments (
|
||||||
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
tenant_id UUID NOT NULL,
|
||||||
|
|
||||||
|
-- Project / Tender
|
||||||
|
project_name VARCHAR(500) NOT NULL,
|
||||||
|
tender_reference VARCHAR(200),
|
||||||
|
customer_name VARCHAR(500),
|
||||||
|
description TEXT,
|
||||||
|
|
||||||
|
-- Scope
|
||||||
|
system_type VARCHAR(100), -- terminal, backend, both, full_stack
|
||||||
|
payment_methods JSONB DEFAULT '[]'::jsonb, -- ["card", "nfc", "girocard", "credit"]
|
||||||
|
protocols JSONB DEFAULT '[]'::jsonb, -- ["zvt", "opi", "emv"]
|
||||||
|
|
||||||
|
-- Assessment
|
||||||
|
total_controls INT DEFAULT 0,
|
||||||
|
controls_passed INT DEFAULT 0,
|
||||||
|
controls_failed INT DEFAULT 0,
|
||||||
|
controls_partial INT DEFAULT 0,
|
||||||
|
controls_not_applicable INT DEFAULT 0,
|
||||||
|
controls_not_checked INT DEFAULT 0,
|
||||||
|
compliance_score NUMERIC(5,2) DEFAULT 0,
|
||||||
|
|
||||||
|
-- Status
|
||||||
|
status VARCHAR(50) DEFAULT 'draft',
|
||||||
|
-- CHECK (status IN ('draft', 'in_progress', 'completed', 'approved'))
|
||||||
|
|
||||||
|
-- Results (per control)
|
||||||
|
control_results JSONB DEFAULT '[]'::jsonb,
|
||||||
|
-- Each entry: {"control_id": "PAY-001", "verdict": "passed|failed|partial|na|unchecked", "evidence": "...", "notes": "..."}
|
||||||
|
|
||||||
|
-- Audit
|
||||||
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
|
created_by VARCHAR(200),
|
||||||
|
approved_by VARCHAR(200),
|
||||||
|
approved_at TIMESTAMPTZ
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pca_tenant ON payment_compliance_assessments (tenant_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_pca_status ON payment_compliance_assessments (status);
|
||||||
198
ai-compliance-sdk/policies/payment_controls_v1.json
Normal file
198
ai-compliance-sdk/policies/payment_controls_v1.json
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
{
|
||||||
|
"schema": "payment_controls",
|
||||||
|
"version": "1.0",
|
||||||
|
"description": "Technische Pruefbibliothek fuer Payment-Terminal-Systeme. Eigene Controls, keine Normkopie.",
|
||||||
|
"domains": [
|
||||||
|
{
|
||||||
|
"id": "PAY",
|
||||||
|
"name": "Payment Flow & Transaction Integrity",
|
||||||
|
"description": "Zahlungsablauf, Zustandslogik, Idempotenz, Betragsvalidierung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LOG",
|
||||||
|
"name": "Logging & Audit",
|
||||||
|
"description": "Protokollierung, Audit Trail, Datenmaskierung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CRYPTO",
|
||||||
|
"name": "Secrets & Cryptography",
|
||||||
|
"description": "Schluesselmanagement, Verschluesselung, Secure Storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "API",
|
||||||
|
"name": "API & Backend Security",
|
||||||
|
"description": "Authentifizierung, Autorisierung, Input Validation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "TERM",
|
||||||
|
"name": "Terminal Communication",
|
||||||
|
"description": "ZVT/OPI Protokolle, Sequenzen, Fehlercodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "FW",
|
||||||
|
"name": "Firmware & Device Integrity",
|
||||||
|
"description": "Signierung, Update-Schutz, Manipulationserkennung"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "REP",
|
||||||
|
"name": "Reporting & Reconciliation",
|
||||||
|
"description": "Transaktionsberichte, Abgleich, Exportdaten"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ACC",
|
||||||
|
"name": "Access Control & Administration",
|
||||||
|
"description": "Rollenkonzept, Privilegien, Session-Management"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ERR",
|
||||||
|
"name": "Error Handling & Resilience",
|
||||||
|
"description": "Fehlerbehandlung, Recovery, Offline-Szenarien"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "BLD",
|
||||||
|
"name": "Build, Deployment & Supply Chain",
|
||||||
|
"description": "CI/CD Sicherheit, Abhaengigkeiten, Release-Integritaet"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"controls": [
|
||||||
|
{"control_id": "PAY-001", "domain": "PAY", "title": "Eindeutige Transaktions-ID pro Zahlungsvorgang", "objective": "Verhindert Vermischung und Mehrfachverarbeitung", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-002", "domain": "PAY", "title": "Idempotente Verarbeitung wiederholter Zahlungsanfragen", "objective": "Verhindert doppelte Buchungen bei Retries", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-003", "domain": "PAY", "title": "Verhinderung doppelter Verbuchung bei Netzwerk-Retry", "objective": "Stellt konsistente Zahlungszustaende sicher", "check_target": "system", "evidence": ["integration_test", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "PAY-004", "domain": "PAY", "title": "Definierter Initialzustand jeder Transaktion", "objective": "Verhindert undefinierte Startbedingungen", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-005", "domain": "PAY", "title": "Definierte erlaubte Zustandsuebergaenge in der Transaktionslogik", "objective": "Verhindert ungueltige State Transitions", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-006", "domain": "PAY", "title": "Keine direkte Transition in terminalen Erfolgszustand ohne Autorisierung", "objective": "Verhindert vorzeitige Freigabe", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-007", "domain": "PAY", "title": "Abbruchpfade fuehren in definierten Endzustand", "objective": "Sichert sauberes Cancel-Handling", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-008", "domain": "PAY", "title": "Timeout fuehrt in nachvollziehbaren und sicheren Zustand", "objective": "Verhindert haengende Transaktionen", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-009", "domain": "PAY", "title": "Rollback oder Reversal-Handling bei Teilfehlschlag", "objective": "Reduziert Inkonsistenzen", "check_target": "system", "evidence": ["integration_test", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "PAY-010", "domain": "PAY", "title": "Fehlerhafte Antworten werden nicht als Erfolg interpretiert", "objective": "Verhindert False Positive bei Zahlungsstatus", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-011", "domain": "PAY", "title": "Betragsvalidierung bei jeder Zahlungsanfrage", "objective": "Verhindert Betragmanipulation und negative Werte", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-012", "domain": "PAY", "title": "Waehrungsfeld wird validiert und konsistent verarbeitet", "objective": "Verhindert Fehlverarbeitung bei Mehrwaehrung", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-013", "domain": "PAY", "title": "Betragsrundung erfolgt deterministisch und dokumentiert", "objective": "Verhindert Abweichungen Frontend/Terminal/Backend", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-014", "domain": "PAY", "title": "Keine lokale Manipulation des autorisierten Betrags nach Freigabe", "objective": "Schuetzt Integritaet der Zahlung", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-015", "domain": "PAY", "title": "Transaktionskontext bleibt ueber Retry-Versuche konsistent", "objective": "Verhindert Kontextverlust", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-016", "domain": "PAY", "title": "Antworten ohne Referenz-ID werden nicht akzeptiert", "objective": "Verhindert verwaiste Zuordnungen", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "PAY-017", "domain": "PAY", "title": "Doppelte Callback-Verarbeitung wird unterdrueckt", "objective": "Verhindert doppelte Statusupdates", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-018", "domain": "PAY", "title": "Asynchrone Statusmeldungen werden korreliert und sequenziell verarbeitet", "objective": "Sichert korrekte Reihenfolge", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-019", "domain": "PAY", "title": "Geschaeftsvorfall wird erst nach bestaetigtem Zahlungsstatus finalisiert", "objective": "Verhindert Business Success ohne Payment Success", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "PAY-020", "domain": "PAY", "title": "Offline-Zahlungen werden explizit gekennzeichnet", "objective": "Verhindert Verwechslung mit final autorisierten Zahlungen", "check_target": "code", "evidence": ["source_code", "reporting_output"], "automation": "medium"},
|
||||||
|
|
||||||
|
{"control_id": "LOG-001", "domain": "LOG", "title": "Keine sensitiven Zahlungsdaten im Anwendungslog", "objective": "Verhindert Offenlegung sensitiver Daten", "check_target": "code", "evidence": ["source_code", "log_config"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-002", "domain": "LOG", "title": "PAN wird in Logs maskiert", "objective": "Reduziert Risiko bei Log-Einsicht", "check_target": "code", "evidence": ["source_code", "log_output_sample"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-003", "domain": "LOG", "title": "CVV/CVC wird niemals geloggt", "objective": "Verhindert Protokollierung sensitiver Authentifizierungsdaten", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-004", "domain": "LOG", "title": "Kryptographische Schluessel werden nicht geloggt", "objective": "Verhindert Kompromittierung durch Logging", "check_target": "code", "evidence": ["source_code", "log_output_sample"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-005", "domain": "LOG", "title": "Admin-Aktionen werden auditierbar protokolliert", "objective": "Ermoeglicht Nachvollziehbarkeit privilegierter Handlungen", "check_target": "system", "evidence": ["source_code", "audit_log_sample"], "automation": "partial"},
|
||||||
|
{"control_id": "LOG-006", "domain": "LOG", "title": "Konfigurationsaenderungen werden protokolliert", "objective": "Ermoeglicht Nachweis kritischer Aenderungen", "check_target": "system", "evidence": ["source_code", "audit_log_sample"], "automation": "partial"},
|
||||||
|
{"control_id": "LOG-007", "domain": "LOG", "title": "Fehlgeschlagene Authentifizierungsversuche werden geloggt", "objective": "Unterstuetzt Erkennung von Missbrauch", "check_target": "code", "evidence": ["source_code", "audit_log_sample"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-008", "domain": "LOG", "title": "Sicherheitsrelevante Ereignisse erhalten eindeutige Event-Typen", "objective": "Erleichtert Korrelation und Monitoring", "check_target": "code", "evidence": ["source_code", "log_schema"], "automation": "medium"},
|
||||||
|
{"control_id": "LOG-009", "domain": "LOG", "title": "Audit-Events enthalten konsistenten Zeitstempel", "objective": "Ermoeglicht zeitliche Rekonstruktion", "check_target": "system", "evidence": ["audit_log_sample", "config"], "automation": "partial"},
|
||||||
|
{"control_id": "LOG-010", "domain": "LOG", "title": "Audit-Events enthalten eindeutige Terminalkennung", "objective": "Ermoeglicht Zuordnung zur Quelle", "check_target": "code", "evidence": ["log_schema", "audit_log_sample"], "automation": "medium"},
|
||||||
|
{"control_id": "LOG-011", "domain": "LOG", "title": "Debug-Logging in Produktion deaktiviert", "objective": "Verhindert Leaks in produktiven Systemen", "check_target": "config", "evidence": ["deployment_config"], "automation": "high"},
|
||||||
|
{"control_id": "LOG-012", "domain": "LOG", "title": "Manipulation von Audit-Logs technisch erschwert", "objective": "Schuetzt Integritaet des Audit Trails", "check_target": "system", "evidence": ["architecture_doc", "storage_config"], "automation": "low"},
|
||||||
|
{"control_id": "LOG-013", "domain": "LOG", "title": "Fehlermeldungen enthalten keine Stacktraces mit sensitiven Payloads", "objective": "Verhindert indirekten Datenabfluss", "check_target": "code", "evidence": ["source_code", "log_output_sample"], "automation": "medium"},
|
||||||
|
{"control_id": "LOG-014", "domain": "LOG", "title": "Jede Zahlungsentscheidung erzeugt Audit-Eintrag", "objective": "Verbindet Business Outcome mit technischer Evidenz", "check_target": "system", "evidence": ["audit_log_sample", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "LOG-015", "domain": "LOG", "title": "Log-Retention konfiguriert und dokumentiert", "objective": "Sichert Verfuegbarkeit relevanter Ereignishistorie", "check_target": "config", "evidence": ["retention_policy", "deployment_config"], "automation": "medium"},
|
||||||
|
|
||||||
|
{"control_id": "CRYPTO-001", "domain": "CRYPTO", "title": "Keine Secrets im Quellcode", "objective": "Verhindert Offenlegung im Repository", "check_target": "code", "evidence": ["source_code", "secret_scan"], "automation": "high"},
|
||||||
|
{"control_id": "CRYPTO-002", "domain": "CRYPTO", "title": "Keine Secrets in Commit-Historie", "objective": "Reduziert Leak-Risiko ueber Entwicklungsartefakte", "check_target": "repository", "evidence": ["secret_scan", "build_scripts"], "automation": "high"},
|
||||||
|
{"control_id": "CRYPTO-003", "domain": "CRYPTO", "title": "Keine Schluessel im Klartext in Konfigurationsdateien", "objective": "Schuetzt ruhende Geheimnisse", "check_target": "config", "evidence": ["config", "secret_scan"], "automation": "high"},
|
||||||
|
{"control_id": "CRYPTO-004", "domain": "CRYPTO", "title": "Secrets aus sicherem Secret Store bezogen", "objective": "Verhindert lokale Persistenz", "check_target": "system", "evidence": ["architecture_doc", "deployment_config"], "automation": "partial"},
|
||||||
|
{"control_id": "CRYPTO-005", "domain": "CRYPTO", "title": "Zugriff auf Secrets rollen-/servicebezogen eingeschraenkt", "objective": "Begrenzt Blast Radius", "check_target": "system", "evidence": ["iam_config", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "CRYPTO-006", "domain": "CRYPTO", "title": "Zentrale und freigegebene Krypto-Bibliotheken verwendet", "objective": "Verhindert unsichere Eigenimplementierungen", "check_target": "code", "evidence": ["source_code", "dependency_list"], "automation": "medium"},
|
||||||
|
{"control_id": "CRYPTO-007", "domain": "CRYPTO", "title": "Keine veralteten kryptographischen Primitive (MD5, SHA1, DES)", "objective": "Verhindert Einsatz schwacher Verfahren", "check_target": "code", "evidence": ["source_code", "dependency_scan"], "automation": "medium"},
|
||||||
|
{"control_id": "CRYPTO-008", "domain": "CRYPTO", "title": "TLS 1.2+ fuer alle externen Verbindungen", "objective": "Schuetzt Daten bei Uebertragung", "check_target": "config", "evidence": ["config", "network_scan"], "automation": "high"},
|
||||||
|
{"control_id": "CRYPTO-009", "domain": "CRYPTO", "title": "Schluesselrotation implementiert und dokumentiert", "objective": "Reduziert Kompromittierungszeitraum", "check_target": "process", "evidence": ["key_mgmt_doc", "config"], "automation": "low"},
|
||||||
|
{"control_id": "CRYPTO-010", "domain": "CRYPTO", "title": "HSM oder Secure Enclave fuer kryptographische Operationen", "objective": "Hardwarebasierter Schluesselschutz", "check_target": "system", "evidence": ["architecture_doc"], "automation": "low"},
|
||||||
|
{"control_id": "CRYPTO-011", "domain": "CRYPTO", "title": "Zertifikats-Pinning fuer kritische Verbindungen", "objective": "Schuetzt gegen MITM", "check_target": "code", "evidence": ["source_code", "config"], "automation": "medium"},
|
||||||
|
{"control_id": "CRYPTO-012", "domain": "CRYPTO", "title": "Kryptographische Zufallszahlen aus sicherem Generator", "objective": "Verhindert vorhersagbare Tokens/Nonces", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "CRYPTO-013", "domain": "CRYPTO", "title": "PIN-Eingabe nur ueber Secure PIN Entry Device", "objective": "Schuetzt PIN vor Abgriff", "check_target": "system", "evidence": ["architecture_doc", "certification"], "automation": "low"},
|
||||||
|
{"control_id": "CRYPTO-014", "domain": "CRYPTO", "title": "Kartendaten werden verschluesselt uebertragen (P2PE)", "objective": "End-to-End Schutz der Kartendaten", "check_target": "system", "evidence": ["architecture_doc", "network_config"], "automation": "partial"},
|
||||||
|
{"control_id": "CRYPTO-015", "domain": "CRYPTO", "title": "Keine persistente Speicherung vollstaendiger Kartendaten", "objective": "Minimiert Daten bei Kompromittierung", "check_target": "code", "evidence": ["source_code", "db_schema"], "automation": "high"},
|
||||||
|
|
||||||
|
{"control_id": "API-001", "domain": "API", "title": "Authentifizierung fuer alle Admin-Endpunkte", "objective": "Verhindert unautorisierten Zugriff", "check_target": "code", "evidence": ["source_code", "api_spec"], "automation": "high"},
|
||||||
|
{"control_id": "API-002", "domain": "API", "title": "Rollenbasierte Autorisierung", "objective": "Least-Privilege Prinzip", "check_target": "code", "evidence": ["source_code", "rbac_config"], "automation": "medium"},
|
||||||
|
{"control_id": "API-003", "domain": "API", "title": "Rate Limiting implementiert", "objective": "Schuetzt gegen Brute Force und DoS", "check_target": "code", "evidence": ["source_code", "config"], "automation": "medium"},
|
||||||
|
{"control_id": "API-004", "domain": "API", "title": "Keine sensiblen Daten in Fehlermeldungen", "objective": "Verhindert Information Leakage", "check_target": "code", "evidence": ["source_code", "api_test"], "automation": "high"},
|
||||||
|
{"control_id": "API-005", "domain": "API", "title": "Input Validation gegen Injection", "objective": "Schuetzt gegen SQL/Command Injection", "check_target": "code", "evidence": ["source_code", "security_test"], "automation": "high"},
|
||||||
|
{"control_id": "API-006", "domain": "API", "title": "CORS korrekt konfiguriert", "objective": "Verhindert Cross-Origin Angriffe", "check_target": "config", "evidence": ["config", "security_test"], "automation": "high"},
|
||||||
|
{"control_id": "API-007", "domain": "API", "title": "Session-Timeout fuer Admin-Sessions", "objective": "Reduziert Risiko bei verlassenen Sessions", "check_target": "config", "evidence": ["config", "source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "API-008", "domain": "API", "title": "API-Versionierung implementiert", "objective": "Ermoeglicht kontrollierte Aenderungen", "check_target": "code", "evidence": ["api_spec", "source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "API-009", "domain": "API", "title": "Webhook-Callbacks werden authentifiziert", "objective": "Verhindert gefaelschte Callbacks", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "API-010", "domain": "API", "title": "Idempotenz-Keys fuer kritische POST-Operationen", "objective": "Verhindert doppelte Ausfuehrung", "check_target": "code", "evidence": ["source_code", "api_spec"], "automation": "medium"},
|
||||||
|
{"control_id": "API-011", "domain": "API", "title": "Request-Signierung fuer sicherheitskritische Operationen", "objective": "Integritaetsschutz der Anfrage", "check_target": "code", "evidence": ["source_code", "api_spec"], "automation": "medium"},
|
||||||
|
{"control_id": "API-012", "domain": "API", "title": "Keine sensiblen Daten in URL-Parametern", "objective": "Verhindert Leakage ueber Logs und Browser-History", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "API-013", "domain": "API", "title": "Content-Type Validierung bei allen Endpunkten", "objective": "Verhindert Content-Type Confusion", "check_target": "code", "evidence": ["source_code"], "automation": "high"},
|
||||||
|
{"control_id": "API-014", "domain": "API", "title": "Health- und Status-Endpunkte exponieren keine sensitiven Details", "objective": "Verhindert Reconnaissance", "check_target": "code", "evidence": ["source_code", "api_test"], "automation": "high"},
|
||||||
|
{"control_id": "API-015", "domain": "API", "title": "Batch-Operationen sind groessenbeschraenkt", "objective": "Verhindert Ressourcenerschoepfung", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
|
||||||
|
{"control_id": "TERM-001", "domain": "TERM", "title": "Korrekte Sequenz von Zahlungsbefehlen", "objective": "Protokollkonformitaet", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-002", "domain": "TERM", "title": "Retry-Mechanismus bei Verbindungsabbruch", "objective": "Sichert Transaktionsabschluss", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-003", "domain": "TERM", "title": "Timeout Handling Terminal-Backend", "objective": "Verhindert Blockierung", "check_target": "code", "evidence": ["source_code", "config"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-004", "domain": "TERM", "title": "Fehlercodes korrekt interpretiert", "objective": "Verhindert Fehlinterpretation", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-005", "domain": "TERM", "title": "Status-Synchronisation zwischen Terminal und Backend", "objective": "Konsistente Zustaende", "check_target": "system", "evidence": ["integration_test", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "TERM-006", "domain": "TERM", "title": "Verbindungsaufbau zum Terminal authentifiziert", "objective": "Verhindert Rogue-Terminal", "check_target": "code", "evidence": ["source_code", "config"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-007", "domain": "TERM", "title": "Terminal-Registrierung mit eindeutiger Kennung", "objective": "Ermoeglicht Asset-Tracking", "check_target": "system", "evidence": ["db_schema", "admin_ui"], "automation": "partial"},
|
||||||
|
{"control_id": "TERM-008", "domain": "TERM", "title": "Heartbeat / Keep-Alive fuer Terminal-Verbindung", "objective": "Erkennt Verbindungsabbruch frueh", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-009", "domain": "TERM", "title": "Protokollversion wird geprueft und erzwungen", "objective": "Verhindert Downgrade-Angriffe", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-010", "domain": "TERM", "title": "Kontaktlos-Transaktionen nur ueber zugelassene Kernel", "objective": "Sichert NFC-Konformitaet", "check_target": "system", "evidence": ["certification", "config"], "automation": "low"},
|
||||||
|
{"control_id": "TERM-011", "domain": "TERM", "title": "Terminal meldet Tamper-Events an Backend", "objective": "Zentrales Monitoring von Manipulationsversuchen", "check_target": "system", "evidence": ["integration_test", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "TERM-012", "domain": "TERM", "title": "Offline-Queue bei Verbindungsunterbrechung", "objective": "Sichert Transaktionsdaten bei Netzausfall", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-013", "domain": "TERM", "title": "Maximale Queue-Groesse definiert", "objective": "Verhindert unkontrollierten Speicherverbrauch", "check_target": "config", "evidence": ["config", "source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "TERM-014", "domain": "TERM", "title": "End-of-Day / Settlement-Prozess implementiert", "objective": "Sichert taeglichen Transaktionsabschluss", "check_target": "system", "evidence": ["source_code", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "TERM-015", "domain": "TERM", "title": "Terminal-Display zeigt korrekten Zahlungsstatus", "objective": "Verhindert Fehlkommunikation an Nutzer", "check_target": "system", "evidence": ["integration_test"], "automation": "low"},
|
||||||
|
|
||||||
|
{"control_id": "FW-001", "domain": "FW", "title": "Firmware signiert", "objective": "Verhindert Installation manipulierter Firmware", "check_target": "system", "evidence": ["build_pipeline", "signing_config"], "automation": "low"},
|
||||||
|
{"control_id": "FW-002", "domain": "FW", "title": "Signaturpruefung vor Firmware-Update", "objective": "Blockiert unsignierte Updates", "check_target": "code", "evidence": ["source_code", "update_process"], "automation": "medium"},
|
||||||
|
{"control_id": "FW-003", "domain": "FW", "title": "Rollback-Mechanismus vorhanden", "objective": "Ermoeglicht Recovery nach fehlerhaftem Update", "check_target": "system", "evidence": ["architecture_doc", "test_report"], "automation": "low"},
|
||||||
|
{"control_id": "FW-004", "domain": "FW", "title": "Debug-Interfaces in Produktion deaktiviert", "objective": "Verhindert unautorisierten Zugriff", "check_target": "config", "evidence": ["deployment_config", "security_test"], "automation": "medium"},
|
||||||
|
{"control_id": "FW-005", "domain": "FW", "title": "Manipulationserkennung loest Alarm/Sperre aus", "objective": "Reaktion auf physische Angriffe", "check_target": "system", "evidence": ["architecture_doc", "test_report"], "automation": "low"},
|
||||||
|
{"control_id": "FW-006", "domain": "FW", "title": "Secure Boot implementiert", "objective": "Verhindert Ausfuehrung manipulierter Boot-Images", "check_target": "system", "evidence": ["architecture_doc"], "automation": "low"},
|
||||||
|
{"control_id": "FW-007", "domain": "FW", "title": "Firmware-Version ist remote abfragbar", "objective": "Ermoeglicht Fleet-Management und Compliance-Nachweis", "check_target": "system", "evidence": ["api_spec", "admin_ui"], "automation": "partial"},
|
||||||
|
{"control_id": "FW-008", "domain": "FW", "title": "Automatische Update-Benachrichtigung bei kritischen Patches", "objective": "Sichert zeitnahe Reaktion auf Schwachstellen", "check_target": "system", "evidence": ["architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "FW-009", "domain": "FW", "title": "Keine Persistenz von Zahlungsdaten ueber Neustart hinaus", "objective": "Schuetzt Daten bei physischem Zugriff", "check_target": "code", "evidence": ["source_code", "architecture_doc"], "automation": "medium"},
|
||||||
|
{"control_id": "FW-010", "domain": "FW", "title": "Physischer Speicher wird bei Tamper-Detection geloescht", "objective": "Zerstoert Schluessel bei Manipulation", "check_target": "system", "evidence": ["architecture_doc", "certification"], "automation": "low"},
|
||||||
|
|
||||||
|
{"control_id": "REP-001", "domain": "REP", "title": "Transaktionsstatus vollstaendig dokumentiert", "objective": "Ermoeglicht Nachvollziehbarkeit jeder Zahlung", "check_target": "system", "evidence": ["reporting_output", "db_schema"], "automation": "medium"},
|
||||||
|
{"control_id": "REP-002", "domain": "REP", "title": "Audit-Trail verknuepft mit Transaktionen", "objective": "Sichert End-to-End Traceability", "check_target": "system", "evidence": ["reporting_output", "audit_log_sample"], "automation": "medium"},
|
||||||
|
{"control_id": "REP-003", "domain": "REP", "title": "Exportdaten plausibel und vollstaendig", "objective": "Sichert korrekte Weitergabe", "check_target": "system", "evidence": ["export_sample", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "REP-004", "domain": "REP", "title": "Fehlercodes nachvollziehbar dokumentiert", "objective": "Ermoeglicht Fehleranalyse", "check_target": "code", "evidence": ["source_code", "documentation"], "automation": "medium"},
|
||||||
|
{"control_id": "REP-005", "domain": "REP", "title": "Revisionssichere Speicherung von Transaktionsdaten", "objective": "GoBD/GDPdU-konforme Aufbewahrung", "check_target": "system", "evidence": ["architecture_doc", "storage_config"], "automation": "low"},
|
||||||
|
{"control_id": "REP-006", "domain": "REP", "title": "Tagesabschluss-Report vollstaendig und konsistent", "objective": "Sichert taeglichen Abgleich", "check_target": "system", "evidence": ["reporting_output", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "REP-007", "domain": "REP", "title": "Summenabgleich Terminal vs. Backend", "objective": "Erkennt Differenzen", "check_target": "system", "evidence": ["reconciliation_report", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "REP-008", "domain": "REP", "title": "Stornierte Transaktionen korrekt ausgewiesen", "objective": "Sichert korrekte Buchhaltungsgrundlage", "check_target": "system", "evidence": ["reporting_output"], "automation": "medium"},
|
||||||
|
{"control_id": "REP-009", "domain": "REP", "title": "Historische Reports nicht nachtraeglich aenderbar", "objective": "Schuetzt Integritaet der Berichterstattung", "check_target": "system", "evidence": ["architecture_doc", "db_config"], "automation": "low"},
|
||||||
|
{"control_id": "REP-010", "domain": "REP", "title": "Abrechnungsdaten enthalten keine vollstaendigen Kartennummern", "objective": "Minimiert Datenexposition in Reports", "check_target": "code", "evidence": ["source_code", "export_sample"], "automation": "high"},
|
||||||
|
|
||||||
|
{"control_id": "ACC-001", "domain": "ACC", "title": "Individuelle Benutzerkonten fuer alle Administratoren", "objective": "Verhindert geteilte Accounts", "check_target": "system", "evidence": ["admin_ui", "iam_config"], "automation": "partial"},
|
||||||
|
{"control_id": "ACC-002", "domain": "ACC", "title": "Standard-Passwoerter werden bei Ersteinrichtung erzwungen zu aendern", "objective": "Verhindert Default-Credential-Angriffe", "check_target": "code", "evidence": ["source_code", "deployment_doc"], "automation": "medium"},
|
||||||
|
{"control_id": "ACC-003", "domain": "ACC", "title": "Multi-Faktor-Authentifizierung fuer Admin-Zugang", "objective": "Erhoehter Schutz privilegierter Konten", "check_target": "system", "evidence": ["iam_config", "admin_ui"], "automation": "partial"},
|
||||||
|
{"control_id": "ACC-004", "domain": "ACC", "title": "Passwort-Komplexitaetsanforderungen implementiert", "objective": "Verhindert schwache Passwoerter", "check_target": "code", "evidence": ["source_code", "config"], "automation": "high"},
|
||||||
|
{"control_id": "ACC-005", "domain": "ACC", "title": "Account-Sperrung nach fehlgeschlagenen Anmeldeversuchen", "objective": "Schuetzt gegen Brute Force", "check_target": "code", "evidence": ["source_code", "config"], "automation": "high"},
|
||||||
|
{"control_id": "ACC-006", "domain": "ACC", "title": "Privilegierte Aktionen erfordern erneute Authentifizierung", "objective": "Step-Up Authentication", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "ACC-007", "domain": "ACC", "title": "Inaktive Sessions werden automatisch beendet", "objective": "Reduziert Angriffsflaeche bei verlassenen Sessions", "check_target": "config", "evidence": ["config", "source_code"], "automation": "high"},
|
||||||
|
{"control_id": "ACC-008", "domain": "ACC", "title": "Berechtigungsaenderungen werden auditiert", "objective": "Nachvollziehbarkeit von Rechteaenderungen", "check_target": "system", "evidence": ["audit_log_sample", "source_code"], "automation": "partial"},
|
||||||
|
{"control_id": "ACC-009", "domain": "ACC", "title": "Least-Privilege Prinzip fuer alle Rollen", "objective": "Minimiert Rechte auf das Notwendige", "check_target": "system", "evidence": ["rbac_config", "architecture_doc"], "automation": "partial"},
|
||||||
|
{"control_id": "ACC-010", "domain": "ACC", "title": "Service-Accounts haben keine interaktive Login-Moeglichkeit", "objective": "Verhindert Missbrauch technischer Konten", "check_target": "config", "evidence": ["iam_config"], "automation": "medium"},
|
||||||
|
|
||||||
|
{"control_id": "ERR-001", "domain": "ERR", "title": "Definierte Fehlerbehandlung fuer alle externen Aufrufe", "objective": "Verhindert unkontrollierte Abbrueche", "check_target": "code", "evidence": ["source_code"], "automation": "medium"},
|
||||||
|
{"control_id": "ERR-002", "domain": "ERR", "title": "Graceful Degradation bei Teilausfall", "objective": "Sichert Basisfunktionalitaet", "check_target": "system", "evidence": ["architecture_doc", "integration_test"], "automation": "partial"},
|
||||||
|
{"control_id": "ERR-003", "domain": "ERR", "title": "Recovery nach Stromausfall ohne Datenverlust", "objective": "Transaktionskonsistenz bei Hardwareausfall", "check_target": "system", "evidence": ["integration_test", "architecture_doc"], "automation": "low"},
|
||||||
|
{"control_id": "ERR-004", "domain": "ERR", "title": "Offline-Modus mit definiertem Funktionsumfang", "objective": "Klare Grenzen bei fehlender Konnektivitaet", "check_target": "code", "evidence": ["source_code", "documentation"], "automation": "medium"},
|
||||||
|
{"control_id": "ERR-005", "domain": "ERR", "title": "Automatische Wiederverbindung nach Netzwerkunterbrechung", "objective": "Minimiert manuelle Intervention", "check_target": "code", "evidence": ["source_code", "integration_test"], "automation": "medium"},
|
||||||
|
{"control_id": "ERR-006", "domain": "ERR", "title": "Circuit Breaker bei Backend-Ueberlast", "objective": "Verhindert Kaskadenausfall", "check_target": "code", "evidence": ["source_code", "config"], "automation": "medium"},
|
||||||
|
{"control_id": "ERR-007", "domain": "ERR", "title": "Fehlerhafte Datenpakete werden verworfen, nicht verarbeitet", "objective": "Verhindert Fehlverarbeitung korrupter Daten", "check_target": "code", "evidence": ["source_code", "unit_test"], "automation": "high"},
|
||||||
|
{"control_id": "ERR-008", "domain": "ERR", "title": "Health-Check-Endpunkt fuer Terminal-Monitoring", "objective": "Ermoeglicht proaktive Fehlererkennung", "check_target": "code", "evidence": ["source_code", "api_spec"], "automation": "high"},
|
||||||
|
{"control_id": "ERR-009", "domain": "ERR", "title": "Eskalationsprozess bei kritischen Fehlern definiert", "objective": "Sichert schnelle Reaktion bei Systemausfall", "check_target": "process", "evidence": ["documentation", "runbook"], "automation": "low"},
|
||||||
|
{"control_id": "ERR-010", "domain": "ERR", "title": "Wartungsmodus ohne Transaktionsverlust aktivierbar", "objective": "Ermoeglicht geplante Wartung ohne Datenverlust", "check_target": "system", "evidence": ["admin_ui", "integration_test"], "automation": "partial"},
|
||||||
|
|
||||||
|
{"control_id": "BLD-001", "domain": "BLD", "title": "Build-Pipeline reproduzierbar", "objective": "Sichert Nachvollziehbarkeit der Artefakte", "check_target": "system", "evidence": ["ci_config", "build_log"], "automation": "medium"},
|
||||||
|
{"control_id": "BLD-002", "domain": "BLD", "title": "Abhaengigkeiten werden auf bekannte Schwachstellen geprueft", "objective": "Verhindert vulnerable Dependencies", "check_target": "system", "evidence": ["dependency_scan", "ci_config"], "automation": "high"},
|
||||||
|
{"control_id": "BLD-003", "domain": "BLD", "title": "Release-Artefakte sind signiert", "objective": "Integritaetsschutz der Auslieferung", "check_target": "system", "evidence": ["signing_config", "release_process"], "automation": "medium"},
|
||||||
|
{"control_id": "BLD-004", "domain": "BLD", "title": "Keine Test-Credentials in Release-Konfiguration", "objective": "Verhindert Produktions-Leaks", "check_target": "config", "evidence": ["deployment_config", "secret_scan"], "automation": "high"},
|
||||||
|
{"control_id": "BLD-005", "domain": "BLD", "title": "Container-Images werden auf Schwachstellen gescannt", "objective": "Sichert Basis-Image Integritaet", "check_target": "system", "evidence": ["container_scan", "ci_config"], "automation": "high"},
|
||||||
|
{"control_id": "BLD-006", "domain": "BLD", "title": "SBOM (Software Bill of Materials) wird generiert", "objective": "Transparenz ueber verwendete Komponenten", "check_target": "system", "evidence": ["sbom_output", "ci_config"], "automation": "medium"},
|
||||||
|
{"control_id": "BLD-007", "domain": "BLD", "title": "Deployment nur ueber autorisierte Pipeline", "objective": "Verhindert manuelle, unkontrollierte Deployments", "check_target": "system", "evidence": ["ci_config", "access_control"], "automation": "medium"},
|
||||||
|
{"control_id": "BLD-008", "domain": "BLD", "title": "Rollback-Prozedur fuer Deployments definiert und getestet", "objective": "Ermoeglicht schnelle Recovery", "check_target": "process", "evidence": ["runbook", "deployment_doc"], "automation": "low"},
|
||||||
|
{"control_id": "BLD-009", "domain": "BLD", "title": "Code-Review vor Merge in Release-Branch", "objective": "Vier-Augen-Prinzip", "check_target": "process", "evidence": ["git_config", "pr_policy"], "automation": "medium"},
|
||||||
|
{"control_id": "BLD-010", "domain": "BLD", "title": "Automatisierte Tests vor jedem Release", "objective": "Sichert Qualitaet vor Auslieferung", "check_target": "system", "evidence": ["ci_config", "test_results"], "automation": "high"}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user