524 lines
17 KiB
C#
524 lines
17 KiB
C#
// -----------------------------------------------------------------------------
|
|
// UnifiedEvidenceContracts.cs
|
|
// Sprint: SPRINT_9200_0001_0002_SCANNER_unified_evidence_endpoint
|
|
// Description: DTOs for unified evidence endpoint that returns all evidence
|
|
// tabs for a finding in one API call.
|
|
// -----------------------------------------------------------------------------
|
|
|
|
namespace StellaOps.Scanner.WebService.Contracts;
|
|
|
|
/// <summary>
|
|
/// Complete evidence package for a finding - all tabs in one response.
|
|
/// </summary>
|
|
public sealed record UnifiedEvidenceResponseDto
|
|
{
|
|
/// <summary>Finding this evidence applies to.</summary>
|
|
public required string FindingId { get; init; }
|
|
|
|
/// <summary>CVE identifier.</summary>
|
|
public required string CveId { get; init; }
|
|
|
|
/// <summary>Affected component PURL.</summary>
|
|
public required string ComponentPurl { get; init; }
|
|
|
|
// === Evidence Tabs ===
|
|
|
|
/// <summary>SBOM evidence - component metadata and linkage.</summary>
|
|
public SbomEvidenceDto? Sbom { get; init; }
|
|
|
|
/// <summary>Reachability evidence - call paths to vulnerable code.</summary>
|
|
public ReachabilityEvidenceDto? Reachability { get; init; }
|
|
|
|
/// <summary>VEX claims from all sources with trust scores.</summary>
|
|
public IReadOnlyList<VexClaimDto>? VexClaims { get; init; }
|
|
|
|
/// <summary>Attestations (in-toto/DSSE) for this artifact.</summary>
|
|
public IReadOnlyList<AttestationSummaryDto>? Attestations { get; init; }
|
|
|
|
/// <summary>Delta comparison since last scan.</summary>
|
|
public DeltaEvidenceDto? Deltas { get; init; }
|
|
|
|
/// <summary>Policy evaluation evidence.</summary>
|
|
public PolicyEvidenceDto? Policy { get; init; }
|
|
|
|
// Sprint: SPRINT_20260112_009_SCANNER_binary_diff_bundle_export (BINDIFF-SCAN-001)
|
|
|
|
/// <summary>Binary diff evidence with semantic and structural changes.</summary>
|
|
public BinaryDiffEvidenceDto? BinaryDiff { get; init; }
|
|
|
|
// === Manifest Hashes ===
|
|
|
|
/// <summary>Content-addressed hashes for determinism verification.</summary>
|
|
public required ManifestHashesDto Manifests { get; init; }
|
|
|
|
// === Verification Status ===
|
|
|
|
/// <summary>Overall verification status of evidence chain.</summary>
|
|
public required VerificationStatusDto Verification { get; init; }
|
|
|
|
// === Replay Command ===
|
|
|
|
/// <summary>Copy-ready CLI command to replay this verdict.</summary>
|
|
public string? ReplayCommand { get; init; }
|
|
|
|
/// <summary>Shortened replay command using snapshot ID.</summary>
|
|
public string? ShortReplayCommand { get; init; }
|
|
|
|
/// <summary>URL to download complete evidence bundle.</summary>
|
|
public string? EvidenceBundleUrl { get; init; }
|
|
|
|
// === Metadata ===
|
|
|
|
/// <summary>When this evidence was assembled.</summary>
|
|
public required DateTimeOffset GeneratedAt { get; init; }
|
|
|
|
/// <summary>Cache key for this response (content-addressed).</summary>
|
|
public string? CacheKey { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// SBOM evidence for evidence panel.
|
|
/// </summary>
|
|
public sealed record SbomEvidenceDto
|
|
{
|
|
/// <summary>SBOM format (spdx, cyclonedx).</summary>
|
|
public required string Format { get; init; }
|
|
|
|
/// <summary>SBOM version.</summary>
|
|
public required string Version { get; init; }
|
|
|
|
/// <summary>Link to full SBOM document.</summary>
|
|
public required string DocumentUri { get; init; }
|
|
|
|
/// <summary>SBOM content digest.</summary>
|
|
public required string Digest { get; init; }
|
|
|
|
/// <summary>Component entry from SBOM.</summary>
|
|
public SbomComponentDto? Component { get; init; }
|
|
|
|
/// <summary>Dependencies of this component.</summary>
|
|
public IReadOnlyList<string>? Dependencies { get; init; }
|
|
|
|
/// <summary>Dependents (things that depend on this component).</summary>
|
|
public IReadOnlyList<string>? Dependents { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Component information from SBOM.
|
|
/// </summary>
|
|
public sealed record SbomComponentDto
|
|
{
|
|
/// <summary>Package URL.</summary>
|
|
public required string Purl { get; init; }
|
|
|
|
/// <summary>Component name.</summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>Component version.</summary>
|
|
public required string Version { get; init; }
|
|
|
|
/// <summary>Ecosystem (npm, maven, pypi, etc.).</summary>
|
|
public string? Ecosystem { get; init; }
|
|
|
|
/// <summary>License(s).</summary>
|
|
public IReadOnlyList<string>? Licenses { get; init; }
|
|
|
|
/// <summary>CPE identifiers.</summary>
|
|
public IReadOnlyList<string>? Cpes { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reachability evidence for evidence panel.
|
|
/// </summary>
|
|
public sealed record ReachabilityEvidenceDto
|
|
{
|
|
/// <summary>Subgraph ID for detailed view.</summary>
|
|
public required string SubgraphId { get; init; }
|
|
|
|
/// <summary>Reachability status.</summary>
|
|
public required string Status { get; init; }
|
|
|
|
/// <summary>Confidence level (0-1).</summary>
|
|
public double Confidence { get; init; }
|
|
|
|
/// <summary>Analysis method (static, binary, runtime).</summary>
|
|
public required string Method { get; init; }
|
|
|
|
/// <summary>Entry points reaching vulnerable code.</summary>
|
|
public IReadOnlyList<EntryPointDto>? EntryPoints { get; init; }
|
|
|
|
/// <summary>Call chain summary.</summary>
|
|
public CallChainSummaryDto? CallChain { get; init; }
|
|
|
|
/// <summary>Link to full reachability graph.</summary>
|
|
public string? GraphUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Entry point information.
|
|
/// </summary>
|
|
public sealed record EntryPointDto
|
|
{
|
|
/// <summary>Entry point identifier.</summary>
|
|
public required string Id { get; init; }
|
|
|
|
/// <summary>Entry point type (http, grpc, function, etc.).</summary>
|
|
public required string Type { get; init; }
|
|
|
|
/// <summary>Display name.</summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>File location if known.</summary>
|
|
public string? Location { get; init; }
|
|
|
|
/// <summary>Distance (hops) to vulnerable code.</summary>
|
|
public int? Distance { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Summary of call chain to vulnerable code.
|
|
/// </summary>
|
|
public sealed record CallChainSummaryDto
|
|
{
|
|
/// <summary>Total path length.</summary>
|
|
public int PathLength { get; init; }
|
|
|
|
/// <summary>Number of distinct paths.</summary>
|
|
public int PathCount { get; init; }
|
|
|
|
/// <summary>Key symbols in the chain.</summary>
|
|
public IReadOnlyList<string>? KeySymbols { get; init; }
|
|
|
|
/// <summary>Link to full call graph.</summary>
|
|
public string? CallGraphUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// VEX claim with trust scoring.
|
|
/// </summary>
|
|
public sealed record VexClaimDto
|
|
{
|
|
/// <summary>VEX statement ID.</summary>
|
|
public required string StatementId { get; init; }
|
|
|
|
/// <summary>Source of the VEX statement.</summary>
|
|
public required string Source { get; init; }
|
|
|
|
/// <summary>Status (affected, not_affected, etc.).</summary>
|
|
public required string Status { get; init; }
|
|
|
|
/// <summary>Justification category.</summary>
|
|
public string? Justification { get; init; }
|
|
|
|
/// <summary>Impact statement.</summary>
|
|
public string? ImpactStatement { get; init; }
|
|
|
|
/// <summary>When issued.</summary>
|
|
public DateTimeOffset IssuedAt { get; init; }
|
|
|
|
/// <summary>Trust score (0-1).</summary>
|
|
public double TrustScore { get; init; }
|
|
|
|
/// <summary>Whether this meets policy threshold.</summary>
|
|
public bool MeetsPolicyThreshold { get; init; }
|
|
|
|
/// <summary>Link to full VEX document.</summary>
|
|
public string? DocumentUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attestation summary for evidence panel.
|
|
/// </summary>
|
|
public sealed record AttestationSummaryDto
|
|
{
|
|
/// <summary>Attestation ID.</summary>
|
|
public required string Id { get; init; }
|
|
|
|
/// <summary>Predicate type.</summary>
|
|
public required string PredicateType { get; init; }
|
|
|
|
/// <summary>Subject digest.</summary>
|
|
public required string SubjectDigest { get; init; }
|
|
|
|
/// <summary>Signer identity.</summary>
|
|
public string? Signer { get; init; }
|
|
|
|
/// <summary>When signed.</summary>
|
|
public DateTimeOffset? SignedAt { get; init; }
|
|
|
|
/// <summary>Verification status.</summary>
|
|
public required string VerificationStatus { get; init; }
|
|
|
|
/// <summary>Transparency log entry if logged.</summary>
|
|
public string? TransparencyLogEntry { get; init; }
|
|
|
|
/// <summary>Link to full attestation.</summary>
|
|
public string? AttestationUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delta evidence showing what changed.
|
|
/// </summary>
|
|
public sealed record DeltaEvidenceDto
|
|
{
|
|
/// <summary>Delta comparison ID.</summary>
|
|
public required string DeltaId { get; init; }
|
|
|
|
/// <summary>Previous scan ID.</summary>
|
|
public required string PreviousScanId { get; init; }
|
|
|
|
/// <summary>Current scan ID.</summary>
|
|
public required string CurrentScanId { get; init; }
|
|
|
|
/// <summary>When comparison was made.</summary>
|
|
public DateTimeOffset ComparedAt { get; init; }
|
|
|
|
/// <summary>Summary of changes.</summary>
|
|
public DeltaSummaryDto? Summary { get; init; }
|
|
|
|
/// <summary>Link to full delta report.</summary>
|
|
public string? DeltaReportUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Summary of delta changes.
|
|
/// </summary>
|
|
public sealed record DeltaSummaryDto
|
|
{
|
|
/// <summary>New findings.</summary>
|
|
public int AddedCount { get; init; }
|
|
|
|
/// <summary>Removed findings.</summary>
|
|
public int RemovedCount { get; init; }
|
|
|
|
/// <summary>Changed findings.</summary>
|
|
public int ChangedCount { get; init; }
|
|
|
|
/// <summary>Was this finding new in this scan?</summary>
|
|
public bool IsNew { get; init; }
|
|
|
|
/// <summary>Was this finding's status changed?</summary>
|
|
public bool StatusChanged { get; init; }
|
|
|
|
/// <summary>Previous status if changed.</summary>
|
|
public string? PreviousStatus { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Policy evaluation evidence.
|
|
/// </summary>
|
|
public sealed record PolicyEvidenceDto
|
|
{
|
|
/// <summary>Policy version used.</summary>
|
|
public required string PolicyVersion { get; init; }
|
|
|
|
/// <summary>Policy digest.</summary>
|
|
public required string PolicyDigest { get; init; }
|
|
|
|
/// <summary>Verdict from policy evaluation.</summary>
|
|
public required string Verdict { get; init; }
|
|
|
|
/// <summary>Rules that fired.</summary>
|
|
public IReadOnlyList<PolicyRuleFiredDto>? RulesFired { get; init; }
|
|
|
|
/// <summary>Counterfactuals - what would change the verdict.</summary>
|
|
public IReadOnlyList<string>? Counterfactuals { get; init; }
|
|
|
|
/// <summary>Link to policy document.</summary>
|
|
public string? PolicyDocumentUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Policy rule that fired during evaluation.
|
|
/// </summary>
|
|
public sealed record PolicyRuleFiredDto
|
|
{
|
|
/// <summary>Rule ID.</summary>
|
|
public required string RuleId { get; init; }
|
|
|
|
/// <summary>Rule name.</summary>
|
|
public required string Name { get; init; }
|
|
|
|
/// <summary>Effect (allow, deny, warn).</summary>
|
|
public required string Effect { get; init; }
|
|
|
|
/// <summary>Reason the rule fired.</summary>
|
|
public string? Reason { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Content-addressed manifest hashes for determinism verification.
|
|
/// </summary>
|
|
public sealed record ManifestHashesDto
|
|
{
|
|
/// <summary>Artifact digest (image or SBOM).</summary>
|
|
public required string ArtifactDigest { get; init; }
|
|
|
|
/// <summary>Run manifest hash.</summary>
|
|
public required string ManifestHash { get; init; }
|
|
|
|
/// <summary>Feed snapshot hash.</summary>
|
|
public required string FeedSnapshotHash { get; init; }
|
|
|
|
/// <summary>Policy hash.</summary>
|
|
public required string PolicyHash { get; init; }
|
|
|
|
/// <summary>Knowledge snapshot ID.</summary>
|
|
public string? KnowledgeSnapshotId { get; init; }
|
|
|
|
/// <summary>Graph revision ID.</summary>
|
|
public string? GraphRevisionId { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Overall verification status.
|
|
/// </summary>
|
|
public sealed record VerificationStatusDto
|
|
{
|
|
/// <summary>Overall status (verified, partial, failed, unknown).</summary>
|
|
public required string Status { get; init; }
|
|
|
|
/// <summary>True if all hashes match expected values.</summary>
|
|
public bool HashesVerified { get; init; }
|
|
|
|
/// <summary>True if attestations verify.</summary>
|
|
public bool AttestationsVerified { get; init; }
|
|
|
|
/// <summary>True if evidence is complete.</summary>
|
|
public bool EvidenceComplete { get; init; }
|
|
|
|
/// <summary>Any verification issues.</summary>
|
|
public IReadOnlyList<string>? Issues { get; init; }
|
|
|
|
/// <summary>Last verification timestamp.</summary>
|
|
public DateTimeOffset? VerifiedAt { get; init; }
|
|
}
|
|
|
|
// Sprint: SPRINT_20260112_009_SCANNER_binary_diff_bundle_export (BINDIFF-SCAN-001)
|
|
|
|
/// <summary>
|
|
/// Binary diff evidence for unified evidence response.
|
|
/// </summary>
|
|
public sealed record BinaryDiffEvidenceDto
|
|
{
|
|
/// <summary>Evidence status.</summary>
|
|
public required string Status { get; init; }
|
|
|
|
/// <summary>SHA-256 hash of the evidence content.</summary>
|
|
public string? Hash { get; init; }
|
|
|
|
/// <summary>Previous binary artifact digest.</summary>
|
|
public string? PreviousBinaryDigest { get; init; }
|
|
|
|
/// <summary>Current binary artifact digest.</summary>
|
|
public string? CurrentBinaryDigest { get; init; }
|
|
|
|
/// <summary>Type of diff (structural, semantic, hybrid).</summary>
|
|
public string? DiffType { get; init; }
|
|
|
|
/// <summary>Binary format/ISA (e.g., elf-x86_64).</summary>
|
|
public string? BinaryFormat { get; init; }
|
|
|
|
/// <summary>Tool and version used for diffing.</summary>
|
|
public string? ToolVersion { get; init; }
|
|
|
|
/// <summary>Overall similarity score (0.0-1.0).</summary>
|
|
public double? SimilarityScore { get; init; }
|
|
|
|
/// <summary>Number of function-level changes.</summary>
|
|
public int FunctionChangeCount { get; init; }
|
|
|
|
/// <summary>Number of symbol-level changes.</summary>
|
|
public int SymbolChangeCount { get; init; }
|
|
|
|
/// <summary>Number of section-level changes.</summary>
|
|
public int SectionChangeCount { get; init; }
|
|
|
|
/// <summary>Number of security-relevant changes.</summary>
|
|
public int SecurityChangeCount { get; init; }
|
|
|
|
/// <summary>Whether semantic diff is available.</summary>
|
|
public bool HasSemanticDiff { get; init; }
|
|
|
|
/// <summary>Semantic similarity score (0.0-1.0).</summary>
|
|
public double? SemanticSimilarity { get; init; }
|
|
|
|
/// <summary>Function-level changes.</summary>
|
|
public IReadOnlyList<BinaryFunctionDiffDto>? FunctionChanges { get; init; }
|
|
|
|
/// <summary>Security-relevant changes.</summary>
|
|
public IReadOnlyList<BinarySecurityChangeDto>? SecurityChanges { get; init; }
|
|
|
|
/// <summary>DSSE attestation reference for binary diff.</summary>
|
|
public AttestationRefDto? Attestation { get; init; }
|
|
|
|
/// <summary>CAS URI for full binary diff evidence.</summary>
|
|
public string? CasUri { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Function-level diff entry for binary diff.
|
|
/// </summary>
|
|
public sealed record BinaryFunctionDiffDto
|
|
{
|
|
/// <summary>Diff operation (added, removed, modified).</summary>
|
|
public required string Operation { get; init; }
|
|
|
|
/// <summary>Function name.</summary>
|
|
public required string FunctionName { get; init; }
|
|
|
|
/// <summary>Function signature (if available).</summary>
|
|
public string? Signature { get; init; }
|
|
|
|
/// <summary>Semantic similarity score for modified functions.</summary>
|
|
public double? Similarity { get; init; }
|
|
|
|
/// <summary>Node hash for reachability correlation.</summary>
|
|
public string? NodeHash { get; init; }
|
|
|
|
/// <summary>Whether this function is security-sensitive.</summary>
|
|
public bool SecuritySensitive { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Security-relevant change in binary.
|
|
/// </summary>
|
|
public sealed record BinarySecurityChangeDto
|
|
{
|
|
/// <summary>Type of security change.</summary>
|
|
public required string ChangeType { get; init; }
|
|
|
|
/// <summary>Severity level (info, warning, critical).</summary>
|
|
public required string Severity { get; init; }
|
|
|
|
/// <summary>Description of the change.</summary>
|
|
public required string Description { get; init; }
|
|
|
|
/// <summary>Affected function name.</summary>
|
|
public string? AffectedFunction { get; init; }
|
|
|
|
/// <summary>Suggested remediation.</summary>
|
|
public string? Remediation { get; init; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attestation reference for evidence.
|
|
/// </summary>
|
|
public sealed record AttestationRefDto
|
|
{
|
|
/// <summary>Attestation ID.</summary>
|
|
public required string Id { get; init; }
|
|
|
|
/// <summary>Predicate type URI.</summary>
|
|
public required string PredicateType { get; init; }
|
|
|
|
/// <summary>DSSE envelope digest.</summary>
|
|
public string? EnvelopeDigest { get; init; }
|
|
|
|
/// <summary>Rekor log index (if anchored).</summary>
|
|
public long? RekorLogIndex { get; init; }
|
|
|
|
/// <summary>CAS URI for full attestation.</summary>
|
|
public string? CasUri { get; init; }
|
|
}
|