Advisory Key Canonicalization Contract
Contract ID: CONTRACT-ADVISORY-KEY-001 Version: 1.0 Status: Published Last Updated: 2025-12-05
Overview
This contract defines how StellaOps canonicalizes advisory and vulnerability identifiers — CVE, GHSA, RHSA, DSA, USN, MSRC, and others — into a single stable key. A consistent canonical key lets VEX observations, policy findings, and risk assessments correlate reliably even when upstream sources name the same vulnerability in different formats.
Audience: developers integrating with Concelier/Excititor ingestion, VEX correlation, or any consumer that joins findings by advisory identifier.
Implementation Reference
Source: src/Concelier/__Libraries/StellaOps.Excititor.Core/Canonicalization/VexAdvisoryKeyCanonicalizer.cs
Data Model
VexCanonicalAdvisoryKey
The canonical advisory key structure returned by the canonicalizer.
public sealed record VexCanonicalAdvisoryKey
{
/// <summary>
/// The canonical advisory key used for correlation and storage.
/// </summary>
public string AdvisoryKey { get; }
/// <summary>
/// The scope/authority level of the advisory.
/// </summary>
public VexAdvisoryScope Scope { get; }
/// <summary>
/// Original and alias identifiers preserved for traceability.
/// </summary>
public ImmutableArray<VexAdvisoryLink> Links { get; }
}
VexAdvisoryLink
Represents a link to an original or alias advisory identifier.
public sealed record VexAdvisoryLink
{
/// <summary>
/// The advisory identifier value.
/// </summary>
public string Identifier { get; }
/// <summary>
/// The type of identifier (cve, ghsa, rhsa, dsa, usn, msrc, other).
/// </summary>
public string Type { get; }
/// <summary>
/// True if this is the original identifier provided at ingest time.
/// </summary>
public bool IsOriginal { get; }
}
VexAdvisoryScope
The scope/authority level of an advisory.
| Value | Code | Description | Examples |
|---|---|---|---|
Global | 1 | Global identifiers | CVE-2024-1234 |
Ecosystem | 2 | Ecosystem-specific | GHSA-xxxx-xxxx-xxxx |
Vendor | 3 | Vendor-specific | RHSA-2024:1234, ADV-2024-1234 |
Distribution | 4 | Distribution-specific | DSA-1234-1, USN-1234-1 |
Unknown | 0 | Unclassified | Custom identifiers |
Canonicalization Rules
Identifier Patterns
| Pattern | Regex | Scope | Type |
|---|---|---|---|
| CVE | ^CVE-\d{4}-\d{4,}$ | Global | cve |
| GHSA | ^GHSA-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}$ | Ecosystem | ghsa |
| RHSA | ^RH[A-Z]{2}-\d{4}:\d+$ | Vendor | rhsa |
| DSA | ^DSA-\d+(-\d+)?$ | Distribution | dsa |
| USN | ^USN-\d+(-\d+)?$ | Distribution | usn |
| MSRC | ^(ADV|CVE)-\d{4}-\d+$ | Vendor | msrc |
| Other | * | Unknown | other |
Canonical Key Format
CVE identifiers remain unchanged as they are globally authoritative:
CVE-2024-1234 → CVE-2024-1234Non-CVE identifiers are prefixed with a scope indicator:
GHSA-xxxx-xxxx-xxxx → ECO:GHSA-XXXX-XXXX-XXXX RHSA-2024:1234 → VND:RHSA-2024:1234 DSA-1234-1 → DST:DSA-1234-1 custom-id → UNK:CUSTOM-ID
Scope Prefixes
| Scope | Prefix |
|---|---|
| Ecosystem | ECO: |
| Vendor | VND: |
| Distribution | DST: |
| Unknown | UNK: |
Usage
Canonicalizing an Identifier
var canonicalizer = new VexAdvisoryKeyCanonicalizer();
// Simple canonicalization
var result = canonicalizer.Canonicalize("CVE-2024-1234");
// result.AdvisoryKey = "CVE-2024-1234"
// result.Scope = VexAdvisoryScope.Global
// With aliases
var result = canonicalizer.Canonicalize(
"GHSA-xxxx-xxxx-xxxx",
aliases: new[] { "CVE-2024-1234" });
// result.AdvisoryKey = "ECO:GHSA-XXXX-XXXX-XXXX"
// result.Links contains both identifiers
Extracting CVE from Aliases
var cve = canonicalizer.ExtractCveFromAliases(
new[] { "GHSA-xxxx-xxxx-xxxx", "CVE-2024-1234" });
// cve = "CVE-2024-1234"
JSON Serialization
{
"advisory_key": "CVE-2024-1234",
"scope": "global",
"links": [
{
"identifier": "CVE-2024-1234",
"type": "cve",
"is_original": true
},
{
"identifier": "GHSA-xxxx-xxxx-xxxx",
"type": "ghsa",
"is_original": false
}
]
}
Determinism Guarantees
- Case normalization: All identifiers are normalized to uppercase internally
- Stable ordering: Links are ordered by original first, then alphabetically
- Deduplication: Duplicate aliases are removed during canonicalization
- Idempotence: Canonicalizing the same input always produces the same output
Unblocks
This contract unblocks the following tasks:
- EXCITITOR-POLICY-20-001
- EXCITITOR-POLICY-20-002
- EXCITITOR-VULN-29-001
- EXCITITOR-VULN-29-002
- EXCITITOR-VULN-29-004
- CONCELIER-VEXLENS-30-001
Related Contracts
- VEX Lens Contract - Uses advisory keys for linkset correlation
- Risk Scoring Contract - References advisory IDs in findings
