""" 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.0–10.0 | Kritisch | **30 Tage** | | 7.0–8.9 | Hoch | 90 Tage | | 4.0–6.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", }