Add unit tests for RabbitMq and Udp transport servers and clients
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
- 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.
This commit is contained in:
317
docs/contracts/vex-lens.md
Normal file
317
docs/contracts/vex-lens.md
Normal file
@@ -0,0 +1,317 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user