Risk Register · Reference guide · Industry practice + Mitigata build implications
This guide explains, in depth, how a single risk gets added to a risk register the way the industry does it — and what each step means for how we build it on Mitigata. It draws on ISO 31000, NIST SP 800-30 / 800-37 / IR 8286, COSO ERM, and current GRC practice. Read it before building; the nuances here are the ones that quietly decide whether the register is trusted or ignored.
How to read the Mitigata notes. Throughout, indented call-outs marked Build note translate the industry practice into a concrete decision for our product, and flag where our foundation PRD (
risk-register-foundation-prd.md) already aligns or still needs work.
A risk register is not a list of bad things — it is a decision-support record. Every field exists so that a specific person can make or defend a specific decision: how bad is this, who owns it, what are we doing, and is that enough? If a field doesn't serve a decision, it's noise. The most common failure in the industry is a register built for an audit and never looked at again ("documentation theater"). Everything below is in service of keeping the register live and decision-grade, not complete.
You cannot add a risk well until two things are configured. Adding risks before these exist is the root cause of inconsistent registers.
Risks are grouped by domain so they can be filtered, aggregated, and routed to the right owner. A practical starting set: cybersecurity, privacy, operational, financial, compliance, reputational, strategic, physical, third-party, social/people. Categories can be added later; 6–10 cover most organisations.
Build note. This is our global risk-type spine. The type is not cosmetic — it selects the taxonomy, the scoring dimensions, the matrix, and which consumers (GRC, Privacy, Insurance, Auditor) surface the risk. Keep types extensible (config-seeded at launch, per the foundation doc), and remember privacy carries an extra impact dimension (Data Principal harm) the others don't.
The industry default is a 5×5 likelihood × impact matrix, because it is simple enough for non-specialists and granular enough to differentiate. Two parts must be pinned down:
Likelihood scale — each level gets a definition with a time horizon (almost always 12 months), not just a label:
| Score | Label | Definition |
|---|---|---|
| 1 | Rare | <5% chance in 12 months |
| 2 | Unlikely | 5–20% |
| 3 | Possible | 20–50% |
| 4 | Likely | 50–80% |
| 5 | Almost Certain | >80% |
Impact scale — defined in concrete terms, usually money, but ideally multi-dimensional:
| Score | Label | Financial proxy |
|---|---|---|
| 1 | Negligible | <$10K |
| 2 | Minor | $10K–$100K |
| 3 | Moderate | $100K–$500K |
| 4 | Major | $500K–$2M |
| 5 | Catastrophic | >$2M / existential |
Risk score = Likelihood × Impact (1–25), mapped to bands: 1–5 Low, 6–12 Medium, 15–20 High, 21–25 Critical. These bands and dollar figures must be tuned to the org — $10K is negligible for an enterprise and severe for a startup.
Build note — the nuance that bites. Manual scoring (our chosen model) is correct and standard — a score must carry human judgment. But manual scoring without shared, visible definitions produces garbage: two owners reading "Likelihood 4" differently. So the discipline must live in the UI — show the scale definition inline at the moment of scoring, not buried in a settings page. This is the single biggest lever on register quality and our foundation doc currently under-specifies it.
These three are routinely confused and they gate the treatment decision:
Build note. At minimum we need appetite/tolerance thresholds per risk type so the register can answer "is this residual within appetite?" — that's what turns a score into a decision (and later powers auto-flagging when residual exceeds tolerance). Capacity is usually a board-level number, optional for v1. Our foundation doc references "appetite" in the lifecycle but doesn't model the threshold object yet — worth adding.
Risks come from four source streams, used together:
Build note. This maps to our hybrid creation: the standard scenario library (
standard_risk_scenarios) is the framework-library checklist, and "promote from finding/threat/vuln" is the continuous-signal stream. Make the library the default starting point — it both speeds entry and enforces good descriptions.
A label is not a risk. The accepted structure has three parts — cause → event → consequence:
"Due to [cause/vulnerability], [threat event] could occur, resulting in [business impact]."
Bad: "Cybersecurity." Good: "Due to insufficient access controls on cloud infrastructure, an unauthorised party could access customer data, resulting in regulatory fines, breach-notification costs, and customer churn."
Build note. Don't store the statement as one free-text blob. Capture cause, event, consequence as separate fields (pre-filled from the scenario template, editable). This makes risks searchable, de-dupable, and forces specificity. NIST 800-30 frames the same thing as threat-source → threat-event → vulnerability → adverse impact.
Assign the risk type, and link it to what it relates to. Cyber registers (NIST 800-30) additionally tag the threat source and vulnerability.
Build note — the anchor rule. Our invariant is no orphan risks. Control linkage is mandatory only for types with a control catalog (cyber, privacy → SCF). For physical/social/operational risk with no catalog, the required anchor is an asset and/or a treatment plan. Enforce "at least one anchor" at save; choose which anchor types are valid based on risk type.
Exactly one owner, and they must have authority to allocate resources. The owner monitors the risk, drives treatment, reports at reviews, and escalates when it breaches tolerance. "Owned by the risk team" = unowned = unmanaged.
Distinguish three roles that beginners merge:
Build note. Owner is a mandatory single-select on create. Control/action owners are separate fields on the treatment plan, not the risk header. Our primary-users list already separates risk owner from approver — keep the approver distinct (you cannot approve your own acceptance).
Inherent = the risk before any controls / treatment, scored on the matrix by a human.
Two nuances:
Build note. Pick a definition of inherent and state it in the UI ("before any controls"). Because privacy risk adds Data Principal harm severity (DPDPA-M-34), the privacy-type impact input needs that extra dimension alongside likelihood × impact. Decide the "max vs weighted" rule once, globally.
List controls already in place that bear on the risk, and how effective each is. Effectiveness is what justifies the gap between inherent and residual: preventive controls lower likelihood, corrective controls lower impact.
Build note. Link to the GRC control catalog where the type supports it. Don't auto-compute residual from coverage (see step 8) — but do surface linked controls and their status so the human residual judgment is informed.
Compare the (inherent, or current) risk to appetite/tolerance, then choose:
| Treatment | When | Records | Approval |
|---|---|---|---|
| Open (default) | Identified, no decision yet | nothing | no |
| Mitigate | You can reduce likelihood/impact with controls | linked controls + actions | no |
| Accept | Within appetite; treatment cost > benefit | approver, rationale, expiry | yes |
| Transfer | Another party absorbs it better | insurance policy / outsourcing ref | no (policy must exist) |
| Avoid | Unacceptable, no sufficient treatment | decision to stop the activity | yes |
Nuances: Accept must be time-boxed (an acceptance with no expiry becomes a permanent blind spot) and signed by someone other than the risk owner. Transfer doesn't eliminate risk — residual remains (deductibles, coverage limits, claim disputes); it reshapes it. Avoid changes the business, so it needs senior sign-off.
Build note. This is our five-value treatment field. The critical nuance for us: Open risks must be visibly chased, not allowed to rot — surface an "untreated risks" view and an age counter, or Open becomes a quiet backlog. Wire Accept →
risk_acceptance(with mandatory expiry), Transfer → Insurance module, Mitigate → controls, Avoid → decision record.
Residual = the risk after controls/treatment, re-scored by a human. Iron rule: residual ≤ inherent, always. If residual exceeds inherent, the controls aren't doing what you think, or the scoring is wrong.
A third value some mature programs track: target risk — the level you expect once planned (not-yet-complete) treatment lands. Useful to show the trajectory, but optional.
Build note — our deliberate stance. Many GRC tools compute residual from control-effectiveness math. We deliberately keep it manual (invariant #3). That's defensible and auditable, but it means the UI must (a) hard-validate residual ≤ inherent, and (b) prompt re-scoring when treatment status changes, or residual goes stale. CRQ (the playlist handoff) is our path for anyone who wants the quantitative dollar version — handled manually, off-platform, future-automatable.
For mitigated risks, record: which controls/actions are in place or planned, who owns each action, and target completion dates. This is the bridge from "we decided" to "we did."
Build note. The treatment plan is a child object of the risk (actions, owners, due dates, status). It's also the anchor for non-control risk types in step 3.
The risk becomes live: it gets a status and a next-review date. Cadence is keyed to severity — Critical/High monthly, Medium quarterly, Low semi-annual — plus event-driven reviews (new regulation, incident, reorg, M&A). A register nobody reviews is the #1 failure mode.
Build note — gap in our foundation doc. Industry treats review cadence + a next-review-date field + automated reminders as core, not optional. Our foundation PRD is light here. Add: a
nextReviewDate, a per-band default cadence, staleness flags, and reminder events. Without it the register dies quietly.
The columns the steps above produce, i.e. the data model of one row:
| Field | Source step | Notes |
|---|---|---|
| Risk ID | auto | Immutable handle. |
| Risk type / category | 3 | Drives lens, matrix, consumers. |
| Cause / Event / Consequence | 2 | Three fields, not one blob. |
| Threat source / Vulnerability | 1,3 | Cyber/privacy types (NIST 800-30). |
| Anchors (control / asset / treatment plan) | 3 | ≥1 mandatory; no orphans. |
| Owner | 4 | Single, with authority. |
| Inherent likelihood / impact / score | 5 | Manual; multi-dimensional impact. |
| Privacy: Data Principal harm | 5 | Privacy type only. |
| Linked controls + effectiveness | 6 | Informs residual. |
| Treatment | 7 | Open / Mitigate / Accept / Transfer / Avoid. |
| Acceptance: approver, rationale, expiry | 7 | If Accepted. |
| Transfer ref (policy) | 7 | If Transferred. |
| Residual likelihood / impact / score | 8 | ≤ inherent. |
| Target risk | 8 | Optional trajectory. |
| Treatment plan: actions, owners, due dates | 9 | Child object. |
| Status | 10 | Lifecycle state. |
| Next review date + cadence | 10 | Drives reminders. |
| Discussion / comments | all | Audit-grade thread. |
| Activity log | all | Every change, who + when. |
Build note. That escalation path is exactly the CRQ request list ("playlist") — qualitative everywhere, quantitative on demand for selected risks. Keep them as two layers; CRQ overlays, never overwrites, the manual score.
| Standard | What it gives you | Where it lands in the steps |
|---|---|---|
| ISO 31000 | The loop: identify → analyse → evaluate (vs appetite) → treat → monitor & communicate | Frames steps 1–10 end to end |
| NIST SP 800-30 | Cyber risk assessment method: threat source → vulnerability → likelihood → impact → response | Steps 1, 3, 5, 7 |
| NIST SP 800-37 (RMF) | System lifecycle: Prepare, Categorize, Select, Implement, Assess, Authorize, Monitor | Program context around the register |
| NIST IR 8286 series | The Cybersecurity Risk Register (CSRR) object + risk dependencies/aggregation | The register data model itself |
| COSO ERM | Enterprise framing, appetite, portfolio view | Steps 1, 7; appetite gating |
What to carry into the build, distilled: