Skip to content
Open
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
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ With 20+ markdown files, know which document answers which question:
| Why does SecID exist? | [RATIONALE.md](docs/explanation/RATIONALE.md) |
| Why was X designed this way? | [DESIGN-DECISIONS.md](docs/explanation/DESIGN-DECISIONS.md) - longer-form explanations |
| When was X decided, what were the alternatives? | [DECISIONS.md](DECISIONS.md) - chronological ADR log with rejected alternatives |
| **How do I decide what type something is?** | **[CLASSIFY.md](docs/guides/CLASSIFY.md)** - the classification procedure: type, subtype, namespace, name; boundary tests; worked examples; how to spot gaps |
| How do I add a namespace? | [REGISTRY-GUIDE.md](docs/guides/REGISTRY-GUIDE.md) - principles, patterns, process |
| How do I add a namespace (step-by-step)? | [ADD-NAMESPACE.md](docs/guides/ADD-NAMESPACE.md) - task-oriented walkthrough |
| How do I update an existing namespace? | [UPDATE-NAMESPACE.md](docs/guides/UPDATE-NAMESPACE.md) |
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ SecID is built across multiple repositories under the [CloudSecurityAlliance](ht
- **Browse namespaces:** [registry/](registry/)
- **Give feedback:** use the `submit_feedback` tool on the [live MCP server](https://secid.cloudsecurityalliance.org/) (AI/MCP-only — request a missing source, report wrong data, suggest improvements)
- **Contribute:** [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)
- **Classify something new:** [CLASSIFY.md](docs/guides/CLASSIFY.md) — how we decide what type a thing is, and how you can decide the same way
- **Hands-on guides:** [docs/guides/](docs/guides/) — step-by-step walkthroughs for adding namespaces, writing patterns, and converting formats

**Using AI tools?** We encourage it. See [AGENTS.md](AGENTS.md) for general agent instructions. If you're using Claude Code, Gemini, or similar tools, run `/init` to pick up the repo-specific configuration files automatically.
Expand Down
195 changes: 195 additions & 0 deletions docs/guides/CLASSIFY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Classification Guide

**How SecID decides what type something is — and how you can decide the same way.**

Classification has to be *reproducible*. The same source, handed to a different person or a different AI
session two years from now, must land in the same place. Otherwise the registry drifts and nobody notices,
because a wrong classification still resolves — it just resolves somewhere nobody thinks to look.

This guide is the procedure. [TYPES-AND-SUBTYPES.md](../reference/TYPES-AND-SUBTYPES.md) is the catalog of
what exists; `registry/<type>.md` holds each type's definition and its boundaries against neighbours.

## TL;DR

Given something new, decide three things:

1. **Type** — one of `advisory`, `weakness`, `ttp`, `control`, `capability`, `methodology`, `disclosure`,
`regulation`, `entity`, `reference`.
2. **Namespace** — the publisher's DNS domain (`nist.gov`, `pcisecuritystandards.org`). Verify with
`host <domain>` when unsure.
3. **Name** — what the publisher calls it (`csf`, `pci-dss`, `ccm`, `cwe`).

Then `secid:<type>/<namespace>/<name>[@version][#subpath]`.

## Step 1 — Determine the type

| The thing is… | Type |
|---|---|
| A publication about a specific vulnerability or event (CVE, GHSA, vendor advisory, incident report) | `advisory` |
| A category of mistake, described abstractly (CWE, OWASP Top 10) | `weakness` |
| A catalog of adversary behaviour (ATT&CK, ATLAS, CAPEC) | `ttp` |
| A statement of what ought to be true — framework, benchmark, hardening guide | `control` |
| A legal obligation — statute, regulation, directive, binding order | `regulation` |
| A repeatable process for producing analysis, a score, a mapping, or a decision | `methodology` |
| A security feature of a specific product, with configuration and audit surface | `capability` |
| A programme or channel for reporting vulnerabilities | `disclosure` |
| An organization, product, or service, referenced as itself | `entity` |
| **Anything else** — paper, blog, report, spec, glossary, model card, course | `reference` |

### `reference` is the catch-all, by design

When something does not clearly fit another type, it is `reference`. This is deliberate policy, not
failure: landing in `reference` and migrating later is cheaper and more honest than force-fitting into a
wrong type or inventing a new one.

The consequence is that `reference` is heterogeneous on purpose — it holds identifier systems (arXiv, DOI),
format specifications (SARIF, CycloneDX), glossaries, papers, and courses simultaneously. That is the policy
working, not the type decaying. Use `subtype:` to discriminate within it.

**The type list is fixed.** Do not invent a type. If something genuinely needs one, it goes through the
four-criteria gate in [TYPES-AND-SUBTYPES.md](../reference/TYPES-AND-SUBTYPES.md) — and adding a type
requires coordinated changes across five repositories, so the bar is high and the default answer is a
subtype.

### Publisher packaging beats object ontology

When a publisher ships several kinds of thing inside one framework, follow the publisher.

ATT&CK is the worked case. It contains techniques, adversary groups, software, and mitigations. A group is
really an organization and a mitigation is really a control — but ATT&CK ships them inside a TTP framework,
so all of them live under `ttp`:

```
secid:ttp/mitre.org/attack#T1059.003 technique
secid:ttp/mitre.org/attack#G0007 adversary group
secid:ttp/mitre.org/attack#S0154 software
secid:ttp/mitre.org/attack#M1036 mitigation
```

This follows [principle 6](../../PRINCIPLES.md#6-follow-the-source). Splitting a publisher's framework
across four SecID types to satisfy an ontology would make every reference to it harder to construct and
harder to verify.

## Step 2 — Check for a subtype

Subtypes refine within a type without inflating the type list. Consult
[TYPES-AND-SUBTYPES.md](../reference/TYPES-AND-SUBTYPES.md) for the current vocabulary — a glossary is
`reference` with `subtype: ["glossary"]`; an incident report is `advisory` with `subtype: ["incident"]`.

Subtype values are **not** freely addable: they are declared in SecID-Service's `type-registry.ts` and CI
rejects registry data using an undeclared value (ADR-008). If the right value does not exist, propose it —
do not invent one in a registry file.

## Step 3 — Determine the namespace

The namespace is the publisher's **DNS domain**, because domains are owned, verifiable, and stable in a way
that names and acronyms are not.

- Use the canonical domain the publisher uses for the thing itself
- For US states, the state portal (`ca.gov`, `ny.gov`, `mass.gov`) — not the expanded form
- For informal groups or individual researchers, the most stable domain available (university, GitHub org,
project site). Record the uncertainty in notes
- Namespace matching is **shortest-to-longest**, so `github.com` and `github.com/advisories` can coexist;
the longest match wins

## Step 4 — Name, version, subpath

- **Name** — what the publisher calls the source, in the publisher's form (`csf`, not `cswp.29`). Record a
publication number separately if one exists
- **Version** — the version string the artifact states about itself, not a marketing label
- **Subpath** — the item identifier, preserved exactly: `RHSA-2026:0932` keeps its colon, `T1059.003` keeps
its dot, `PR.AC-1` is unchanged. Never lossy-normalise ([principle 7](../../PRINCIPLES.md#7-never-normalize-lossily))

## Boundary tests

Each type file carries the authoritative test for its own boundaries, and several are sharper than a
one-line summary can be. Use these; do not re-derive them.

| Pair | Test | Authority |
|---|---|---|
| `control` / `methodology` / `reference` | **Duck test** — hand someone the document and ask "what do I DO with it?" *Implement these requirements* → control. *Follow this process to produce an output* → methodology. *Read or cite it* → reference | [methodology.md](../../registry/methodology.md) |
| `control` / `capability` / `entity` | **Verb test** — control says MUST ("encrypt data at rest"), entity says IS (neutral description), capability says CAN, actionably ("S3 default encryption, audit with `get-bucket-encryption`") | [capability.md](../../registry/capability.md) |
| `disclosure` / `entity` / `advisory` | *What is this organization?* → entity. *What vulnerabilities were published?* → advisory. *How do I report one?* → disclosure | [disclosure.md](../../registry/disclosure.md) |
| `weakness` / `advisory` | A *category* of mistake (CWE-79) vs a *specific instance* (CVE-2024-1234) | [weakness.md](../../registry/weakness.md) |
| `weakness` / `ttp` / `control` | What's wrong vs how attackers exploit it vs how to prevent it | [ttp.md](../../registry/ttp.md) |
| `regulation` / `control` | Is compliance *mandatory by law* in some jurisdiction? Regulations are mandatory; control frameworks are voluntary unless a regulation adopts one | [regulation.md](../../registry/regulation.md) |
| everything → `reference` | The negative list — what explicitly does *not* belong in the catch-all | [reference.md](../../registry/reference.md) |

When two tests both apply, pick the **primary purpose** and note the secondary. Genuine dual membership is
allowed and sometimes correct — CVSS is both a `methodology` (the scoring process) and a `reference` (the
specification document), because people cite each separately.

When nothing applies, use `reference`.

## Worked examples

| Thing | SecID | Why |
|---|---|---|
| NIST CSF 2.0 control PR.AC-1 | `control/nist.gov/csf@2.0#PR.AC-1` | Voluntary framework stating what ought to be true |
| EU AI Act | `regulation/eu/europa/...` | Legally binding within its jurisdiction |
| CVE-2021-44228 | `advisory/mitre.org/cve#CVE-2021-44228` | A specific vulnerability record |
| CWE-79 | `weakness/mitre.org/cwe#CWE-79` | A category of mistake |
| CVSS v4.0 | `methodology/first.org/cvss@4.0` | A repeatable process producing a score |
| The CVSS v4.0 specification document | `reference/first.org/cvss@4.0` | The document, as distinct from the method |
| AWS S3 default encryption | `capability/amazon.com/aws/s3/...` | A feature of one product |
| A NIST mapping between CSF and 800-53 | `methodology/nist.gov/...` | Produces a mapping — judgement, not format |
| ISO 27001 (licensed) | `control/iso.org/27001@2022` | Type is unaffected by licensing; content is metadata-only |
| An arXiv paper | `reference/arxiv.org/2303.08774` | Catch-all: a document |

Note CVSS appearing as **both** `methodology` and `reference`. That is the cross-type pattern, not an
error: the scoring process and the document describing it are different things people cite differently.
See [registry/README.md](../../registry/README.md).

## Edge cases

1. **Spans multiple types** — pick the primary purpose; note the secondary. Cross-type entries are legitimate
when both are genuinely cited (see CVSS above)
2. **Multiple versions** — each version is addressable via `@version`; the registry records lifecycle
3. **Publication number vs semantic name** — use the semantic name, record the publication number separately
4. **Already canonical elsewhere** — if a publisher hosts authoritative, version-tagged data (CVE, ATT&CK
STIX), point at it. Do not duplicate
5. **Unbounded identifier space** — blog slugs, usernames, paper IDs. Declare `open_pattern: true` so the
node is excluded from unscoped search while namespace-scoped resolution still works. Never use a
permissive regex without declaring it

## When to stop and ask

- **Type cannot be determined** after working through this guide → ask, and default to `reference` meanwhile
- **Publisher naming conflicts with an existing registry entry** → ask; propose a registry update
- **Licence terms unclear** → do not host content; metadata-only with a link to the source
- **The identifier set cannot be enumerated and has no structure** → stop. A pattern that matches everything
asserts everything is valid and degrades every query in the system

## Spotting gaps

A catalog of what exists cannot show you what is missing. To find gaps, ask which question something
answers — and notice which answers have no home:

| Question | Types answering it |
|---|---|
| What exists? | `weakness`, `advisory`, `ttp`, `entity` |
| What ought to be true? | `control`, `regulation` |
| What do I run? | *(nothing today)* |
| What was found or verified? | *(nothing today)* |
| Where is it written down? | `reference` |
| *(cross-cutting)* | `methodology`, `capability`, `disclosure` |

Two columns are empty. Machine-runnable content — Sigma rules, Semgrep rules, Nuclei templates, exploits —
answers *what do I run* and has nowhere to go. Recorded findings — certifications, malware analyses,
assessments — answer *what was found* and likewise. Both gaps were confirmed independently by mapping STIX's
object model, which leaves exactly Indicator, Malware Analysis, and Opinion homeless.

Use this frame when surveying a new vocabulary or corpus: map its object types onto the questions, and
whatever falls off the table is a real gap rather than an oversight. See
[docs/project/TODO.md](../project/TODO.md) for the standing vocabulary survey.

## Where the authoritative answers live

| Question | Source |
|---|---|
| What types exist, and what do they mean? | `registry/<type>.md` |
| What subtypes exist? | [TYPES-AND-SUBTYPES.md](../reference/TYPES-AND-SUBTYPES.md) |
| Is this subtype value legal? | SecID-Service `src/type-registry.ts` (CI-enforced) |
| Why was this decided? | [DECISIONS.md](../../DECISIONS.md), [DESIGN-DECISIONS.md](../explanation/DESIGN-DECISIONS.md) |
| How do I write the entry? | [ADD-NAMESPACE.md](ADD-NAMESPACE.md), [REGISTRY-GUIDE.md](REGISTRY-GUIDE.md) |
| How do I write the pattern? | [REGEX-WORKFLOW.md](REGEX-WORKFLOW.md) |
15 changes: 15 additions & 0 deletions registry/advisory.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,21 @@ secid:advisory/debian.org/dla#DLA-3456-1 # Debian LTS Advisory (extended
secid:advisory/debian.org/tracker#CVE-2024-1234 # Debian Security Tracker (CVE status)
```

## Advisory vs Weakness vs Reference

- **Advisory** (advisory): A publication about a *specific* vulnerability or event — CVE-2021-44228, RHSA-2026:0932
- **Weakness** (weakness): The *category* of mistake that instance belongs to — CWE-79
- **Reference** (reference): A document *about* vulnerabilities generally — a threat report, a research paper

The test is specificity plus event-hood. An advisory says "this happened, here, to this" and carries an
identifier issued when it was published. A report discussing a class of vulnerabilities is `reference`,
even when it names CVEs; a paper proposing a new attack technique is `reference`, even when the technique
later earns a `ttp` entry.

Incident reports — an AI system caused harm, a vehicle behaved unsafely — are `advisory` with
`subtype: ["incident"]`. They differ from vulnerability advisories in *what happened* (an event occurred, as
against a flaw being disclosed) but are identical in resolution shape: a publication with an ID and a URL.

## Relationships

Advisories connect through aliasing and enrichment:
Expand Down
2 changes: 1 addition & 1 deletion registry/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ References are for citing source materials that aren't covered by other types. I
| NIST SP 800-53 | `secid:control/nist.gov/800-53#AC-1` |
| NIST CSF | `secid:control/nist.gov/csf@2.0#GV.RM-01` |
| ISO 27001 | `secid:control/iso.org/27001@2022#A.8.1` |
| IETF RFCs | `secid:control/ietf.org/...` or specific protocol |
| An RFC that defines a *process* | `secid:methodology/ietf.org/rfc-3227` (evidence collection) — but RFCs are `reference` by default: `secid:reference/ietf.org/9116` |
| GDPR, HIPAA | `secid:regulation/europa.eu/gdpr`, `secid:regulation/govinfo.gov/hipaa` |
| CWE, OWASP Top 10 | `secid:weakness/mitre.org/cwe#CWE-79`, `secid:weakness/owasp.org/top10#A01` |
| CVE, NVD | `secid:advisory/mitre.org/cve#...`, `secid:advisory/nist.gov/nvd#...` |
Expand Down