feat: Dreistufenmodell normative Verbindlichkeit + Duplikat-Filter + Auto-Deploy
- Source-Type-Klassifikation (58 Regulierungen: law/guideline/framework) - Backfill-Endpoint POST /controls/backfill-normative-strength - exclude_duplicates Filter fuer Control-Library (Backend + Proxy + UI-Toggle) - MkDocs-Kapitel: Normative Verbindlichkeit mit Mermaid-Diagrammen - scripts/deploy.sh: Auto-Push + Mac Mini rebuild + Coolify health monitoring - 26 Unit Tests fuer Klassifikations-Logik Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
201
docs-src/services/sdk-modules/normative-verbindlichkeit.md
Normal file
201
docs-src/services/sdk-modules/normative-verbindlichkeit.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# Normative Verbindlichkeit — Dreistufenmodell
|
||||
|
||||
## Uebersicht
|
||||
|
||||
Nicht jede Quelle, aus der Controls abgeleitet werden, hat die gleiche rechtliche
|
||||
Verbindlichkeit. Ein Control, das aus einem EU-Gesetz stammt, hat ein anderes
|
||||
Gewicht als eines aus einem freiwilligen Framework.
|
||||
|
||||
Das Dreistufenmodell klassifiziert jede Quell-Regulierung und leitet daraus die
|
||||
**effektive normative Staerke** der daraus erzeugten Obligations ab.
|
||||
|
||||
## Die drei Stufen
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Stufe 1 — GESETZ (law)"
|
||||
direction LR
|
||||
A1["DSGVO, NIS2, AI Act, CRA..."]
|
||||
A2["Rechtlich bindend"]
|
||||
A3["Bussgeld bei Verstoss"]
|
||||
A4["normative_strength: must/should/may"]
|
||||
end
|
||||
|
||||
subgraph "Stufe 2 — LEITLINIE (guideline)"
|
||||
direction LR
|
||||
B1["EDPB-Leitlinien, BSI-TR, WP29"]
|
||||
B2["Offizielle Auslegungshilfe"]
|
||||
B3["Beweislastumkehr"]
|
||||
B4["max normative_strength: should"]
|
||||
end
|
||||
|
||||
subgraph "Stufe 3 — FRAMEWORK (framework)"
|
||||
direction LR
|
||||
C1["ENISA, NIST, OWASP, OECD"]
|
||||
C2["Freiwillige Best Practice"]
|
||||
C3["Stand der Technik"]
|
||||
C4["max normative_strength: can"]
|
||||
end
|
||||
|
||||
A1 --> A2 --> A3 --> A4
|
||||
B1 --> B2 --> B3 --> B4
|
||||
C1 --> C2 --> C3 --> C4
|
||||
```
|
||||
|
||||
### Stufe 1: Gesetz (law)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Rechtlich bindend, Bussgeld bei Verstoss |
|
||||
| **normative_strength** | Bleibt wie im Gesetzestext: `must`, `should` oder `may` |
|
||||
| **Beispiele** | DSGVO (EU) 2016/679, NIS2-Richtlinie, KI-Verordnung, CRA, BDSG |
|
||||
| **Warum relevant** | "Sie MUESSEN angemessene technische Massnahmen ergreifen" (Art. 32 DSGVO) |
|
||||
|
||||
!!! warning "Wichtig"
|
||||
Gesetze formulieren Pflichten **abstrakt**. Art. 32 DSGVO sagt:
|
||||
"dem Stand der Technik entsprechende Massnahmen" — aber NICHT
|
||||
"verwende AES-256". Das WAS ist Pflicht, das WIE bleibt offen.
|
||||
|
||||
### Stufe 2: Leitlinie (guideline)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Nicht direkt bindend, aber Beweislastumkehr |
|
||||
| **normative_strength** | Maximal `should` — auch wenn die Leitlinie intern "must" schreibt |
|
||||
| **Beispiele** | EDPB-Leitlinien, BSI Technische Richtlinien, WP29-Dokumente |
|
||||
| **Warum relevant** | "Daten at rest muessen verschluesselt werden" (BSI-TR) → `should` |
|
||||
|
||||
!!! info "Beweislastumkehr"
|
||||
Wenn eine Aufsichtsbehoerde fragt "Warum verschluesselt ihr nicht?",
|
||||
muss die Firma begruenden, warum sie von der Leitlinie abweicht.
|
||||
Die Firma muss aber nicht genau so verschluesseln wie die BSI vorschlaegt.
|
||||
|
||||
### Stufe 3: Framework (framework)
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
|---|---|
|
||||
| **Verbindlichkeit** | Freiwillig, nicht rechtsverbindlich |
|
||||
| **normative_strength** | Maximal `can` — unabhaengig von interner Sprache |
|
||||
| **Beispiele** | ENISA CCM, NIST CSF, OWASP Top 10, OECD KI-Empfehlung |
|
||||
| **Warum relevant** | "Organizations SHALL implement..." (ENISA) → `can` fuer den Anwender |
|
||||
|
||||
!!! tip "Stand der Technik"
|
||||
NIS2 Art. 21 verweist auf ENISA-Leitlinien als Referenz fuer den
|
||||
"Stand der Technik". Das hebt ENISA-Controls faktisch auf Stufe 2 (`should`)
|
||||
— aber nur im Kontext von NIS2-pflichtigen Unternehmen, nicht generell.
|
||||
|
||||
## Ableitungskette
|
||||
|
||||
Die vollstaendige Kette von der Rechtsquelle zum atomaren Control:
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
R["Regulierung<br/>(DSGVO Art. 32)"] -->|"MUSS"| O["Obligation<br/>(Daten schuetzen)"]
|
||||
O -->|decomposition| RC["Rich Control<br/>(Verschluesselung)"]
|
||||
RC -->|pass0b| AC["Atomares Control<br/>(AES-256 at rest)"]
|
||||
|
||||
R2["Framework<br/>(ENISA CCM)"] -->|"KANN"| AC
|
||||
|
||||
style R fill:#fee2e2,stroke:#dc2626
|
||||
style R2 fill:#dbeafe,stroke:#2563eb
|
||||
style O fill:#fef3c7,stroke:#d97706
|
||||
style RC fill:#e0e7ff,stroke:#4f46e5
|
||||
style AC fill:#d1fae5,stroke:#059669
|
||||
```
|
||||
|
||||
**Beispiel**: Das atomare Control "AES-256 Verschluesselung at rest"
|
||||
|
||||
- Aus DSGVO Art. 32 abgeleitet → Obligation "must secure data" → **MUSS** (die Pflicht zu schuetzen)
|
||||
- Aus ENISA CCM konkretisiert → **KANN** (AES-256 ist *eine* moegliche Umsetzung)
|
||||
- Resultat: Die Firma MUSS verschluesseln, KANN aber waehlen wie
|
||||
|
||||
## Multi-Parent-Links
|
||||
|
||||
Ein atomares Control kann aus mehreren Quellen stammen:
|
||||
|
||||
| Control | Parent 1 | Parent 2 | Parent 3 | Effektive Staerke |
|
||||
|---|---|---|---|---|
|
||||
| SEC-042 (Encrypt at rest) | DSGVO Art. 32 (law) | NIS2 Art. 21 (law) | ENISA CCM (framework) | **must** (Gesetz uebertrumpft) |
|
||||
| NET-015 (Zero Trust) | NIST SP 800-207 (framework) | CISA (framework) | — | **can** (nur Frameworks) |
|
||||
| AUTH-003 (MFA) | DSGVO Art. 32 (law) | BSI-TR (guideline) | OWASP ASVS (framework) | **must** (Gesetz vorhanden) |
|
||||
|
||||
**Regel**: Der hoechste source_type bestimmt, ob die normative_strength begrenzt wird.
|
||||
Wenn mindestens ein Parent-Link ein Gesetz ist, bleibt die Staerke wie extrahiert.
|
||||
|
||||
## Technische Umsetzung
|
||||
|
||||
### Klassifikations-Map
|
||||
|
||||
Datei: `backend-compliance/compliance/data/source_type_classification.py`
|
||||
|
||||
Jeder `source_regulation`-Wert aus `control_parent_links` wird klassifiziert:
|
||||
|
||||
```python
|
||||
SOURCE_REGULATION_CLASSIFICATION = {
|
||||
"DSGVO (EU) 2016/679": "law",
|
||||
"EDPB Leitlinien 01/2020 (Datentransfers)": "guideline",
|
||||
"NIST Cybersecurity Framework 2.0": "framework",
|
||||
# ... 55+ Eintraege
|
||||
}
|
||||
```
|
||||
|
||||
### Backfill-Endpoint
|
||||
|
||||
```
|
||||
POST /api/compliance/v1/canonical/controls/backfill-normative-strength?dry_run=true
|
||||
```
|
||||
|
||||
Ablauf:
|
||||
|
||||
1. Alle aktiven `obligation_candidates` laden
|
||||
2. Fuer jede Obligation den Parent-Control finden
|
||||
3. Ueber `control_parent_links` die source_regulations ermitteln
|
||||
4. Hoechsten source_type bestimmen
|
||||
5. `normative_strength` begrenzen falls noetig
|
||||
6. Bei `dry_run=false`: Aenderungen in die DB schreiben
|
||||
|
||||
### Cap-Funktion
|
||||
|
||||
```python
|
||||
def cap_normative_strength(original: str, source_type: str) -> str:
|
||||
"""
|
||||
cap_normative_strength("must", "framework") → "can"
|
||||
cap_normative_strength("should", "law") → "should"
|
||||
cap_normative_strength("must", "guideline") → "should"
|
||||
"""
|
||||
```
|
||||
|
||||
## Frontend-Anzeige
|
||||
|
||||
In der Control-Detail-Ansicht werden Obligations mit farbcodierten Badges angezeigt:
|
||||
|
||||
| normative_strength | Badge | Farbe | Bedeutung |
|
||||
|---|---|---|---|
|
||||
| `must` | **MUSS** | Rot | Gesetzliche Pflicht |
|
||||
| `should` | **SOLL** | Gelb/Amber | Empfohlen, Begruendungspflicht bei Abweichung |
|
||||
| `can` / `may` | **KANN** | Gruen | Freiwillige Best Practice |
|
||||
|
||||
## Haeufige Fragen
|
||||
|
||||
### Warum steht bei einem ENISA-Control "MUSS"?
|
||||
|
||||
**Vor dem Backfill**: Das System uebernahm die Sprache des Quelldokuments 1:1.
|
||||
ENISA schreibt intern "shall/must" weil es innerhalb seines Frameworks
|
||||
verbindlich formuliert. Fuer den Anwender ist das ENISA-Dokument aber nicht
|
||||
rechtsverbindlich.
|
||||
|
||||
**Nach dem Backfill**: ENISA-Controls zeigen maximal "KANN", es sei denn
|
||||
ein Gesetz (z.B. NIS2) referenziert dasselbe Control — dann gilt die
|
||||
gesetzliche Verbindlichkeit.
|
||||
|
||||
### Was bedeutet "Stand der Technik"?
|
||||
|
||||
NIS2 und DSGVO verweisen auf den "Stand der Technik", ohne ihn zu definieren.
|
||||
In der Praxis werden ENISA- und BSI-Dokumente als Referenz herangezogen.
|
||||
Das macht ihre Empfehlungen relevant ("SOLL"), aber nicht zu Gesetzen ("MUSS").
|
||||
|
||||
### Wie gehe ich mit unbekannten Quellen um?
|
||||
|
||||
Neue Regulierungen muessen in der `SOURCE_REGULATION_CLASSIFICATION` Map
|
||||
eingetragen werden. Der Fallback fuer unbekannte Quellen ist `framework`
|
||||
(konservativstes Ergebnis — geringste Verbindlichkeit zugewiesen).
|
||||
Reference in New Issue
Block a user