Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/tutorials/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Create a policy document that translates risk appetite into mandatory rules —

### Creating a risk catalog

When you need a structured inventory of organizational or system risks—**risk categories** (appetite, optional max-severity), per-risk **severity**, optional RACI **owner** and **impact**, and optional **threats** links backed by `metadata.mapping-references`—so policies can reference mitigated or accepted risks → [Risk Catalog Guide](policy/risk-catalog-guide) (Layer 3).
When you need a structured inventory of organizational or system risks—**risk categories** (appetite, optional max-severity), per-risk **severity**, optional **`rank`** for ordering within the catalog, optional RACI **owner** and **impact**, and optional **threats** links backed by `metadata.mapping-references`—so policies can reference mitigated or accepted risks → [Risk Catalog Guide](policy/risk-catalog-guide) (Layer 3).

## What You'll Build

Expand All @@ -58,7 +58,7 @@ When you need a structured inventory of organizational or system risks—**risk
| **Layer 1** — Guidance | Guidance Catalog (guidelines, groups, mapping-references); [Principle Catalog](https://gemara.openssf.org/schema/principlecatalog.html) (principles, groups) | [Guidance Catalog Guide](guidance/guidance-guide) |
| **Layer 2** — Controls | Threat Catalog + Control Catalog (assessment requirements, threats) | [Threat Assessment](controls/threat-assessment-guide), [Control Catalog](controls/control-catalog-guide) |
| **Layer 3** — Policy | Policy Document (scope, imports, adherence) | [Policy Guide](policy/policy-guide) |
| **Layer 3** — Risks | Risk Catalog (risk categories, appetite, risks, optional threat mappings) | [Risk Catalog Guide](policy/risk-catalog-guide) |
| **Layer 3** — Risks | Risk Catalog (risk categories, appetite, risks, optional rank and threat mappings) | [Risk Catalog Guide](policy/risk-catalog-guide) |
| **Cross-artifact** | Mapping Document (typed `source`/`target` references, `targets` per mapping, relationship types; entry types per schema include guidelines, controls, Principle, threats, risks, and others) | [Mapping Document Guide](mapping/mapping-document-guide) |

## What You'll Need
Expand Down
2 changes: 2 additions & 0 deletions docs/tutorials/policy/risk-catalog-example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ risks:
description: "Mutable image tags or lack of verification can lead to pulling stale or compromised images, increasing supply chain and runtime risk."
group: "infrastructure"
severity: "High"
rank: 2
impact: "Supply chain compromise, unauthorized code execution, or data exfiltration."
owner:
responsible:
Expand All @@ -62,6 +63,7 @@ risks:
description: "Images may be tampered with in transit or at rest, or built from poisoned dependencies or build pipelines."
group: "infrastructure"
severity: "High"
rank: 1
threats:
- reference-id: CCC
entries:
Expand Down
15 changes: 10 additions & 5 deletions docs/tutorials/policy/risk-catalog-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ description: Step-by-step guide to creating Gemara-compatible risk catalogs

This guide walks through creating a **Risk Catalog** using the [Gemara](https://gemara.openssf.org/) project. The document conforms to `#RiskCatalog` in [riskcatalog.cue](https://github.com/gemaraproj/gemara/blob/main/riskcatalog.cue) and the published [Risk Catalog schema](https://gemara.openssf.org/schema/riskcatalog.html). Examples use `gemara-version: "1.0.0"` to match the [v1.0.0](https://github.com/gemaraproj/gemara/releases/tag/v1.0.0) specification release; adjust if you target a different Gemara version.

**The basic idea:** A Risk Catalog is a structured list of **[risks](../../model/02-definitions.html#risk)** that might affect an organization, system, or service. You organize them into **groups** (schema type `#RiskCategory`) that express how much risk you are willing to carry ( risk appetite) and optionally cap how bad a single risk in that group can be (**max-severity**). Each risk has an assessed **severity** and can point to Layer 2 **[threats](../../model/02-definitions.html#threat)** so mitigations and policies stay traceable to threat catalogs.
**The basic idea:** A Risk Catalog is a structured list of **[risks](../../model/02-definitions.html#risk)** that might affect an organization, system, or service. You organize them into **groups** (schema type `#RiskCategory`) that express how much risk you are willing to carry ( risk appetite) and optionally cap how bad a single risk in that group can be (**max-severity**). Each risk has an assessed **severity** and can point to Layer 2 **[threats](../../model/02-definitions.html#threat)** so mitigations and policies stay traceable to threat catalogs. Optionally, **`rank`** records a total order among risks in this catalog (for example when several share the same severity); see Step 2.

In technical terms:
* **[Risk catalogs](../../model/02-definitions.html#risk-catalog)** declare `metadata` (including `type: RiskCatalog`), **groups** as `#RiskCategory` (id, title, description, `#RiskAppetite` appetite, optional `#Severity` max-severity), and **[risks](../../model/02-definitions.html#risk)** (`#Risk`) with required id, title, description, `group`, and `severity`, plus optional owner (`#RACI`), impact, and `threats` (`#MultiEntryMapping` from [mapping_inline.cue](https://github.com/gemaraproj/gemara/blob/main/mapping_inline.cue).
* **[Risk catalogs](../../model/02-definitions.html#risk-catalog)** declare `metadata` (including `type: RiskCatalog`), **groups** as `#RiskCategory` (id, title, description, `#RiskAppetite` appetite, optional `#Severity` max-severity), and **[risks](../../model/02-definitions.html#risk)** (`#Risk`) with required id, title, description, `group`, and `severity`, plus optional **`rank`**, owner (`#RACI`), impact, and `threats` (`#MultiEntryMapping` from [mapping_inline.cue](https://github.com/gemaraproj/gemara/blob/main/mapping_inline.cue).
* **When the catalog lists any risks**, the schema requires at least one group (`groups` becomes required in that case).
* **Groups** use appetite values `Minimal`, `Low`, `Moderate`, or `High` and optional group-level **max-severity** (`Low`, `Medium`, `High`, `Critical`).
* **Risks** use **severity** at the same scale (`Low` through `Critical`). Links under `threats` use the same multi-entry mapping pattern as control and threat catalogs; every outer `reference-id` must match an id in `metadata.mapping-references`.
* **Risks** use **severity** at the same scale (`Low` through `Critical`). Optional **`rank`** is an integer; **lower values mean higher relative importance** among risks that set `rank`. Each `rank` value must be **unique** among risks that specify it (partial ranking is allowed: risks without `rank` are unconstrained). Links under `threats` use the same multi-entry mapping pattern as control and threat catalogs; every outer `reference-id` must match an id in `metadata.mapping-references`.

This exercise produces a risk catalog that you can reference from a [Policy](policy-guide) when documenting mitigated and accepted risks.

Expand Down Expand Up @@ -105,6 +105,7 @@ Define **[risks](../../model/02-definitions.html#risk)** with required fields an
| `description` | Yes | Explains the risk scenario and context |
| `group` | Yes | Id of a group in this catalog (from Step 1) |
| `severity` | Yes | Impact level: `Low`, `Medium`, `High`, or `Critical` |
| `rank` | No | Optional ordering within the catalog: integer; **lower = more important**. Unique among risks that set `rank`. Omit when severity alone is enough. |
| `owner` | No | RACI roles (responsible, accountable, consulted, informed) for this risk |
| `impact` | No | Business or operational impact description |
| `threats` | No | Links to Layer 2 threat entries via reference-id and entries (see below) |
Expand All @@ -120,6 +121,7 @@ risks:
description: "Mutable image tags or lack of verification can lead to pulling stale or compromised images, increasing supply chain and runtime risk."
group: "infrastructure"
severity: "High"
rank: 2
impact: "Supply chain compromise, unauthorized code execution, or data exfiltration."
owner:
responsible:
Expand All @@ -138,6 +140,7 @@ risks:
description: "Images may be tampered with in transit or at rest, or built from poisoned dependencies or build pipelines."
group: "infrastructure"
severity: "High"
rank: 1
threats:
- reference-id: CCC
entries:
Expand Down Expand Up @@ -169,12 +172,12 @@ From a **clone of this repository** (uses the working tree’s CUE package, incl
cue vet -c -d '#RiskCatalog' . docs/tutorials/policy/risk-catalog-example.yaml
```

Fix any errors (e.g. missing required fields, risks present without `groups`, invalid `group` reference, severity or appetite not in allowed enums, or a `threats` outer `reference-id` not listed in `metadata.mapping-references`) so the catalog is schema-valid.
Fix any errors (e.g. missing required fields, risks present without `groups`, invalid `group` reference, severity or appetite not in allowed enums, **duplicate `rank` values** among risks that set `rank`, or a `threats` outer `reference-id` not listed in `metadata.mapping-references`) so the catalog is schema-valid.


## Minimal Full Example

A complete, schema-valid example is in [risk-catalog-example.yaml](risk-catalog-example.yaml) in this directory. The following matches that file (omit optional sections such as `owner`, `impact`, or `threats` in your own catalogs if you do not need them). Both align with `#RiskCatalog` in [riskcatalog.cue](https://github.com/gemaraproj/gemara/blob/main/riskcatalog.cue).
A complete, schema-valid example is in [risk-catalog-example.yaml](risk-catalog-example.yaml) in this directory. The following matches that file (omit optional sections such as `rank`, `owner`, `impact`, or `threats` in your own catalogs if you do not need them). Both align with `#RiskCatalog` in [riskcatalog.cue](https://github.com/gemaraproj/gemara/blob/main/riskcatalog.cue).

```yaml
# Organization Risk Catalog for Cloud and Container Workloads (Layer 3)
Expand Down Expand Up @@ -223,6 +226,7 @@ risks:
description: "Mutable image tags or lack of verification can lead to pulling stale or compromised images, increasing supply chain and runtime risk."
group: "infrastructure"
severity: "High"
rank: 2
impact: "Supply chain compromise, unauthorized code execution, or data exfiltration."
owner:
responsible:
Expand All @@ -241,6 +245,7 @@ risks:
description: "Images may be tampered with in transit or at rest, or built from poisoned dependencies or build pipelines."
group: "infrastructure"
severity: "High"
rank: 1
threats:
- reference-id: CCC
entries:
Expand Down
8 changes: 8 additions & 0 deletions riskcatalog.cue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import "list"

if risks != _|_ {
_uniqueRiskIds: {for i, r in risks {(r.id): i}}
// Each distinct rank value may appear at most once among risks that set rank (partial ranking allowed).
// Keys are stringified ranks because CUE struct labels cannot be raw integers.
_uniqueRiskRanks: {for i, r in risks if r.rank != _|_ {"\(r.rank)": i}}
groups: [#RiskCategory, ...#RiskCategory]
let _validGroupIds = [for g in groups {g.id}]

Expand Down Expand Up @@ -82,6 +85,11 @@ import "list"
// severity describes the assessed level of this risk
severity: #Severity @go(Severity)

// rank optionally orders risks for the same catalog (e.g. when several share the same severity).
// Lower values mean higher relative importance. Omitted when the four severity levels are enough.
// When set, each value must be unique among all risks in the catalog that specify rank.
rank?: int @go(Rank) @yaml("rank,omitempty")

// owner defines the RACI roles responsible for managing this risk
owner?: #RACI @go(Owner)

Expand Down
3 changes: 3 additions & 0 deletions test/schema_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ func TestSchemaValidation(t *testing.T) {
// RiskCatalog — positive
{"valid risk catalog", "./test-data/good-risk-catalog.yaml", "#RiskCatalog", false, ""},

// RiskCatalog — negative
{"risk catalog with duplicate rank", "./test-data/bad-risk-catalog-duplicate-rank.yaml", "#RiskCatalog", true, ""},

// Policy — positive
{"valid policy", "./test-data/good-policy.yaml", "#Policy", false, ""},
{"valid security policy", "./test-data/good-security-policy.yml", "#Policy", false, ""},
Expand Down
37 changes: 37 additions & 0 deletions test/test-data/bad-risk-catalog-duplicate-rank.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
metadata:
id: BAD-RISK-CATALOG-DUP-RANK
type: RiskCatalog
gemara-version: "0.20.0"
version: "1.0.0"
description: Invalid — two risks use the same rank
author:
id: test
name: Test
type: Human
mapping-references:
- id: EXAMPLE-THREAT-CATALOG
title: Example
version: "1.0.0"
description: x

title: Invalid duplicate rank

groups:
- id: CAT-SECURITY
title: Security
description: x
appetite: Low

risks:
- id: RISK-A
title: A
description: x
group: CAT-SECURITY
severity: High
rank: 1
- id: RISK-B
title: B
description: x
group: CAT-SECURITY
severity: High
rank: 1
9 changes: 9 additions & 0 deletions test/test-data/good-risk-catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ risks:
description: Third-party or base container images may contain known vulnerabilities or malicious code, leading to exploitation at runtime.
group: CAT-SECURITY
severity: High
rank: 2
owner:
responsible:
- name: Platform Engineering Lead
Expand Down Expand Up @@ -81,3 +82,11 @@ risks:
group: CAT-COMPLIANCE
severity: Medium
impact: Regulatory fines, audit findings, and reputational damage.

- id: RISK-004
title: Cluster Admin Credential Exposure
description: Long-lived admin credentials for the cluster control plane may be exposed through logs, tickets, or shared stores.
group: CAT-SECURITY
severity: High
rank: 1
impact: Full cluster compromise; all workloads and secrets on the platform are at risk.
Loading