diff --git a/obligations/capabilities.json b/obligations/capabilities.json new file mode 100644 index 00000000..da4dc2c6 --- /dev/null +++ b/obligations/capabilities.json @@ -0,0 +1,253 @@ +{ + "schema_version": "capability_layer_v1", + "model": "Modell C (docs-src/development/capability_model_v1.md)", + "note": "Capability = technische Faehigkeit (regulierungs-agnostisch). realized_by = Obligations, die sie erfuellt (n:m). guidance_basis hier KANONISCH hochgezogen aus den realisierten Obligations (die Obligation-Kopien bleiben vorerst als Legacy; Strip = Folge-Cleanup). Sicherheitsziele sind KEINE Capabilities -> cra_core.json.", + "dropped": { + "access_control": "OVERLAP (credential_confidentiality <-> sbom_confidentiality), nicht materialisiert" + }, + "candidate_capabilities_followup": [ + "automatic_update_delivery", + "update_rollback", + "trusted_update_source", + "hash_verification", + "secure_boot", + "least_functionality", + "credential_storage" + ], + "capabilities": [ + { + "capability_id": "multi_factor_authentication", + "name": "Multi-Factor Authentication", + "description": "Mehrfaktor-Authentisierung als technische Faehigkeit (Besitz/Wissen/Inhaerenz).", + "type": "technical_capability", + "realized_by": [ + "mfa_required", + "privileged_op_reauth", + "remote_access_authentication", + "remote_access_mfa", + "remote_access_user_validation_ot", + "supplier_access_auth" + ], + "realizes_count": 6, + "guidance_basis": [ + { + "source": "NIST", + "anchor": "SP 800-63B", + "role": "best_practice" + }, + { + "source": "Out-of-Band-Authentifizierung", + "anchor": "", + "role": "implementation_guidance", + "merged_from": "out_of_band_authentication" + }, + { + "source": "Hardware-basierte Authentifizierung (AAL3)", + "anchor": "", + "role": "implementation_guidance", + "merged_from": "hardware_authenticators" + }, + { + "source": "E-Mail-Authentifizierungsmechanismen (SPF/DKIM/DMARC)", + "anchor": "", + "role": "implementation_guidance", + "merged_from": "email_authentication" + }, + { + "source": "NIST", + "anchor": "IA-02", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "IA-02(1)", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "AC-17", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "SP 800-53 IA-2", + "role": "best_practice" + }, + { + "source": "BSI", + "anchor": "ICS Security Kompendium", + "role": "best_practice" + }, + { + "source": "ISO", + "anchor": "ISO 27001 A.5.19", + "role": "best_practice" + } + ], + "domains": [ + "authentication", + "remote_access" + ], + "provenance": { + "source": "cross_domain_relationships.json SHARED_CAPABILITY" + } + }, + { + "capability_id": "session_management", + "name": "Session Management", + "description": "Sichere Sitzungsverwaltung: Timeouts, Bindung, Re-Auth, Beendigung.", + "type": "technical_capability", + "realized_by": [ + "reauth_after_inactivity", + "remote_session_management", + "session_binding_management", + "temporary_remote_access_mgmt" + ], + "realizes_count": 4, + "guidance_basis": [ + { + "source": "NIST", + "anchor": "SP 800-63B 4.3", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "SP 800-53 AC-12", + "role": "best_practice" + }, + { + "source": "OWASP", + "anchor": "ASVS V3", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "AC-2(5)", + "role": "best_practice" + } + ], + "domains": [ + "authentication", + "remote_access" + ], + "provenance": { + "source": "cross_domain_relationships.json SHARED_CAPABILITY" + } + }, + { + "capability_id": "transport_encryption", + "name": "Transport Encryption", + "description": "Verschluesselter Transport (TLS, mutual-TLS, Zertifikats-Auth, VPN/Tunnel).", + "type": "technical_capability", + "realized_by": [ + "encrypted_auth_channel", + "mutual_authentication", + "reject_insecure_remote_protocols", + "remote_access_confidentiality_integrity", + "remote_access_encryption", + "service_to_service_auth", + "tls_certificate_auth" + ], + "realizes_count": 7, + "guidance_basis": [ + { + "source": "BSI", + "anchor": "TR-02102-2", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "IA-03", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "SC-8", + "role": "best_practice" + }, + { + "source": "BSI", + "anchor": "IT-Grundschutz NET.3.3", + "role": "best_practice" + }, + { + "source": "OWASP", + "anchor": "API Security Top 10", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "IA-05(2)", + "role": "best_practice" + } + ], + "domains": [ + "authentication", + "remote_access" + ], + "provenance": { + "source": "cross_domain_relationships.json SHARED_CAPABILITY" + } + }, + { + "capability_id": "code_signing", + "name": "Code & Update Signing", + "description": "Digitale Signatur + Integritaets-/Authentizitaetspruefung von Firmware/Software/Updates.", + "type": "technical_capability", + "realized_by": [ + "firmware_software_authentication", + "signed_update_integrity" + ], + "realizes_count": 2, + "guidance_basis": [ + { + "source": "NIST", + "anchor": "SI-07", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "SP 800-147 BIOS Protection", + "role": "best_practice" + } + ], + "domains": [ + "authentication", + "updates" + ], + "provenance": { + "source": "cross_domain_relationships.json SHARED_CAPABILITY" + } + }, + { + "capability_id": "security_monitoring_alerting", + "name": "Security Monitoring & Alerting", + "description": "Anomalie-/Bedrohungserkennung und Alarmierung aus Logs/Telemetrie.", + "type": "technical_capability", + "realized_by": [ + "log_monitoring_alerting", + "remote_access_threat_detection" + ], + "realizes_count": 2, + "guidance_basis": [ + { + "source": "NIST", + "anchor": "AU-6/SI-4", + "role": "best_practice" + }, + { + "source": "NIST", + "anchor": "SP 800-94", + "role": "best_practice" + } + ], + "domains": [ + "logging", + "remote_access" + ], + "provenance": { + "source": "cross_domain_relationships.json SHARED_CAPABILITY" + } + } + ] +} \ No newline at end of file diff --git a/obligations/cra.json b/obligations/cra.json index 8b21a6c6..2788d2b7 100644 --- a/obligations/cra.json +++ b/obligations/cra.json @@ -966,7 +966,10 @@ "relationships": [], "citation_anchor_ids": [], "citation_status": "pending_span_anchor", - "review_status": "draft" + "review_status": "draft", + "merged_into": "provide_security_updates", + "status": "deprecated_alias", + "merge_note": "SAME_OBLIGATION (Cross-Domain-Review). Kanonisch: provide_security_updates ((2)(c)/Art.13). ID bleibt als Alias aufloesbar; downstream provide_security_updates nutzen." }, { "id": "vuln_handling_process", diff --git a/obligations/cra_authentication.json b/obligations/cra_authentication.json index 3e16527d..3e3faf82 100644 --- a/obligations/cra_authentication.json +++ b/obligations/cra_authentication.json @@ -10281,7 +10281,11 @@ "cluster_size": 4, "llm_model": "claude-opus-4-8", "synthesis_version": "v1" - } + }, + "specializes": "software_integrity_protection", + "objective_tags": [ + "integrity" + ] } ], "relationships": [ diff --git a/obligations/cra_core.json b/obligations/cra_core.json new file mode 100644 index 00000000..323afd16 --- /dev/null +++ b/obligations/cra_core.json @@ -0,0 +1,82 @@ +{ + "schema_version": "obligation_registry_v1", + "regulation": "CRA", + "regulation_code": "CRA", + "family": "core", + "theme": "CORE Security Objectives (CRA Annex I als regulierungs-agnostische Sicherheitsziele)", + "generated_by": "materialize_capabilities.py (#5b, Modell C)", + "note": "CORE Legal Obligations = Sicherheitsziele (Modell C: KEINE eigene SecurityObjective-Klasse). DOMAIN-Obligations specializes-en hierauf. objective_tags = Vorwaerts-Kompat zu Modell B.", + "citation_status": "pending_span_anchor", + "obligations": [ + { + "id": "attack_surface_minimization", + "name": "Minimierung der Angriffsflaeche", + "family": "core", + "description": "Das Produkt minimiert seine Angriffsflaeche: unnoetige Funktionen/Ports/Dienste/Schnittstellen sind deaktiviert (Least Functionality).", + "tier": "LEGAL_MINIMUM", + "source_role": "LEGAL_BASIS", + "applicability": "universal", + "objective_tags": [ + "attack_surface" + ], + "legal_basis": [ + { + "source": "CRA", + "anchor": "Annex I Part I (2)(j)", + "citation": "limit attack surfaces, including external interfaces" + } + ], + "guidance_basis": [ + { + "source": "NIST", + "anchor": "CM-7 Least Functionality", + "role": "best_practice" + } + ], + "specialized_by": [ + "remote_access_attack_surface_min", + "component_remote_interface_security" + ], + "primary_implementation": "NIST CM-7", + "citation_status": "pending_span_anchor", + "review_status": "core_from_5b" + }, + { + "id": "software_integrity_protection", + "name": "Schutz der Software-/Firmware-Integritaet", + "family": "core", + "description": "Das Produkt schuetzt Integritaet und Authentizitaet von Software/Firmware (Manipulationserkennung, Secure Boot, Signaturpruefung, Runtime-Integritaet).", + "tier": "LEGAL_MINIMUM", + "source_role": "LEGAL_BASIS", + "applicability": "universal", + "objective_tags": [ + "integrity" + ], + "legal_basis": [ + { + "source": "CRA", + "anchor": "Annex I Part I (2)(f)", + "citation": "protect the integrity of stored, transmitted or processed data, software and configuration" + } + ], + "guidance_basis": [ + { + "source": "NIST", + "anchor": "SI-7 Software, Firmware, and Information Integrity", + "role": "best_practice" + } + ], + "specialized_by": [ + "signed_update_integrity", + "firmware_software_authentication" + ], + "realized_by_capabilities": [ + "code_signing" + ], + "primary_implementation": "NIST SI-7", + "citation_status": "pending_span_anchor", + "review_status": "core_from_5b" + } + ], + "relationships": [] +} \ No newline at end of file diff --git a/obligations/cra_remote_access.json b/obligations/cra_remote_access.json index 266bfdff..8298c00f 100644 --- a/obligations/cra_remote_access.json +++ b/obligations/cra_remote_access.json @@ -1187,7 +1187,11 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "remote_access" + "family": "remote_access", + "specializes": "attack_surface_minimization", + "objective_tags": [ + "attack_surface" + ] }, { "id": "remote_access_vuln_patch_mgmt", @@ -1465,7 +1469,8 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "remote_access" + "family": "remote_access", + "tier_note": "Bleibt BEST_PRACTICE (NICHT LM) bis Data-Act/Export-Scope sauber ist (User #5b.6). Evtl. Capability-or-Procedure statt Obligation." }, { "id": "component_remote_interface_security", @@ -1508,7 +1513,11 @@ "llm_model": "claude-opus-4-8", "synthesis_version": "v1" }, - "family": "remote_access" + "family": "remote_access", + "specializes": "attack_surface_minimization", + "objective_tags": [ + "attack_surface" + ] }, { "id": "remote_access_fallback_concept", diff --git a/obligations/cra_updates.json b/obligations/cra_updates.json index ee801ba3..a4b87b9b 100644 --- a/obligations/cra_updates.json +++ b/obligations/cra_updates.json @@ -1392,7 +1392,11 @@ "synthesis_version": "v1" }, "family": "updates", - "capability_candidate": true + "capability_candidate": true, + "specializes": "software_integrity_protection", + "objective_tags": [ + "integrity" + ] }, { "id": "trusted_update_source", diff --git a/obligations/obligation_join_keys.json b/obligations/obligation_join_keys.json index 7a5d5bec..e5838c54 100644 --- a/obligations/obligation_join_keys.json +++ b/obligations/obligation_join_keys.json @@ -1,7 +1,7 @@ { "schema_version": "obligation_join_keys_v1", "contract": "obligation_id ist der stabile Join-Key. Legal Knowledge Graph haengt citation_spans an obligation_id; Compliance Execution Graph mappt control_mapping.source_norm -> obligation_id. Interim-Bruecke = citation_units. obligation_id NIE neu vergeben (re-link).", - "count": 93, + "count": 95, "obligation_ids": [ { "obligation_id": "sbom_creation", @@ -175,6 +175,26 @@ ], "source_role": "LEGAL_BASIS" }, + { + "obligation_id": "attack_surface_minimization", + "regulation": "CRA", + "family": "core", + "tier": "LEGAL_MINIMUM", + "citation_units": [ + "Annex I Part I (2)(j)" + ], + "source_role": "LEGAL_BASIS" + }, + { + "obligation_id": "software_integrity_protection", + "regulation": "CRA", + "family": "core", + "tier": "LEGAL_MINIMUM", + "citation_units": [ + "Annex I Part I (2)(f)" + ], + "source_role": "LEGAL_BASIS" + }, { "obligation_id": "user_authentication_required", "regulation": "CRA", diff --git a/scripts/obligation_discovery/materialize_capabilities.py b/scripts/obligation_discovery/materialize_capabilities.py new file mode 100644 index 00000000..6e7f2796 --- /dev/null +++ b/scripts/obligation_discovery/materialize_capabilities.py @@ -0,0 +1,170 @@ +"""#5b — Materialisierung der Capability-Schicht (Modell C, User-Entscheidung 2026-06-26). + +Aus `cross_domain_relationships.json` (SHARED_CAPABILITY) + den 6 CRA-P1-Registries: +- `obligations/capabilities.json` — Capability-Knoten: realized_by (n:m) + guidance_basis hochgezogen. +- `obligations/cra_core.json` — 2 CORE-Obligations (Sicherheitsziele): attack_surface_minimization, + software_integrity_protection (Modell C: KEINE eigene SecurityObjective-Klasse; das Ziel IST eine + abstrakte CORE-Pflicht). +- patcht DOMAIN-Obligations in ihren Registries: `specializes` (→CORE) + `objective_tags` (Vorwärts- + Kompat zu Modell B: Tags, keine Klasse). +- markiert `vuln_remediation_patching` als deprecated_alias von `provide_security_updates` (Merge). +- `remote_access_data_export_protection` bleibt BEST_PRACTICE (Notiz: pending Data-Act-Scope). + +Deterministisch. Lokal lauffähig (nur json). Danach export_join_keys neu (inkl. cra_core). +""" +from __future__ import annotations + +import json + +REG_FILES = ["obligations/cra.json", "obligations/cra_authentication.json", + "obligations/cra_logging.json", "obligations/cra_remote_access.json", + "obligations/cra_updates.json"] + +# Cluster-capability_name -> kanonische capability_id (access_control absichtlich gedroppt: zu schwach) +CONSOLIDATE = {"mfa": "multi_factor_authentication", "session_management": "session_management", + "tls_encryption": "transport_encryption", "mutual_tls": "transport_encryption", + "tls_certificate_auth": "transport_encryption", "code_signing": "code_signing", + "anomaly_detection": "security_monitoring_alerting"} +CAP_META = { + "multi_factor_authentication": ("Multi-Factor Authentication", + "Mehrfaktor-Authentisierung als technische Faehigkeit (Besitz/Wissen/Inhaerenz)."), + "session_management": ("Session Management", + "Sichere Sitzungsverwaltung: Timeouts, Bindung, Re-Auth, Beendigung."), + "transport_encryption": ("Transport Encryption", + "Verschluesselter Transport (TLS, mutual-TLS, Zertifikats-Auth, VPN/Tunnel)."), + "code_signing": ("Code & Update Signing", + "Digitale Signatur + Integritaets-/Authentizitaetspruefung von Firmware/Software/Updates."), + "security_monitoring_alerting": ("Security Monitoring & Alerting", + "Anomalie-/Bedrohungserkennung und Alarmierung aus Logs/Telemetrie."), +} +CAP_ORDER = ["multi_factor_authentication", "session_management", "transport_encryption", + "code_signing", "security_monitoring_alerting"] + +# DOMAIN-Obligation -> (CORE-Ziel, objective_tags) +SPECIALIZES = { + "remote_access_attack_surface_min": ("attack_surface_minimization", ["attack_surface"]), + "component_remote_interface_security": ("attack_surface_minimization", ["attack_surface"]), + "signed_update_integrity": ("software_integrity_protection", ["integrity"]), + "firmware_software_authentication": ("software_integrity_protection", ["integrity"]), +} + + +def main() -> None: + regs = {f: json.load(open(f, encoding="utf-8")) for f in REG_FILES} + idx = {} + for f, r in regs.items(): + for o in r.get("obligations", []): + idx[o["id"]] = (f, o) + + # realized_by aus dem Artefakt + art = json.load(open("obligations/cross_domain_relationships.json", encoding="utf-8")) + realized = {cid: set() for cid in CONSOLIDATE.values()} + for rr in art["raw_results"]: + if rr["relation"] == "SHARED_CAPABILITY" and rr.get("capability_name") in CONSOLIDATE: + cid = CONSOLIDATE[rr["capability_name"]] + realized[cid].update([rr["a"], rr["b"]]) + + def guidance_for(ids): + seen, out = set(), [] + for oid in ids: + if oid in idx: + for g in idx[oid][1].get("guidance_basis", []): + k = (g.get("source", ""), g.get("anchor", "")) + if k not in seen: + seen.add(k) + out.append(g) + return out + + caps = [] + for cid in CAP_ORDER: + obls = sorted(realized[cid]) + name, desc = CAP_META[cid] + caps.append({"capability_id": cid, "name": name, "description": desc, + "type": "technical_capability", "realized_by": obls, "realizes_count": len(obls), + "guidance_basis": guidance_for(obls), + "domains": sorted({idx[o][1].get("family", "") for o in obls if o in idx}), + "provenance": {"source": "cross_domain_relationships.json SHARED_CAPABILITY"}}) + + capabilities = { + "schema_version": "capability_layer_v1", "model": "Modell C (docs-src/development/capability_model_v1.md)", + "note": "Capability = technische Faehigkeit (regulierungs-agnostisch). realized_by = Obligations, " + "die sie erfuellt (n:m). guidance_basis hier KANONISCH hochgezogen aus den realisierten " + "Obligations (die Obligation-Kopien bleiben vorerst als Legacy; Strip = Folge-Cleanup). " + "Sicherheitsziele sind KEINE Capabilities -> cra_core.json.", + "dropped": {"access_control": "OVERLAP (credential_confidentiality <-> sbom_confidentiality), nicht materialisiert"}, + "candidate_capabilities_followup": ["automatic_update_delivery", "update_rollback", + "trusted_update_source", "hash_verification", "secure_boot", "least_functionality", + "credential_storage"], + "capabilities": caps} + json.dump(capabilities, open("obligations/capabilities.json", "w", encoding="utf-8"), + ensure_ascii=False, indent=1) + + core = [ + {"id": "attack_surface_minimization", "name": "Minimierung der Angriffsflaeche", "family": "core", + "description": "Das Produkt minimiert seine Angriffsflaeche: unnoetige Funktionen/Ports/Dienste/" + "Schnittstellen sind deaktiviert (Least Functionality).", + "tier": "LEGAL_MINIMUM", "source_role": "LEGAL_BASIS", "applicability": "universal", + "objective_tags": ["attack_surface"], + "legal_basis": [{"source": "CRA", "anchor": "Annex I Part I (2)(j)", + "citation": "limit attack surfaces, including external interfaces"}], + "guidance_basis": [{"source": "NIST", "anchor": "CM-7 Least Functionality", "role": "best_practice"}], + "specialized_by": ["remote_access_attack_surface_min", "component_remote_interface_security"], + "primary_implementation": "NIST CM-7", "citation_status": "pending_span_anchor", + "review_status": "core_from_5b"}, + {"id": "software_integrity_protection", "name": "Schutz der Software-/Firmware-Integritaet", "family": "core", + "description": "Das Produkt schuetzt Integritaet und Authentizitaet von Software/Firmware " + "(Manipulationserkennung, Secure Boot, Signaturpruefung, Runtime-Integritaet).", + "tier": "LEGAL_MINIMUM", "source_role": "LEGAL_BASIS", "applicability": "universal", + "objective_tags": ["integrity"], + "legal_basis": [{"source": "CRA", "anchor": "Annex I Part I (2)(f)", + "citation": "protect the integrity of stored, transmitted or processed data, software and configuration"}], + "guidance_basis": [{"source": "NIST", "anchor": "SI-7 Software, Firmware, and Information Integrity", "role": "best_practice"}], + "specialized_by": ["signed_update_integrity", "firmware_software_authentication"], + "realized_by_capabilities": ["code_signing"], + "primary_implementation": "NIST SI-7", "citation_status": "pending_span_anchor", + "review_status": "core_from_5b"}, + ] + json.dump({"schema_version": "obligation_registry_v1", "regulation": "CRA", "regulation_code": "CRA", + "family": "core", + "theme": "CORE Security Objectives (CRA Annex I als regulierungs-agnostische Sicherheitsziele)", + "generated_by": "materialize_capabilities.py (#5b, Modell C)", + "note": "CORE Legal Obligations = Sicherheitsziele (Modell C: KEINE eigene SecurityObjective-Klasse). " + "DOMAIN-Obligations specializes-en hierauf. objective_tags = Vorwaerts-Kompat zu Modell B.", + "citation_status": "pending_span_anchor", "obligations": core, "relationships": []}, + open("obligations/cra_core.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1) + + dirty = set() + patched = [] + for oid, (coreid, tags) in SPECIALIZES.items(): + if oid in idx: + f, o = idx[oid] + o["specializes"] = coreid + o["objective_tags"] = tags + dirty.add(f) + patched.append(oid) + if "vuln_remediation_patching" in idx: + f, o = idx["vuln_remediation_patching"] + o["merged_into"] = "provide_security_updates" + o["status"] = "deprecated_alias" + o["merge_note"] = ("SAME_OBLIGATION (Cross-Domain-Review). Kanonisch: provide_security_updates " + "((2)(c)/Art.13). ID bleibt als Alias aufloesbar; downstream provide_security_updates nutzen.") + dirty.add(f) + if "remote_access_data_export_protection" in idx: + f, o = idx["remote_access_data_export_protection"] + o["tier_note"] = ("Bleibt BEST_PRACTICE (NICHT LM) bis Data-Act/Export-Scope sauber ist (User #5b.6). " + "Evtl. Capability-or-Procedure statt Obligation.") + dirty.add(f) + for f in dirty: + json.dump(regs[f], open(f, "w", encoding="utf-8"), ensure_ascii=False, indent=1) + + print("capabilities.json:", len(caps), "Capabilities") + for c in caps: + print(f" {c['capability_id']:30s} realizes {c['realizes_count']:2d} | guidance {len(c['guidance_basis'])} | {c['domains']}") + print("cra_core.json: 2 CORE (attack_surface_minimization, software_integrity_protection)") + print("specializes gepatcht:", patched) + print("alias: vuln_remediation_patching -> provide_security_updates") + print("dirty registries:", sorted(dirty)) + + +if __name__ == "__main__": + main()