# 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: - `TrustDelta` is bounded to `[-1.0, +1.0]` - `BeforeTrust` and `AfterTrust` are 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 ```csharp 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)` = 3 - `ReachablePaths(after)` = 0 - `PatchVerificationConfidence` = 0.97 - `SymbolSimilarity` = 0.85 - `HasDsseAttestation` = true - `IssuerAuthorityScore` = 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.85 - `VexConsensus(3.1.0)` = 0.55 (regression CVE) - `ReachablePaths(before)` = 0 - `ReachablePaths(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 1. **CVE Context**: List CVEs affecting the package 2. **Version Change**: Document version transition 3. **Patch Verification**: Report verification method and confidence 4. **Symbol Similarity**: Report code similarity metrics 5. **Reachability**: Report call path changes 6. **Attestation**: Note DSSE attestation presence 7. **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 ```python 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 `double` precision - 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: ```json { "algorithmVersion": "1.0" } ``` ### Backward Compatibility - v1.0: Initial release - Future versions will maintain semantic versioning - Breaking changes will increment major version --- ## References - [Change-Trace Schema Contract](./change-trace-schema.md) - [Architecture Document](../modules/scanner/design/change-trace-architecture.md) - [VexLens Trust Scoring](../modules/vexlens/architecture.md) - [ReachGraph Analysis](../modules/reachgraph/architecture.md) --- *Document Version: 1.0.0* *Last Updated: 2026-01-12*