11 KiB
Change-Trace Trust-Delta Formula Contract
Version: 1.0.0 Status: Draft Last Updated: 2026-01-12
Overview
This document specifies the mathematical formula and algorithm for computing trust-delta scores in Change-Trace. The formula integrates VEX consensus scores, reachability analysis, and patch verification to produce a deterministic risk assessment.
Core Formula
Trust Delta Calculation
TrustDelta = (AfterTrust - BeforeTrust) / max(BeforeTrust, 0.01)
Where:
TrustDeltais bounded to[-1.0, +1.0]BeforeTrustandAfterTrustare computed per the sections below
Before Trust Score
BeforeTrust = VexConsensus(purl, fromVersion) * ReachabilityFactor(fromReachablePaths)
After Trust Score
AfterTrust = VexConsensus(purl, toVersion) * ReachabilityFactor(toReachablePaths) + PatchBonus
Component Definitions
VEX Consensus Score
The VEX consensus score is obtained from VexLens, which aggregates trust assessments across multiple issuers.
VexConsensus(purl, version) -> [0.0, 1.0]
| Score Range | Interpretation |
|---|---|
| 0.9 - 1.0 | High trust (no known vulnerabilities) |
| 0.7 - 0.9 | Moderate trust (minor issues) |
| 0.5 - 0.7 | Low trust (some concerns) |
| 0.0 - 0.5 | Very low trust (significant vulnerabilities) |
Reachability Factor
The reachability factor adjusts trust based on whether vulnerable code is actually reachable.
ReachabilityFactor(callPaths) =
- 1.0 if callPaths > 0 (reachable)
- 0.7 if callPaths == 0 (unreachable, 30% reduction in impact)
- 1.0 if callPaths is null (unknown, assume reachable)
Rationale: Unreachable vulnerable code poses less risk, so we reduce the weight of the trust score. However, we don't eliminate it entirely because reachability analysis may have gaps.
Patch Verification Bonus
The patch bonus rewards verified security patches with additional trust.
PatchBonus =
FunctionMatchWeight * PatchVerificationConfidence
+ SectionMatchWeight * SymbolSimilarity
+ AttestationWeight * IssuerAuthorityScore (if DSSE present)
Default Weights
| Weight | Value | Description |
|---|---|---|
FunctionMatchWeight |
0.25 | Weight for function-level match confidence |
SectionMatchWeight |
0.15 | Weight for section-level similarity |
AttestationWeight |
0.10 | Weight for DSSE attestation presence |
Configurable via TrustDeltaOptions
public sealed record TrustDeltaOptions
{
public double FunctionMatchWeight { get; init; } = 0.25;
public double SectionMatchWeight { get; init; } = 0.15;
public double AttestationWeight { get; init; } = 0.10;
public double RuntimeConfirmWeight { get; init; } = 0.10;
public double SignificantDeltaThreshold { get; init; } = 0.3;
}
Verdict Mapping
Trust Delta to Verdict
| Delta Range | Verdict | Description |
|---|---|---|
delta <= -0.3 |
risk_down |
Significant risk reduction |
-0.3 < delta < 0.3 |
neutral |
No significant change |
delta >= 0.3 |
risk_up |
Significant risk increase |
Reachability Impact
| Before Paths | After Paths | Impact |
|---|---|---|
| null | any | unchanged |
| any | null | unchanged |
| 0 | > 0 | introduced |
| > 0 | 0 | eliminated |
| N | < N | reduced |
| N | > N | increased |
| N | N | unchanged |
Exploitability Impact
| Delta Range | Impact |
|---|---|
delta <= -0.5 |
eliminated |
-0.5 < delta < -0.1 |
down |
-0.1 <= delta <= 0.1 |
unchanged |
0.1 < delta < 0.5 |
up |
delta >= 0.5 |
introduced |
Worked Examples
Example 1: Security Patch Applied
Scenario: libssl3 updated from 3.0.9-1 to 3.0.9-1+deb12u3 (Debian security patch)
Inputs:
VexConsensus(3.0.9-1)= 0.45 (CVE-2026-12345 present)VexConsensus(3.0.9-1+deb12u3)= 0.95 (CVE patched)ReachablePaths(before)= 3ReachablePaths(after)= 0PatchVerificationConfidence= 0.97SymbolSimilarity= 0.85HasDsseAttestation= trueIssuerAuthorityScore= 0.90
Calculation:
BeforeTrust = 0.45 * 1.0 = 0.45
PatchBonus = (0.25 * 0.97) + (0.15 * 0.85) + (0.10 * 0.90)
= 0.2425 + 0.1275 + 0.09
= 0.46
AfterTrust = 0.95 * 0.7 + 0.46 = 0.665 + 0.46 = 1.125
-> Capped at 1.0 for display, but used as-is for delta
TrustDelta = (1.125 - 0.45) / max(0.45, 0.01)
= 0.675 / 0.45
= 1.5
-> Clamped to 1.0
Final: TrustDelta = 1.0 (clamped)
Verdict: risk_down (delta < -0.3 equivalent in impact)
Note: The formula produces a positive delta when risk decreases because AfterTrust > BeforeTrust. The display inverts this for user comprehension (risk_down means good).
Example 2: Version Upgrade with New Vulnerability
Scenario: openssl upgraded from 3.0.8 to 3.1.0 (new version introduces regression)
Inputs:
VexConsensus(3.0.8)= 0.85VexConsensus(3.1.0)= 0.55 (regression CVE)ReachablePaths(before)= 0ReachablePaths(after)= 5- No patch verification (upgrade, not patch)
Calculation:
BeforeTrust = 0.85 * 0.7 = 0.595 (unreachable before)
AfterTrust = 0.55 * 1.0 + 0 = 0.55 (reachable after)
TrustDelta = (0.55 - 0.595) / max(0.595, 0.01)
= -0.045 / 0.595
= -0.076
Final: TrustDelta = -0.08
Verdict: neutral (within -0.3 to +0.3 range)
Analysis: Despite the new CVE, the overall delta is neutral because the before version also had some risk (though unreachable). The reachability change from 0 to 5 is captured in reachabilityImpact: introduced.
Example 3: Rebuild Without Changes
Scenario: Same source, different build timestamp
Inputs:
VexConsensus(1.0.0)= 0.90 (both versions)ReachablePaths= 2 (both versions)SymbolSimilarity= 1.0 (identical code)
Calculation:
BeforeTrust = 0.90 * 1.0 = 0.90
AfterTrust = 0.90 * 1.0 + 0 = 0.90 (no patch bonus for rebuild)
TrustDelta = (0.90 - 0.90) / max(0.90, 0.01)
= 0 / 0.90
= 0
Final: TrustDelta = 0.00
Verdict: neutral
Proof Step Generation
Step Categories
- CVE Context: List CVEs affecting the package
- Version Change: Document version transition
- Patch Verification: Report verification method and confidence
- Symbol Similarity: Report code similarity metrics
- Reachability: Report call path changes
- Attestation: Note DSSE attestation presence
- Verdict: Final determination
Step Format
1. {CVE-ID} affects {function_name}
2. Version changed: {from} -> {to}
3. Patch verified via {method}: {confidence}% confidence
4. Symbol similarity: {percentage}%
5. Reachable call paths: {before} -> {after}
6. DSSE attestation present
7. Verdict: {verdict} ({delta:+0.00})
Example Output
1. CVE-2026-12345 affects ssl3_get_record
2. Version changed: 3.0.9-1 -> 3.0.9-1+deb12u3
3. Patch verified via CFG match: 97% confidence
4. Symbol similarity: 85%
5. Reachable call paths: 3 -> 0 after patch
6. DSSE attestation present
7. Verdict: risk_down (-0.27)
Algorithm Pseudocode
def calculate_trust_delta(context: TrustDeltaContext, options: TrustDeltaOptions) -> TrustDelta:
# Get VEX consensus scores
before_consensus = vexlens.get_consensus(context.purl, context.from_version)
after_consensus = vexlens.get_consensus(context.purl, context.to_version)
# Compute reachability factors
before_reach = compute_reachability_factor(context.reachable_paths_before)
after_reach = compute_reachability_factor(context.reachable_paths_after)
# Compute base trust scores
before_trust = before_consensus.trust_score * before_reach
after_trust = after_consensus.trust_score * after_reach
# Add patch verification bonus
patch_bonus = 0.0
if context.patch_verification_confidence:
patch_bonus += options.function_match_weight * context.patch_verification_confidence
if context.symbol_similarity:
patch_bonus += options.section_match_weight * context.symbol_similarity
if context.has_dsse_attestation and context.issuer_authority_score:
patch_bonus += options.attestation_weight * context.issuer_authority_score
after_trust += patch_bonus
# Compute delta with division protection
delta = (after_trust - before_trust) / max(before_trust, 0.01)
delta = clamp(delta, -1.0, 1.0)
# Determine impacts
reachability_impact = determine_reachability_impact(
context.reachable_paths_before,
context.reachable_paths_after
)
exploitability_impact = determine_exploitability_impact(delta)
# Generate proof steps
proof_steps = generate_proof_steps(context, delta)
return TrustDelta(
score=round(delta, 2),
before_score=round(before_trust, 2),
after_score=round(after_trust, 2),
reachability_impact=reachability_impact,
exploitability_impact=exploitability_impact,
proof_steps=proof_steps
)
def compute_reachability_factor(call_paths: int | None) -> float:
if call_paths is None:
return 1.0 # Unknown, assume reachable
if call_paths == 0:
return 0.7 # Unreachable, 30% reduction
return 1.0 # Reachable
def determine_reachability_impact(before: int | None, after: int | None) -> ReachabilityImpact:
if before is None or after is None:
return ReachabilityImpact.UNCHANGED
if before == 0 and after > 0:
return ReachabilityImpact.INTRODUCED
if before > 0 and after == 0:
return ReachabilityImpact.ELIMINATED
if after < before:
return ReachabilityImpact.REDUCED
if after > before:
return ReachabilityImpact.INCREASED
return ReachabilityImpact.UNCHANGED
def determine_exploitability_impact(delta: float) -> ExploitabilityImpact:
if delta <= -0.5:
return ExploitabilityImpact.ELIMINATED
if delta < -0.1:
return ExploitabilityImpact.DOWN
if delta > 0.5:
return ExploitabilityImpact.INTRODUCED
if delta > 0.1:
return ExploitabilityImpact.UP
return ExploitabilityImpact.UNCHANGED
Determinism Requirements
Reproducibility
Given identical inputs, the formula must produce identical outputs:
- Same VEX consensus data
- Same reachability data
- Same patch verification results
- Same options
Floating Point Handling
- All intermediate calculations use
doubleprecision - Final scores are rounded to 2 decimal places
- Comparisons use exact equality after rounding
Timestamp Independence
The formula does not depend on current time. All inputs are point-in-time snapshots.
Versioning
Algorithm Version
The algorithm version is tracked in the output:
{
"algorithmVersion": "1.0"
}
Backward Compatibility
- v1.0: Initial release
- Future versions will maintain semantic versioning
- Breaking changes will increment major version
References
Document Version: 1.0.0 Last Updated: 2026-01-12