Skip to content

Commit 58c87d8

Browse files
feat(oscal): multi-framework regulator deliverables — DORA ICT-risk register + NIST AI RMF crosswalk (14th & 15th assurance checks)
One verified OSCAL 1.1.2 catalog (source of truth) -> multiple regulator deliverables. Extends the round-4 Annex IV dossier generator to two more frameworks, all assembled from the same conformance-verified catalog + live re-run evidence. New shared engine: - crosswalk_common.py: load_catalogs, CONTROL_EVIDENCE, run_conformance (refuses to assemble against a non-conformant catalog), EvidenceRunner (re-runs each control's backing assurance check this run), status_for (SATISFIED / PARTIAL / PENDING-EVIDENCE), resolve_controls. New deliverables (Tier A, runnable): - generate_dora_ict_register.py + dora_framework_map.yaml: DORA (Reg. (EU) 2022/2554) 5-pillar ICT-risk register. Result 3/5 pillars SATISFIED; P4 (third-party) and P5 (info-sharing) reported as HONEST coverage gaps (is_coverage_gap=true, PENDING-EVIDENCE), not hidden. - generate_nist_rmf_crosswalk.py + nist_ai_rmf_map.yaml: NIST AI RMF 1.0 4-function (GOVERN/MAP/MEASURE/MANAGE) crosswalk with per-function coverage analysis. Result 4/4 functions SATISFIED (100%). - generated/{dora_ict_register,nist_ai_rmf_crosswalk}.{json,md} samples. Honesty guarantees (preserved): each generator embeds an integrity_statement ('not a DORA conformity attestation' / 'not a certification'); both refuse a non-conformant catalog and raise on unknown control ids (falsifiable — negative tests confirm exit 1). Wiring: - run_runnable_assurance.sh: 13 -> 15 steps (14=DORA, 15=NIST), using --no-verify --print piped to inline python validators. - tests/governance: +3 tests (18 total): DORA gaps reported, NIST full coverage with live evidence, and --no-verify does not fabricate SATISFIED. - CI: pytest selector adds dora/nist/crosswalk; assembles all 3 deliverables and uploads them as the 'regulator-deliverables' artifact. Docs synced: RUNNABLE_ASSURANCE.md (rows 14-15, thirteen->fifteen), DECADAL plan (13/13 -> 15/15 + 2 verification-ledger rows), pilot P6-REPRO 15/15, oscal/README.md. Verification (this commit): - bash governance_artifacts/run_runnable_assurance.sh -> 15/15 PASS - pytest tests/governance/test_governance_artifacts.py -> 18 passed - pilot acceptance gates -> 6/6 automated PASS (P6-REPRO 15/15) - DORA 3/5 SATISFIED (P4/P5 gaps); NIST 4/4 SATISFIED (100%); catalog conformance 0 failures for both.
1 parent 816e120 commit 58c87d8

19 files changed

Lines changed: 1564 additions & 32 deletions

.github/workflows/runnable-assurance.yml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,24 @@ jobs:
9191
pytest governance_artifacts/routing/test_sara_acr_router.py -q
9292
pytest governance_artifacts/kafka/test_pqc_worm_logger_v2.py -q
9393
pytest governance_blueprint/contracts/test_contract_logic.py -q
94-
pytest tests/governance/test_governance_artifacts.py -q -k "oscal or annex"
94+
pytest tests/governance/test_governance_artifacts.py -q -k "oscal or annex or dora or nist or crosswalk"
9595
9696
- name: Run runnable assurance suite
9797
run: bash governance_artifacts/run_runnable_assurance.sh
9898

9999
- name: 2028 pilot acceptance-gate checklist
100100
run: python3 governance_artifacts/pilot/run_pilot_acceptance_gates.py
101101

102-
- name: Assemble Annex IV dossier (live evidence) and upload
103-
run: python3 governance_artifacts/oscal/generate_annex_iv_dossier.py
102+
- name: Assemble regulator deliverables (live evidence)
103+
run: |
104+
python3 governance_artifacts/oscal/generate_annex_iv_dossier.py
105+
python3 governance_artifacts/oscal/generate_dora_ict_register.py
106+
python3 governance_artifacts/oscal/generate_nist_rmf_crosswalk.py
104107
105-
- name: Upload Annex IV dossier artifact
108+
- name: Upload regulator deliverables artifact
106109
uses: actions/upload-artifact@v4
107110
with:
108-
name: annex-iv-dossier
111+
name: regulator-deliverables
109112
path: governance_artifacts/oscal/generated/
110113

111114
dashboard-tests:

governance_artifacts/RUNNABLE_ASSURANCE.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ the master reference documents assert that a control "holds," the artifacts here
1717
bash governance_artifacts/run_runnable_assurance.sh
1818
```
1919

20-
Runs all thirteen checks below and fails fast on any error.
20+
Runs all fifteen checks below and fails fast on any error.
2121

2222
## What is proven, and against which control
2323

@@ -36,6 +36,8 @@ Runs all thirteen checks below and fails fast on any error.
3636
| 11 | Governance artifact schema validation | Python validator | manifest/schema integrity | OSCAL, evidence logging (EU AI Act Art. 12) |
3737
| 12 | OSCAL catalog conformance — every control's `tla-spec` / `rego-policy` / `circuit` / `simulator` prop resolves to a real in-repo artifact; every regime `#href` resolves to a back-matter anchor (no dangling references); `feasibility-tier ∈ {A,B,C,D}`; `freshness-sla` is a valid ISO-8601 duration (43 cross-reference checks, falsifiable) | Python (`oscal_conformance.py`) + pytest | all `con-*`, `cry-*`, `env-*`, `rte-*` | OSCAL 1.1.2 compliance-as-code integrity (EU AI Act Annex IV, NIST AI RMF, DORA, Basel, SR 11-7) |
3838
| 13 | Annex IV dossier auto-assembly — builds an OSCAL-native 8-section (A–H) EU AI Act technical-documentation dossier from the conformant catalog + live assurance evidence; refuses to run on a non-conformant catalog or unknown control id; never marks a section SATISFIED without a green runnable check | Python (`generate_annex_iv_dossier.py`) + pytest | all controls → Annex IV §A–H | EU AI Act Annex IV technical documentation (auto-assembled deliverable) |
39+
| 14 | DORA ICT-risk register auto-assembly — builds a 5-pillar (P1–P5) DORA register from the same catalog + live evidence; reports P4/P5 as honest coverage gaps; same refusal/honesty guarantees | Python (`generate_dora_ict_register.py`) + pytest | `env-*`, `cry-02`, `con-04/07` → DORA pillars | DORA (Reg. (EU) 2022/2554) ICT-risk register (auto-assembled deliverable) |
40+
| 15 | NIST AI RMF profile crosswalk auto-assembly — builds a 4-function (GOVERN/MAP/MEASURE/MANAGE) crosswalk with per-function coverage analysis from the same catalog + live evidence; same refusal/honesty guarantees | Python (`generate_nist_rmf_crosswalk.py`) + pytest | all controls → NIST AI RMF functions | NIST AI RMF 1.0 coverage crosswalk (auto-assembled deliverable) |
3941

4042
### Companion reviews & plan (this iteration)
4143

governance_artifacts/oscal/README.md

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,16 @@ honest and turn them into regulator deliverables.
1111
| `catalog_sentinel_v24_env_rte.json` | OSCAL 1.1.2 catalog — Confidential-computing (ENV) + MoE-routing (RTE) controls, with regime back-matter. |
1212
| `sentinel_control_catalog_v1.yaml` | Higher-level control families + regulatory mapping (legacy/companion view). |
1313
| `oscal_conformance.py` | **Conformance validator** — verifies every control's `tla-spec` / `rego-policy` / `circuit` / `simulator` prop resolves to a real in-repo artifact, every regime `#href` resolves to a back-matter anchor, `feasibility-tier ∈ {A,B,C,D}`, and `freshness-sla` is a valid ISO-8601 duration. |
14+
| `crosswalk_common.py` | **Shared crosswalk engine** — one source of truth for catalog loading, the control→live-evidence map, the conformance gate, and the evidence-status rule. Reused by all three generators. |
1415
| `annex_iv_section_map.yaml` | Auditable map: each EU AI Act Annex IV section (A–H) → the OSCAL control ids that evidence it, plus a provider narrative. |
16+
| `dora_framework_map.yaml` | Auditable map: each DORA pillar (P1–P5) → controls + register narrative. |
17+
| `nist_ai_rmf_map.yaml` | Auditable map: each NIST AI RMF function (GOVERN/MAP/MEASURE/MANAGE) → controls + crosswalk narrative. |
1518
| `generate_annex_iv_dossier.py` | **Dossier generator** — auto-assembles an OSCAL-native Annex IV technical-documentation dossier from the catalogs + live assurance evidence. |
16-
| `generated/annex_iv_dossier.{json,md}` | Sample auto-assembled dossier (regenerate any time; `generated_at` changes per run). |
19+
| `generate_dora_ict_register.py` | **DORA register generator** — auto-assembles a scoped DORA ICT-risk register; reports P4/P5 as coverage gaps. |
20+
| `generate_nist_rmf_crosswalk.py` | **NIST RMF crosswalk generator** — auto-assembles a NIST AI RMF coverage crosswalk with per-function coverage analysis. |
21+
| `generated/annex_iv_dossier.{json,md}` | Sample auto-assembled Annex IV dossier (regenerate any time; `generated_at` changes per run). |
22+
| `generated/dora_ict_register.{json,md}` | Sample auto-assembled DORA ICT-risk register. |
23+
| `generated/nist_ai_rmf_crosswalk.{json,md}` | Sample auto-assembled NIST AI RMF crosswalk. |
1724

1825
## Run it
1926

@@ -27,12 +34,18 @@ python3 governance_artifacts/oscal/generate_annex_iv_dossier.py
2734
# -> generated/annex_iv_dossier.json (machine-readable)
2835
# -> generated/annex_iv_dossier.md (human-readable)
2936

30-
# Faster, assembly-only (does NOT run backing checks; no section reported SATISFIED)
37+
# 3. Assemble the multi-framework deliverables from the SAME verified catalog
38+
python3 governance_artifacts/oscal/generate_dora_ict_register.py # DORA ICT-risk register (5 pillars)
39+
python3 governance_artifacts/oscal/generate_nist_rmf_crosswalk.py # NIST AI RMF crosswalk (4 functions)
40+
41+
# Faster, assembly-only (does NOT run backing checks; nothing reported SATISFIED)
3142
python3 governance_artifacts/oscal/generate_annex_iv_dossier.py --no-verify
3243
```
3344

34-
Both tools are wired into `governance_artifacts/run_runnable_assurance.sh`
35-
(steps 12 and 13) and into CI.
45+
All four tools are wired into `governance_artifacts/run_runnable_assurance.sh`
46+
(steps 12–15) and into CI. One verified source of truth (the OSCAL catalog)
47+
produces three regulator deliverables — Annex IV, DORA, NIST AI RMF — that can
48+
never drift from each other because they share `crosswalk_common.py`.
3649

3750
## Evidence-status semantics (honesty model)
3851

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Shared crosswalk engine for OSCAL-native regulator deliverables.
4+
5+
One source of truth for:
6+
- loading the Sentinel OSCAL catalogs into enriched control dicts
7+
(statement, feasibility-tier, freshness-sla, evidence-query, resolved
8+
regime citations);
9+
- the control -> live assurance-evidence map (CONTROL_EVIDENCE) and a
10+
cached runner that records whether each control's backing check passed;
11+
- the OSCAL conformance gate (refuse to assemble on a non-conformant catalog).
12+
13+
Used by:
14+
generate_annex_iv_dossier.py (EU AI Act Annex IV)
15+
generate_dora_ict_register.py (DORA ICT-risk register)
16+
generate_nist_rmf_crosswalk.py (NIST AI RMF profile crosswalk)
17+
18+
Evidence-status semantics (shared honesty model):
19+
SATISFIED - >=1 mapped control whose runnable check passed this run.
20+
PARTIAL - has runnable-backed controls but none passed this run.
21+
PENDING-EVIDENCE - mapped only to organisational/hardware evidence, or no
22+
controls mapped (i.e. a genuine coverage gap).
23+
"""
24+
from __future__ import annotations
25+
26+
import json
27+
import subprocess
28+
import sys
29+
from datetime import datetime, timezone
30+
from pathlib import Path
31+
32+
OSCAL_DIR = Path(__file__).resolve().parent
33+
GA_DIR = OSCAL_DIR.parent
34+
REPO_ROOT = GA_DIR.parent
35+
DEFAULT_CATALOGS = [
36+
"catalog_sentinel_v24_excerpt.json",
37+
"catalog_sentinel_v24_env_rte.json",
38+
]
39+
40+
# Control -> live assurance evidence. `kind` describes the evidence character
41+
# truthfully; `command` is what a regulator re-runs (None = organisational
42+
# evidence, reported PENDING). Kept here so all generators agree on what each
43+
# control's evidence actually is.
44+
CONTROL_EVIDENCE = {
45+
"con-04": {
46+
"check": "TLA+ KillSwitchAbstract reachability / dead-man's switch",
47+
"kind": "model-checked",
48+
"command": "java -cp governance_artifacts/tla/tools/tla2tools.jar tlc2.TLC "
49+
"-config governance_artifacts/tla/KillSwitchAbstract.cfg "
50+
"governance_artifacts/tla/KillSwitchAbstract.tla",
51+
},
52+
"con-07": {
53+
"check": "TLA+ KillSwitchAbstract one-way ratchet (ASA cannot de-escalate)",
54+
"kind": "model-checked",
55+
"command": "java -cp governance_artifacts/tla/tools/tla2tools.jar tlc2.TLC "
56+
"-config governance_artifacts/tla/KillSwitchAbstract.cfg "
57+
"governance_artifacts/tla/KillSwitchAbstract.tla",
58+
},
59+
"cry-02": {
60+
"check": "PQC WORM audit log (ML-DSA-65 sign + hash chain + tamper detect)",
61+
"kind": "cryptographically-verified",
62+
"command": "python3 -m pytest governance_artifacts/kafka/test_pqc_worm_logger_v2.py -q",
63+
},
64+
"cry-05": {
65+
"check": "SRC-1 Groth16 systemic-risk concentration bound proof",
66+
"kind": "zk-proven",
67+
"command": "bash governance_artifacts/zk/run_src1_proof.sh",
68+
},
69+
"env-01": {
70+
"check": "TLA+ AdmissionWithAttestation (no T0 run without valid attestation)",
71+
"kind": "model-checked",
72+
"command": "java -cp governance_artifacts/tla/tools/tla2tools.jar tlc2.TLC "
73+
"-config governance_artifacts/tla/AdmissionWithAttestation.cfg "
74+
"governance_artifacts/tla/AdmissionWithAttestation.tla",
75+
},
76+
"env-02": {
77+
"check": "Enclave-bound PQC key custody (hardware-dependent)",
78+
"kind": "organisational-record-PENDING",
79+
"command": None,
80+
},
81+
"rte-01": {
82+
"check": "SARA/ACR MoE routing stabilization invariants",
83+
"kind": "simulated",
84+
"command": "python3 -m pytest governance_artifacts/routing/test_sara_acr_router.py -q",
85+
},
86+
}
87+
88+
89+
def now_iso() -> str:
90+
return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
91+
92+
93+
def load_catalogs(catalog_names: list[str] | None = None) -> dict[str, dict]:
94+
"""Return {control_id: enriched control dict} across the named catalogs."""
95+
names = catalog_names or DEFAULT_CATALOGS
96+
controls: dict[str, dict] = {}
97+
for name in names:
98+
path = OSCAL_DIR / name
99+
if not path.is_file():
100+
raise FileNotFoundError(f"catalog not found: {path}")
101+
cat = json.loads(path.read_text())["catalog"]
102+
anchors = {r["uuid"]: r.get("title", r["uuid"])
103+
for r in cat.get("back-matter", {}).get("resources", [])
104+
if r.get("uuid")}
105+
106+
def walk(groups):
107+
for g in groups:
108+
for c in g.get("controls", []):
109+
props = {p["name"]: p["value"] for p in c.get("props", [])}
110+
stmt = next((p["prose"] for p in c.get("parts", [])
111+
if p.get("name") == "statement"), "")
112+
regimes = []
113+
for link in c.get("links", []):
114+
href = link.get("href", "")
115+
if href.startswith("#"):
116+
a = href[1:]
117+
regimes.append({
118+
"rel": link.get("rel", "regime"),
119+
"anchor": a,
120+
"citation": anchors.get(a, a),
121+
})
122+
controls[c["id"]] = {
123+
"id": c["id"],
124+
"title": c.get("title", ""),
125+
"statement": stmt,
126+
"catalog": name,
127+
"feasibility_tier": props.get("feasibility-tier"),
128+
"freshness_sla": props.get("freshness-sla"),
129+
"evidence_query": props.get("evidence-query"),
130+
"regimes": regimes,
131+
}
132+
walk(g.get("groups", []))
133+
walk(cat.get("groups", []))
134+
return controls
135+
136+
137+
def run_conformance() -> dict:
138+
"""Run oscal_conformance.py --json; raise if non-conformant."""
139+
proc = subprocess.run(
140+
[sys.executable, str(OSCAL_DIR / "oscal_conformance.py"), "--json"],
141+
cwd=REPO_ROOT, capture_output=True, text=True,
142+
)
143+
if proc.returncode != 0:
144+
raise RuntimeError(
145+
"OSCAL conformance failed; refusing to assemble a deliverable on a "
146+
f"non-conformant catalog:\n{proc.stdout}\n{proc.stderr}"
147+
)
148+
return json.loads(proc.stdout)
149+
150+
151+
class EvidenceRunner:
152+
"""Runs (and caches) each control's backing assurance check."""
153+
154+
def __init__(self, verify: bool = True):
155+
self.verify = verify
156+
self._cache: dict[str, bool | None] = {}
157+
158+
def evidence(self, control_id: str) -> dict:
159+
desc = CONTROL_EVIDENCE.get(control_id, {
160+
"check": "(no runnable check mapped)",
161+
"kind": "organisational-record-PENDING",
162+
"command": None,
163+
})
164+
if control_id not in self._cache:
165+
if self.verify and desc["command"]:
166+
proc = subprocess.run(desc["command"], cwd=REPO_ROOT, shell=True,
167+
capture_output=True, text=True)
168+
self._cache[control_id] = proc.returncode == 0
169+
else:
170+
self._cache[control_id] = None
171+
return {
172+
"control_id": control_id,
173+
"check": desc["check"],
174+
"evidence_kind": desc["kind"],
175+
"command": desc["command"],
176+
"passed": self._cache[control_id],
177+
}
178+
179+
180+
def status_for(control_entries: list[dict]) -> str:
181+
"""Shared evidence-status rule given a section/element's resolved controls
182+
(each carrying a 'live_evidence' dict)."""
183+
if not control_entries:
184+
return "PENDING-EVIDENCE"
185+
any_passed = any(c["live_evidence"]["passed"] is True for c in control_entries)
186+
any_runnable = any(c["live_evidence"]["command"] for c in control_entries)
187+
if any_passed:
188+
return "SATISFIED"
189+
if any_runnable:
190+
return "PARTIAL"
191+
return "PENDING-EVIDENCE"
192+
193+
194+
def resolve_controls(control_ids: list[str], catalog: dict[str, dict],
195+
runner: EvidenceRunner) -> tuple[list[dict], list[str]]:
196+
"""Resolve control ids -> enriched entries with live_evidence. Returns
197+
(resolved, unknown_ids)."""
198+
resolved, unknown = [], []
199+
for cid in control_ids:
200+
if cid not in catalog:
201+
unknown.append(cid)
202+
continue
203+
entry = dict(catalog[cid])
204+
entry["live_evidence"] = runner.evidence(cid)
205+
resolved.append(entry)
206+
return resolved, unknown
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# DORA (Regulation (EU) 2022/2554) ICT-risk-register framework map.
2+
#
3+
# Auditable bridge: each of the five DORA pillars -> the Sentinel OSCAL control
4+
# ids that provide evidence for it, plus a register narrative. The generator
5+
# (generate_dora_ict_register.py) consumes this and attaches LIVE assurance
6+
# evidence per control.
7+
#
8+
# Pillars with no in-scope control are reported as coverage GAPS (PENDING-
9+
# EVIDENCE), not silently dropped — DORA conformance for a real institution is
10+
# far broader than the AI-governance control surface modelled here.
11+
#
12+
# Control ids must exist in a catalog under governance_artifacts/oscal/; the
13+
# generator fails on any unknown id (no dangling references).
14+
framework: "DORA — Regulation (EU) 2022/2554"
15+
scope_note: >
16+
This register scopes DORA ICT-risk pillars to the AI-governance control surface
17+
of the Sentinel stack only. It is NOT a complete institutional DORA register;
18+
enterprise ICT scope (networks, endpoints, core banking) is out of scope here.
19+
catalogs:
20+
- catalog_sentinel_v24_excerpt.json
21+
- catalog_sentinel_v24_env_rte.json
22+
pillars:
23+
- id: P1
24+
name: "ICT risk management framework (Arts. 5-16)"
25+
narrative: >
26+
Hardware-attested admission and enclave-bound key custody establish the
27+
ICT control baseline for the AI execution environment; PQC-signed evidence
28+
protects the integrity of the risk-management record.
29+
controls: [env-01, env-02, cry-02]
30+
- id: P2
31+
name: "ICT-related incident management, classification & reporting (Arts. 17-23)"
32+
narrative: >
33+
The tamper-evident PQC WORM audit log provides the append-only,
34+
cryptographically verifiable incident record DORA requires; containment
35+
reachability ensures incidents can be terminally actuated.
36+
controls: [cry-02, con-04]
37+
- id: P3
38+
name: "Digital operational resilience testing (Arts. 24-27)"
39+
narrative: >
40+
The containment kill-switch ratchet is verified by model checking (daily
41+
reachability) and by quarterly live-actuation testing on canaries —
42+
DORA advanced-testing evidence for the terminal AI risk control.
43+
controls: [con-04, con-07]
44+
- id: P4
45+
name: "ICT third-party risk management (Arts. 28-44)"
46+
narrative: >
47+
Third-party / GPAI-provider assurance (supplier attestation, contractual
48+
auditability, exit plans) is an organisational control surface not yet
49+
represented by a runnable Sentinel control — reported as a coverage gap.
50+
controls: []
51+
- id: P5
52+
name: "Information & intelligence sharing (Art. 45)"
53+
narrative: >
54+
Cross-institution threat/intelligence sharing maps to the GIEN / SIP v3.0
55+
federated layer (Tier C, design-stage); no Tier-A runnable control backs
56+
it yet — reported as a coverage gap.
57+
controls: []

0 commit comments

Comments
 (0)