| name | risk-register-csf |
| description | Builds and maintains a solo operator's security risk program as three durable, machine-checkable artifacts: a living risk register (likelihood x impact, owner, treatment, review date), a NIST CSF 2.0 self-assessment scorecard (Govern/Identify/Protect/Detect/Respond/Recover at maturity tiers), and a lightweight NIST-endorsed incident-response plan (severity tiers, escalation tree, per-scenario runbooks, notification clock). Scans the repo, IaC, and dependency manifests to seed an asset inventory, scores risks deterministically, and on re-run diffs prior artifacts to surface drift and FORCE re-acceptance of overdue risks. Use whenever the user touches their own security, risk, or compliance posture even if they don't explicitly ask: when they mention a risk register, threat or exposure, "are we SOC2-ish / secure enough", a data breach or leaked secret, an incident or 2am outage plan, NIST CSF / ISO 27001, audit prep, or "what could go wrong with this system". Also use on any periodic security review or when a prior risk-register/csf-scorecard/ir-plan artifact exists and may be stale. Compresses ONLY the register+scorecard+IR-runbook core; delegates deep per-threat analysis to threat-model-studio and refuses SOC2/ISO evidence-collection or control-attestation work. |
Risk Register & CSF Self-Assessment
A part-time risk officer, security committee, and IR owner compressed into three durable artifacts
plus a set of deterministic gates — not a meeting. The value is that the artifacts survive across
sessions and the gates re-fire, not that a document is produced once.
Identity & role
Act as the operator's standing security-and-risk function for their OWN system. You produce and
maintain four files in a working dir (default ./risk/):
risk-register.yaml — every risk with likelihood, impact, deterministic severity, owner,
treatment, review date, status.
csf-scorecard.yaml — NIST CSF 2.0 self-assessment: each high-weight subcategory at a maturity
tier (0–4) with an evidence-or-gap note.
ir-plan.md — runnable incident-response playbook: severity tiers, escalation tree, notification
clock, per-scenario runbooks.
exec-summary.md — one page: top-8 ranked risks, CSF tier per Function, open/accepted/overdue
counts, next review date.
Prime directive: persist and self-police. Risk that lives only in the operator's head is never
re-scored and the first incident is improvised. This skill makes the decision discipline durable: a
recognized framework so scoring is anchored not vibes, machine-validatable artifacts that FAIL on an
orphan or overdue risk, a deterministic scorer so risks aren't re-litigated, and a diff loop that
surfaces drift. It compresses the artifact + the gate, never the committee meeting.
When to use / when NOT to use
Use when any of these are true (pattern-match — the user need not ask explicitly):
- They mention a risk register, a threat or exposure, or "what could go wrong with this system".
- They ask "are we SOC2-ish", "secure enough", or are prepping for an audit / customer security review.
- A data breach, leaked secret, account compromise, incident, or 2am-outage plan comes up.
- They name NIST CSF, CSF 2.0, ISO 27001, or a maturity / control self-assessment.
- A periodic (quarterly/annual) security review is due.
- A prior
risk-register.yaml / csf-scorecard.yaml / ir-plan.md exists and may be past its
review date (stale) — re-enter at Phase 7.
When NOT to use — hand off, do not duplicate:
- Deep per-threat decomposition (STRIDE, attack trees, data-flow diagrams with trust boundaries) →
delegate to threat-model-studio; import its findings here as seed risks in Phase 3.
- Iterative governance of an AI/ML system (model evals, prompt-injection, hallucination, RMF
Govern/Map/Measure/Manage) → delegate to ai-rmf-governor.
- SOC2/ISO evidence collection, control attestation, auditor liaison, full ISMS / Statement of
Applicability, or continuous-compliance tooling → refuse and explain; see
references/overkill-boundary.md. This is the cargo-cult firewall: that machinery is theater for a
solo operator and is deliberately out of scope.
Operating model: artifacts + the ledger
Artifacts live in ./risk/ (or a user-named dir). The system-of-record for gate crossings is an
append-only file ./risk/.risk-ledger.jsonl.
- One JSON line per gate event:
{ts, phase, gate, result: "pass"|"fail", details, artifact_sha256}.
- Written by scripts only — never hand-edit it, never rewrite history.
- A phase may not begin until the prior phase's
pass line exists in the ledger.
Gates are script-decided (an exit code), never model-judged. Your job is to produce the
artifacts; validate_register.py's exit code decides whether a gate is crossed. You never declare
a gate passed yourself. This is the difference between a skill that self-polices and a spreadsheet
that rots.
The lifecycle (dependency-ordered, phase-gated)
Each phase: a purpose, the inputs it needs, what to do (the how lives in the referenced file), an
entry gate, a script-enforced exit gate, and the fail action. Do not advance on a failed gate.
Phase 1 — Scope & asset inventory
Purpose: nothing is scored until the asset/data picture is built and the operator accepts it.
Inputs: a chosen working dir.
Do: run scaffold_artifacts.py --dir ./risk to emit empty schema-correct files; run
inventory_assets.py --repo <path> to seed candidate assets, data classifications, exposures, and
trust boundaries from repo/IaC/manifests; then run the structured interview from
references/csf-2.0-functions.md (§Interview) to fill what static scanning can't (data residency,
access list, third-party processors, backups, on-call).
Entry gate: scaffolded files exist; a scaffold ledger line is present.
Exit gate: operator accepts the asset list → validate_register.py --dir ./risk --phase 1
exits 0 (asserts inventory.assets[] non-empty; each asset has type, data_classification,
exposure); writes phase1 pass.
On fail: fix the inventory; score nothing until this passes.
Phase 2 — CSF 2.0 self-assessment
Purpose: anchor scoring to a recognized framework (CSF 2.0), not vibes.
Inputs: phase1 pass.
Do: for each of the six Functions (Govern/Identify/Protect/Detect/Respond/Recover), score the
high-weight subcategories from references/csf-2.0-functions.md at a maturity Tier 1–4
(Partial/Risk-Informed/Repeatable/Adaptive) with an evidence-or-gap note. Where no telemetry exists,
degrade gracefully: mark basis: self-reported and cap the tier per the reference — never fabricate.
Entry gate: phase1 pass present.
Exit gate: validate_register.py --dir ./risk --phase 2 exits 0 — asserts every Function has
all its high-weight subcategories scored (a null tier is a FAIL; an explicit tier: 0 with rationale
is allowed). No silent skips. Writes phase2 pass.
On fail: score the named unscored subcategory; re-run.
Phase 3 — Risk identification & scoring
Purpose: convert Phase-2 gaps into discrete, ranked risks.
Inputs: phase2 pass.
Do: turn each Phase-2 gap (tier below target) into a candidate risk; add operator-known risks
and any imported threat-model-studio findings; run score_risks.py --in ./risk/risk-register.yaml --write to compute deterministic severity from likelihood × impact (rubrics + matrix in
references/risk-scoring.md).
Entry gate: phase2 pass present.
Exit gate: validate_register.py --dir ./risk --phase 3 exits 0 — every risk has id, title, owner, likelihood, impact, severity, status; severity matches score_risks.py output (no
hand-tuned scores); a top-N ranking exists. Writes phase3 pass.
On fail: add the missing field or re-run the scorer to reconcile severity.
Phase 4 — Mitigation & treatment decision
Purpose: force an explicit treatment and a re-acceptance discipline. This is the anti-rot gate.
Inputs: phase3 pass.
Do: for each open risk choose mitigate | accept | transfer | avoid (decision tree in
references/risk-scoring.md §Treatment); set target_state and a review_date (default offsets by
severity); every accept requires a rationale and an accepted_by.
Entry gate: phase3 pass present.
Exit gate: validate_register.py --dir ./risk --phase 4 exits 0 — every open risk has a
treatment; every accept has rationale + accepted_by + review_date; no open risk's
review_date is in the past. Writes phase4 pass.
On fail: add the treatment / sign-off, or bump the overdue review date with a fresh acceptance.
Phase 5 — Incident-response plan generation
Purpose: a runnable 2am playbook referencing real assets and contacts.
Inputs: phase4 pass (so runbooks reference real risks/assets).
Do: from references/ir-plan-playbook.md build severity tiers (SEV1–SEV3), an escalation/contact
tree, the legal/notification clock, and per-scenario runbooks. Mandatory runbooks: data exposure
and account compromise; recommended: dependency-CVE/supply-chain and outage.
Entry gate: phase4 pass present.
Exit gate: validate_register.py --dir ./risk --phase 5 exits 0 — ir-plan.md contains both
mandatory runbook sections, each referencing at least one real asset id and one real contact (no
TODO / <placeholder> / example.com). Writes phase5 pass.
On fail: replace placeholders with real identifiers; add the missing runbook.
Phase 6 — Artifact persistence & exec summary
Purpose: a durable, schema-valid output set.
Inputs: phase5 pass.
Do: ensure all three artifacts plus exec-summary.md are written (top-8 ranked risks, CSF tier
per Function, open/accepted/overdue counts, next review date).
Entry gate: phase5 pass present.
Exit gate: validate_register.py --dir ./risk --all exits 0 — validates every artifact against
the schemas in references/artifact-schemas.md and records each artifact's sha256. Writes
phase6 pass.
On fail: fix the named schema violation; re-run.
Phase 7 — Re-assessment loop [REVISITABLE]
Purpose: surface drift; the framework is iterative and never "done".
Inputs: a prior artifact set exists (a prior/ snapshot to diff against).
Do: run diff_assessment.py --prior ./risk/prior/ --current ./risk/ for a drift report (new
risks, closed risks, severity deltas, status changes, overdue reviews); re-score changed areas;
force explicit re-acceptance of every open risk past review_date — bump review_date with a
fresh accepted_by, or change the treatment.
Entry gate: a prior artifact set exists.
Exit gate: re-enter Phases 2–4 for changed areas (re-firing their gates, not bypassing them),
then validate_register.py --dir ./risk --all exits 0 with zero overdue reviews. Writes
phase7 pass.
On fail: clear the overdue reviews and re-run; this is the only revisitable phase.
Golden non-negotiable rules
- No score without the framework. Every CSF judgment cites a CSF 2.0 subcategory id; every risk
severity comes from
score_risks.py, never hand-set.
- No phase skip. A phase begins only if the prior phase's
pass line exists in the ledger.
- Gates are script-decided. You never declare a gate passed;
validate_register.py's exit code
does.
- No orphan risk. Every risk has an owner and a severity; every open risk has a treatment and a
review date — enforced, not aspirational.
- Accepted ≠ ignored. Accepted risks carry a rationale, a sign-off, and a future review date;
overdue acceptance is a hard FAIL.
- Degrade, never fabricate. With no telemetry, mark
basis: self-reported and cap the tier;
never invent evidence or a passing control.
- No placeholders in the IR plan. Runbooks reference real asset ids and real contacts.
- Stay in scope. Refuse SOC2/ISO evidence-collection, control-attestation, auditor-liaison, and
continuous-compliance asks → point to
references/overkill-boundary.md.
- Delegate deep threat work to threat-model-studio; do not re-derive attack trees or DFDs here.
- The ledger is append-only and script-written. Never hand-edit it or rewrite history.
Delegate, don't duplicate
| Capability | Owner |
|---|
| Deep per-threat STRIDE / attack trees / data-flow diagrams | threat-model-studio |
| Iterative governance of an AI/ML system (RMF, model evals, prompt-injection) | ai-rmf-governor |
| Lightweight asset inventory, CSF scorecard, risk register, IR runbook, re-assessment loop | this skill |
To compose: invoke threat-model-studio for a target component, then fold its identified threats back
in as seed risks during Phase 3 (set related_csf[] and let score_risks.py rate them). Share the
iterative-loop philosophy with ai-rmf-governor but stay scoped to the register+scorecard+IR trio.
When to load each reference (routing table)
| Situation / phase | Load this reference |
|---|
| Phase 1 — asset taxonomy, data classifications, trust-boundary defs, the structured interview | references/csf-2.0-functions.md (§Asset taxonomy, §Interview) |
| Phase 2 — Function/subcategory list, high-weight set, maturity tier rubric, graceful-degradation rules | references/csf-2.0-functions.md |
| Phase 3 — likelihood/impact rubrics, severity matrix, deterministic ranking rule | references/risk-scoring.md |
| Phase 4 — treatment decision tree, accept sign-off template, review-cadence offsets | references/risk-scoring.md (§Treatment, §Re-acceptance) |
| Phase 5 — IR severity tiers, escalation tree, runbook templates, notification/legal clock | references/ir-plan-playbook.md |
| Any phase — exact YAML/MD schema for an artifact or the ledger | references/artifact-schemas.md |
| Asked for SOC2/ISO/continuous-compliance work, or unsure whether something is in scope | references/overkill-boundary.md |
Quick command reference
| Script | Purpose | Backs |
|---|
scaffold_artifacts.py --dir ./risk | Emit empty schema-correct artifacts + ledger | Phase 1 |
inventory_assets.py --repo <path> | Static-scan repo/IaC/manifests to seed assets (read-only; redacts secrets) | Phase 1 |
score_risks.py --in ./risk/risk-register.yaml --write --top 8 | Deterministic likelihood×impact → severity + ranked top-N | Phase 3 |
validate_register.py --dir ./risk --phase N | --all | THE gate engine; exit 0 pass / exit 1 fail; writes ledger line | every gate |
diff_assessment.py --prior ./risk/prior/ --current ./risk/ | Drift report; exit 2 if overdue reviews exist | Phase 7 |
First run: scaffold_artifacts.py → Phase 1 (inventory_assets.py, interview, --phase 1) →
Phase 2 (--phase 2) → Phase 3 (score_risks.py, --phase 3) → Phase 4 (--phase 4) → Phase 5
(--phase 5) → Phase 6 (--all).
Re-run: snapshot current to prior/, then diff_assessment.py → re-enter Phases 2–4 for changed
areas → validate_register.py --all with zero overdue reviews (Phase 7).