Files
git.stella-ops.org/src/SbomService/StellaOps.SbomService/Models/SbomLedgerModels.cs
2026-01-13 18:53:39 +02:00

410 lines
12 KiB
C#

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<string> Warnings { get; init; } = Array.Empty<string>();
[JsonPropertyName("errors")]
public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
[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<SbomNormalizedComponent> 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<SbomNormalizedComponent> Components { get; init; } = Array.Empty<SbomNormalizedComponent>();
// LIN-BE-023: Replay hash for reproducibility verification
public string? ReplayHash { get; init; }
public ReplayHashInputSnapshot? ReplayHashInputs { get; init; }
}
/// <summary>
/// Snapshot of inputs used to compute a replay hash.
/// Stored alongside the version for audit and reproducibility.
/// Sprint: LIN-BE-023
/// </summary>
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<SbomVersionHistoryItem> 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<SbomDiffComponent> Added { get; init; } = Array.Empty<SbomDiffComponent>();
public IReadOnlyList<SbomDiffComponent> Removed { get; init; } = Array.Empty<SbomDiffComponent>();
public IReadOnlyList<SbomVersionChange> VersionChanged { get; init; } = Array.Empty<SbomVersionChange>();
public IReadOnlyList<SbomLicenseChange> LicenseChanged { get; init; } = Array.Empty<SbomLicenseChange>();
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<SbomLineageNode> Nodes,
IReadOnlyList<SbomLineageEdge> Edges);
// -----------------------------------------------------------------------------
// Extended Lineage Models for SBOM Lineage Graph
// Sprint: SPRINT_20251228_005_BE_sbom_lineage_graph_i (LIN-BE-004/005)
// -----------------------------------------------------------------------------
/// <summary>
/// Persistent lineage edge stored in database.
/// Uses artifact digests as stable identifiers across systems.
/// </summary>
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; }
}
/// <summary>
/// Lineage relationship type.
/// </summary>
public enum LineageRelationship
{
/// <summary>
/// Direct version succession (v1.0 → v1.1).
/// </summary>
Parent,
/// <summary>
/// Same CI build produced multiple artifacts (multi-arch).
/// </summary>
Build,
/// <summary>
/// Derived from base image (FROM instruction).
/// </summary>
Base
}
/// <summary>
/// Extended lineage node with badge information for UI.
/// </summary>
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; }
}
/// <summary>
/// Badge information for lineage node.
/// </summary>
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; }
}
/// <summary>
/// Extended lineage edge with digest-based endpoints.
/// </summary>
public sealed record SbomLineageEdgeExtended
{
public required string From { get; init; }
public required string To { get; init; }
public required string Relationship { get; init; }
}
/// <summary>
/// Complete lineage graph response.
/// </summary>
public sealed record SbomLineageGraphResponse
{
public required string Artifact { get; init; }
public required IReadOnlyList<SbomLineageNodeExtended> Nodes { get; init; }
public required IReadOnlyList<SbomLineageEdgeExtended> Edges { get; init; }
}
/// <summary>
/// Lineage diff response with component and VEX deltas.
/// </summary>
public sealed record SbomLineageDiffResponse
{
public required SbomDiffResult SbomDiff { get; init; }
public required IReadOnlyList<VexDeltaSummary> VexDiff { get; init; }
public IReadOnlyList<ReachabilityDeltaSummary>? ReachabilityDiff { get; init; }
public required string ReplayHash { get; init; }
}
/// <summary>
/// VEX status change between versions.
/// </summary>
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; }
}
/// <summary>
/// Reachability change between versions.
/// </summary>
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<string>? GatesAdded { get; init; }
public IReadOnlyList<string>? GatesRemoved { get; init; }
}
/// <summary>
/// Query options for lineage graph.
/// </summary>
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<string> 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);