using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Text.Json; using System.Text.Json.Serialization; namespace StellaOps.SbomService.Models; public sealed record SbomUploadRequest { [JsonPropertyName("artifactRef")] public string ArtifactRef { get; init; } = string.Empty; [JsonPropertyName("sbom")] public JsonElement? Sbom { get; init; } [JsonPropertyName("sbomBase64")] public string? SbomBase64 { get; init; } [JsonPropertyName("format")] public string? Format { get; init; } [JsonPropertyName("source")] public SbomUploadSource? Source { get; init; } // LIN-BE-003: Lineage ancestry fields [JsonPropertyName("parentArtifactDigest")] public string? ParentArtifactDigest { get; init; } [JsonPropertyName("baseImageRef")] public string? BaseImageRef { get; init; } [JsonPropertyName("baseImageDigest")] public string? BaseImageDigest { get; init; } } public sealed record SbomUploadSource { [JsonPropertyName("tool")] public string? Tool { get; init; } [JsonPropertyName("version")] public string? Version { get; init; } [JsonPropertyName("ciContext")] public SbomUploadCiContext? CiContext { get; init; } } public sealed record SbomUploadCiContext { [JsonPropertyName("buildId")] public string? BuildId { get; init; } [JsonPropertyName("repository")] public string? Repository { get; init; } } public sealed record SbomUploadResponse { [JsonPropertyName("sbomId")] public string SbomId { get; init; } = string.Empty; [JsonPropertyName("artifactRef")] public string ArtifactRef { get; init; } = string.Empty; [JsonPropertyName("digest")] public string Digest { get; init; } = string.Empty; [JsonPropertyName("format")] public string Format { get; init; } = string.Empty; [JsonPropertyName("formatVersion")] public string FormatVersion { get; init; } = string.Empty; [JsonPropertyName("validationResult")] public SbomValidationSummary ValidationResult { get; init; } = new(); [JsonPropertyName("analysisJobId")] public string AnalysisJobId { get; init; } = string.Empty; } public sealed record SbomValidationSummary { [JsonPropertyName("valid")] public bool Valid { get; init; } [JsonPropertyName("qualityScore")] public double QualityScore { get; init; } [JsonPropertyName("warnings")] public IReadOnlyList Warnings { get; init; } = Array.Empty(); [JsonPropertyName("errors")] public IReadOnlyList Errors { get; init; } = Array.Empty(); [JsonPropertyName("componentCount")] public int ComponentCount { get; init; } } public sealed record SbomNormalizedComponent( string Key, string Name, string? Version, string? Purl, string? License); public sealed record SbomLedgerSubmission( string ArtifactRef, string Digest, string Format, string FormatVersion, string Source, SbomUploadSource? Provenance, IReadOnlyList Components, Guid? ParentVersionId, // LIN-BE-003: Lineage ancestry fields string? ParentArtifactDigest = null, string? BaseImageRef = null, string? BaseImageDigest = null, string? BuildId = null); public sealed record SbomLedgerVersion { public required Guid VersionId { get; init; } public required Guid ChainId { get; init; } public required string ArtifactRef { get; init; } public required int SequenceNumber { get; init; } public required string Digest { get; init; } public required string Format { get; init; } public required string FormatVersion { get; init; } public required string Source { get; init; } public required DateTimeOffset CreatedAtUtc { get; init; } public SbomUploadSource? Provenance { get; init; } public Guid? ParentVersionId { get; init; } public string? ParentDigest { get; init; } // LIN-BE-003: Lineage ancestry fields public string? ParentArtifactDigest { get; init; } public string? BaseImageRef { get; init; } public string? BaseImageDigest { get; init; } public string? BuildId { get; init; } public IReadOnlyList Components { get; init; } = Array.Empty(); // LIN-BE-023: Replay hash for reproducibility verification public string? ReplayHash { get; init; } public ReplayHashInputSnapshot? ReplayHashInputs { get; init; } } /// /// Snapshot of inputs used to compute a replay hash. /// Stored alongside the version for audit and reproducibility. /// Sprint: LIN-BE-023 /// public sealed record ReplayHashInputSnapshot { public required string SbomDigest { get; init; } public required string FeedsSnapshotDigest { get; init; } public required string PolicyVersion { get; init; } public required string VexVerdictsDigest { get; init; } public required DateTimeOffset ComputedAt { get; init; } } public sealed record SbomVersionHistoryItem( Guid VersionId, int SequenceNumber, string Digest, string Format, string FormatVersion, string Source, DateTimeOffset CreatedAtUtc, Guid? ParentVersionId, string? ParentDigest, int ComponentCount); public sealed record SbomVersionHistoryResult( string ArtifactRef, Guid ChainId, IReadOnlyList Versions, string? NextCursor); public sealed record SbomTemporalQueryResult( string ArtifactRef, SbomVersionHistoryItem? Version); public sealed record SbomDiffComponent( string Key, string Name, string? Purl, string? Version, string? License); public sealed record SbomVersionChange( string Key, string Name, string? Purl, string? FromVersion, string? ToVersion); public sealed record SbomLicenseChange( string Key, string Name, string? Purl, string? FromLicense, string? ToLicense); public sealed record SbomDiffSummary( int AddedCount, int RemovedCount, int VersionChangedCount, int LicenseChangedCount); public sealed record SbomDiffResult { public required Guid BeforeVersionId { get; init; } public required Guid AfterVersionId { get; init; } public IReadOnlyList Added { get; init; } = Array.Empty(); public IReadOnlyList Removed { get; init; } = Array.Empty(); public IReadOnlyList VersionChanged { get; init; } = Array.Empty(); public IReadOnlyList LicenseChanged { get; init; } = Array.Empty(); public SbomDiffSummary Summary { get; init; } = new(0, 0, 0, 0); } public sealed record SbomLineageNode( Guid VersionId, int SequenceNumber, string Digest, string Source, DateTimeOffset CreatedAtUtc); public sealed record SbomLineageEdge( Guid FromVersionId, Guid ToVersionId, string Relationship); public static class SbomLineageRelationships { public const string Parent = "parent"; public const string Build = "build"; public const string Base = "base"; } public sealed record SbomLineageResult( string ArtifactRef, Guid ChainId, IReadOnlyList Nodes, IReadOnlyList Edges); // ----------------------------------------------------------------------------- // Extended Lineage Models for SBOM Lineage Graph // Sprint: SPRINT_20251228_005_BE_sbom_lineage_graph_i (LIN-BE-004/005) // ----------------------------------------------------------------------------- /// /// Persistent lineage edge stored in database. /// Uses artifact digests as stable identifiers across systems. /// public sealed record SbomLineageEdgeEntity { public required Guid Id { get; init; } public required string ParentDigest { get; init; } public required string ChildDigest { get; init; } public required LineageRelationship Relationship { get; init; } public required Guid TenantId { get; init; } public required DateTimeOffset CreatedAt { get; init; } } /// /// Lineage relationship type. /// public enum LineageRelationship { /// /// Direct version succession (v1.0 → v1.1). /// Parent, /// /// Same CI build produced multiple artifacts (multi-arch). /// Build, /// /// Derived from base image (FROM instruction). /// Base } /// /// Extended lineage node with badge information for UI. /// public sealed record SbomLineageNodeExtended { public required Guid Id { get; init; } public required string Digest { get; init; } public required string ArtifactRef { get; init; } public required int SequenceNumber { get; init; } public required DateTimeOffset CreatedAt { get; init; } public required string Source { get; init; } public SbomLineageBadges? Badges { get; init; } public string? ReplayHash { get; init; } } /// /// Badge information for lineage node. /// public sealed record SbomLineageBadges { public int NewVulns { get; init; } public int ResolvedVulns { get; init; } public string SignatureStatus { get; init; } = "unknown"; public int ComponentCount { get; init; } } /// /// Extended lineage edge with digest-based endpoints. /// public sealed record SbomLineageEdgeExtended { public required string From { get; init; } public required string To { get; init; } public required string Relationship { get; init; } } /// /// Complete lineage graph response. /// public sealed record SbomLineageGraphResponse { public required string Artifact { get; init; } public required IReadOnlyList Nodes { get; init; } public required IReadOnlyList Edges { get; init; } } /// /// Lineage diff response with component and VEX deltas. /// public sealed record SbomLineageDiffResponse { public required SbomDiffResult SbomDiff { get; init; } public required IReadOnlyList VexDiff { get; init; } public IReadOnlyList? ReachabilityDiff { get; init; } public required string ReplayHash { get; init; } } /// /// VEX status change between versions. /// public sealed record VexDeltaSummary { public required string Cve { get; init; } public required string FromStatus { get; init; } public required string ToStatus { get; init; } public string? Reason { get; init; } public string? EvidenceLink { get; init; } } /// /// Reachability change between versions. /// public sealed record ReachabilityDeltaSummary { public required string Cve { get; init; } public required string FromStatus { get; init; } public required string ToStatus { get; init; } public int PathsAdded { get; init; } public int PathsRemoved { get; init; } public IReadOnlyList? GatesAdded { get; init; } public IReadOnlyList? GatesRemoved { get; init; } } /// /// Query options for lineage graph. /// public sealed record SbomLineageQueryOptions { public int MaxDepth { get; init; } = 10; public bool IncludeVerdicts { get; init; } = true; public bool IncludeBadges { get; init; } = true; public bool IncludeReplayHash { get; init; } } public sealed record SbomRetentionResult( int VersionsPruned, int ChainsTouched, IReadOnlyList Messages); public sealed class SbomLedgerOptions { [Range(1, 10000)] public int MaxVersionsPerArtifact { get; init; } = 50; [Range(0, 36500)] public int MaxAgeDays { get; init; } [Range(1, 10000)] public int MinVersionsToKeep { get; init; } = 1; } public sealed record SbomLedgerAuditEntry( string ArtifactRef, Guid VersionId, string Action, DateTimeOffset TimestampUtc, string? Details); public sealed record SbomAnalysisJob( string JobId, string ArtifactRef, Guid VersionId, DateTimeOffset CreatedAtUtc, string Status);