11 KiB
11 KiB
VEX Proof Object Schema Reference
Version: 1.0 (stellaops.vex-proof.v1) Last Updated: 2026-01-03
Overview
VEX Proof Objects provide a cryptographically verifiable audit trail of how VEX consensus verdicts are computed. Every VEX resolution in StellaOps produces a proof object that documents the inputs, merge process, and decision rationale.
JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "VEX Proof Object",
"type": "object",
"required": ["schema", "proofId", "computedAt", "verdict", "inputs", "mergeTrace", "confidence"],
"properties": {
"schema": {
"type": "string",
"const": "stellaops.vex-proof.v1"
},
"proofId": {
"type": "string",
"description": "Unique identifier for this proof"
},
"computedAt": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 timestamp of proof computation"
},
"digest": {
"type": "object",
"description": "Content-addressed hash of the proof"
},
"verdict": {
"$ref": "#/definitions/verdict"
},
"inputs": {
"$ref": "#/definitions/inputs"
},
"mergeTrace": {
"$ref": "#/definitions/mergeTrace"
},
"propagation": {
"$ref": "#/definitions/propagation"
},
"conditions": {
"$ref": "#/definitions/conditions"
},
"confidence": {
"$ref": "#/definitions/confidence"
}
}
}
Object Definitions
Verdict
The final consensus result.
| Field | Type | Required | Description |
|---|---|---|---|
vulnerabilityId |
string | Yes | CVE or vulnerability identifier |
productKey |
string | Yes | Product identifier (typically a PURL) |
status |
enum | Yes | VEX status: affected, not_affected, fixed, under_investigation |
justification |
enum | No | Justification code if status is not_affected |
confidence |
number | Yes | Confidence score [0.0, 1.0] |
{
"vulnerabilityId": "CVE-2023-12345",
"productKey": "pkg:npm/lodash@4.17.21",
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"confidence": 0.78
}
Inputs
All VEX statements considered in the consensus.
| Field | Type | Required | Description |
|---|---|---|---|
qualifiedCount |
integer | Yes | Number of statements meeting weight threshold |
disqualifiedCount |
integer | Yes | Number of statements filtered out |
statements |
array | Yes | Array of input statements |
disqualified |
array | No | Array of disqualified statements with reasons |
Statement Object
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Statement identifier |
source |
string | Yes | Source document identifier |
issuer |
object | Yes | Issuer information |
status |
enum | Yes | VEX status from this statement |
justification |
enum | No | Justification if not_affected |
weight |
object | Yes | Weight calculation details |
timestamp |
datetime | Yes | Statement timestamp |
signatureVerified |
boolean | Yes | Whether signature was verified |
Issuer Object
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Issuer identifier |
category |
enum | Yes | vendor, distributor, community, internal, aggregator |
trustTier |
enum | Yes | authoritative, trusted, untrusted, unknown |
Weight Object
| Field | Type | Required | Description |
|---|---|---|---|
composite |
number | Yes | Final composite weight [0.0, 1.0] |
factors |
object | Yes | Individual weight factors |
Weight factors:
issuer: Issuer trust weightsignature: Signature verification weightfreshness: Statement age weightformat: Source format weight (OpenVEX vs CSAF vs CycloneDX)specificity: Status specificity weight
MergeTrace
Step-by-step documentation of the merge algorithm.
| Field | Type | Required | Description |
|---|---|---|---|
mode |
enum | Yes | Consensus mode used |
latticeOrdering |
array | No | Status lattice order (for Lattice mode) |
steps |
array | Yes | Array of merge steps |
conflicts |
array | No | Array of detected conflicts |
Consensus Modes
highest_weight: Select statement with highest trust weightweighted_vote: Weighted voting across all statementslattice: Lattice-based conservative consensusauthoritative_first: Prefer vendor/authoritative sources
Merge Step
| Field | Type | Required | Description |
|---|---|---|---|
stepNumber |
integer | Yes | Sequential step number |
statementId |
string | Yes | Statement being processed |
inputStatus |
enum | Yes | Status from this statement |
inputWeight |
number | Yes | Weight of this statement |
action |
enum | Yes | initialize, merge, skip |
conflictDetected |
boolean | Yes | Whether conflict was detected |
resolution |
string | No | How conflict was resolved |
positionAfter |
enum | Yes | Current consensus position after step |
Conflict
| Field | Type | Required | Description |
|---|---|---|---|
statement1Id |
string | Yes | First conflicting statement |
statement2Id |
string | Yes | Second conflicting statement |
status1 |
enum | Yes | Status of first statement |
status2 |
enum | Yes | Status of second statement |
severity |
enum | Yes | critical, high, medium, low |
resolution |
string | Yes | Resolution method |
winnerId |
string | No | Winning statement ID |
Propagation
Dependency propagation analysis (optional).
| Field | Type | Required | Description |
|---|---|---|---|
applied |
boolean | Yes | Whether propagation was applied |
rules |
array | Yes | Propagation rules evaluated |
paths |
array | No | Analyzed dependency paths |
inheritedStatus |
enum | No | Status inherited via propagation |
overrideApplied |
boolean | No | Whether propagation overrode consensus |
Propagation Rules
direct_dependency_affected: Affected status propagates to direct dependentstransitive_dependency: Transitive propagation through dependency treedependency_fixed: Fixed status doesn't propagate transitivelydependency_not_affected: NotAffected can propagate under conditions
Conditions
Condition evaluation results (optional).
| Field | Type | Required | Description |
|---|---|---|---|
results |
array | Yes | Condition evaluation results |
unevaluated |
array | No | Conditions that couldn't be evaluated |
coverage |
number | Yes | Percentage of conditions evaluated |
Condition Result
| Field | Type | Required | Description |
|---|---|---|---|
conditionId |
string | Yes | Condition identifier |
expression |
string | Yes | Condition expression |
result |
enum | Yes | true, false, unknown |
contextValue |
string | No | Evaluated context value |
Condition Types
platform: OS/architecture (e.g.,linux/amd64)distro: Distribution (e.g.,rhel:9)feature: Feature flag conditionbuild_flag: Compile-time flagenvironment: Environment variable
Confidence
Confidence score breakdown.
| Field | Type | Required | Description |
|---|---|---|---|
score |
number | Yes | Overall confidence [0.0, 1.0] |
tier |
enum | Yes | high, medium, low |
components |
object | Yes | Score components |
improvements |
array | No | Suggestions for improving confidence |
Score component contributions:
weightSpread: Statement weight distributionfreshnessBonus: Bonus for recent statementssignatureBonus: Bonus for signed statementsconflictPenalty: Penalty for conflictsconditionCoverage: Condition evaluation coverage
Digest Computation
The proof digest is computed using RFC 8785 canonical JSON serialization:
- Serialize proof object to canonical JSON (sorted keys, minimal escaping)
- Compute SHA-256 hash of canonical JSON bytes
- Encode as hexadecimal
{
"digest": {
"algorithm": "sha256",
"value": "a1b2c3d4..."
}
}
Example Proof Object
{
"schema": "stellaops.vex-proof.v1",
"proofId": "proof-2026-01-02T10:30:00Z-abc123",
"computedAt": "2026-01-02T10:30:00Z",
"digest": {
"algorithm": "sha256",
"value": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
},
"verdict": {
"vulnerabilityId": "CVE-2023-12345",
"productKey": "pkg:npm/lodash@4.17.21",
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"confidence": 0.78
},
"inputs": {
"qualifiedCount": 2,
"disqualifiedCount": 0,
"statements": [
{
"id": "stmt-001",
"source": "openvex",
"issuer": {
"id": "lodash-maintainers",
"category": "vendor",
"trustTier": "authoritative"
},
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"weight": {
"composite": 0.85,
"factors": {
"issuer": 0.90,
"signature": 1.00,
"freshness": 0.95,
"format": 1.00,
"specificity": 0.70
}
},
"timestamp": "2023-06-15T10:30:00Z",
"signatureVerified": true
}
]
},
"mergeTrace": {
"mode": "lattice",
"latticeOrdering": ["affected", "under_investigation", "fixed", "not_affected"],
"steps": [
{
"stepNumber": 1,
"statementId": "stmt-001",
"inputStatus": "not_affected",
"inputWeight": 0.85,
"action": "initialize",
"conflictDetected": false,
"positionAfter": "not_affected"
}
],
"conflicts": []
},
"confidence": {
"score": 0.78,
"tier": "medium",
"components": {
"weightSpread": 0.80,
"freshnessBonus": 0.05,
"signatureBonus": 0.05,
"conflictPenalty": 0.00,
"conditionCoverage": 0.00
},
"improvements": [
"Add statements from additional authoritative sources",
"Evaluate platform-specific conditions"
]
}
}
Policy Integration
VEX proofs integrate with the policy gate system via VexProofGate:
| Setting | Type | Default | Description |
|---|---|---|---|
Enabled |
bool | true | Enable/disable gate |
MinimumConfidenceTier |
string | medium | Required confidence tier |
RequireProofForNotAffected |
bool | true | Require proof for NotAffected |
RequireProofForFixed |
bool | false | Require proof for Fixed |
MaxAllowedConflicts |
int | 5 | Maximum allowed conflicts |
MaxProofAgeHours |
int | 168 | Maximum proof age (hours) |
RequireSignedStatements |
bool | false | Require all statements signed |
MinimumInputStatements |
int | 1 | Minimum input statement count |