Files
git.stella-ops.org/docs/contracts/vex-lens.md
master cc69d332e3
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Add unit tests for RabbitMq and Udp transport servers and clients
- Implemented comprehensive unit tests for RabbitMqTransportServer, covering constructor, disposal, connection management, event handlers, and exception handling.
- Added configuration tests for RabbitMqTransportServer to validate SSL, durable queues, auto-recovery, and custom virtual host options.
- Created unit tests for UdpFrameProtocol, including frame parsing and serialization, header size validation, and round-trip data preservation.
- Developed tests for UdpTransportClient, focusing on connection handling, event subscriptions, and exception scenarios.
- Established tests for UdpTransportServer, ensuring proper start/stop behavior, connection state management, and event handling.
- Included tests for UdpTransportOptions to verify default values and modification capabilities.
- Enhanced service registration tests for Udp transport services in the dependency injection container.
2025-12-05 19:01:12 +02:00

318 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# VEX Lens Contract
**Contract ID:** `CONTRACT-VEX-LENS-005`
**Version:** 1.0
**Status:** Published
**Last Updated:** 2025-12-05
## Overview
This contract defines the VEX Lens (VexLinkset) data model used to correlate multiple VEX observations for a specific vulnerability and product. The VEX Lens captures provider agreement, disagreements, and calculates consensus confidence.
## Implementation Reference
**Source:** `src/Excititor/__Libraries/StellaOps.Excititor.Core/Observations/VexLinkset.cs`
## Data Model
### VexLinkset
The core VEX Lens structure correlating observations.
```csharp
public sealed record VexLinkset
{
/// <summary>
/// Unique identifier: SHA256(tenant|vulnerabilityId|productKey)
/// </summary>
public string LinksetId { get; }
/// <summary>
/// Tenant identifier (normalized to lowercase).
/// </summary>
public string Tenant { get; }
/// <summary>
/// The vulnerability identifier (CVE, GHSA, vendor ID).
/// </summary>
public string VulnerabilityId { get; }
/// <summary>
/// Product key (typically a PURL or CPE).
/// </summary>
public string ProductKey { get; }
/// <summary>
/// Canonical scope metadata for the product key.
/// </summary>
public VexProductScope Scope { get; }
/// <summary>
/// References to observations that contribute to this linkset.
/// </summary>
public ImmutableArray<VexLinksetObservationRefModel> Observations { get; }
/// <summary>
/// Conflict annotations capturing disagreements between providers.
/// </summary>
public ImmutableArray<VexObservationDisagreement> Disagreements { get; }
/// <summary>
/// When this linkset was first created.
/// </summary>
public DateTimeOffset CreatedAt { get; }
/// <summary>
/// When this linkset was last updated.
/// </summary>
public DateTimeOffset UpdatedAt { get; }
}
```
### JSON Representation
```json
{
"linkset_id": "sha256:abc123...",
"tenant": "default",
"vulnerability_id": "CVE-2024-1234",
"product_key": "pkg:npm/lodash@4.17.20",
"scope": {
"ecosystem": "npm",
"namespace": null,
"name": "lodash",
"version": "4.17.20"
},
"observations": [
{
"observation_id": "obs-001",
"provider_id": "github",
"status": "affected",
"confidence": 0.9
},
{
"observation_id": "obs-002",
"provider_id": "redhat",
"status": "not_affected",
"confidence": 0.85
}
],
"disagreements": [
{
"provider_id": "github",
"status": "affected",
"justification": null,
"confidence": 0.9
},
{
"provider_id": "redhat",
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"confidence": 0.85
}
],
"created_at": "2025-12-05T10:00:00Z",
"updated_at": "2025-12-05T10:00:00Z"
}
```
### VexLinksetObservationRefModel
Reference to an observation contributing to the linkset.
```json
{
"observation_id": "obs-001",
"provider_id": "github",
"status": "affected",
"confidence": 0.9
}
```
| Field | Type | Description |
|-------|------|-------------|
| `observation_id` | string | Unique observation identifier |
| `provider_id` | string | VEX provider identifier |
| `status` | string | VEX status claim |
| `confidence` | double? | Optional confidence [0.0-1.0] |
### VexObservationDisagreement
Captures conflict between providers.
```json
{
"provider_id": "github",
"status": "affected",
"justification": null,
"confidence": 0.9
}
```
### VEX Status Values
| Status | Description |
|--------|-------------|
| `affected` | Product is affected by vulnerability |
| `not_affected` | Product is not affected |
| `fixed` | Vulnerability has been fixed |
| `under_investigation` | Status is being determined |
### VEX Justification Codes
When `status` is `not_affected`, justification may include:
| Code | Description |
|------|-------------|
| `component_not_present` | Vulnerable component not present |
| `vulnerable_code_not_present` | Vulnerable code not present |
| `vulnerable_code_not_in_execute_path` | Code present but not reachable |
| `vulnerable_code_cannot_be_controlled_by_adversary` | Not exploitable |
| `inline_mitigations_already_exist` | Mitigations in place |
## Confidence Levels
### VexLinksetConfidence
Computed confidence based on linkset state.
| Level | Conditions |
|-------|------------|
| `Low` | Conflicts exist, or < 1 observation, or multiple distinct statuses |
| `Medium` | Single provider, or consistent observations |
| `High` | 2+ providers agree on status |
### Confidence Calculation
```csharp
public VexLinksetConfidence Confidence
{
get
{
if (HasConflicts)
return VexLinksetConfidence.Low;
if (Observations.Length == 0)
return VexLinksetConfidence.Low;
if (Statuses.Count > 1)
return VexLinksetConfidence.Low;
if (ProviderIds.Count >= 2)
return VexLinksetConfidence.High;
return VexLinksetConfidence.Medium;
}
}
```
## Linkset ID Generation
Deterministic ID from key components:
```csharp
public static string CreateLinksetId(string tenant, string vulnerabilityId, string productKey)
{
var input = $"{tenant.ToLowerInvariant()}|{vulnerabilityId}|{productKey}";
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(input));
return $"sha256:{Convert.ToHexString(hash).ToLowerInvariant()}";
}
```
## API Endpoints
### Resolve VEX for Finding
```
POST /excititor/resolve
Content-Type: application/json
{
"tenant_id": "default",
"queries": [
{
"vulnerability_id": "CVE-2024-1234",
"product_key": "pkg:npm/lodash@4.17.20"
}
]
}
Response: 200 OK
{
"results": [
{
"linkset_id": "sha256:...",
"vulnerability_id": "CVE-2024-1234",
"product_key": "pkg:npm/lodash@4.17.20",
"rollup_status": "affected",
"confidence": "medium",
"has_conflicts": false,
"provider_count": 1
}
]
}
```
### Get Linkset Details
```
GET /excititor/linksets/{linkset_id}
Response: 200 OK
{
"linkset_id": "sha256:...",
"vulnerability_id": "CVE-2024-1234",
"observations": [...],
"disagreements": [...],
"confidence": "low"
}
```
## Consensus Algorithm
The consensus rollup algorithm:
1. **Filter:** Remove invalid statements by signature policy
2. **Score:** `score = weight(provider) × freshnessFactor(lastObserved)`
3. **Aggregate:** `W(status) = Σ score` per status
4. **Pick:** `rollupStatus = argmax_status W(status)`
5. **Tie-breakers:**
- Higher max single provider score
- More recent `lastObserved`
- Lexicographic order (fixed > not_affected > under_investigation > affected)
### Provider Weights
| Provider Type | Default Weight |
|---------------|----------------|
| Vendor | 1.0 |
| Distribution | 0.9 |
| Platform | 0.7 |
| Attestation | 0.6 |
| Hub | 0.5 |
### Freshness Factor
```
freshnessFactor = clamp(0.8, 1.0 - (age_days / 30), 1.0)
```
## Determinism Guarantees
1. **Stable ID:** LinksetId is deterministic from (tenant, vulnId, productKey)
2. **Sorted observations:** Observations sorted by observationId
3. **Sorted disagreements:** Disagreements sorted by (providerId, status)
4. **Immutable records:** Linksets are immutable; updates create new versions
## Unblocks
This contract unblocks the following tasks:
- CONCELIER-VEXLENS-30-001
- EXCITITOR-VEXLENS-30-001
## Related Contracts
- [Advisory Key Contract](./advisory-key.md) - Vulnerability ID canonicalization
- [Risk Scoring Contract](./risk-scoring.md) - VEX evidence for scoring