- 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.
7.5 KiB
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.
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
{
"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.
{
"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.
{
"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
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:
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:
- Filter: Remove invalid statements by signature policy
- Score:
score = weight(provider) × freshnessFactor(lastObserved) - Aggregate:
W(status) = Σ scoreper status - Pick:
rollupStatus = argmax_status W(status) - 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
- Stable ID: LinksetId is deterministic from (tenant, vulnId, productKey)
- Sorted observations: Observations sorted by observationId
- Sorted disagreements: Disagreements sorted by (providerId, status)
- 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 - Vulnerability ID canonicalization
- Risk Scoring Contract - VEX evidence for scoring