246 lines
10 KiB
Markdown
246 lines
10 KiB
Markdown
# VEX Consensus and Issuer Trust
|
|
|
|
This document consolidates the VEX concepts StellaOps relies on: ingesting upstream VEX without rewriting it, correlating evidence across sources, and producing a deterministic, explainable "effective" status for a component-vulnerability pair.
|
|
|
|
## Scope
|
|
|
|
- VEX ingestion and provenance (what is stored and why)
|
|
- Correlation (linksets) versus consensus (effective status)
|
|
- Issuer trust and offline operation
|
|
|
|
This is not an API reference; module dossiers define concrete schemas and endpoints.
|
|
|
|
## Vocabulary (Minimal)
|
|
|
|
- **VEX statement:** a claim about vulnerability status for a product/component (for example: `affected`, `fixed`, `not_affected`, `under_investigation`).
|
|
- **Observation:** an immutable record of a single upstream VEX document as received (including provenance and raw payload).
|
|
- **Linkset:** a deterministic correlation group that ties together statements that refer to the same `(vulnerabilityId, productKey)` across providers.
|
|
- **Consensus decision (effective VEX):** the platform's deterministic result after policy rules evaluate available VEX/advisory/reachability evidence.
|
|
|
|
## Observation Model (Link, Not Merge)
|
|
|
|
StellaOps treats upstream VEX as append-only evidence.
|
|
|
|
An observation records:
|
|
|
|
- **Provenance:** tenant, provider/issuer identity, receive timestamps (UTC), signature status, and content hash.
|
|
- **Raw payload:** stored losslessly so auditors and operators can retrieve exactly what was ingested.
|
|
- **Derived tuples:** extracted `(vulnerabilityId, productKey, status, justification?, version hints, references)` used for correlation and UI presentation.
|
|
|
|
An observation is never mutated. If upstream publishes a revision, StellaOps stores a new observation and records a supersedes relationship.
|
|
|
|
## Linksets (Correlation Without Consensus)
|
|
|
|
Linksets exist to make multi-source evidence explainable without collapsing it:
|
|
|
|
- Group statements that likely refer to the same product-vulnerability pair.
|
|
- Preserve conflicts (status disagreements, justification divergence, version range clashes) as first-class facts.
|
|
- Provide stable IDs generated from canonical, sorted inputs (deterministic hashing).
|
|
|
|
Linksets do not invent consensus; they only align evidence so downstream layers (Policy/Console/Exports) can explain what is known and what disagrees.
|
|
|
|
## Consensus (Effective Status)
|
|
|
|
The effective VEX status is computed by policy evaluation using:
|
|
|
|
- Correlated VEX evidence (observations + linksets)
|
|
- Advisory evidence (observations/linksets from Concelier)
|
|
- Optional reachability and other signals
|
|
|
|
Key properties:
|
|
|
|
- **Deterministic:** the same inputs yield the same output.
|
|
- **Explainable:** the decision includes an explanation trace and evidence references.
|
|
- **Uncertainty-aware:** when critical evidence is missing or conflicts are unresolved, the result can remain `under_investigation` instead of implying safety.
|
|
|
|
## Aggregation-Only Guardrails (AOC)
|
|
|
|
To avoid hidden rewriting of upstream data, the platform enforces:
|
|
|
|
- **Raw-first storage:** upstream payloads are stored as received; normalized projections are derived but do not replace raw data.
|
|
- **No merge of sources:** each provider's statements remain independently addressable.
|
|
- **Provenance is mandatory:** missing provenance or unverifiable signatures are surfaced as ingestion failures or warnings (policy-driven).
|
|
- **Idempotent writes:** identical content hashes do not create duplicate observations.
|
|
- **Deterministic outputs:** stable ordering and canonical hashing for linksets and exports.
|
|
|
|
## Issuer Directory and Trust
|
|
|
|
Issuer trust is a first-class input:
|
|
|
|
- Issuers are identified by stable provider IDs and, where applicable, cryptographic identity (certificate chain, key id, transparency proof).
|
|
- The issuer directory defines which issuers are trusted per tenant/environment and how they are weighted/accepted by policy.
|
|
- Offline sites carry required trust material (roots and allowlists) inside the Offline Kit so verification does not require network access.
|
|
|
|
## Console Integration
|
|
|
|
The Console uses these concepts to keep VEX explainable:
|
|
|
|
- VEX views show provider provenance, signature/issuer status, and snapshot timestamps.
|
|
- Conflicts are displayed as conflicts (what disagrees and why), not silently resolved in the UI.
|
|
- The effective VEX status shown in triage views links back to underlying observations/linksets and the policy explanation.
|
|
|
|
See `docs/UI_GUIDE.md` for the operator workflow perspective.
|
|
|
|
## Anchor-Aware Mode (v1.1)
|
|
|
|
> **Sprint:** SPRINT_20260112_004_BE_policy_determinization_attested_rules
|
|
|
|
Anchor-aware mode enforces cryptographic attestation requirements on VEX proofs used for allow decisions.
|
|
|
|
### VexProofGate Options
|
|
|
|
| Option | Type | Default | Strict Mode |
|
|
|--------|------|---------|-------------|
|
|
| `AnchorAwareMode` | bool | `false` | `true` |
|
|
| `RequireVexAnchoring` | bool | `false` | `true` |
|
|
| `RequireRekorVerification` | bool | `false` | `true` |
|
|
| `RequireSignedStatements` | bool | `false` | `true` |
|
|
| `RequireProofForFixed` | bool | `false` | `true` |
|
|
| `MaxAllowedConflicts` | int | `5` | `0` |
|
|
| `MaxProofAgeHours` | int | `168` | `72` |
|
|
|
|
### Strict Anchor-Aware Preset
|
|
|
|
For production environments requiring maximum security:
|
|
|
|
```csharp
|
|
var options = VexProofGateOptions.StrictAnchorAware;
|
|
// Enables: RequireVexAnchoring, RequireRekorVerification,
|
|
// RequireSignedStatements, RequireProofForFixed
|
|
// Sets: MinimumConfidenceTier=high, MaxAllowedConflicts=0, MaxProofAgeHours=72
|
|
```
|
|
|
|
### Metadata Keys
|
|
|
|
When passing VEX proof context through policy evaluation:
|
|
|
|
| Key | Type | Description |
|
|
|-----|------|-------------|
|
|
| `vex_proof_anchored` | bool | Whether proof has DSSE anchoring |
|
|
| `vex_proof_envelope_digest` | string | DSSE envelope sha256 digest |
|
|
| `vex_proof_rekor_verified` | bool | Whether Rekor transparency verified |
|
|
| `vex_proof_rekor_log_index` | long | Rekor log index if verified |
|
|
|
|
### Failure Reasons
|
|
|
|
| Reason | Description |
|
|
|--------|-------------|
|
|
| `vex_not_anchored` | VEX proof requires DSSE anchoring but is not anchored |
|
|
| `rekor_verification_missing` | VEX proof requires Rekor verification but not verified |
|
|
|
|
## VEX Change Events
|
|
|
|
> Sprint: SPRINT_20260112_006_EXCITITOR_vex_change_events
|
|
|
|
Excititor emits deterministic events when VEX statements change, enabling policy reanalysis.
|
|
|
|
### Event Types
|
|
|
|
| Event | Description | Policy Trigger |
|
|
|-------|-------------|----------------|
|
|
| `vex.statement.added` | New statement ingested | Immediate reanalysis |
|
|
| `vex.statement.superseded` | Statement replaced | Immediate reanalysis |
|
|
| `vex.statement.conflict` | Status disagreement detected | Queue for review |
|
|
| `vex.status.changed` | Effective status changed | Immediate reanalysis |
|
|
|
|
### Conflict Detection
|
|
|
|
Conflicts are detected when multiple providers report different statuses for the same vulnerability-product pair:
|
|
|
|
| Conflict Type | Description |
|
|
|---------------|-------------|
|
|
| `status_mismatch` | Different status values (e.g., affected vs not_affected) |
|
|
| `trust_tie` | Equal trust scores with different recommendations |
|
|
| `supersession_conflict` | Disagreement on which statement supersedes |
|
|
|
|
### Event Ordering
|
|
|
|
Events follow deterministic ordering:
|
|
1. Ordered by timestamp (ascending)
|
|
2. Conflict events after related statement events
|
|
3. Same-timestamp events sorted by provider ID
|
|
|
|
### Integration with Policy
|
|
|
|
Subscribe to VEX events for automatic reanalysis:
|
|
|
|
```yaml
|
|
subscriptions:
|
|
- event: vex.statement.*
|
|
action: reanalyze
|
|
filter:
|
|
trustScore: { $gte: 0.7 }
|
|
```
|
|
|
|
See [Excititor Architecture](docs/modules/excititor/architecture.md#33-vex-change-events) for full event schemas.
|
|
|
|
## Offline / Air-Gap Operation
|
|
|
|
- VEX observations/linksets are included in Offline Kit snapshots with content hashes and timestamps.
|
|
- Verification workflows (signatures, issuer trust) must work offline using bundled trust roots and manifests.
|
|
- The Console should surface snapshot identity and staleness budgets when operating offline.
|
|
|
|
## Related Docs
|
|
|
|
- `docs/modules/excititor/architecture.md`
|
|
- `docs/modules/vex-lens/architecture.md`
|
|
- `docs/ARCHITECTURE_OVERVIEW.md`
|
|
- `docs/OFFLINE_KIT.md`
|
|
- `docs/modules/policy/determinization-api.md`
|
|
|
|
---
|
|
|
|
## Grey Queue and Unknown Mapping
|
|
|
|
> **Sprint:** SPRINT_20260112_004_POLICY_unknowns_determinization_greyqueue
|
|
|
|
When VEX correlation produces inconclusive results, observations are routed to the Grey Queue for monitoring or manual adjudication.
|
|
|
|
### Mapping to OpenVEX Status
|
|
|
|
Uncertain observations preserve OpenVEX spec alignment:
|
|
|
|
| Internal State | OpenVEX Status | Description |
|
|
|----------------|----------------|-------------|
|
|
| `PendingDeterminization` | `under_investigation` | Evidence incomplete; monitoring active |
|
|
| `Disputed` | `under_investigation` | Conflicting evidence from multiple sources |
|
|
| `GuardedPass` | `under_investigation` | Allowed with runtime guardrails |
|
|
|
|
### VEX Conflict Types
|
|
|
|
The Grey Queue surfaces VEX-specific conflicts:
|
|
|
|
| Conflict | Example | Resolution Path |
|
|
|----------|---------|-----------------|
|
|
| Status mismatch | Vendor says `not_affected`, distro says `affected` | Trust-weighted consensus or manual |
|
|
| Justification gap | Status `not_affected` but no justification provided | Request clarification or manual |
|
|
| Version range conflict | Overlapping but different affected ranges | Manual analysis |
|
|
| Supersession dispute | Multiple statements claim to supersede | Timestamp and trust resolution |
|
|
|
|
### Deterministic Conflict Detection
|
|
|
|
Conflicts are detected via structured comparison:
|
|
|
|
1. **Same vulnerability, same product, different status** → `VexStatusConflict`
|
|
2. **VEX not_affected + confirmed reachability** → `VexReachabilityContradiction`
|
|
3. **Multiple issuers, equal trust, opposite conclusions** → `TrustTie`
|
|
|
|
### Console Behavior for Grey Queue
|
|
|
|
When displaying Grey Queue items:
|
|
|
|
- Show the observation state badge (e.g., "Pending" or "Disputed")
|
|
- Display all conflicting sources with provider identity
|
|
- Surface the reanalysis fingerprint for reproducibility
|
|
- List pending triggers awaiting data
|
|
- Provide action buttons for manual resolution
|
|
|
|
### Offline Grey Queue
|
|
|
|
In offline/air-gap mode:
|
|
|
|
- Grey Queue state is included in Offline Kit snapshots
|
|
- Manual adjudications are recorded locally and synced on reconnection
|
|
- Staleness budgets apply to pending determinations
|
|
- Conflict detection works with cached issuer trust data
|