Compare commits

...

34 Commits

Author SHA1 Message Date
Benjamin Admin 8609b696c9 fix(ucca): CM-7 repo_scan is required evidence for attack_surface_minimization
CI / detect-changes (push) Successful in 12s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (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 / build-sha-integrity (push) Successful in 7s
CI / validate-canonical-controls (push) Successful in 5s
CI / loc-budget (push) Successful in 18s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Successful in 59s
CI / iace-gt-coverage (push) Successful in 19s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
evidence_required lists only required:true rows; repo_scan was required:false so
attack_surface_minimization surfaced config_export alone. An attack-surface scan
IS required to evidence a minimized attack surface. Adds a test pinning the curated
evidence_required set per NIST obligation (the table test only checked control count).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 09:42:12 +02:00
Benjamin Admin 207fc9cb56 Merge remote-tracking branch 'origin/main' into feat/advisor-status 2026-06-26 09:35:46 +02:00
Benjamin Admin fdaf547b06 feat(ucca): re-point NIST primary_implementation to CORE obligations (#6)
Registry materialized the generic CORE security objectives (#5b, Modell C), so
the two broad NIST controls now point at their canonical parents instead of the
domain-scoped matches:
  SI-7 -> software_integrity_protection  (CORE, Annex I (2)(f))
  CM-7 -> attack_surface_minimization    (CORE, Annex I (2)(j))
Non-breaking: the domain-scoped obligations stay valid and specialize the CORE.

SI-7 evidence = sbom + config_export (SBOM evidences component/supply-chain
integrity; config = signing/secure-boot). Export proposed_obligation_id + handler
test (2 CORE cases) updated. go test green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 09:35:46 +02:00
Benjamin Admin fa536f9714 docs: compliance_meta_model_v1.md — FROZEN v1.0 + Architektur-Freeze
User-Entscheidung: Metamodell als v1.0 einfrieren (nur META-SEMANTIK: 6 Klassen + Kanten-
Vokabular + Attribute; NICHT Registry/Capabilities/Procedures). Architektur-Freeze in Kraft:
neue Regulierung = DATEN nicht Architektur; 0 neue Objektklassen erwartet; reopen nur bei
nachgewiesenem Scheitern (Hazard/Threat = einzige bekannte künftige Öffnungs-Ursache, nur fuer
FMEA). Reuse-Metrik-KPI definiert (Wissens-Akkumulations-Beweis). Validiert gegen 5
Regulierungsarten (DSGVO/CRA/MaschVO/Data-Act/NIS2). Erster Live-Durchlauf: MaschVO.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 09:29:44 +02:00
Benjamin Admin cba066f49b Merge origin/main (f85fff43) in meta-model-validation 2026-06-26 01:09:16 +02:00
Benjamin Admin 75f7bd8de4 docs: meta_model_validation_v1.md (Phase 6) — Modell ist regulierungsunabhaengig
User-Stresstest VOR der naechsten Regulierung: passt MaschVO/Data-Act/AI-Act/NIS2 ins
6-Klassen-Modell (Obligation/Capability/Procedure/Control/Evidence + Guidance) OHNE neue
Objektklasse? Ergebnis 4x NEIN -> Compliance Meta Model steht. 2 Verfeinerungen
(realized_by Capability OPTIONAL; Risiko-Niveau/Frist/Hazard-Schwere/Risiko-Tier = Attribute,
keine Klassen). 1 Watch-Point: Hazard/Threat (erst noetig bei quantitativem FMEA-Risiko als
First-Class-Knoten, nicht fuer Compliance-Abbildung). Kein Code, keine Regulierung ingestiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 01:08:57 +02:00
Benjamin Admin f85fff4398 chore(ucca): re-sync data/obligations join-keys copy (93 -> 95)
CI / detect-changes (push) Successful in 4s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (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 / build-sha-integrity (push) Successful in 4s
CI / validate-canonical-controls (push) Successful in 4s
CI / loc-budget (push) Successful in 17s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / test-go (push) Successful in 59s
CI / iace-gt-coverage (push) Successful in 17s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
Registry grew to 95 (Capability materialization #5b added CORE obligations).
Keep the ai-sdk build-context copy current so obligation-status reflects the
live registry contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 01:02:02 +02:00
Benjamin Admin 3bcffaf52c Merge remote-tracking branch 'origin/main' into feat/advisor-status 2026-06-26 01:01:16 +02:00
Benjamin Admin 3a19affb67 ci(compliance): re-trigger scoped ai-sdk build + doc synced join-keys copy
Prior gitea push's build-ai-sdk failed on a transient registry push (arm64 built
clean on macmini; amd64 cross-compile is green) and last-build/main got poisoned
to that SHA, so a plain re-run scopes to nothing. A real touch in ai-compliance-sdk/
re-scopes the build. Also documents the synced-copy contract for
data/obligations/obligation_join_keys.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 01:00:53 +02:00
Benjamin Admin 2b985ad526 Merge origin/main (9aef5ecf) in capability-materialization 2026-06-26 00:54:43 +02:00
Benjamin Admin 4e761c1363 feat: #5b materialize capability layer (Modell C) — capabilities.json + cra_core.json
User-Entscheidung Modell C + objective_tags-Safeguard (Tags, keine Klasse). Deterministisch
via materialize_capabilities.py:
- obligations/capabilities.json: 5 Capabilities (multi_factor_authentication/session_management/
  transport_encryption/code_signing/security_monitoring_alerting), realized_by (n:m) +
  guidance_basis KANONISCH hochgezogen. access_control gedroppt (OVERLAP).
- obligations/cra_core.json: 2 CORE-Sicherheitsziele (attack_surface_minimization (2)(j)/CM-7 +
  software_integrity_protection (2)(f)/SI-7) -> fuellt den #4-NIST-Gap.
- DOMAIN specializes->CORE (remote_access_attack_surface_min, component_remote_interface_security,
  signed_update_integrity, firmware_software_authentication) + objective_tags.
- Merge: vuln_remediation_patching -> deprecated_alias von provide_security_updates.
- remote_access_data_export_protection bleibt BEST_PRACTICE (pending Data-Act-Scope).
- join_keys 93->95 (core 2). Bidirektional validiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 00:54:23 +02:00
Benjamin Admin 9aef5ecf6c Merge remote-tracking branch 'origin/main' into feat/advisor-status
CI / detect-changes (push) Successful in 6s
CI / branch-name (push) Has been skipped
CI / guardrail-integrity (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 / build-sha-integrity (push) Successful in 7s
CI / validate-canonical-controls (push) Successful in 5s
CI / loc-budget (push) Successful in 18s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-build (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go (push) Successful in 58s
CI / iace-gt-coverage (push) Successful in 18s
CI / test-python-backend (push) Has been skipped
CI / test-python-document-crawler (push) Has been skipped
CI / test-python-dsms-gateway (push) Has been skipped
2026-06-26 00:26:56 +02:00
Benjamin Admin f6c5f4e0a9 fix(ucca): SI-2 evidence = config_export + test_report
Aligns provide_security_updates -> SI-2 evidence to the curated acceptance set:
config_export (secure-update mechanism config) + test_report (patch verification).
For "provide updates" the patch-verification test is more on-point than a vuln
scan; repo_scan stays on CM-7 for attack-surface.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 00:26:29 +02:00
Benjamin Admin c72fd3eb5a Merge origin/main (Compliance endpoint+graph-loader 2341bda6) in capability-model 2026-06-26 00:24:37 +02:00
Benjamin Admin b0435f9885 docs: capability_model_v1.md (#5a) — Objektarten + Beziehungstypen, NICHT materialisiert
Schema-Papier statt capabilities.json (User-Entscheidung). Befund: die 8 SHARED_CAPABILITY-
Cluster zerfallen in Typ-1 (technische Capabilities: mfa/tls/code_signing/session/anomaly)
und Typ-2 (Sicherheitsziele: attack_surface_min/software_integrity = die #4-Gaps). Empfehlung
Modell C: Capability = EINZIGE neue Klasse; Sicherheitsziele = CORE Legal Obligations
(CORE/DOMAIN existiert bereits). Kanten-Graph (realized_by/specializes/...). guidance_basis
gehört konzeptionell an die Capability. 4 Entscheidungen offen (User). #5b Materialisierung
GEGATED auf Modell-Annahme — keine Daten verschoben.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 00:24:09 +02:00
Benjamin Admin 2341bda621 feat(ucca): adopt NIST obligation_ids (Registry Handoff #4, 10/10)
Registry filled proposed_obligation_id for the 3 NIST primary_implementation
controls: SI-7->signed_update_integrity, SI-2->provide_security_updates,
CM-7->remote_access_attack_surface_min. Adopted onto cra_nist.jsonl so the join
is now EXACT (obligation_id) instead of the coarse citation_unit fallback.
obligation-status now surfaces SI-2 under provide_security_updates; test extended.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 19:37:14 +02:00
Benjamin Admin 4634cc09d0 Merge remote-tracking branch 'origin/main' into feat/advisor-status 2026-06-25 19:31:20 +02:00
Benjamin Admin d4df1e01df feat(compliance): GET /sdk/v1/compliance/obligation-status (file-backed graph)
Vertical slice over the Compliance Execution Graph: obligation_id -> accepted
controls -> required evidence -> status. NEVER auto-asserts fulfillment - with
no evidence collection wired (MVP), a mapped obligation is "not_assessed" and
every required evidence is "missing". Fail-closed: no id -> 400; unknown id ->
unknown_obligation; mapped-but-no-control -> unmapped; graph not loaded -> 503.

- ComplianceGraphHandlers (separate from the DB-backed ObligationsHandlers):
  loads Registry join keys + accepted control mappings + evidence once at start.
- LoadComplianceGraph: candidate-path resolution across dev/container/test.
- Data plumbing: Dockerfile now COPYs data/{control_mappings,evidence_requirements,
  obligations}; data/obligations/obligation_join_keys.json is a SYNCED COPY of the
  repo-root Registry contract (re-sync on Registry growth).
- Table-driven handler test (mapped/unmapped/unknown/400 + no-fulfillment-claim).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 19:29:37 +02:00
Benjamin Admin ed31fdc0df fill: NIST primary_implementation -> obligation_id (Handoff #4, jetzt 10/10)
SI-2 -> provide_security_updates (stark, (2)(c)/Art.13) · SI-7 -> signed_update_integrity
(update-scoped) · CM-7 -> remote_access_attack_surface_min (remote-scoped). Validiert gegen
Registries (join_keys 93). GAP-BEFUND (Cross-Domain-Review): generische Parent-Obligations
software_integrity_protection + attack_surface_minimization fehlen (SI-7/CM-7 sind breiter
als die domaenen-scoped Treffer) -> Kandidaten fuer neue Obligations (User-Entscheidung).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 19:15:49 +02:00
Benjamin Admin 5412bf0ba3 Merge origin/main (NIST-Export e46e74dd) in cross-domain-discovery 2026-06-25 19:13:21 +02:00
Benjamin Admin 8a9d5e7c4d Merge remote-tracking branch 'origin/main' into feat/advisor-status 2026-06-25 19:12:41 +02:00
Benjamin Admin 01956ee690 feat: cross-domain relationship discovery — Capability-Schicht-Entwurf (CRA P1)
Stufe 1+2 der Ontologie-Entdeckung (User-Schaerfung #54): nicht Aehnlichkeit sondern
STRUKTURELLE Beziehung. 93 Obligations -> BGE-M3 -> 101 cross-family Paare -> Opus
klassifiziert in 8 Kategorien (genau eine je Paar).
- scripts/obligation_discovery/cross_domain_pairs.py (Stufe 1, key-frei)
- scripts/obligation_discovery/classify_relationships.py (Stufe 2, Opus)
- obligations/cross_domain_relationships.json: 16 SHARED_CAPABILITY -> 8 Capabilities
  (mfa/session/transport-tls/code_signing/anomaly_detection), 23 SUPPORTED_BY
  (Hubs: vuln_identification_inventory<-SBOM-Familie 5x, vuln_remediation_patching 5x),
  1 SAME_OBLIGATION (vuln_remediation_patching == provide_security_updates, MERGE-Kandidat),
  42 OVERLAP_ONLY sauber verworfen. Erstentwurf der Capability-Schicht (Phase 4).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 19:12:17 +02:00
Benjamin Admin e46e74ddbb feat(bridge): export 3 CRA->NIST controls (primary_implementation) for obligation_id
Adds SI-7/SI-2/CM-7 to controls_for_obligation_mapping.json (7 OWASP -> 10),
mapping_type=primary_implementation (the single canonical control per obligation).
proposed_obligation_id left empty for the Registry to assign. Notes aligned to the
updates family (join_keys 93): SI-2 -> provide_security_updates (strong),
SI-7 -> signed_update_integrity (partial; SI-7 broader), CM-7 ->
remote_access_attack_surface_min (partial; CM-7 broader).

Origin-only (data/tooling; backend does not load obligations/* at runtime) -> no Orca.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 18:57:17 +02:00
Benjamin Admin 63d65af41b feat(ucca): persist 3 CRA->NIST mappings (primary_implementation) + evidence
CRA Annex I Part I (2)(e)/(2)(l)/(2)(i) had no clean OWASP target (rejected:
"Mapping ueber NIST/BSI erforderlich"). Their NIST home, curated + accepted:
  (2)(e) Integritaet     -> SI-7 (Software/Firmware/Information Integrity)
  (2)(l) Sichere Updates -> SI-2 (Flaw Remediation)
  (2)(i) Angriffsflaeche -> CM-7 (Least Functionality)

New mapping_type=primary_implementation = the single canonical control per
obligation (stronger than implements/supports); related controls (SC-3(3),
RA-5, AC-6, SI-16, ...) follow later as supports.

Evidence is framework-AGNOSTIC: SI-7/SI-2/CM-7 reuse the shared evidence_type
catalog (config_export/test_report/repo_scan) - same types carry CRA, NIST,
ISO 27001, IEC 62443, BSI. (framework,control) is only the link, not the type.

obligation_id left empty: the Obligation Registry assigns it (exported via
controls_for_obligation_mapping.json), then we adopt. go test ./internal/ucca green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 18:52:35 +02:00
Benjamin Admin 8937f105ea feat(bridge): security-updates obligation cut (CRA Annex I (2)(c)/Art 13) — 9 obligations
- obligations/cra_updates.json: 9 (6 LEGAL_MINIMUM + 3 BEST_PRACTICE), Beziehungen.
  Pipeline 670->318 micro->15 review-units -> Opus-Synthese. Synthese gut kalibriert ->
  light review (KEINE Hart-Re-Tier, vs Auth/Remote-Access). out_of_scope M4/M7.
  5 capability_candidate-Marker (signed/trusted/automatic/rollback/testing) fuer
  Phase-4-Capability-Pruefung. Anker approximativ (curation.anchor_quality).
- obligation_join_keys.json: 84 -> 93 (updates 9). Alle 6 CRA-P1-Domaenen abgedeckt.
- precluster.py: updates-Scope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 18:51:09 +02:00
Benjamin Admin 1584b8fb2f feat(bridge): remote-access obligation cut (CRA Annex I) — 18 obligations
- obligations/cra_remote_access.json: 18 (5 LEGAL_MINIMUM outcomes + 13 BEST_PRACTICE),
  15 Beziehungen. Two-stage clustering 445->209 micro->27 review-units -> Opus-Synthese.
  Synthese vergab 14 LM -> key-free re-tier nach Auth-Regel (Mechanismen MFA/Session/VPN/
  insecure-protocol/OT/Wartungs-Governance/temp/data-export/component -> BEST_PRACTICE +
  supports-Kante zur Eltern-LM). out_of_scope M5/M11 = physische Maschinen-Fernsteuerung
  (MaschinenVO 2023/1230). Anker approximativ (siehe curation.anchor_quality).
- obligation_join_keys.json: 66 -> 84 (remote_access 18).
- precluster.py: remote_access-Scope.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 18:37:10 +02:00
Benjamin Admin 2301fb2122 feat(ucca): adopt obligation_id + harden join to semantic (step 3 core)
The Obligation Registry filled proposed_obligation_id (7/7) + cut the logging
family (obligations 47->66). Adopted obligation_id onto our 7 accepted CRA->OWASP
mappings; the join now prefers the EXACT obligation_id over the coarse
citation_unit (which stays as fallback for not-yet-adopted rows).

Effect: semantic coverage 2->4 (user_authentication_required,
credential_confidentiality_protection, auth_key_management,
event_logging_security_events). Befund 1 resolved: V11.2.1 crypto now sits under
credential_confidentiality_protection, not user_authentication_required.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 12:18:34 +02:00
Benjamin Admin 4aa6aa9812 Merge remote-tracking branch 'origin/main' into feat/advisor-status 2026-06-25 12:04:47 +02:00
Benjamin Admin a53d67a35a feat(bridge): logging/audit obligation cut (CRA Annex I (2)(k)) + 7/7 control mapping
- obligations/cra_logging.json: 19 obligations (6 LEGAL_MINIMUM auf (2)(k) korrekt
  verankert, 13 BEST_PRACTICE), 13 Beziehungen; out_of_scope M8/M5/M81 (AI-Act/FRT/PIN).
  Two-stage clustering (2601->1361 micro->100 review-units) -> Opus-Synthese -> Kuration.
- controls_for_obligation_mapping.json: V16.1.1/V16.3.3/V16.3.4 -> event_logging_security_events
  (Umbrella-LM; spezifische Alternativen via ASVS-Control-Text). Jetzt 7/7 gefuellt.
- obligation_join_keys.json: 47->66 obligation_ids (logging family).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 11:57:16 +02:00
Benjamin Admin 3259984d1c Fill semantic control->obligation_id (4/7; V16 pending logging cut)
V6.x->user_authentication_required, V11.2.1->credential_confidentiality_protection,
V11.7.1->auth_key_management; semantisch (NICHT CRA-Anker, die sind approximativ).
V16.x pending bis Logging-Cut. anchor_quality_note dokumentiert.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 11:44:56 +02:00
Benjamin Admin 5e3ed4071b Merge remote-tracking branch 'origin/main' into feat/obligation-aggregation 2026-06-25 11:41:00 +02:00
Benjamin Admin c090617afd Add logging scope to precluster (logging cut)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 11:40:59 +02:00
Benjamin Admin 417bcda68c feat(ucca): Advisor obligation-status Durchstich (step 3 complete)
AssessObligationStatus traverses obligation_id -> (citation_unit) -> accepted
controls -> required evidence -> status (erfuellt|offen|unklar). Evidence
presence is a callback; MVP passes nil (nothing collected yet) -> offen.
citation_spans = "pending" until the Legal-Knowledge-Graph session attaches
them. This is the vertical slice that makes the graph a product feature:
"CRA obligation fulfilled because evidence X/Y/Z is present", not "a doc exists".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 11:15:57 +02:00
Benjamin Admin 86d1473a6a feat(ucca): obligation-join loader + citation_unit bridge + coverage report
Consumes the cross-session contract obligations/obligation_join_keys.json (47
obligation_ids). Interim bridge = citation_unit (our source_norm <-> registry
citation_units), to be hardened to the stable obligation_id (field now optional
on ControlMapping).

ComputeObligationCoverage joins the 47 registry obligations to our accepted
control mappings: covered=2 (user_authentication_required, firmware_software_
authentication), mapped_rejected=3 ((2)(e) -> our OWASP mappings rejected,
route via NIST/BSI), uncovered=42. This coverage signal is the feedback to the
Obligation session for what to cut/refine next.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-25 11:10:53 +02:00
32 changed files with 13027 additions and 64 deletions
+8
View File
@@ -33,6 +33,14 @@ COPY migrations/ ./migrations/
# Copy policy files (YAML rules)
COPY policies/ ./policies/
# Copy Compliance Execution Graph data (file-backed: Registry join-key copy + accepted control
# mappings + evidence requirements) consumed by GET /sdk/v1/compliance/obligation-status.
# data/obligations/obligation_join_keys.json is a synced copy of the repo-root Registry contract
# (the Obligation Registry owns the canonical file) — re-sync it when the Registry grows.
COPY data/control_mappings/ ./data/control_mappings/
COPY data/evidence_requirements/ ./data/evidence_requirements/
COPY data/obligations/ ./data/obligations/
# Create non-root user
RUN adduser -D -u 1000 appuser
USER appuser
@@ -0,0 +1,8 @@
// Control-Mapping: CRA Annex I -> NIST SP 800-53 Rev. 5. Eine Zeile = ein Mapping (Schema: ControlMapping).
// Reviewt 2026-06-25 (benjamin): 3 accepted, mapping_type=primary_implementation (kanonische Primaer-Control je Anforderung).
// Heimat der OWASP-Rejects (2)(e)/(2)(l)/(2)(i): dort war OWASP nicht der Zielstandard ("Mapping ueber NIST/BSI erforderlich").
// related-Controls (SC-3(3), RA-5, AC-6, SI-16, ...) folgen separat als mapping_type=supports — hier nur der kanonische Einstieg.
// obligation_id (Registry-Handoff #4 adoptiert, #6 auf CORE re-pointet 2026-06-26): SI-7->software_integrity_protection (CORE (2)(f)), SI-2->provide_security_updates, CM-7->attack_surface_minimization (CORE (2)(j)). Join exakt. Die domaenen-scoped IDs (signed_update_integrity, remote_access_attack_surface_min) bleiben gueltige Obligations und zeigen per specializes->CORE auf diese Ziele.
{"source_norm": "CRA Annex I Part I (2)(e) — Integritaet", "source_role": "operational_requirement", "target_framework": "NIST SP 800-53", "target_control": "SI-7", "mapping_type": "primary_implementation", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "NIST SI-7 = Software, Firmware, and Information Integrity — kanonische Integritaetskontrolle (Signaturpruefung, Manipulationserkennung).", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Primaere Implementierung der CRA-Integritaetsanforderung; OWASP war hier kein passender Treffer. Related (spaeter, supports): SA-10, CM-14.", "version": "2026-06-25", "obligation_id": "software_integrity_protection"}
{"source_norm": "CRA Annex I Part I (2)(l) — Sichere Updates", "source_role": "operational_requirement", "target_framework": "NIST SP 800-53", "target_control": "SI-2", "mapping_type": "primary_implementation", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "NIST SI-2 = Flaw Remediation — kanonische Update-/Patch-Kontrolle.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Primaere Implementierung der CRA-Update-Anforderung. Related (spaeter, supports): RA-5, CM-3, SA-11.", "version": "2026-06-25", "obligation_id": "provide_security_updates"}
{"source_norm": "CRA Annex I Part I (2)(i) — Angriffsflaeche minimieren", "source_role": "operational_requirement", "target_framework": "NIST SP 800-53", "target_control": "CM-7", "mapping_type": "primary_implementation", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "NIST CM-7 = Least Functionality — Deaktivierung nicht benoetigter Ports/Dienste/Funktionen.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "CM-7 als Primaer-Control fuer Angriffsflaeche (nicht SC-3(3)). Related (spaeter, supports): SC-3(3), AC-6, SI-16.", "version": "2026-06-25", "obligation_id": "attack_surface_minimization"}
@@ -2,13 +2,13 @@
// Reviewt 2026-06-25 (benjamin): 7 accepted, 13 rejected. accepted = Audit-Wahrheit (Advisor nutzt acceptedOnly).
// rejected bleiben als Audit-Spur ("warum verworfen"). KEIN confidence — kuratiert = fachliche Feststellung.
// Architekturbeweis: CRA -> OWASP fuer AppSec/Auth/Crypto/Logging; Ops/Update/Attack-Surface/Integritaet -> NIST/BSI.
{"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V6.3.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V6 = Authentication.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V6 = Authentication, sauberer Treffer fuer Zugriffsschutz/Authentisierung.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V6.1.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V6 = Authentication.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V6 = Authentication, sauberer Treffer fuer Zugriffsschutz/Authentisierung.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V11.2.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V11 = Cryptography.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Korrektur von V14: V11 = Cryptography, richtiger Bereich fuer Verschluesselung.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V11.7.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V11.7 = Key Management.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Korrektur von V14: V11.7 = Key Management fuer Verschluesselung/Schluesselverwaltung.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.3.3", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.3.4", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.1.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V6.3.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V6 = Authentication.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V6 = Authentication, sauberer Treffer fuer Zugriffsschutz/Authentisierung.", "version": "2026-06-25", "obligation_id": "user_authentication_required"}
{"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V6.1.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V6 = Authentication.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V6 = Authentication, sauberer Treffer fuer Zugriffsschutz/Authentisierung.", "version": "2026-06-25", "obligation_id": "user_authentication_required"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V11.2.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V11 = Cryptography.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Korrektur von V14: V11 = Cryptography, richtiger Bereich fuer Verschluesselung.", "version": "2026-06-25", "obligation_id": "credential_confidentiality_protection"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V11.7.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V11.7 = Key Management.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "Korrektur von V14: V11.7 = Key Management fuer Verschluesselung/Schluesselverwaltung.", "version": "2026-06-25", "obligation_id": "auth_key_management"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.3.3", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25", "obligation_id": "event_logging_security_events"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.3.4", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25", "obligation_id": "event_logging_security_events"}
{"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V16.1.1", "mapping_type": "supports", "mapping_status": "accepted", "provenance": "human_curated", "rationale": "V16 = Security Logging.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V16 = Logging, sauberer Treffer fuer sicherheitsrelevante Ereignisse.", "version": "2026-06-25", "obligation_id": "event_logging_security_events"}
{"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V14.2.4", "mapping_type": "related", "mapping_status": "rejected", "provenance": "human_curated", "rationale": "Retriever-Kandidat.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V14 = Config, kein Auth — verworfen.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V14.2.4", "mapping_type": "related", "mapping_status": "rejected", "provenance": "human_curated", "rationale": "Retriever-Kandidat.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V14 = Config, Crypto gehoert zu V11 — verworfen.", "version": "2026-06-25"}
{"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung", "source_role": "operational_requirement", "target_framework": "OWASP ASVS", "target_control": "V14.3.2", "mapping_type": "related", "mapping_status": "rejected", "provenance": "human_curated", "rationale": "Retriever-Kandidat.", "reviewed_by": "benjamin", "review_date": "2026-06-25", "review_reason": "V14 = Config, Crypto gehoert zu V11 — verworfen.", "version": "2026-06-25"}
@@ -0,0 +1,10 @@
// Evidence-Requirements je NIST-SP-800-53-Control (Schema: EvidenceRequirement). Eine Zeile = eine geforderte Evidenz.
// WICHTIG: evidence_type ist FRAMEWORK-AGNOSTISCH (geteilter Katalog config_export/test_report/repo_scan/sbom/...) —
// dieselben Typen tragen CRA, NIST, ISO 27001, IEC 62443, BSI. (framework, control) ist nur der Verweis, nicht der Typ.
// Stand 2026-06-25, Basis: die 3 accepted CRA->NIST primary_implementation-Mappings (SI-7 Integritaet, SI-2 Updates, CM-7 Angriffsflaeche).
{"framework": "NIST SP 800-53", "control": "SI-7", "evidence_type": "sbom", "evidence_source": "ci", "freshness_requirement": "per_release", "required": true, "rationale": "SBOM weist die Integritaet/Herkunft der Software-Bestandteile nach (bekannte, unmanipulierte Komponenten).", "version": "2026-06-25"}
{"framework": "NIST SP 800-53", "control": "SI-7", "evidence_type": "config_export", "evidence_source": "github", "freshness_requirement": "per_release", "required": true, "rationale": "Secure-Boot-/Code-Signing-Konfiguration als Nachweis der Integritaetspruefung.", "version": "2026-06-25"}
{"framework": "NIST SP 800-53", "control": "SI-2", "evidence_type": "config_export", "evidence_source": "github", "freshness_requirement": "per_release", "required": true, "rationale": "Konfiguration des sicheren Update-/Patch-Mechanismus (signierte/automatische Updates) als technischer Nachweis.", "version": "2026-06-25"}
{"framework": "NIST SP 800-53", "control": "SI-2", "evidence_type": "test_report", "evidence_source": "ci", "freshness_requirement": "per_release", "required": true, "rationale": "Update-/Patch-Verifikationstest (CI) belegt, dass Sicherheitsupdates greifen.", "version": "2026-06-25"}
{"framework": "NIST SP 800-53", "control": "CM-7", "evidence_type": "config_export", "evidence_source": "github", "freshness_requirement": "per_release", "required": true, "rationale": "Konfiguration deaktivierter Ports/Dienste/Funktionen als Nachweis minimierter Angriffsflaeche.", "version": "2026-06-25"}
{"framework": "NIST SP 800-53", "control": "CM-7", "evidence_type": "repo_scan", "evidence_source": "scanner", "freshness_requirement": "per_release", "required": true, "rationale": "Angriffsflaechen-Scan (offene Ports/Dienste) als Nachweis tatsaechlich minimierter Angriffsflaeche.", "version": "2026-06-25"}
@@ -0,0 +1,846 @@
{
"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": 95,
"obligation_ids": [
{
"obligation_id": "sbom_creation",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_dependency_coverage",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 3(36) i.V.m. Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_format_standard",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_maintenance_update",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_completeness_verification",
"regulation": "CRA",
"family": "sbom",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "sbom_tooling_automation",
"regulation": "CRA",
"family": "sbom",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "IMPLEMENTATION"
},
{
"obligation_id": "sbom_access_provision",
"regulation": "CRA",
"family": "sbom",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "sbom_authority_provision",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 31 / Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_confidentiality",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 31(4)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "sbom_supply_chain_contracts",
"regulation": "CRA",
"family": "sbom",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "sbom_technical_documentation",
"regulation": "CRA",
"family": "sbom",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 31 i.V.m. Annex VII"
],
"source_role": "EVIDENCE"
},
{
"obligation_id": "vuln_identification_inventory",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "vuln_assessment_prioritization",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "vuln_remediation_patching",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (2) & (8)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "vuln_handling_process",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Article 13(8) & Annex VII"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "coordinated_vulnerability_disclosure",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (5)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "exploited_vuln_reporting_authorities",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Article 14 & Article 16"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "vuln_info_dissemination_users",
"regulation": "CRA",
"family": "vuln",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part II (4) & (6)"
],
"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",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(d)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "authentication_policy_documented",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "auth_exceptions_documented",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "mfa_required",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "step_up_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "privileged_op_reauth",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "strong_crypto_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(e)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "credential_lifecycle_management",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "credential_confidentiality_protection",
"regulation": "CRA",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(e)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "password_policy",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "no_default_credentials",
"regulation": "CRA",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(a)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "account_lockout_failed_attempts",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "server_side_validation",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "session_binding_management",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "reauth_after_inactivity",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "token_validation_lifecycle",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "mutual_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "revocation_check",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "encrypted_auth_channel",
"regulation": "CRA",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(e)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "tls_certificate_auth",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "service_to_service_auth",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "auth_key_management",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "biometric_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "federated_auth_assertions",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "separate_authn_authz",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "supplier_access_auth",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "personal_admin_accounts",
"regulation": "CRA",
"family": "authentication",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "firmware_software_authentication",
"regulation": "CRA",
"family": "authentication",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "event_logging_security_events",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "access_control_event_logging",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "audit_trail_admin_actions",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_integrity_immutability",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_access_control_protection",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_retention_archival",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "centralized_log_management",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_monitoring_alerting",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_data_minimization_privacy",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_format_standardization",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_timestamp_synchronization",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_availability_resilience",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_thread_safety_correctness",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "IMPLEMENTATION"
},
{
"obligation_id": "logging_library_supply_chain",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_config_management",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_governance_roles",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "incident_response_logging",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_transmission_security",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "network_traffic_logging",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_control_least_privilege",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(d)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_confidentiality_integrity",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(b)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_session_management",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_mfa",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_encryption",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "reject_insecure_remote_protocols",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_logging_audit",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(g)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_user_validation_ot",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_training",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_architecture_design",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_attack_surface_min",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(a)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_vuln_patch_mgmt",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_threat_detection",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_maintenance_governance",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "temporary_remote_access_mgmt",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_data_export_protection",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "component_remote_interface_security",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_fallback_concept",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "provide_security_updates",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(c)",
"Art. 13"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "support_period_maintenance",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 13(8)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "signed_update_integrity",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(3)(f)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "trusted_update_source",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(3)(d)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "update_testing_validation",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "update_rollback",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "automatic_updates_optout",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "update_risk_assessment",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "secure_modification_control",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "IMPLEMENTATION"
}
]
}
@@ -0,0 +1,126 @@
package handlers
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
)
// ComplianceGraphHandlers serves the read-only Compliance Execution Graph
// (Regulation -> Obligation -> Control -> Evidence) over the file-backed bridge artifacts.
// It is intentionally SEPARATE from the DB-backed ObligationsHandlers: this is the curated
// cross-session graph (Registry join keys + accepted control mappings + evidence requirements),
// loaded once at startup. Fail-closed: if the graph could not load, every request answers 503.
type ComplianceGraphHandlers struct {
joins *ucca.ObligationJoinKeys
mappings *ucca.ControlMappingSet
evidence *ucca.EvidenceRequirementSet
loadErr error
}
// NewComplianceGraphHandlers loads the graph once. Construction never fails; a load error is
// retained and surfaced as 503 per request (matches the codebase's load-warn-continue startup).
func NewComplianceGraphHandlers() *ComplianceGraphHandlers {
joins, mappings, evidence, err := ucca.LoadComplianceGraph()
return &ComplianceGraphHandlers{joins: joins, mappings: mappings, evidence: evidence, loadErr: err}
}
// LoadError exposes a startup load failure so the wiring can log a warning.
func (h *ComplianceGraphHandlers) LoadError() error { return h.loadErr }
// RegisterRoutes mounts the compliance-graph routes under /compliance.
func (h *ComplianceGraphHandlers) RegisterRoutes(r *gin.RouterGroup) {
g := r.Group("/compliance")
g.GET("/obligation-status", h.ObligationStatus)
}
type cgControlDTO struct {
Framework string `json:"framework"`
Control string `json:"control"`
MappingType string `json:"mapping_type"`
EvidenceRequired []string `json:"evidence_required"`
EvidenceStatus string `json:"evidence_status"` // missing | partial | present | none_required
}
type cgStatusResponse struct {
ObligationID string `json:"obligation_id"`
OverallStatus string `json:"overall_status"` // unknown_obligation | unmapped | not_assessed | open | met
LegalBasis []string `json:"legal_basis,omitempty"`
CitationSpans string `json:"citation_spans"` // "pending" until the Legal-KG attaches spans
Controls []cgControlDTO `json:"controls"`
Note string `json:"note,omitempty"`
}
// ObligationStatus answers GET /sdk/v1/compliance/obligation-status?obligation_id=...
//
// It NEVER asserts fulfillment automatically. With no evidence collection wired (MVP), a mapped
// obligation is "not_assessed" and every required evidence is "missing" — the honest picture is
// "required vs present evidence", not "a document exists". Fail-closed otherwise:
// - no obligation_id -> 400
// - graph not loaded -> 503
// - id not in the Registry -> 200 overall_status=unknown_obligation
// - mapped but no control yet -> 200 overall_status=unmapped
func (h *ComplianceGraphHandlers) ObligationStatus(c *gin.Context) {
if h.loadErr != nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "compliance graph unavailable", "detail": h.loadErr.Error()})
return
}
obID := strings.TrimSpace(c.Query("obligation_id"))
if obID == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "obligation_id query parameter required"})
return
}
resp := cgStatusResponse{ObligationID: obID, CitationSpans: "pending", Controls: []cgControlDTO{}}
if h.joins.FindObligation(obID) == nil {
resp.OverallStatus = "unknown_obligation"
resp.Note = "obligation_id not in the Registry join-key contract"
c.JSON(http.StatusOK, resp)
return
}
// MVP: hasEvidence=nil -> no collection wired -> all required evidence counts as missing.
st := ucca.AssessObligationStatus(h.joins, h.mappings, h.evidence, obID, nil)
resp.LegalBasis = st.LegalBasis
if len(st.Controls) == 0 {
resp.OverallStatus = "unmapped"
resp.Note = "no accepted control maps to this obligation yet"
c.JSON(http.StatusOK, resp)
return
}
for _, cs := range st.Controls {
types := make([]string, 0, len(cs.RequiredEvidence))
for _, e := range cs.RequiredEvidence {
types = append(types, e.EvidenceType)
}
resp.Controls = append(resp.Controls, cgControlDTO{
Framework: cs.Framework,
Control: cs.Control,
MappingType: cs.MappingType,
EvidenceRequired: types,
EvidenceStatus: cgEvidenceStatus(len(cs.RequiredEvidence), len(cs.MissingEvidence)),
})
}
// No fulfillment claim without real evidence collection.
resp.OverallStatus = "not_assessed"
resp.Note = "evidence collection not wired (MVP) — fulfillment not asserted"
c.JSON(http.StatusOK, resp)
}
func cgEvidenceStatus(required, missing int) string {
switch {
case required == 0:
return "none_required"
case missing == 0:
return "present"
case missing == required:
return "missing"
default:
return "partial"
}
}
@@ -0,0 +1,133 @@
package handlers
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
)
func newComplianceGraphTestRouter(t *testing.T) *gin.Engine {
t.Helper()
gin.SetMode(gin.TestMode)
h := NewComplianceGraphHandlers()
if err := h.LoadError(); err != nil {
t.Fatalf("compliance graph failed to load (candidate paths): %v", err)
}
r := gin.New()
h.RegisterRoutes(r.Group("/sdk/v1"))
return r
}
func getObligationStatus(t *testing.T, r *gin.Engine, query string) (int, cgStatusResponse) {
t.Helper()
w := httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodGet, "/sdk/v1/compliance/obligation-status"+query, nil)
r.ServeHTTP(w, req)
var resp cgStatusResponse
if w.Code == http.StatusOK {
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode body %q: %v", w.Body.String(), err)
}
}
return w.Code, resp
}
func TestObligationStatus(t *testing.T) {
r := newComplianceGraphTestRouter(t)
tests := []struct {
name string
query string
wantHTTP int
wantOverall string
wantControls bool // expect >=1 control
}{
{"missing param -> 400", "", http.StatusBadRequest, "", false},
{"unknown id -> unknown_obligation", "?obligation_id=does_not_exist", http.StatusOK, "unknown_obligation", false},
{"mapped (OWASP V6) -> not_assessed", "?obligation_id=user_authentication_required", http.StatusOK, "not_assessed", true},
{"NIST adopted (SI-2) -> not_assessed", "?obligation_id=provide_security_updates", http.StatusOK, "not_assessed", true},
{"CORE attack_surface_minimization -> CM-7", "?obligation_id=attack_surface_minimization", http.StatusOK, "not_assessed", true},
{"CORE software_integrity_protection -> SI-7", "?obligation_id=software_integrity_protection", http.StatusOK, "not_assessed", true},
{"in registry, no control -> unmapped", "?obligation_id=sbom_creation", http.StatusOK, "unmapped", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
code, resp := getObligationStatus(t, r, tt.query)
if code != tt.wantHTTP {
t.Fatalf("http %d, want %d", code, tt.wantHTTP)
}
if tt.wantHTTP != http.StatusOK {
return
}
if resp.OverallStatus != tt.wantOverall {
t.Errorf("overall_status=%q, want %q", resp.OverallStatus, tt.wantOverall)
}
if tt.wantControls && len(resp.Controls) == 0 {
t.Error("expected >=1 control")
}
if !tt.wantControls && len(resp.Controls) != 0 {
t.Errorf("expected 0 controls, got %d", len(resp.Controls))
}
if resp.CitationSpans != "pending" {
t.Errorf("citation_spans=%q, want pending", resp.CitationSpans)
}
})
}
}
// The MVP must NEVER auto-assert fulfillment: with no evidence collection wired, every required
// evidence is "missing" and the overall status stays "not_assessed".
func TestObligationStatus_NoFulfillmentClaim(t *testing.T) {
r := newComplianceGraphTestRouter(t)
code, resp := getObligationStatus(t, r, "?obligation_id=user_authentication_required")
if code != http.StatusOK {
t.Fatalf("http %d", code)
}
if resp.OverallStatus == "met" || resp.OverallStatus == "erfuellt" {
t.Fatalf("MVP must not assert fulfillment, got overall_status=%q", resp.OverallStatus)
}
for _, ctl := range resp.Controls {
if len(ctl.EvidenceRequired) > 0 && ctl.EvidenceStatus != "missing" {
t.Errorf("control %s/%s evidence_status=%q, want missing (no collection wired)", ctl.Framework, ctl.Control, ctl.EvidenceStatus)
}
}
}
// Pin the curated evidence_required set per NIST obligation. A required:false row silently
// drops from evidence_required, which the table test above (control-count only) would miss.
func TestObligationStatus_NISTEvidenceTypes(t *testing.T) {
r := newComplianceGraphTestRouter(t)
want := map[string][]string{
"attack_surface_minimization": {"config_export", "repo_scan"},
"software_integrity_protection": {"sbom", "config_export"},
"provide_security_updates": {"config_export", "test_report"},
}
for ob, exp := range want {
_, resp := getObligationStatus(t, r, "?obligation_id="+ob)
if len(resp.Controls) != 1 {
t.Fatalf("%s: want 1 control, got %d", ob, len(resp.Controls))
}
if got := resp.Controls[0].EvidenceRequired; !sameStringSet(got, exp) {
t.Errorf("%s evidence_required = %v, want %v", ob, got, exp)
}
}
}
func sameStringSet(a, b []string) bool {
if len(a) != len(b) {
return false
}
m := make(map[string]bool, len(a))
for _, x := range a {
m[x] = true
}
for _, x := range b {
if !m[x] {
return false
}
}
return true
}
+8 -1
View File
@@ -153,6 +153,12 @@ func buildRouter(cfg *config.Config, pool *pgxpool.Pool) *gin.Engine {
ragHandlers := handlers.NewRAGHandlers(corpusVersionStore)
obligationsHandlers := handlers.NewObligationsHandlersWithStore(obligationsStore)
// Compliance Execution Graph (file-backed: Registry join keys + accepted control mappings + evidence)
complianceGraphHandlers := handlers.NewComplianceGraphHandlers()
if err := complianceGraphHandlers.LoadError(); err != nil {
log.Printf("WARNING: compliance graph not loaded (obligation-status -> 503): %v", err)
}
// Regulatory News
allV2Regs, err := ucca.LoadAllV2Regulations()
if err != nil {
@@ -201,7 +207,8 @@ func buildRouter(cfg *config.Config, pool *pgxpool.Pool) *gin.Engine {
uccaHandlers, escalationHandlers, obligationsHandlers, ragHandlers,
roadmapHandlers, workshopHandlers, portfolioHandlers,
academyHandlers, trainingHandlers, whistleblowerHandlers, iaceHandler,
gapHandler, maximizerHandlers, regulatoryNewsHandlers, useCaseHandler)
gapHandler, maximizerHandlers, regulatoryNewsHandlers, useCaseHandler,
complianceGraphHandlers)
return router
}
+2
View File
@@ -30,6 +30,7 @@ func registerRoutes(
maximizerHandlers *handlers.MaximizerHandlers,
regulatoryNewsHandlers *handlers.RegulatoryNewsHandlers,
useCaseHandler *handlers.UseCaseHandler,
complianceGraphHandlers *handlers.ComplianceGraphHandlers,
) {
v1 := router.Group("/sdk/v1")
{
@@ -54,6 +55,7 @@ func registerRoutes(
registerMaximizerRoutes(v1, maximizerHandlers)
registerUseCaseRoutes(v1, useCaseHandler)
v1.GET("/regulatory-news", regulatoryNewsHandlers.GetNews)
complianceGraphHandlers.RegisterRoutes(v1)
}
}
@@ -0,0 +1,89 @@
package ucca
import (
"fmt"
"os"
"path/filepath"
"runtime"
)
// graphCallerRel resolves a path relative to THIS source file (build-time location), so the
// graph data is findable under `go test` (cwd = package dir) regardless of working directory.
// In a built container the source is gone, so cwd-relative candidates carry the load instead.
func graphCallerRel(rel string) string {
_, file, _, ok := runtime.Caller(0)
if !ok {
return ""
}
return filepath.Join(filepath.Dir(file), rel)
}
// firstExisting returns the first candidate path that exists with the requested kind (dir vs
// file). Empty candidates (e.g. unset env overrides) are skipped.
func firstExisting(candidates []string, wantDir bool) string {
for _, p := range candidates {
if p == "" {
continue
}
info, err := os.Stat(p)
if err != nil || info.IsDir() != wantDir {
continue
}
return p
}
return ""
}
// LoadComplianceGraph loads the file-backed Compliance Execution Graph: the Registry join-key
// contract (obligations/obligation_join_keys.json — owned by the Obligation session) + our
// curated, accepted control mappings + evidence requirements. Locations are resolved across
// three layouts: dev (cwd = ai-compliance-sdk/, canonical contract at ../obligations), container
// (WORKDIR /app, data/ copied in incl. a synced data/obligations/ copy) and `go test`
// (cwd = package dir, via graphCallerRel). Fail-closed: a missing/invalid source returns an
// error so the handler serves 503 — never a half-built graph.
//
// NOTE: data/obligations/obligation_join_keys.json is a SYNCED COPY of the repo-root contract
// (the canonical owner is the Obligation session). Re-sync it when the Registry grows; dev/test
// prefer the canonical repo-root path, only the container falls back to the copy.
func LoadComplianceGraph() (*ObligationJoinKeys, *ControlMappingSet, *EvidenceRequirementSet, error) {
joinPath := firstExisting([]string{
os.Getenv("BP_OBLIGATION_JOIN_KEYS"),
"../obligations/obligation_join_keys.json",
graphCallerRel("../../../obligations/obligation_join_keys.json"),
"data/obligations/obligation_join_keys.json",
graphCallerRel("../../data/obligations/obligation_join_keys.json"),
}, false)
if joinPath == "" {
return nil, nil, nil, fmt.Errorf("obligation_join_keys.json not found in any candidate path")
}
mapDir := firstExisting([]string{
os.Getenv("BP_CONTROL_MAPPINGS_DIR"),
"data/control_mappings",
graphCallerRel("../../data/control_mappings"),
}, true)
if mapDir == "" {
return nil, nil, nil, fmt.Errorf("control_mappings dir not found in any candidate path")
}
evDir := firstExisting([]string{
os.Getenv("BP_EVIDENCE_DIR"),
"data/evidence_requirements",
graphCallerRel("../../data/evidence_requirements"),
}, true)
if evDir == "" {
return nil, nil, nil, fmt.Errorf("evidence_requirements dir not found in any candidate path")
}
joins, err := LoadObligationJoinKeys(joinPath)
if err != nil {
return nil, nil, nil, fmt.Errorf("load join keys (%s): %w", joinPath, err)
}
mappings, err := LoadControlMappings(mapDir)
if err != nil {
return nil, nil, nil, fmt.Errorf("load control mappings (%s): %w", mapDir, err)
}
evidence, err := LoadEvidenceRequirements(evDir)
if err != nil {
return nil, nil, nil, fmt.Errorf("load evidence (%s): %w", evDir, err)
}
return joins, mappings, evidence, nil
}
@@ -0,0 +1,71 @@
package ucca
// ObligationStatus is the Advisor's vertical slice over the compliance graph for ONE legal
// obligation: which accepted controls satisfy it, what evidence they require, what's missing,
// and the resulting status. The point is "the required evidence is (not) present", not "a
// document exists". citation_spans is pending until the Legal-Knowledge-Graph session attaches
// them to the obligation (the upper half of the bridge).
type ObligationStatus struct {
ObligationID string `json:"obligation_id"`
LegalBasis []string `json:"legal_basis"` // the obligation's citation_units
Status string `json:"status"` // erfuellt | offen | unklar
Controls []ObligationControlStatus `json:"controls"`
CitationSpans string `json:"citation_spans"` // "pending" until the registry fills them
}
// ObligationControlStatus is one control under an obligation with its evidence picture.
type ObligationControlStatus struct {
Framework string `json:"framework"`
Control string `json:"control"`
MappingType string `json:"mapping_type"`
RequiredEvidence []EvidenceRequirement `json:"required_evidence"`
MissingEvidence []EvidenceRequirement `json:"missing_evidence"`
}
// AssessObligationStatus traverses obligation_id -> (citation_unit) -> accepted Controls ->
// required Evidence -> Status. hasEvidence reports whether a given (framework, control,
// evidence_type) is already collected; pass nil in the MVP (no collection yet) -> everything
// required is missing and the status is "offen". Unknown or unmapped obligation -> "unklar".
func AssessObligationStatus(joins *ObligationJoinKeys, mappings *ControlMappingSet, evidence *EvidenceRequirementSet, obligationID string, hasEvidence func(framework, control, evidenceType string) bool) ObligationStatus {
ob := joins.FindObligation(obligationID)
if ob == nil {
return ObligationStatus{ObligationID: obligationID, Status: "unklar", CitationSpans: "pending"}
}
st := ObligationStatus{
ObligationID: obligationID,
LegalBasis: ob.CitationUnits,
CitationSpans: "pending",
Controls: []ObligationControlStatus{},
}
ctrls := AcceptedControlsForObligation(*ob, mappings)
if len(ctrls) == 0 {
st.Status = "unklar" // no accepted control reaches it — we cannot assess
return st
}
anyMissing := false
for _, m := range ctrls {
req := evidence.RequiredFor(m.TargetFramework, m.TargetControl)
missing := make([]EvidenceRequirement, 0, len(req))
for _, e := range req {
if hasEvidence == nil || !hasEvidence(e.Framework, e.Control, e.EvidenceType) {
missing = append(missing, e)
}
}
if len(missing) > 0 {
anyMissing = true
}
st.Controls = append(st.Controls, ObligationControlStatus{
Framework: m.TargetFramework,
Control: m.TargetControl,
MappingType: m.MappingType,
RequiredEvidence: req,
MissingEvidence: missing,
})
}
if anyMissing {
st.Status = "offen"
} else {
st.Status = "erfuellt"
}
return st
}
@@ -0,0 +1,59 @@
package ucca
import "testing"
func loadGraph(t *testing.T) (*ObligationJoinKeys, *ControlMappingSet, *EvidenceRequirementSet) {
t.Helper()
joins, err := LoadObligationJoinKeys("../../../obligations/obligation_join_keys.json")
if err != nil {
t.Fatalf("join keys: %v", err)
}
maps, err := LoadControlMappings("../../data/control_mappings")
if err != nil {
t.Fatalf("mappings: %v", err)
}
ev, err := LoadEvidenceRequirements("../../data/evidence_requirements")
if err != nil {
t.Fatalf("evidence: %v", err)
}
return joins, maps, ev
}
func TestAssessObligationStatus(t *testing.T) {
joins, maps, ev := loadGraph(t)
// covered obligation, no evidence collected yet (MVP) -> offen
st := AssessObligationStatus(joins, maps, ev, "user_authentication_required", nil)
if st.Status != "offen" {
t.Errorf("want offen, got %q", st.Status)
}
if len(st.Controls) == 0 {
t.Fatal("expected controls for a covered obligation")
}
for _, c := range st.Controls {
if len(c.MissingEvidence) != len(c.RequiredEvidence) {
t.Error("MVP: all required evidence should be missing")
}
}
t.Logf("DURCHSTICH user_authentication_required: status=%s legal_basis=%v citation_spans=%s",
st.Status, st.LegalBasis, st.CitationSpans)
for _, c := range st.Controls {
t.Logf(" %s %s (%s): %d required evidence, %d missing", c.Framework, c.Control, c.MappingType, len(c.RequiredEvidence), len(c.MissingEvidence))
}
// all evidence present -> erfuellt
st2 := AssessObligationStatus(joins, maps, ev, "user_authentication_required", func(f, c, et string) bool { return true })
if st2.Status != "erfuellt" {
t.Errorf("want erfuellt with all evidence present, got %q", st2.Status)
}
// uncovered obligation (no accepted control reaches it) -> unklar
if st3 := AssessObligationStatus(joins, maps, ev, "sbom_creation", nil); st3.Status != "unklar" {
t.Errorf("uncovered sbom_creation: want unklar, got %q", st3.Status)
}
// unknown obligation_id -> unklar
if st4 := AssessObligationStatus(joins, maps, ev, "does_not_exist", nil); st4.Status != "unklar" {
t.Errorf("unknown obligation: want unklar, got %q", st4.Status)
}
}
@@ -19,13 +19,14 @@ import (
// professional statement, not an AI guess. The retriever's score lives only in the rationale
// of a candidate, never as structured truth.
type ControlMapping struct {
SourceNorm string `json:"source_norm"` // e.g. "CRA Annex I Part I (2)(c)"
SourceRole string `json:"source_role"` // source_role of the norm (operational_requirement, ...)
TargetFramework string `json:"target_framework"` // e.g. "OWASP ASVS"
TargetControl string `json:"target_control"` // e.g. "V6.3.1"
MappingType string `json:"mapping_type"` // supports | partially_supports | implements | related | contradicts
MappingStatus string `json:"mapping_status"` // candidate | accepted | rejected | superseded
Provenance string `json:"provenance"` // retriever_candidate | human_curated | rule_based
SourceNorm string `json:"source_norm"` // e.g. "CRA Annex I Part I (2)(c)"
SourceRole string `json:"source_role"` // source_role of the norm (operational_requirement, ...)
TargetFramework string `json:"target_framework"` // e.g. "OWASP ASVS"
TargetControl string `json:"target_control"` // e.g. "V6.3.1"
MappingType string `json:"mapping_type"` // primary_implementation | implements | supports | partially_supports | related | contradicts
MappingStatus string `json:"mapping_status"` // candidate | accepted | rejected | superseded
Provenance string `json:"provenance"` // retriever_candidate | human_curated | rule_based
ObligationID string `json:"obligation_id,omitempty"` // stable cross-session join key (Obligation Registry); empty until adopted, citation_unit is the interim bridge
Rationale string `json:"rationale"`
ReviewedBy string `json:"reviewed_by,omitempty"` // who decided (human or rule id)
ReviewDate string `json:"review_date,omitempty"` // YYYY-MM-DD
@@ -35,7 +36,7 @@ type ControlMapping struct {
// Allowed enum values — the deterministic "rule" layer that keeps the curated store clean.
var (
mappingTypeValues = map[string]bool{"supports": true, "partially_supports": true, "implements": true, "related": true, "contradicts": true}
mappingTypeValues = map[string]bool{"primary_implementation": true, "implements": true, "supports": true, "partially_supports": true, "related": true, "contradicts": true}
mappingStatusValues = map[string]bool{"candidate": true, "accepted": true, "rejected": true, "superseded": true}
provenanceValues = map[string]bool{"retriever_candidate": true, "human_curated": true, "rule_based": true}
)
@@ -0,0 +1,172 @@
package ucca
import (
"encoding/json"
"os"
"regexp"
"strings"
)
// ObligationKey is one entry of the Obligation Registry's cross-session contract
// (obligations/obligation_join_keys.json). obligation_id is the STABLE join key — assigned
// only by the Registry, never minted here. citation_units are the interim bridge until our
// ControlMapping adopts obligation_id directly.
type ObligationKey struct {
ObligationID string `json:"obligation_id"`
Regulation string `json:"regulation"`
Family string `json:"family"`
Tier string `json:"tier"`
CitationUnits []string `json:"citation_units"`
SourceRole string `json:"source_role"`
}
// ObligationJoinKeys is the loaded contract + a citation-unit index for the interim join.
type ObligationJoinKeys struct {
SchemaVersion string `json:"schema_version"`
Count int `json:"count"`
ObligationIDs []ObligationKey `json:"obligation_ids"`
byCitationKey map[string][]string
}
var citationRefRe = regexp.MustCompile(`\(([0-9a-zA-Z]+)\)`)
// citationUnitKey normalizes a CRA Annex I reference for the INTERIM citation_unit join, so
// our "CRA Annex I Part I (2)(c)" and the Registry's "Annex I (2)(c)" collapse to the same
// key ("i:2.c"). Interim only — superseded by the stable obligation_id once adopted.
func citationUnitKey(cu string) string {
low := strings.ToLower(cu)
part := ""
switch {
case strings.Contains(low, "part ii"):
part = "ii"
case strings.Contains(low, "part i"), strings.Contains(low, "(2)"):
part = "i" // CRA Annex I Part I = the (2)(x) essential requirements
}
var refs []string
for _, m := range citationRefRe.FindAllStringSubmatch(cu, -1) {
refs = append(refs, strings.ToLower(m[1]))
}
return part + ":" + strings.Join(refs, ".")
}
// LoadObligationJoinKeys reads the Registry contract and indexes it by citation-unit key.
func LoadObligationJoinKeys(path string) (*ObligationJoinKeys, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var o ObligationJoinKeys
if err := json.Unmarshal(raw, &o); err != nil {
return nil, err
}
o.byCitationKey = map[string][]string{}
for _, ob := range o.ObligationIDs {
for _, cu := range ob.CitationUnits {
k := citationUnitKey(cu)
o.byCitationKey[k] = append(o.byCitationKey[k], ob.ObligationID)
}
}
return &o, nil
}
// ObligationsForCitation returns the obligation_ids that join (interim) to a citation
// reference such as a control_mapping.source_norm.
func (o *ObligationJoinKeys) ObligationsForCitation(citationRef string) []string {
return o.byCitationKey[citationUnitKey(citationRef)]
}
// FindObligation returns the registry entry for an obligation_id (nil if unknown).
func (o *ObligationJoinKeys) FindObligation(obligationID string) *ObligationKey {
for i := range o.ObligationIDs {
if o.ObligationIDs[i].ObligationID == obligationID {
return &o.ObligationIDs[i]
}
}
return nil
}
// mappingReaches reports whether a control mapping reaches an obligation — EXACT via the
// adopted obligation_id (semantic, preferred), else via the interim citation_unit join (for
// not-yet-adopted rows). Once obligation_id is set, the coarse citation_unit match is ignored:
// that is how the semantic join replaces the structural one (e.g. V11.2.1 crypto no longer
// rides (2)(d) into user_authentication_required — it goes to credential_confidentiality_protection).
func mappingReaches(m ControlMapping, ob ObligationKey, citationKeys map[string]bool) bool {
if m.ObligationID != "" {
return m.ObligationID == ob.ObligationID
}
return citationKeys[citationUnitKey(m.SourceNorm)]
}
// AcceptedControlsForObligation returns our accepted control mappings that reach an obligation
// (deduped by target control), obligation_id-exact where adopted, citation_unit otherwise.
func AcceptedControlsForObligation(ob ObligationKey, mappings *ControlMappingSet) []ControlMapping {
keys := make(map[string]bool, len(ob.CitationUnits))
for _, cu := range ob.CitationUnits {
keys[citationUnitKey(cu)] = true
}
out := []ControlMapping{}
seen := map[string]bool{}
for _, m := range mappings.All {
if !m.IsAccepted() || !mappingReaches(m, ob, keys) {
continue
}
ck := m.TargetFramework + ":" + m.TargetControl
if seen[ck] {
continue
}
seen[ck] = true
out = append(out, m)
}
return out
}
// ObligationCoverage is one row of the cross-session coverage report.
type ObligationCoverage struct {
ObligationID string `json:"obligation_id"`
Family string `json:"family"`
Status string `json:"status"` // covered | mapped_rejected | uncovered
AcceptedControls []string `json:"accepted_controls"`
EvidenceCount int `json:"evidence_count"`
}
// ComputeObligationCoverage joins the Registry obligations to our control mappings — exact via
// obligation_id where adopted, else via the interim citation_unit join — and reports per
// obligation: covered (>=1 accepted control reaches it), mapped_rejected (only rejected
// mappings reach it), or uncovered. The signal back to the Obligation session.
func ComputeObligationCoverage(joins *ObligationJoinKeys, mappings *ControlMappingSet, evidence *EvidenceRequirementSet) []ObligationCoverage {
out := make([]ObligationCoverage, 0, len(joins.ObligationIDs))
for _, ob := range joins.ObligationIDs {
keys := make(map[string]bool, len(ob.CitationUnits))
for _, cu := range ob.CitationUnits {
keys[citationUnitKey(cu)] = true
}
cov := ObligationCoverage{ObligationID: ob.ObligationID, Family: ob.Family}
seen := map[string]bool{}
rejected := false
for _, m := range mappings.All {
if !mappingReaches(m, ob, keys) {
continue
}
if m.IsAccepted() {
ck := m.TargetFramework + ":" + m.TargetControl
if !seen[ck] {
seen[ck] = true
cov.AcceptedControls = append(cov.AcceptedControls, ck)
cov.EvidenceCount += len(evidence.RequiredFor(m.TargetFramework, m.TargetControl))
}
} else if m.MappingStatus == "rejected" {
rejected = true
}
}
switch {
case len(cov.AcceptedControls) > 0:
cov.Status = "covered"
case rejected:
cov.Status = "mapped_rejected"
default:
cov.Status = "uncovered"
}
out = append(out, cov)
}
return out
}
@@ -0,0 +1,61 @@
package ucca
import "testing"
func TestCitationUnitKey_Join(t *testing.T) {
// our source_norm and the registry citation_unit must collapse to the SAME key.
if citationUnitKey("CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff") != citationUnitKey("Annex I (2)(c)") {
t.Errorf("interim join broken: %q vs %q",
citationUnitKey("CRA Annex I Part I (2)(c)"), citationUnitKey("Annex I (2)(c)"))
}
// Part II must NOT collide with Part I.
if citationUnitKey("Annex I Part II (1)") == citationUnitKey("CRA Annex I Part I (2)(c)") {
t.Error("Part II must not join to Part I")
}
}
func TestLoadObligationJoinKeys(t *testing.T) {
o, err := LoadObligationJoinKeys("../../../obligations/obligation_join_keys.json")
if err != nil {
t.Fatalf("load: %v", err)
}
if o.Count != len(o.ObligationIDs) {
t.Errorf("count %d != len %d", o.Count, len(o.ObligationIDs))
}
if len(o.ObligationIDs) == 0 {
t.Fatal("empty contract")
}
if got := o.ObligationsForCitation("CRA Annex I Part I (2)(c)"); len(got) == 0 {
t.Error("expected an obligation joined to (2)(c)")
}
}
func TestObligationCoverage_Report(t *testing.T) {
joins, err := LoadObligationJoinKeys("../../../obligations/obligation_join_keys.json")
if err != nil {
t.Fatalf("join keys: %v", err)
}
maps, err := LoadControlMappings("../../data/control_mappings")
if err != nil {
t.Fatalf("mappings: %v", err)
}
ev, err := LoadEvidenceRequirements("../../data/evidence_requirements")
if err != nil {
t.Fatalf("evidence: %v", err)
}
cov := ComputeObligationCoverage(joins, maps, ev)
if len(cov) == 0 {
t.Fatal("no coverage computed")
}
byStatus := map[string]int{}
for _, c := range cov {
byStatus[c.Status]++
}
t.Logf("COVERAGE: %d Obligations | covered=%d mapped_rejected=%d uncovered=%d",
len(cov), byStatus["covered"], byStatus["mapped_rejected"], byStatus["uncovered"])
for _, c := range cov {
if c.Status != "uncovered" {
t.Logf(" %-15s %-36s controls=%v evidence=%d", c.Status, c.ObligationID, c.AcceptedControls, c.EvidenceCount)
}
}
}
+203
View File
@@ -0,0 +1,203 @@
# Capability Model v1 — Objektarten & Beziehungstypen (Schema-Papier, NICHT materialisiert)
Status: **OFFEN / Entscheidung erforderlich (2026-06-26).** Dies ist Schritt **#5a** (Papier).
Schritt **#5b** (Materialisierung: `capabilities.json`, Migration, Obligation→Capability-Links,
Guidance-Mapping, Runtime) ist **GEGATED** auf die Annahme dieses Papiers. **Es wurde noch keine
Zeile Daten verschoben.**
Baut auf [legal_obligation_layer_v1.md](legal_obligation_layer_v1.md),
[obligation_registry_v1.md](obligation_registry_v1.md) und dem Cross-Domain-Review
(`obligations/cross_domain_relationships.json`, Commit `ed31fdc0`).
---
## 0. Warum ein Papier statt `capabilities.json`
Die Plattform hat drei empirische Architektur-Sprünge gemacht:
1. **Control ≠ Wissensobjekt** → Legal Obligation (sofort implementiert, datenbestätigt).
2. **Procedure ist eigenständig** (implementiert: `cra_procedures.json`).
3. **Capabilities tauchen domänenübergreifend wieder auf** (Cross-Domain-Review).
(1) und (2) waren breit datenbelegt → sofort umgesetzt. Bei (3) ist die **Objektart selbst noch
nicht definiert.** Wir wissen NICHT genau, was eine Capability ist. Materialisieren wir jetzt,
riskieren wir, in drei Wochen festzustellen: „Attack Surface war gar keine Capability" → Umbau.
---
## 1. Der Auslöser: die 8 „Capabilities" sind NICHT eine Objektart
Der Cross-Domain-Review fand 16 `SHARED_CAPABILITY`-Paare → 8 Cluster. Bei Inspektion zerfallen
sie in **zwei verschiedene Objektarten**:
| Cluster (Opus-Benennung) | Art | Begründung |
|---|---|---|
| `mfa` | **Capability** | implementierbar als Funktion |
| `session_management` | **Capability** | implementierbar |
| `transport_encryption` (tls/mutual_tls/cert) | **Capability** | implementierbar (vom Klassifikator fein gesplittet → 1 Capability) |
| `code_signing` | **Capability** | implementierbar |
| `anomaly_detection` | **Capability** | implementierbar |
| `access_control` | **Ziel** (schwach) | abstraktes Ziel, kein Baustein — eher OVERLAP (siehe Konsolidierung) |
Dazu die **zwei Gap-„Obligations" aus Handoff #4** (NIST SI-7/CM-7 waren breiter als jeder
einzelne Treffer):
| Kandidat | Art | Begründung |
|---|---|---|
| `software_integrity_protection` (SI-7) | **Sicherheitsziel** | wird NICHT direkt gebaut; erreicht durch code_signing + hash_verification + secure_boot |
| `attack_surface_minimization` (CM-7) | **Sicherheitsziel** | erreicht durch least_functionality + Port-Deaktivierung + Interface-Reduktion |
**Kernbeobachtung (User):** Es gibt **Typ 1 — technische Fähigkeiten** (implementierbar) und
**Typ 2 — Sicherheitsziele** (nicht direkt implementierbar, durch mehrere Capabilities erreicht).
Sie in eine `capabilities.json` zu werfen wäre der Fehler.
```
Integrity Protection (Ziel) Access Protection (Ziel)
↑ erreicht durch ↑ erreicht durch
code_signing · hash_verification · mfa · session_management ·
secure_boot (Capabilities) credential_storage (Capabilities)
```
Das erklärt rückwirkend auch das **systematische Synth-Über-Tiering** (Auth 14→6, Remote 14→5):
das LLM mischte ziel-nahe Obligations mit fähigkeits-nahen Mechanismen, weil die Modellsprache
die Ebenen nicht trennte.
---
## 2. Kandidat-Objektarten
| Objektart | Definition | Diskriminator-Test |
|---|---|---|
| **Regulation** | Rechtsakt (CRA, NIS2, DSGVO, MaschVO) | „Ist es ein Gesetz/VO?" |
| **Legal Obligation** | rechtlich verankerte Pflicht. **CORE** (abstrakt, oft = Sicherheitsziel) ⊇ **DOMAIN** (spezialisiert) — die CORE/DOMAIN-Achse existiert bereits (portability). | „Steht das so (sinngemäß) im Recht? Kann ein Prüfer FEHLT/ERFÜLLT sagen?" |
| **Capability** *(NEU)* | implementierbare, **regulierungs-agnostische** technische Funktion, als Einheit baubar & testbar | „Kann ein Hersteller GENAU DAS bauen/konfigurieren?" → ja |
| **Procedure** | wiederholbarer operativer Prozess, der eine Capability ausbringt/erhält (bereits modelliert) | „Ist es eine Tätigkeit/ein Ablauf?" |
| **Control** | testbare Prüfanweisung | „Kann man es prüfen (pass/fail)?" |
| **Evidence** | Nachweis-Artefakt (Audit-Log, SBOM, Release Notes) | „Ist es ein Beleg-Dokument/Datum?" |
| **Guidance** *(quer)* | externe Empfehlung WIE (NIST/OWASP/ENISA/BSI). **Nicht-bindend.** | „Beschreibt es eine empfohlene Umsetzung, kein Primärrecht?" |
---
## 3. DER ZENTRALE KNACKPUNKT: Ist „Security Objective" eine eigene Klasse?
### Modell A — flach (Objektive = Obligations)
```
Regulation → Legal Obligation → Capability → Procedure → Control → Evidence
```
Sicherheitsziele sind einfach **CORE Legal Obligations**; domänen-scoped Pflichten sind DOMAIN-
Obligations, die per `specializes` an die CORE hängen.
### Modell B — mit eigener Security-Objective-Klasse
```
Regulation → Legal Obligation → Security Objective → Capability → Procedure → Control → Evidence
```
### Modell C — hybrid (Capability als einzige neue Klasse) ← **EMPFEHLUNG**
```
Regulation → Legal Obligation (CORE ⊇ DOMAIN) --realized_by--> Capability → Procedure → Control → Evidence
▲ ▲
└── specializes (DOMAIN→CORE) └── described_by ── Guidance (NIST/OWASP/…)
```
**Empfehlung: Modell C.** Begründung aus den Daten:
- Die „Sicherheitsziele" (`software_integrity_protection`, `attack_surface_minimization`, CIA,
access-protection) **SIND im CRA bindende Pflichten** (Annex I (2)(am) ist Primärrecht). Ein
Sicherheitsziel ist also eine **CORE Legal Obligation**, kein neuer Objekttyp.
- Die **CORE/DOMAIN-Achse existiert schon** (portability_core ⊇ health/data_act). `attack_surface_
minimization` (CORE) ⊇ `remote_access_attack_surface_min` (DOMAIN) ist exakt dasselbe Muster.
→ keine neue Klasse, nur konsequente Nutzung des Vorhandenen.
- **Genau EINE** wirklich neue Klasse (**Capability**) ist sparsam und niedrig-risiko.
- Modell B verdoppelt die normative Ebene (Obligation vs Objective), die im CRA 1:1 zusammenfällt
→ Klasse, die niemand sauber befüllt.
**Konsequenz für die #4-Gap:** `software_integrity_protection` + `attack_surface_minimization`
werden als **CORE Legal Obligations** angelegt (nicht als Capabilities), und die domänen-scoped
Treffer (`signed_update_integrity`, `remote_access_attack_surface_min`) `specializes` → CORE.
NIST SI-7/CM-7 mappen dann `primary_implementation` auf die CORE.
**Offen für den User:** Modell C akzeptieren? Oder ist die regulierungs-AGNOSTISCHE Vereinheitlichung
(eine „confidentiality" über CRA+NIS2+ISO) so wertvoll, dass „Security Objective" doch eine eigene
Klasse verdient (Modell B)? Das ist die einzige wirklich offene Architekturentscheidung.
---
## 4. Beziehungstypen — das Modell ist ein GRAPH, keine flache Ebene
Der Review fand **vier distinkte Cross-Domain-Strukturrelationen** (nicht eine):
`SUPPORTED_BY` 23 · `SHARED_CAPABILITY` 16 · `SHARED_EVIDENCE` 7 · `SHARED_PROCEDURE` 5 (+ 1 Merge).
Das ist kein Baum. Vorgeschlagenes gerichtetes Kanten-Vokabular:
| Kante | von → nach | aus Review-Relation |
|---|---|---|
| `specializes` | DOMAIN-Obligation → CORE-Obligation | (SUPPORTED_BY, Spezialfall) |
| `contributes_to` | Obligation → Obligation | (SUPPORTED_BY, Beitrag) |
| `realized_by` | Obligation → Capability | (SHARED_CAPABILITY ⇒ 2 Obl. teilen 1 Capability) |
| `deployed_via` | Capability → Procedure | (SHARED_PROCEDURE) |
| `verified_by` | Procedure/Capability → Control | — |
| `produces` | Procedure → Evidence | (SHARED_EVIDENCE ⇒ 2 Obl. teilen 1 Nachweis) |
| `described_by` | Capability → Guidance | (guidance_basis) |
| `same_as` | Obligation ↔ Obligation | (SAME_OBLIGATION, Merge) |
`SHARED_CAPABILITY`/`SHARED_EVIDENCE`/`SHARED_PROCEDURE` sind also **keine Obligation-Obligation-
Kanten**, sondern Belege, dass zwei Obligations **denselben Knoten einer tieferen Ebene** teilen
(Capability / Evidence / Procedure). Genau das ist der Mehrwert gegenüber „sieht ähnlich aus".
---
## 5. Die 8 offenen Fragen (Antwort + Tradeoff)
1. **Was ist eine Capability?** Eine implementierbare, regulierungs-agnostische technische Funktion,
als Einheit baubar/konfigurierbar/testbar (MFA, TLS, Code Signing, Session-Mgmt, Anomaly-Detection).
2. **Unterschied zur Obligation?** Obligation = rechtliche Pflicht (WAS das Recht verlangt, regulierungs-
verankert, normativ). Capability = technisches Mittel (WIE man sie erfüllt, agnostisch). n:m.
3. **Unterschied zum Security Objective?** Ziel = erwünschter Sicherheitszustand (CIA, attack-surface-min);
Capability = Mittel dorthin. **Empfehlung (Modell C):** das Ziel ist eine CORE Obligation, kein
eigener Typ → Unterschied reduziert sich auf Obligation(abstrakt) vs Capability(Mittel).
4. **Wann Guidance?** Wenn es eine **nicht-bindende externe Empfehlung zur Umsetzung** ist (NIST AC-12,
OWASP ASVS V6). Hängt an der **Capability** (meist) bzw. Procedure — NIE als `legal_basis` einer
LEGAL_MINIMUM-Obligation (Primärrecht-Regel bleibt).
5. **Wann Procedure?** Wenn es ein **wiederholbarer operativer Ablauf** ist, der eine Capability
ausbringt/erhält (MFA konfigurieren, Schlüssel rotieren, Patch-Zyklus fahren).
6. **Capability → mehrere Obligations?** **JA, belegt:** `mfa` erfüllt 6 Obligations (auth+remote),
`code_signing` 2 (auth+updates). n:m.
7. **Obligation → mehrere Capabilities?** **JA, belegt:** access-protection ← mfa + session_management
+ credential_storage. n:m.
8. **Wo hängen NIST/OWASP/ENISA/BSI?** Primär an der **Capability** (sie beschreiben deren Umsetzung),
teils an Procedure. **Das erklärt, warum die über-getierten BP-Obligations `guidance_basis` trugen:
sie waren in Wahrheit Capabilities.** Sauberer Sitz von `guidance_basis` = Capability.
---
## 6. Worked Examples (4 Domänen, echte IDs)
**Authentication** — `user_authentication_required` (Obl, CORE: access-protection)
`--realized_by-->` { `mfa`, `session_management`, `credential_storage` } (Capabilities)
`--described_by-->` NIST IA-2 / OWASP ASVS V6 (Guidance).
**Updates** — `provide_security_updates` (Obl, LEGAL_MINIMUM) `--realized_by-->`
{ `code_signing` (= signed_update_integrity-Capability), `automatic_update_delivery`, `rollback` }
— exakt die `capability_candidate`-Marker aus `cra_updates.json`.
**Remote Access** — CORE `attack_surface_minimization` (NEU, = CM-7-Ziel) `⊇ specializes ⊇`
`remote_access_attack_surface_min` (DOMAIN) `--realized_by-->` { `least_functionality`, `port_disabling` }.
**SBOM** — Sonderfall: die SBOM-Familie ist im Cross-Review der **Evidence-/Procedure-Input** für
`vuln_identification_inventory` (5× SUPPORTED_BY-Hub), weniger Capability. → bestätigt, dass nicht
jede Domäne primär Capabilities beisteuert; manche liefern **Evidence**. Stützt den Graph-Charakter.
---
## 7. Entscheidung, die ich vom User brauche (vor #5b)
1. **Modell C** (Capability = einzige neue Klasse; Sicherheitsziele = CORE-Obligations) — akzeptiert?
Oder Modell B (Security Objective als eigene Klasse für regulierungs-agnostische Vereinheitlichung)?
2. **Kanten-Vokabular** aus §4 — so einfrieren?
3. **`guidance_basis` wandert konzeptionell an die Capability** — einverstanden? (Bricht nichts sofort;
die Obligations behalten den Verweis bis #5b.)
4. Erst danach **#5b**: `capabilities.json` (capability_id, fulfills_obligations[] via `realized_by`,
guidance_basis hochgezogen), die 2 CORE-Gap-Obligations, der Merge (`vuln_remediation_patching` ≈
`provide_security_updates`), und die 2 Remote-Grenzfälle final tiern.
## 8. Bewusst NICHT in #5a (gegated)
Keine `capabilities.json`, keine Migration, kein Obligation-Rewrite, kein Guidance-Move, kein Runtime.
Erst Modell-Annahme, dann Daten. „Erst das Schema, dann verschieben."
@@ -0,0 +1,108 @@
# Compliance Operating System — Meta Model v1.0 (FROZEN)
> **STATUS: EINGEFROREN (2026-06-26). ARCHITEKTUR-FREEZE IN KRAFT.**
> Ab v1.0 dürfen neue Regulierungen das Modell **nicht mehr verändern** — sie müssen sich
> **einfügen**. Das Modell wird nur wieder geöffnet, wenn eine Regulierung **nachweislich
> scheitert** (eine Anforderung lässt sich ohne neue Objektklasse nicht abbilden).
> Validiert gegen 5 Regulierungsarten: DSGVO · CRA · MaschVO · Data Act · NIS2.
Konsolidiert + friert ein: [legal_obligation_layer_v1.md](legal_obligation_layer_v1.md),
[capability_model_v1.md](capability_model_v1.md) (Modell C), [meta_model_validation_v1.md](meta_model_validation_v1.md).
Was hier eingefroren wird, ist **ausschließlich die Meta-Semantik** — NICHT die Registry, NICHT die
Capabilities-Liste, NICHT die Procedures (diese wachsen als Daten weiter).
## 1. Objektklassen (6 + Guidance) — eingefroren
| Klasse | Was | Regulierungs-Bindung |
|---|---|---|
| **Regulation** | Rechtsakt | — |
| **Legal Obligation** | rechtlich verankerte Pflicht; **CORE ⊇ DOMAIN** | regulierungs-anchored |
| **Capability** | implementierbare technische Faehigkeit (OPTIONAL für eine Obligation) | **agnostisch** (n:m über Regulierungen) |
| **Procedure** | wiederholbarer operativer Prozess | agnostisch |
| **Control** | testbare Prüfanweisung | agnostisch |
| **Evidence** | Nachweis-Artefakt | agnostisch |
| **Guidance** *(quer)* | externe nicht-bindende Empfehlung (NIST/OWASP/ISO/BSI) — hängt an der **Capability** | agnostisch |
## 2. Die Kette + kanonisches Kanten-Vokabular — eingefroren
```
Regulation
↓ definiert
Legal Obligation (CORE ⊇ DOMAIN)
↓ realized_by (OPTIONAL — rein prozessuale/dokumentarische Obligations überspringen Capability)
Capability
↓ deployed_via (alias: operationalized_by)
Procedure
↓ verified_by
Control
↓ produces (alias: produces_evidence_for)
Evidence
→ Produktstatus
```
Kanten (gerichtet, eingefroren):
`specializes` (DOMAIN→CORE) · `realized_by` (Obligation→Capability) · `deployed_via` (Capability→Procedure) ·
`verified_by` (Procedure/Capability→Control) · `produces` (Procedure→Evidence) · `described_by` (Capability→Guidance) ·
`supports` / `depends_on` / `contributes_to` (Obligation↔Obligation) · `same_as` (Merge/Alias).
**Das Modell ist ein GRAPH, kein Baum** (n:m an realized_by, supports, produces).
## 3. Attribute (KEINE Klassen) — eingefroren
`applicability` · `tier` (LEGAL_MINIMUM/BEST_PRACTICE) · `legal_basis` (Primärrecht) ·
`guidance_basis` (NIST/OWASP/…, kanonisch an der Capability) · `objective_tags`
(integrity/confidentiality/attack_surface/… — Vorwärts-Kompat zu einer späteren Security-Objective-
Klasse) · `risk_level` · `deadline` · **`hazard` (Attribut, KEINE Klasse)**.
**Watch-Point (bewusste Nicht-Klasse):** `Hazard/Threat` bleibt ein Risiko-Treiber-Attribut. Es wird
*erst dann* eine eigene Klasse, wenn quantitatives Risiko (FMEA: Hazard→Risiko→Maßnahme) als
First-Class-Graph-Knoten modelliert werden soll — das ist die einzige bekannte künftige Öffnungs-Ursache.
## 4. Architektur-Freeze-Policy
1. **Neue Regulierung = Daten, nicht Architektur.** Sie läuft durch `Parser → Discovery-Pipeline →
Review → Registry` und fügt Obligations/Capabilities/Procedures/Evidence hinzu.
2. **Eine neue Objektklasse ist eine Architektur-Änderung** und erfordert explizite Wieder-Öffnung +
Begründung (nachgewiesenes Scheitern der Abbildung). Default-Erwartung: **0 neue Klassen.**
3. Verfeinerungen an Attributen (neues `*_tag`, neues risk-Attribut) sind erlaubt, solange keine
neue Klasse entsteht.
## 5. Reuse-Metrik (KPI je neuer Regulierung) — der Wissens-Akkumulations-Beweis
Für jede neue Regulierung gemessen (Baseline = der jeweils vorhandene Bestand):
| Kennzahl | Soll/Bedeutung |
|---|---|
| **Neue Objektklassen** | **= 0** (Invariante; sonst Freeze gebrochen) |
| Neue Capabilities | additiv (z.B. +8) |
| **Wiederverwendete Capabilities %** | Kern-KPI (z.B. NIS2 ~7080 % erwartet) |
| Wiederverwendete Procedures % | (z.B. 58 %) |
| Wiederverwendete Evidence % | (z.B. 81 %) |
| Neue Obligations | additiv (z.B. +42) |
Zielaussage: *„Beim AI Act: 0 neue Objektklassen, 12 neue Capabilities, 41 neue Obligations,
78 % der vorhandenen Capabilities wiederverwendet."* → belegt, dass das System **Wissen akkumuliert**,
statt je Regulierung neu gebaut zu werden. (Tool zur Berechnung folgt mit dem ersten Live-Durchlauf.)
## 6. Der Burggraben (warum das mehr ist als ein Advisor / RAG)
Der Kunde denkt nicht in Artikeln, sondern: *„Wir haben Remote-Updates / signierte Firmware / einen
Vuln-Prozess."* Über die Capability-Schicht bildet das System diese Aussagen auf **alle betroffenen
Obligations mehrerer Regulierungen** ab und beantwortet die eigentliche Frage aus dem Kundengespräch:
> **„Habe ich das Gesetz richtig verstanden, und reicht das, was wir umgesetzt haben?"**
Das ist regel-/wissensgestütztes Reasoning über ein gemeinsames Modell — keine RAG-Aufgabe.
(Die Reasoning-Session hält dabei die Welt-Grenze: `ClaimCoverage` „potenziell relevant" ⊥
`ComplianceStatus` „erfüllt aus Nachweisen".)
## 7. Was NICHT eingefroren ist (wächst weiter als Daten)
Registry-Inhalte (Obligations je Regulierung), die Capabilities-Liste, Procedures, Evidence-Typen,
Applicability-Prädikate, Citation-Spans. Diese Schicht ist **Wissensaufbau** — explizit erwünschtes
Wachstum gegen das eingefrorene Modell.
## 8. Erster Live-Durchlauf (User-Priorität nach Informationswert)
1. **MaschVO** ⭐⭐⭐⭐⭐ — beweist „Compliance-OS ≠ Cybersecurity" (physische Safety, CE, Restgefahren).
2. **NIS2** ⭐⭐⭐⭐ — misst maximalen Capability-Reuse (erwartet 7080 %).
3. **AI Act** ⭐⭐⭐⭐ — Risikoklassifizierung/Governance, vermutlich 0 neue Klassen.
4. **Data Act** ⭐⭐⭐ — bestätigt „Capability optional".
@@ -0,0 +1,159 @@
# Meta-Model Validation v1 — Ist das Modell regulierungsunabhängig?
Status: **Phase 6 — Meta-Validierung (2026-06-26). KEIN neues Coding, KEINE Regulierung ingestiert.**
Dieses Dokument ist der Stresstest VOR der nächsten Regulierung. Baut auf
[capability_model_v1.md](capability_model_v1.md) (Modell C, #5b materialisiert) +
[legal_obligation_layer_v1.md](legal_obligation_layer_v1.md).
## Die eigentliche Frage
Nicht „welche Regulierung kommt als nächstes?", sondern:
> **Kann eine völlig neue Regulierung in dieses Modell eingeordnet werden, OHNE eine neue
> Objektklasse einzuführen?**
Wenn ja für MaschVO + Data Act + AI Act + NIS2 → das ist kein CRA-Graph mehr, sondern ein
**Compliance Meta Model**. Ab dann bringt jede Regulierung primär *Daten*, nicht *Architektur*.
## Das zu testende Modell (6 Klassen + Attribute, KEINE weitere Klasse erlaubt)
```
Regulation
↓ definiert
Legal Obligation (CORE ⊇ DOMAIN; tier=LEGAL_MINIMUM/BEST_PRACTICE; objective_tags[]; applicability)
↓ realized_by (OPTIONAL)
Capability (regulierungs-agnostische technische Faehigkeit; guidance_basis hier)
↓ deployed_via
Procedure
↓ verified_by
Control
↓ produces
Evidence
```
Quer: **Guidance** (NIST/OWASP/ISO/BSI) hängt an der Capability. **Attribute** (keine Klassen):
`tier`, `objective_tags`, `applicability`, später `deadline`/`risk_level`/`severity`.
Kanten: realized_by · specializes · contributes_to · deployed_via · verified_by · produces · described_by · same_as.
---
## Test 1 — Maschinenverordnung (EU) 2023/1230
| Modell-Klasse | MaschVO-Inhalt |
|---|---|
| Legal Obligation (CORE) | `hazard_minimization` (Sicherheits-Analogon zu attack_surface_minimization), `safe_control_systems`, `machine_risk_assessment`, `ce_conformity`, `instructions_for_use` — exakt die `machine_*`-Obligations, die die Reasoning-Session bereits unabhängig geprägt hat. |
| Capability | **physische Sicherheitsfunktionen**: `emergency_stop`, `safety_interlock`, `two_hand_control`, `guarding`, `safe_torque_off`. → die **Capability-Klasse generalisiert von Cyber auf physische Safety** (gleiche Klasse, andere Domäne). |
| Procedure | Risikobeurteilung (ISO 12100), CE-Konformitätsbewertung. |
| Evidence | Technische Unterlagen, Risikobeurteilungsbericht, Konformitätserklärung. |
**Stress-Punkt:** „**Hazard**" (mechanisch/elektrisch/thermisch) = Schadensquelle — weder Obligation
noch Capability. Kandidat für eine neue Klasse? → **Nein, für die Repräsentation:** ein Hazard ist
ein *Risiko-Treiber* (Attribut/Applicability der Risikobeurteilungs-Procedure); eine Capability
*mitigiert* einen Hazard, genau wie eine Cyber-Capability eine (implizite) Bedrohung kontert. `PL/SIL`
= Attribut (wie `tier`). **Hazard wird erst dann eine Klasse, wenn ihr quantitatives FMEA-Risiko als
First-Class-Graph-Knoten wollt** (vgl. [[project-fmea-safety-direction]]) — nicht für Compliance-Abbildung.
**Verdikt: KEINE neue Klasse.** (Stärkstes Ergebnis: die Capability-Klasse trägt von Cyber zu Safety.)
---
## Test 2 — Data Act (EU) 2023/2854
| Modell-Klasse | Data-Act-Inhalt |
|---|---|
| Legal Obligation | `data_act_data_access_by_design`, `data_act_user_data_access`, `data_portability_switching`, `fair_contract_terms` (FRAND), `interoperability` — deckt sich mit den `data_act_*`-Obligations der Reasoning-Session. |
| Capability | `data_export_api`, `interoperability_interface`, `access_control` (**Reuse**). ABER: `fair_contract_terms` hat **KEINE technische Capability**. |
| Procedure | FRAND-Klauseln entwerfen; Switching-Prozess. |
| Evidence | Vertrag/Klauselwerk, API-Doku. |
**Stress-Punkt:** **vertraglich-rechtliche Pflichten** (FRAND, Verbot unfairer Klauseln) haben kein
technisches Mittel. → Beleg, dass **`realized_by Capability` OPTIONAL ist**: manche Obligations werden
rein über **Procedure (Entwurf) + Evidence (Vertrag)** erfüllt. Das ist KEINE neue Klasse — wir haben
es schon gesehen (SBOM-Familie war Evidence-/Procedure-lastig, kaum Capability).
**Verdikt: KEINE neue Klasse.** Verfeinerung: Capability ist optional (Obligation → Procedure → Evidence
ohne Capability ist gültig).
---
## Test 3 — AI Act (EU) 2024/1689
| Modell-Klasse | AI-Act-Inhalt |
|---|---|
| Legal Obligation | `ai_risk_management_system`, `ai_data_governance`, `ai_technical_documentation`, `ai_transparency_disclosure`, `human_oversight`, `accuracy_robustness`, `fundamental_rights_assessment`. |
| Capability | `event_logging` (**Reuse**!), `bias_detection`, `accuracy_testing`, `human_oversight_mechanism`, `ai_transparency_notice`. |
| Procedure | Risikomanagement-Prozess; FRIA-Durchführung; Human-Oversight-Prozess. |
| Evidence | Technische Dokumentation, FRIA-Bericht, Logs. |
**Stress-Punkt:** **Risiko-Klassifikation** (unacceptable/high/limited/minimal) bestimmt, WELCHE
Obligations gelten. → das ist **Applicability** (existiert bereits; analog zu CRA-Produktklasse).
`human_oversight` = Procedure + Capability (Oversight-UI). `transparency` = Disclosure (Capability/Evidence,
wie Cookie/DSE-Offenlegung). `FRIA` = Procedure + Evidence.
**Verdikt: KEINE neue Klasse.** Verfeinerung: Risiko-Tier = Applicability-Attribut (vorhanden).
---
## Test 4 — NIS2 (EU) 2022/2555
| Modell-Klasse | NIS2-Inhalt |
|---|---|
| Legal Obligation | `nis2_risk_management_measures`, `nis2_incident_reporting`, `supply_chain_security`, `governance_accountability`, `business_continuity`. |
| Capability | **MFA, transport_encryption, security_monitoring_alerting, patch/update, backup****dieselben Capabilities wie CRA**. Das ist die Auszahlung: NIS2-Obligations `realized_by` die bereits gebaute Capability-Schicht. |
| Procedure | Incident-Response-Prozess; Lieferketten-Audit; Governance-Prozess. |
| Evidence | Incident-Reports, Audit-Logs, Vorstandsprotokolle. |
**Stress-Punkt:** **Meldefristen** (24h/72h/1 Monat) = zeitgebundene Procedure → `deadline` = Attribut.
`governance_accountability` (Management-Haftung) = organisatorische Obligation → Procedure + Evidence.
**Verdikt: KEINE neue Klasse.** Stärkster Reuse-Fall (teilt die CRA-Capability-Schicht vollständig).
---
## Ergebnis: 4 × NEIN → das Metamodell steht
Alle vier Regulierungen passen in die 6 Klassen **ohne neue Objektklasse** — unter zwei
Verfeinerungen, die der Test selbst aufdeckt (beide sind KEINE neuen Klassen):
1. **`realized_by Capability` ist OPTIONAL.** Vertraglich/dokumentarisch/prozessuale Obligations
(Data-Act-FRAND, NIS2-Governance, AI-Act-FRIA) werden rein über Procedure + Evidence erfüllt.
2. **Risiko-Niveau / Frist / Hazard-Schwere / Risiko-Tier sind ATTRIBUTE**, keine Klassen
(`tier`-Muster: `deadline`, `risk_level`, `severity`, `risk_tier`).
**Der einzige Watch-Point:** **Hazard / Threat.** Heute implizit (Obligations existieren, um sie zu
kontern). Eine eigene Klasse wird *erst* nötig, wenn ihr **quantitatives Risiko first-class** modelliert
(FMEA: Hazard→Risiko→Maßnahme als Graph-Knoten). Für die reine Compliance-Abbildung: nicht nötig.
→ Das ist die präzise Antwort auf „wo wäre erstmals eine neue Klasse nötig?".
## Empirische Stütze (nicht nur Theorie)
Die 3. Session (Reasoning Engine) hat **unabhängig** `proposed=True`-Obligations für MaschVO
(`machine_*`) und Data Act (`data_act_*`) geprägt — und brauchte dafür **keine neue Objektklasse**,
nur Obligation-IDs. Zwei Sessions kommen unabhängig zum selben Schluss.
## Konsequenz für die Reasoning-Schicht (Produktvision)
Heute: `Product → Applicable Regulations → Applicable Obligations`.
Mit der Capability-Schicht wird daraus:
```
Applicable Capabilities → Required Procedures → Expected Evidence
```
Antwort auf die Kundenaussage „Ich habe X umgesetzt" ist dann nicht „CRA Artikel …", sondern:
```
✓ Capability A ✓ Capability B ✗ Capability C
erfüllt CRA, MaschVO, NIS2 (teilweise)
```
Eine Capability erfüllt Obligations über *mehrere Regulierungen* (n:m) → eine Umsetzung wird gegen
das gesamte Regelwerk bewertet. Das ist qualitativ ein anderes Produkt als RAG.
## Entscheidung / nächster Schritt
Wenn dieses Dokument akzeptiert ist („keine weitere Klasse nötig"), verschiebt sich die Arbeit von
**Architektur** zu **Wissensaufbau**: jede neue Regulierung läuft durch
`Parser → Discovery-Pipeline → Review → Registry` (vorhandene Tooling), statt das Modell zu ändern.
Offen für den User: (a) Metamodell als stabil einfrieren? (b) den Hazard/Threat-Watch-Point als
bewusste Nicht-Klasse dokumentieren (bis FMEA-Quantifizierung)? (c) dann erste Regulierung als DATEN.
## Bewusst NICHT in diesem Schritt
Kein Code, keine Regulierung ingestiert, keine neue Klasse angelegt. Reiner Modell-Stresstest.
+253
View File
@@ -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"
}
}
]
}
@@ -1,71 +1,92 @@
{
"schema_version": "controls_for_obligation_mapping_v1",
"purpose": "Accepted CRA->OWASP controls (Compliance Execution Graph) for the Obligation Registry to propose the SEMANTIC control->obligation_id, replacing the coarse citation_unit interim join. Fill proposed_obligation_id per control, then we adopt it into control_mapping.obligation_id.",
"source": "ai-compliance-sdk control_mappings, mapping_status=accepted, reviewed_by=benjamin 2026-06-25",
"count": 7,
"purpose": "Accepted CRA->Framework controls (Compliance Execution Graph) for the Obligation Registry to propose the SEMANTIC control->obligation_id, replacing the coarse citation_unit interim join. Fill proposed_obligation_id per control, then we adopt it into control_mapping.obligation_id.",
"source": "ai-compliance-sdk control_mappings, mapping_status=accepted, reviewed_by=benjamin 2026-06-25. OWASP ASVS (7, gefuellt) + NIST SP 800-53 (3, pending).",
"filled_by": "obligation-registry-session 2026-06-25. OWASP 7/7 (4 auth/crypto + 3 logging). NIST 3/3 GEFUELLT (Obligation-Session): SI-2->provide_security_updates (stark, (2)(c)/Art.13) · SI-7->signed_update_integrity (update-scoped; SI-7 breiter) · CM-7->remote_access_attack_surface_min (remote-scoped; CM-7 breiter). GAP-BEFUND (Cross-Domain-Review): generische Parent-Obligations software_integrity_protection + attack_surface_minimization FEHLEN — SI-7/CM-7 sind breiter als die domaenen-scoped Treffer. Kandidaten fuer neue generische Obligations (User-Entscheidung). Damit 10/10 proposed_obligation_id gefuellt.",
"join_principle": "SEMANTISCH via obligation_id, NICHT via citation_unit/legal_basis-Anker. Die CRA-Anker sind im Registry teils approximativ (siehe anchor_quality_note) — daher ist obligation_id der stabile Primaerschluessel, nicht der Anker.",
"anchor_quality_note": "Registry-legal_basis-Anker sind teils CRA-Part-I-fehlzugeordnet (Opus-Synthese): user_authentication_required steht auf (2)(d) statt (2)(c); Crypto-Obligations auf (2)(e) statt (2)(d). CRA Annex I Part I: (2)(c)=Zugriffsschutz, (2)(d)=Vertraulichkeit, (2)(e)=Integritaet. Korrektur kommt mit dem zitierfaehigen Re-Ingest (span-genau). Deshalb: NICHT auf Anker joinen. ABER: der Logging-Cut (V16.*) ist korrekt auf (2)(k) verankert (echte Logging-Subsektion, kein Fehl-Anker).",
"mapping_type_note": "NEU: mapping_type=primary_implementation = die kanonische Primaer-Control einer Anforderung (genau eine), staerker als implements/supports. related-Controls (SC-3(3), RA-5, AC-6, SI-16, SA-10, ...) folgen separat als supports. Eine Obligation kann mehrere Controls haben, aber genau einen primary_implementation-Einstieg.",
"count": 10,
"controls": [
{
"framework": "OWASP ASVS",
"control": "V6.3.1",
"framework": "OWASP ASVS", "control": "V6.3.1",
"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff",
"citation_unit": "Annex I (2)(c)",
"family": "auth",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(c)", "family": "auth", "mapping_type": "supports",
"proposed_obligation_id": "user_authentication_required",
"mapping_method": "semantic",
"mapping_note": "Zugriffsschutz/Authentisierung-vor-Zugriff = Nutzer-Auth (NICHT firmware, trotz strukturellem (2)(c)-Join)"
},
{
"framework": "OWASP ASVS",
"control": "V6.1.1",
"framework": "OWASP ASVS", "control": "V6.1.1",
"source_norm": "CRA Annex I Part I (2)(c) — Schutz vor unbefugtem Zugriff",
"citation_unit": "Annex I (2)(c)",
"family": "auth",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(c)", "family": "auth", "mapping_type": "supports",
"proposed_obligation_id": "user_authentication_required",
"mapping_method": "semantic",
"mapping_note": "wie V6.3.1"
},
{
"framework": "OWASP ASVS",
"control": "V11.2.1",
"framework": "OWASP ASVS", "control": "V11.2.1",
"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung",
"citation_unit": "Annex I (2)(d)",
"family": "crypto",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(d)", "family": "crypto", "mapping_type": "supports",
"proposed_obligation_id": "credential_confidentiality_protection",
"mapping_method": "semantic",
"mapping_note": "Vertraulichkeit von Auth-Daten. ALT: encrypted_auth_channel, falls V11.2.1 transit-/kanal-spezifisch ist — bitte aus eurem Control-Text bestaetigen."
},
{
"framework": "OWASP ASVS",
"control": "V11.7.1",
"framework": "OWASP ASVS", "control": "V11.7.1",
"source_norm": "CRA Annex I Part I (2)(d) — Vertraulichkeit / Verschluesselung",
"citation_unit": "Annex I (2)(d)",
"family": "crypto",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(d)", "family": "crypto", "mapping_type": "supports",
"proposed_obligation_id": "auth_key_management",
"mapping_method": "semantic",
"mapping_note": "Key Management = Schluessel erzeugen/speichern/HSM"
},
{
"framework": "OWASP ASVS",
"control": "V16.3.3",
"framework": "OWASP ASVS", "control": "V16.3.3",
"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging",
"citation_unit": "Annex I (2)(k)",
"family": "logging",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(k)", "family": "logging", "mapping_type": "supports",
"proposed_obligation_id": "event_logging_security_events",
"mapping_method": "semantic",
"mapping_note": "Umbrella-LM 'Produkt protokolliert sicherheitsrelevante Ereignisse' (CRA (2)(k)). ALT bei access-decision-spezifischem Control-Text: access_control_event_logging — bitte aus eurem ASVS-V16.3-Text bestaetigen."
},
{
"framework": "OWASP ASVS",
"control": "V16.3.4",
"framework": "OWASP ASVS", "control": "V16.3.4",
"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging",
"citation_unit": "Annex I (2)(k)",
"family": "logging",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(k)", "family": "logging", "mapping_type": "supports",
"proposed_obligation_id": "event_logging_security_events",
"mapping_method": "semantic",
"mapping_note": "Umbrella-LM (CRA (2)(k)). ALT bei admin-/privileg-spezifischem Control-Text: audit_trail_admin_actions — bitte aus eurem ASVS-V16.3-Text bestaetigen."
},
{
"framework": "OWASP ASVS",
"control": "V16.1.1",
"framework": "OWASP ASVS", "control": "V16.1.1",
"source_norm": "CRA Annex I Part I (2)(k) — Sicherheitsrelevante Ereignisse / Logging",
"citation_unit": "Annex I (2)(k)",
"family": "logging",
"mapping_type": "supports",
"proposed_obligation_id": ""
"citation_unit": "Annex I (2)(k)", "family": "logging", "mapping_type": "supports",
"proposed_obligation_id": "event_logging_security_events",
"mapping_method": "semantic",
"mapping_note": "V16.1 = allgemeine Logging-Anforderung -> Umbrella-LM event_logging_security_events. Hohe Konfidenz."
},
{
"framework": "NIST SP 800-53", "control": "SI-7",
"source_norm": "CRA Annex I Part I (2)(e) — Integritaet",
"citation_unit": "Annex I (2)(e)", "family": "integrity", "mapping_type": "primary_implementation",
"proposed_obligation_id": "software_integrity_protection",
"mapping_method": "semantic",
"mapping_note": "NIST SI-7 = Software/Firmware/Information Integrity (gesamte Produkt-Integritaet). #6 ADOPTIERT (2026-06-26) auf CORE software_integrity_protection (Annex I (2)(f)) — die in #5b materialisierte generische Integritaets-Obligation. Die domaenen-scoped signed_update_integrity (Update-Signatur, (1)(3)(f)) bleibt gueltig als DOMAIN, specializes->CORE. NICHT log_integrity_immutability (Audit-Log-Schutz, andere Ebene)."
},
{
"framework": "NIST SP 800-53", "control": "SI-2",
"source_norm": "CRA Annex I Part I (2)(l) — Sichere Updates",
"citation_unit": "Annex I (2)(l)", "family": "update", "mapping_type": "primary_implementation",
"proposed_obligation_id": "provide_security_updates",
"mapping_method": "semantic",
"mapping_note": "NIST SI-2 = Flaw Remediation. STARKER Treffer in eurer NEUEN updates-Familie (93-Stand): provide_security_updates (LEGAL_MINIMUM, Annex I (2)(c) + Art. 13) = DAS sichere-Update-LM. -> SI-2 primary_implementation = provide_security_updates. Verwandt (supports): vuln_remediation_patching (Part II Remediations-PROZESS), support_period_maintenance, update_testing_validation, update_rollback. Mein source_norm-Anker (2)(l) ist approximativ -> bitte (2)(c)/Art.13 via provide_security_updates nutzen."
},
{
"framework": "NIST SP 800-53", "control": "CM-7",
"source_norm": "CRA Annex I Part I (2)(i) — Angriffsflaeche minimieren",
"citation_unit": "Annex I (2)(i)", "family": "attack_surface", "mapping_type": "primary_implementation",
"proposed_obligation_id": "attack_surface_minimization",
"mapping_method": "semantic",
"mapping_note": "NIST CM-7 = Least Functionality (deaktivierte Ports/Dienste/Funktionen, GESAMTE Angriffsflaeche). #6 ADOPTIERT (2026-06-26) auf CORE attack_surface_minimization (Annex I (2)(j)) — die in #5b materialisierte generische Obligation. Die domaenen-scoped remote_access_attack_surface_min (nur Remote-Access-Flaeche) bleibt gueltig als DOMAIN, specializes->CORE. related (supports): SC-3(3)/AC-6/SI-16."
}
]
}
+4 -1
View File
@@ -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",
+5 -1
View File
@@ -10281,7 +10281,11 @@
"cluster_size": 4,
"llm_model": "claude-opus-4-8",
"synthesis_version": "v1"
}
},
"specializes": "software_integrity_protection",
"objective_tags": [
"integrity"
]
}
],
"relationships": [
+82
View File
@@ -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": []
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+424 -1
View File
@@ -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": 47,
"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",
@@ -418,6 +438,409 @@
"Annex I (2)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "event_logging_security_events",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "access_control_event_logging",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "audit_trail_admin_actions",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_integrity_immutability",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_access_control_protection",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_retention_archival",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "centralized_log_management",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_monitoring_alerting",
"regulation": "CRA",
"family": "logging",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I Part I (2)(k)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "log_data_minimization_privacy",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_format_standardization",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_timestamp_synchronization",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_availability_resilience",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_thread_safety_correctness",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "IMPLEMENTATION"
},
{
"obligation_id": "logging_library_supply_chain",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_config_management",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "logging_governance_roles",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "incident_response_logging",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "log_transmission_security",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "network_traffic_logging",
"regulation": "CRA",
"family": "logging",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_control_least_privilege",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(d)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_confidentiality_integrity",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(b)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_session_management",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_mfa",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_encryption",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "reject_insecure_remote_protocols",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_logging_audit",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(g)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_user_validation_ot",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_training",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_architecture_design",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_attack_surface_min",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)(a)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_vuln_patch_mgmt",
"regulation": "CRA",
"family": "remote_access",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(1)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "remote_access_threat_detection",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_maintenance_governance",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "temporary_remote_access_mgmt",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_data_export_protection",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "component_remote_interface_security",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "remote_access_fallback_concept",
"regulation": "CRA",
"family": "remote_access",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "provide_security_updates",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(c)",
"Art. 13"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "support_period_maintenance",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Art. 13(8)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "signed_update_integrity",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(3)(f)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "trusted_update_source",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(3)(d)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "update_testing_validation",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "update_rollback",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "GUIDANCE"
},
{
"obligation_id": "automatic_updates_optout",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (2)(c)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "update_risk_assessment",
"regulation": "CRA",
"family": "updates",
"tier": "LEGAL_MINIMUM",
"citation_units": [
"Annex I (1)(2)"
],
"source_role": "LEGAL_BASIS"
},
{
"obligation_id": "secure_modification_control",
"regulation": "CRA",
"family": "updates",
"tier": "BEST_PRACTICE",
"citation_units": [],
"source_role": "IMPLEMENTATION"
}
]
}
@@ -0,0 +1,85 @@
"""Cross-Domain Relationship Discovery — Stufe 2: Opus klassifiziert jede Kandidaten-Beziehung
in GENAU EINE Kategorie. Liefert das Rohmaterial der Compliance-Ontologie (insb. SHARED_CAPABILITY
= Capability-Schicht). ANTHROPIC_API_KEY aus ENV (nie hartcodiert). Streaming.
ANTHROPIC_API_KEY=… python3 classify_relationships.py --pairs /tmp/cd_pairs.json \
--only-cross-family --out /tmp/cd_classified.json
"""
from __future__ import annotations
import argparse
import json
import os
import re
from collections import Counter
SYS = """Du bist Compliance-Ontologe. Gegeben Paare von Legal Obligations (CRA), bestimme fuer
JEDES Paar GENAU EINE Beziehung. Ziel ist NICHT Aehnlichkeit, sondern die STRUKTURELLE Beziehung.
Kategorien (genau EINE; bei Mehrdeutigkeit gilt diese Prioritaet):
1 SAME_OBLIGATION — dieselbe rechtliche Pflicht, nur pro Domaene anders formuliert -> MERGE-Kandidat.
2 SUPPORTED_BY — A ist domaenenspezifische Auspraegung/Teilfall von B ODER A traegt zur Erfuellung von B bei. RICHTUNG angeben.
3 SHARED_CAPABILITY — beide werden durch DIESELBE technische Faehigkeit erfuellt (z.B. MFA, TLS-Verschluesselung, digitale Signatur, Session-Management, Patch-Management, Logging-Pipeline). capability_name (snake_case) angeben.
4 SHARED_PROCEDURE — beide ueber denselben operativen Prozess erfuellt, ohne gemeinsames technisches Artefakt.
5 SHARED_EVIDENCE — beide erzeugen/nutzen denselben Nachweis (Audit-Log, SBOM, Release Notes). evidence_name angeben.
6 SHARED_GUIDANCE — beide berufen sich auf denselben externen Standard (NIST/OWASP/ISO), sonst distinkt.
7 OVERLAP_ONLY — nur oberflaechliche Wort-/Themenueberlappung, keine echte strukturelle Beziehung.
8 UNRELATED — Falsch-Positiv der Embedding-Naehe.
Gib AUSSCHLIESSLICH JSON aus:
{"results":[{"i":0,"relation":"SHARED_CAPABILITY","direction":"a->b|b->a|none","capability_name":"","evidence_name":"","reason":"max 18 Woerter"}]}
Regeln: relation = genau eine der 8 Strings. direction nur bei SUPPORTED_BY, sonst "none".
capability_name NUR bei SHARED_CAPABILITY (sonst ""), evidence_name NUR bei SHARED_EVIDENCE (sonst "").
Sei streng: SHARED_GUIDANCE/OVERLAP_ONLY/UNRELATED grosszuegig nutzen; SAME_OBLIGATION nur bei echter Deckungsgleichheit.
Gib fuer JEDES Paar (per Index i) genau ein Ergebnis."""
def build_user(pairs: list[dict]) -> str:
lines = []
for i, p in enumerate(pairs):
lines.append(f'[{i}] A={p["a"]} ({p["fa"]}/{p["ta"]}): {p["da"]}\n'
f' B={p["b"]} ({p["fb"]}/{p["tb"]}): {p["db"]} [sim={p["sim"]}]')
return "Paare:\n" + "\n".join(lines)
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--pairs", required=True)
ap.add_argument("--only-cross-family", action="store_true")
ap.add_argument("--min-sim", type=float, default=0.0)
ap.add_argument("--model", default="claude-opus-4-8")
ap.add_argument("--out", required=True)
a = ap.parse_args()
d = json.load(open(a.pairs, encoding="utf-8"))
pairs = [p for p in d["pairs"]
if (not a.only_cross_family or p["cross_family"]) and p["sim"] >= a.min_sim]
import anthropic
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
with client.messages.stream(model=a.model, max_tokens=24000, system=SYS,
messages=[{"role": "user", "content": build_user(pairs)}]) as st:
msg = st.get_final_message()
txt = msg.content[0].text
m = re.search(r"\{.*\}", txt, re.DOTALL)
data = json.loads(m.group(0) if m else txt)
res = []
for r in data.get("results", []):
i = r.get("i")
if not isinstance(i, int) or i < 0 or i >= len(pairs):
continue
p = pairs[i]
res.append({"a": p["a"], "fa": p["fa"], "b": p["b"], "fb": p["fb"], "sim": p["sim"],
"relation": r.get("relation", "?"), "direction": r.get("direction", "none"),
"capability_name": r.get("capability_name", ""),
"evidence_name": r.get("evidence_name", ""), "reason": r.get("reason", "")})
dist = Counter(r["relation"] for r in res)
out = {"n_pairs": len(pairs), "n_classified": len(res), "distribution": dict(dist),
"model": a.model, "results": res}
json.dump(out, open(a.out, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print(f"classified {len(res)}/{len(pairs)} | {dict(dist)}")
print("written:", a.out)
if __name__ == "__main__":
main()
@@ -0,0 +1,66 @@
"""Cross-Domain Relationship Discovery — Stufe 1 (key-frei, im bp-compliance-backend-Container).
Alle Obligations mehrerer Registries -> BGE-M3-Embedding -> je Obligation Top-K Nachbarn ->
Kandidaten-Paare (cross- UND same-family) >= min-sim. KEIN Urteil hier — nur Kandidaten.
Stufe 2 (classify_relationships.py) klassifiziert die Beziehung per Opus.
python3 cross_domain_pairs.py /tmp/reg/cra.json /tmp/reg/cra_authentication.json ... \
--top-k 8 --min-sim 0.60 --out /tmp/cd_pairs.json
"""
from __future__ import annotations
import argparse
import asyncio
import json
from _core import cosine
async def run(paths: list[str], top_k: int, min_sim: float, out: str) -> None:
from compliance.services.mc_embedding_matcher import _embed_texts
obls: list[dict] = []
for p in paths:
reg = json.load(open(p, encoding="utf-8"))
fam = reg.get("family", "")
for o in reg.get("obligations", []):
obls.append({"id": o["id"], "family": o.get("family", "") or fam,
"tier": o.get("tier", ""), "name": o.get("name", ""),
"desc": o.get("description", "")})
vecs = await _embed_texts([f'{o["name"]}. {o["desc"]}' for o in obls])
n = len(obls)
print(f"obligations={n}")
best: dict[tuple[int, int], float] = {}
for i in range(n):
nbrs = sorted(((cosine(vecs[i], vecs[j]), j) for j in range(n) if j != i), reverse=True)[:top_k]
for s, j in nbrs:
if s < min_sim:
continue
a, b = sorted((i, j))
if (a, b) not in best or s > best[(a, b)]:
best[(a, b)] = s
pairs = []
for (a, b), s in sorted(best.items(), key=lambda x: -x[1]):
pairs.append({
"a": obls[a]["id"], "fa": obls[a]["family"], "ta": obls[a]["tier"], "da": obls[a]["desc"][:220],
"b": obls[b]["id"], "fb": obls[b]["family"], "tb": obls[b]["tier"], "db": obls[b]["desc"][:220],
"sim": round(s, 3), "cross_family": obls[a]["family"] != obls[b]["family"]})
cf = sum(1 for p in pairs if p["cross_family"])
json.dump({"n_obligations": n, "n_pairs": len(pairs), "cross_family": cf, "pairs": pairs},
open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=1)
print(f"pairs={len(pairs)} (cross-family={cf}, same-family={len(pairs) - cf}) written: {out}")
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("registries", nargs="+")
ap.add_argument("--top-k", type=int, default=8)
ap.add_argument("--min-sim", type=float, default=0.60)
ap.add_argument("--out", default="/tmp/cd_pairs.json")
a = ap.parse_args()
asyncio.run(run(a.registries, a.top_k, a.min_sim, a.out))
if __name__ == "__main__":
main()
@@ -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()
@@ -19,6 +19,20 @@ SCOPES = {
"vuln": ["%schwachstellenbehandl%", "%schwachstellenmanagement%", "%vulnerability handling%",
"%coordinated vulnerab%", "%vulnerability disclosure%", "%cvd-konzept%"],
"auth": ["%authentisierung%", "%authentifizierung%", "%authentication%"],
"logging": ["%logging%", "%protokollierung%", "%audit-log%", "%audit-trail%",
"%ereignisprotokoll%", "%sicherheitsprotokoll%", "%audit-protokoll%",
"%log-management%", "%sicherheitsereignis%protokoll%", "%audit-trail%"],
"remote_access": ["%fernwartung%", "%fernzugriff%", "%fernzugang%", "%fernwartungs%",
"%remote access%", "%remote maintenance%", "%remote management%",
"%remote-wartung%", "%remote-zugriff%", "%remote-zugang%",
"%sichere fernwartung%", "%fernsteuerung%"],
"updates": ["%sicherheitsupdate%", "%security update%", "%sicherheits-update%",
"%security patch%", "%sicherheitspatch%", "%patch-management%",
"%patchmanagement%", "%patch management%", "%firmware-update%",
"%firmware update%", "%software-update%", "%software update%",
"%automatische aktualisierung%", "%update-mechanismus%",
"%update-bereitstellung%", "%bereitstellung von updates%",
"%sichere aktualisierung%", "%signierte update%", "%update-paket%"],
}