Files
breakpilot-compliance/backend-compliance/compliance/api/cra_doc_templates.py
T
Benjamin Admin 27384aea09
CI / detect-changes (push) Successful in 11s
CI / branch-name (push) Has been skipped
CI / secret-scan (push) Has been skipped
CI / dep-audit (push) Has been skipped
CI / sbom-scan (push) Has been skipped
CI / validate-canonical-controls (push) Successful in 15s
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / guardrail-integrity (push) Has been skipped
CI / loc-budget (push) Failing after 16s
CI / go-lint (push) Has been skipped
CI / nodejs-build (push) Successful in 3m1s
CI / test-go (push) Has been skipped
CI / iace-gt-coverage (push) Has been skipped
CI / test-python-backend (push) Successful in 39s
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
feat(cra): Phase 5 — Technical Doc + DoC Generator (Annex V + VII)
Migration 122: compliance_cra_documents with versioning + approval workflow
- doc_type whitelist: doc_eu_conformity, doc_technical, doc_cvd_policy,
  doc_update_policy, doc_sbom_report
- Status state machine: draft → reviewed → approved (+ superseded)
- Snapshot generation_context for audit trail

New module cra_doc_templates.py — pure-function generators (no DB access):
- doc_eu_conformity: EU DoC structured per CRA Annex VII (all 7 mandatory fields)
- doc_technical: Technische Dokumentation per CRA Annex V
- doc_cvd_policy: ISO/IEC 29147-compliant CVD policy with SLA table
- doc_update_policy: Patch/Update policy with Lifecycle + CSAF reference
- doc_sbom_report: Latest SBOM summary with top-10 components
Returns (title, markdown_content, requirements_coverage) — coverage tracks
how many mandatory fields are filled vs placeholders.

Backend endpoints:
- POST /documents/generate — generates doc, supersedes previous version,
  increments version number atomically
- GET /documents — lists all 5 doc types (also "not_generated" stubs)
- GET /documents/{id} — full content_md
- POST /documents/{id}/approve — set status + signed_by + signed_at

Frontend:
- /documents page: 5 doc-type cards with Generate/Re-Generate buttons,
  inline Markdown preview with .md download, 2-step approval flow
  (reviewed → approved with signature)
- Optional params form: manufacturer, notified_body, security_contact
- Dashboard: +1 button (Dokumente, 7 buttons total)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:10:23 +02:00

445 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
CRA Document Templates — generates Markdown documents from project state.
Each template takes a project dict + optional context (sboms, vulnerabilities,
checks) and returns (title, markdown_content). Pure functions, no DB access.
Templates implement:
- doc_eu_conformity — EU Declaration of Conformity (CRA Annex VII)
- doc_technical — Technische Dokumentation (CRA Annex V)
- doc_cvd_policy — Coordinated Vulnerability Disclosure Policy
- doc_update_policy — Update / Patch Policy
- doc_sbom_report — SBOM-Zusammenfassung
"""
from datetime import datetime
from typing import Optional
PATH_LABELS = {
"self_assessment": "Modul A — Self-Assessment durch Hersteller",
"harmonized_standard": "Modul B — Harmonisierte Norm",
"eucc": "Modul H — EUCC-Zertifizierung",
"notified_body": "Modul C — Konformitaetsbewertung durch Notified Body",
}
CLASS_LABELS = {
"NOT_IN_SCOPE": "Nicht im CRA-Scope",
"STANDARD": "Standard-Produkt mit digitalen Elementen",
"IMPORTANT_I": "Wichtiges Produkt — Annex III Klasse I",
"IMPORTANT_II": "Wichtiges Produkt — Annex III Klasse II",
"CRITICAL": "Kritisches Produkt — Annex IV",
}
def _today() -> str:
return datetime.utcnow().strftime("%Y-%m-%d")
def _fmt_or_placeholder(v: Optional[str], placeholder: str = "[zu ergaenzen]") -> str:
return v if v else placeholder
def doc_eu_conformity(project: dict, *, manufacturer: Optional[str] = None,
notified_body: Optional[str] = None) -> tuple[str, str]:
"""EU Declaration of Conformity nach CRA Annex VII.
Pflichtfelder gemaess CRA Art. 28 + Annex VII:
1. Produkt + Modell + eindeutige Kennung
2. Hersteller + Anschrift
3. (Optional) Bevollmaechtigter
4. Konformitaetserklaerung mit Bezug auf CRA
5. Verweis auf angewandte harmonisierte Normen
6. Notified Body (falls Modul C)
7. Datum + Ort + Unterschrift
"""
name = project.get("name", "[zu ergaenzen]")
classification = project.get("cra_classification", "unbewertet")
path = project.get("conformity_path", "self_assessment")
path_label = PATH_LABELS.get(path, path)
class_label = CLASS_LABELS.get(classification, classification)
desc = project.get("description") or project.get("intended_use") or ""
title = f"EU-Konformitaetserklaerung — {name}"
content = f"""# EU-Konformitaetserklaerung (DoC)
**Gemaess Verordnung (EU) 2024/2847 — Cyber Resilience Act, Anhang VII**
---
## 1. Eindeutige Kennung des Produkts
**Produktname:** {name}
**Modell / Typ:** {_fmt_or_placeholder(project.get('primary_language'), 'Software-Produkt')}
**Repository / Source:** {_fmt_or_placeholder(project.get('repo_url'), '[Repo-URL ergaenzen]')}
**Beschreibung:** {desc or '[Produktbeschreibung ergaenzen]'}
## 2. Hersteller
**Name:** {_fmt_or_placeholder(manufacturer, '[Hersteller-Name + Adresse ergaenzen]')}
**Anschrift:** [zu ergaenzen]
**Kontakt:** [zu ergaenzen]
## 3. Bevollmaechtigter (Authorised Representative)
[Falls Hersteller nicht in EU ansaessig — sonst entfallen]
## 4. Konformitaetserklaerung
Die alleinige Verantwortung fuer die Ausstellung dieser Konformitaetserklaerung
traegt der Hersteller. Der Gegenstand der oben beschriebenen Erklaerung
**erfuellt die einschlaegigen Harmonisierungsrechtsvorschriften der Union**:
- **Verordnung (EU) 2024/2847** (Cyber Resilience Act) — vollstaendig
einschliesslich der **wesentlichen Cybersicherheitsanforderungen nach
Anhang I**.
**CRA-Klassifizierung:** {class_label}
**Angewandtes Konformitaetsbewertungsverfahren:** {path_label}
## 5. Verweise auf angewandte harmonisierte Normen und technische Spezifikationen
- Harmonisierte Norm: DIN EN 40000-1-2 (Entwurf, Stand 11/2025)
- ETSI EN 303 645 (Consumer-IoT-Cybersicherheit)
- ISO/IEC 29147 (Vulnerability Disclosure)
- ISO/IEC 30111 (Vulnerability Handling Processes)
- ENISA Technical Implementation Guidance (NIS2-Bezug)
## 6. Notified Body
{'_' if not notified_body else notified_body}
{'Identifikationsnummer: [zu ergaenzen]' if notified_body else '(keine — Self-Assessment durch Hersteller)'}
## 7. Zusatzangaben
Diese Konformitaetserklaerung wurde unterzeichnet im Namen von:
**Ort, Datum:** [zu ergaenzen], {_today()}
**Name, Funktion:** [zu ergaenzen]
**Unterschrift:** ___________________________
---
*Diese Erklaerung wurde automatisch aus dem BreakPilot CRA-Modul generiert.
Pflichtangaben sind als `[zu ergaenzen]` markiert und muessen vor Inverkehrbringen
ausgefuellt + manuell unterzeichnet werden.*
"""
coverage = {
"doc_type": "doc_eu_conformity",
"annex_anchor": "CRA Anhang VII",
"fields_required": 7,
"fields_filled": sum(1 for v in [name, classification, path, desc] if v and not str(v).startswith("[")),
}
return title, content, coverage
def doc_technical(project: dict) -> tuple[str, str, dict]:
"""Technische Dokumentation nach CRA Annex V."""
name = project.get("name", "[Produktname]")
desc = project.get("description") or project.get("intended_use") or ""
title = f"Technische Dokumentation — {name}"
content = f"""# Technische Dokumentation
**{name}**
Gemaess Verordnung (EU) 2024/2847 (CRA), Anhang V
Generiert am: {_today()}
---
## 1. Allgemeine Beschreibung des Produkts
**Produktname:** {name}
**Verwendungszweck:** {_fmt_or_placeholder(project.get('intended_use'))}
**Hauptfunktionen:** {desc or '[ergaenzen]'}
### 1.1 Technische Eigenschaften
- **Primaere Programmiersprache:** {_fmt_or_placeholder(project.get('primary_language'))}
- **Enthaelt Firmware:** {'Ja' if project.get('has_firmware') else 'Nein'}
- **Internet-Konnektivitaet:** {'Ja' if project.get('connected_to_internet') else 'Nein'}
- **Software-Updates:** {'Ja' if project.get('has_software_updates') else 'Nein'}
- **Verarbeitet personenbezogene Daten:** {'Ja' if project.get('processes_personal_data') else 'Nein'}
- **Kritische Infrastruktur:** {'Ja' if project.get('is_critical_infra_supplier') else 'Nein'}
## 2. Konzeption, Entwicklung und Produktion
### 2.1 Cybersicherheits-Risikobewertung (CRA Art. 13(2))
Risikobewertung wurde durchgefuehrt fuer die Klassifikation
**{CLASS_LABELS.get(project.get('cra_classification', ''), 'unbewertet')}**.
### 2.2 Secure-by-Design-Massnahmen
Siehe `doc_eu_conformity` Abschnitt 4 fuer die Einhaltung von Anhang I.
Detail-Mapping (40 atomare Annex-I-Anforderungen) ist im BreakPilot
CRA-Modul unter `/sdk/cra/{{projectId}}/requirements` einsehbar.
## 3. Bewertung der Konformitaet
### 3.1 Verfahren
{PATH_LABELS.get(project.get('conformity_path', ''), 'Nicht festgelegt')}
### 3.2 Klassifikation
**{CLASS_LABELS.get(project.get('cra_classification', ''), 'unbewertet')}**
Begruendung:
{chr(10).join(f'- {r}' for r in project.get('classification_rationale', []) or ['(keine Rationale erfasst)'])}
## 4. Kontaktangaben
**Hersteller:** [zu ergaenzen]
**Anschrift:** [zu ergaenzen]
**Cybersicherheits-Kontakt:** [zu ergaenzen, siehe CVD-Policy]
---
*Diese Datei wurde automatisch aus dem BreakPilot CRA-Modul generiert.
Sie ist ein Skelett — Hersteller muss alle `[ergaenzen]`-Stellen fuellen,
zusaetzliche Anhaenge (Architekturzeichnungen, Test-Berichte, SBOM) beilegen.*
"""
coverage = {
"doc_type": "doc_technical",
"annex_anchor": "CRA Anhang V",
"fields_required": 8,
"fields_filled": sum(1 for v in [
project.get("name"), project.get("intended_use"),
project.get("cra_classification"), project.get("conformity_path"),
] if v),
}
return title, content, coverage
def doc_cvd_policy(project: dict, *, security_contact: Optional[str] = None) -> tuple[str, str, dict]:
"""Coordinated Vulnerability Disclosure Policy."""
name = project.get("name", "[Produkt]")
contact = _fmt_or_placeholder(security_contact, "security@[your-domain]")
title = f"Coordinated Vulnerability Disclosure Policy — {name}"
content = f"""# Coordinated Vulnerability Disclosure (CVD) Policy
**{name}**
Generiert am: {_today()}
Diese Policy beschreibt, wie Sicherheitsforscher und Nutzer Schwachstellen
verantwortungsvoll an uns melden koennen.
---
## Wir freuen uns ueber Schwachstellen-Meldungen
Wenn Sie eine Sicherheitsluecke in **{name}** entdeckt haben, melden Sie sie
bitte vertraulich an unsere Sicherheitsstelle. Wir verpflichten uns:
- **Eingangsbestaetigung innerhalb von 5 Werktagen**
- Kommunikation auf Augenhoehe waehrend der Triage
- Koordinierte Veroeffentlichung nach Patch-Verfuegbarkeit
- Public Acknowledgement (auf Wunsch)
- Kein juristisches Vorgehen gegen Forscher, die diese Policy befolgen
## Meldekanal
- **E-Mail:** {contact}
- **PGP-Key:** [Fingerprint einfuegen]
- **Web:** https://[your-domain]/.well-known/security.txt
- **Sprachen:** Deutsch, English
## Was wir brauchen
- Eindeutige Produkt-Identifikation: {name} + Version
- Reproduktionsschritte (PoC bevorzugt)
- Auswirkungsbeschreibung (Confidentiality / Integrity / Availability)
- (Optional) Vorschlag fuer Behebung
## Reaktionszeit-Ziele (SLAs)
| Severity (CVSS) | Triage | Patch verfuegbar |
|---|---|---|
| Kritisch (9.0+) | 24h | 30 Tage |
| Hoch (7.0-8.9) | 72h | 90 Tage |
| Mittel (4.0-6.9) | 7 Tage | 180 Tage |
| Niedrig (<4.0) | 14 Tage | naechster Release-Zyklus |
## Embargo + Disclosure
Wir bitten Sicherheitsforscher um **Embargo bis Patch ausgeliefert** (typ. 90 Tage).
Nach Patch koordinieren wir die Veroeffentlichung gemeinsam (Forscher +
Hersteller). CVE-Beantragung erfolgt durch uns.
## CRA-Bezug
Diese Policy ist Pflicht gemaess **CRA Anhang I Teil 2(5)** sowie
**ISO/IEC 29147**. Bei aktiv ausgenutzten Schwachstellen melden wir nach
**CRA Art. 14(2)**:
- **24h:** Fruehwarnung an ENISA / nationale CSIRT
- **72h:** Detaillierter Bericht
- **14 Tage:** Abschlussbericht nach Behebung
---
*Diese CVD-Policy wurde automatisch generiert. Hersteller-spezifische Angaben
(Kontakt, PGP, Domain) muessen ergaenzt werden vor Veroeffentlichung.*
"""
coverage = {
"doc_type": "doc_cvd_policy",
"annex_anchor": "CRA Anhang I Teil 2(5), Art. 14",
"fields_required": 3,
"fields_filled": 1 if security_contact else 0,
}
return title, content, coverage
def doc_update_policy(project: dict) -> tuple[str, str, dict]:
"""Update / Patch Policy."""
name = project.get("name", "[Produkt]")
title = f"Update- und Patch-Policy — {name}"
content = f"""# Update- und Patch-Policy
**{name}**
Generiert am: {_today()}
---
## Lifecycle-Support
Wir verpflichten uns, **Sicherheits-Updates fuer {name}** waehrend der
gesamten erwarteten Nutzungsdauer bereitzustellen, jedoch **mindestens 5 Jahre**
ab Inverkehrbringen — gemaess CRA Anhang I, 1(4).
## Patch-SLA (Severity-basiert)
| CVSS Score | Severity | Max. Patch-Zeit |
|---|---|---|
| 9.010.0 | Kritisch | **30 Tage** |
| 7.08.9 | Hoch | 90 Tage |
| 4.06.9 | Mittel | 180 Tage |
| <4.0 | Niedrig | naechster Release |
## Update-Mechanismus
- **Distribution:** Ueber sichere HTTPS-Kanaele
- **Authentizitaet:** Alle Updates sind digital signiert (X.509)
- **Integritaet:** SHA-256-Hash vor Installation geprueft
- **Rollback:** Manuelles Rollback auf vorherige Version moeglich
- **Manipulationsschutz:** Downgrade auf verwundbare Versionen blockiert
## Benachrichtigung
Bei kritischen Updates: aktive Benachrichtigung der Endnutzer ueber
{_fmt_or_placeholder(project.get('repo_url'), '[Kommunikationskanal ergaenzen]')}
binnen 24h nach Verfuegbarkeit.
## Security Advisories
Veroeffentlicht im CSAF-Format (Common Security Advisory Framework) unter:
- https://[your-domain]/security/advisories
- Maschinenlesbar fuer SCA-Tools
## End-of-Life
Mindestens **6 Monate vor EOL** wird der Termin bekannt gegeben. Letztes
Sicherheits-Update wird mit der EOL-Ankuendigung ausgeliefert.
## CRA-Bezug
Diese Policy erfuellt CRA Anhang I, 1(4) (sichere Updates) und
2(3) (Patch-Bereitstellung), sowie Art. 13 (Lifecycle-Support).
---
*Hersteller-spezifische Angaben muessen ergaenzt werden.*
"""
coverage = {
"doc_type": "doc_update_policy",
"annex_anchor": "CRA Anhang I 1(4) + 2(3), Art. 13",
"fields_required": 4,
"fields_filled": 1 if project.get("has_software_updates") else 0,
}
return title, content, coverage
def doc_sbom_report(project: dict, latest_sbom: Optional[dict] = None) -> tuple[str, str, dict]:
"""SBOM-Zusammenfassung des aktuellsten Uploads."""
name = project.get("name", "[Produkt]")
title = f"SBOM-Bericht — {name}"
if not latest_sbom:
body = """## Kein SBOM hochgeladen
Bitte SBOM (CycloneDX oder SPDX) hochladen unter:
`/sdk/cra/{projectId}/sbom`
CRA Anhang I, Anforderung 23 verlangt ein maschinenlesbares
Software Bill of Materials fuer jedes Produkt mit digitalen Elementen.
"""
fields_filled = 0
else:
summary = latest_sbom.get("summary") or {}
sample = summary.get("sample_components") or summary.get("sample_packages") or []
sample_list = "\n".join(f"- `{c.get('name', '?')}` v{c.get('version', '?')}" for c in sample[:10])
body = f"""## Aktuelle Version
- **Datei:** {latest_sbom.get('filename', 'unknown')}
- **Format:** {latest_sbom.get('format', '?')} v{latest_sbom.get('spec_version', '?')}
- **Hochgeladen:** {latest_sbom.get('uploaded_at', '?')}
- **Komponenten:** {latest_sbom.get('component_count', 0)}
- **Scan-Status:** {latest_sbom.get('scan_status', '?')}
## Beispielkomponenten (Top 10)
{sample_list or '_(keine extrahiert)_'}
## Schwachstellen-Status
{'Scan ausstehend — wird durch separates Tool durchgefuehrt.' if latest_sbom.get('scan_status') == 'pending' else 'Siehe `/sdk/cra/{projectId}/vuln` fuer aktive Vulnerabilities.'}
"""
fields_filled = 4
content = f"""# SBOM-Bericht
**{name}**
Generiert am: {_today()}
{body}
---
## CRA-Bezug
Anhang I, Anforderung 23: maschinenlesbares SBOM (CycloneDX oder SPDX)
fuer jedes Produkt mit digitalen Elementen. Pflicht bei jedem Release zu
aktualisieren.
## Naechste Schritte
1. SBOM bei jedem Release-Update neu hochladen
2. osv.dev-Scan pruefen (Phase 3.5)
3. CVEs in `affected_components` der `/vuln`-Seite tracken
"""
coverage = {
"doc_type": "doc_sbom_report",
"annex_anchor": "CRA Anhang I, Req. 23",
"fields_required": 4,
"fields_filled": fields_filled,
}
return title, content, coverage
# =============================================================================
# Registry — for the /documents/generate endpoint
# =============================================================================
DOC_GENERATORS = {
"doc_eu_conformity": doc_eu_conformity,
"doc_technical": doc_technical,
"doc_cvd_policy": doc_cvd_policy,
"doc_update_policy": doc_update_policy,
"doc_sbom_report": doc_sbom_report,
}
DOC_TYPE_LABELS = {
"doc_eu_conformity": "EU-Konformitaetserklaerung (Annex VII)",
"doc_technical": "Technische Dokumentation (Annex V)",
"doc_cvd_policy": "CVD Policy",
"doc_update_policy": "Update- und Patch-Policy",
"doc_sbom_report": "SBOM-Bericht",
}