# Advisory Key Canonicalization Contract
**Contract ID:** `CONTRACT-ADVISORY-KEY-001`
**Version:** 1.0
**Status:** Published
**Last Updated:** 2025-12-05
## Overview
This contract defines the canonicalization rules for advisory and vulnerability identifiers used throughout StellaOps. It ensures consistent correlation of VEX observations, policy findings, and risk assessments across different identifier formats.
## Implementation Reference
**Source:** `src/Excititor/__Libraries/StellaOps.Excititor.Core/Canonicalization/VexAdvisoryKeyCanonicalizer.cs`
## Data Model
### VexCanonicalAdvisoryKey
The canonical advisory key structure returned by the canonicalizer.
```csharp
public sealed record VexCanonicalAdvisoryKey
{
///
/// The canonical advisory key used for correlation and storage.
///
public string AdvisoryKey { get; }
///
/// The scope/authority level of the advisory.
///
public VexAdvisoryScope Scope { get; }
///
/// Original and alias identifiers preserved for traceability.
///
public ImmutableArray Links { get; }
}
```
### VexAdvisoryLink
Represents a link to an original or alias advisory identifier.
```csharp
public sealed record VexAdvisoryLink
{
///
/// The advisory identifier value.
///
public string Identifier { get; }
///
/// The type of identifier (cve, ghsa, rhsa, dsa, usn, msrc, other).
///
public string Type { get; }
///
/// True if this is the original identifier provided at ingest time.
///
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
1. **CVE identifiers** remain unchanged as they are globally authoritative:
```
CVE-2024-1234 → CVE-2024-1234
```
2. **Non-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
```csharp
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
```csharp
var cve = canonicalizer.ExtractCveFromAliases(
new[] { "GHSA-xxxx-xxxx-xxxx", "CVE-2024-1234" });
// cve = "CVE-2024-1234"
```
## JSON Serialization
```json
{
"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
1. **Case normalization:** All identifiers are normalized to uppercase internally
2. **Stable ordering:** Links are ordered by original first, then alphabetically
3. **Deduplication:** Duplicate aliases are removed during canonicalization
4. **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](./vex-lens.md) - Uses advisory keys for linkset correlation
- [Risk Scoring Contract](./risk-scoring.md) - References advisory IDs in findings