namespace StellaOps.Scanner.ProofIntegration; using Microsoft.Extensions.Logging; using StellaOps.Attestor.ProofChain.Generators; using StellaOps.Attestor.ProofChain.Models; using StellaOps.Attestor.ProofChain.Statements; using StellaOps.Concelier.ProofService; /// /// Generates VEX verdicts with cryptographic proof references. /// Integrates Scanner vulnerability detection with proof-driven backport detection. /// public sealed class ProofAwareVexGenerator { private readonly ILogger _logger; private readonly BackportProofService _proofService; private readonly TimeProvider _timeProvider; public ProofAwareVexGenerator( ILogger logger, BackportProofService proofService, TimeProvider? timeProvider = null) { _logger = logger; _proofService = proofService; _timeProvider = timeProvider ?? TimeProvider.System; } /// /// Generate VEX verdict with proof for a vulnerability finding. /// /// Vulnerability finding from scanner /// SBOM entry ID for the component /// Policy version used for decisioning /// Cancellation token /// VEX verdict statement with embedded proof reference public async Task GenerateVexWithProofAsync( VulnerabilityFinding finding, string sbomEntryId, string policyVersion, CancellationToken cancellationToken = default) { _logger.LogInformation( "Generating proof-carrying VEX verdict for {CveId} in {Package}", finding.CveId, finding.PackagePurl); // Step 1: Generate cryptographic proof using four-tier detection var proof = await _proofService.GenerateProofAsync( finding.CveId, finding.PackagePurl, cancellationToken); if (proof == null) { _logger.LogWarning( "No proof generated for {CveId} in {Package}, using fallback verdict", finding.CveId, finding.PackagePurl); // Fallback: Generate VEX without proof return GenerateFallbackVex(finding, sbomEntryId, policyVersion); } _logger.LogInformation( "Generated proof {ProofId} with confidence {Confidence:P0} for {CveId}", proof.ProofId, proof.Confidence, finding.CveId); // Step 2: Generate VEX verdict with proof reference var reasoningId = GenerateReasoningId(finding, proof); var (statement, proofPayload) = VexProofIntegrator.GenerateWithProofMetadata( proof, sbomEntryId, policyVersion, reasoningId); return new VexVerdictWithProof { Statement = statement, ProofPayload = proofPayload, Proof = proof, GeneratedAt = _timeProvider.GetUtcNow() }; } /// /// Generate VEX verdicts for multiple findings in batch. /// public async Task> GenerateBatchVexWithProofAsync( IEnumerable findings, string policyVersion, Func sbomEntryIdResolver, CancellationToken cancellationToken = default) { var tasks = findings.Select(finding => { var sbomEntryId = sbomEntryIdResolver(finding); return GenerateVexWithProofAsync(finding, sbomEntryId, policyVersion, cancellationToken); }); var results = await Task.WhenAll(tasks); return results.ToList(); } /// /// Retrieve existing proof for a CVE + package combination. /// Useful for audit replay and verification. /// public async Task RetrieveProofAsync( string cveId, string packagePurl, CancellationToken cancellationToken = default) { return await _proofService.GenerateProofAsync(cveId, packagePurl, cancellationToken); } private VexVerdictWithProof GenerateFallbackVex( VulnerabilityFinding finding, string sbomEntryId, string policyVersion) { // Generate basic VEX without proof // This is used when no evidence is available (e.g., newly disclosed CVE) var unknownProof = BackportProofGenerator.Unknown( finding.CveId, finding.PackagePurl, "no_evidence_available", Array.Empty()); var reasoningId = $"reasoning:{finding.CveId}:{finding.PackagePurl}"; var (statement, proofPayload) = VexProofIntegrator.GenerateWithProofMetadata( unknownProof, sbomEntryId, policyVersion, reasoningId); return new VexVerdictWithProof { Statement = statement, ProofPayload = proofPayload, Proof = unknownProof, GeneratedAt = _timeProvider.GetUtcNow() }; } private string GenerateReasoningId(VulnerabilityFinding finding, ProofBlob proof) { // Reasoning ID format: reasoning:{cve}:{method}:{snapshot} return $"reasoning:{finding.CveId}:{proof.Method}:{proof.SnapshotId}"; } } /// /// Vulnerability finding from scanner. /// public sealed record VulnerabilityFinding { public required string CveId { get; init; } public required string PackagePurl { get; init; } public required string PackageName { get; init; } public required string PackageVersion { get; init; } public required string Severity { get; init; } } /// /// VEX verdict with associated proof. /// public sealed record VexVerdictWithProof { public required VexVerdictStatement Statement { get; init; } public required VexVerdictProofPayload ProofPayload { get; init; } public required ProofBlob Proof { get; init; } public required DateTimeOffset GeneratedAt { get; init; } }