using System.Text.Json; namespace StellaOps.Scanner.Storage.Oci; /// /// Builds OCI manifests for reachability slices. /// Sprint: SPRINT_3850_0001_0001 /// public sealed class SliceOciManifestBuilder { private static readonly JsonSerializerOptions SerializerOptions = new(JsonSerializerDefaults.Web) { WriteIndented = false }; /// /// Build OCI push request for a slice artifact. /// public OciArtifactPushRequest BuildSlicePushRequest(SliceArtifactInput input) { ArgumentNullException.ThrowIfNull(input); ArgumentNullException.ThrowIfNull(input.Slice); ArgumentException.ThrowIfNullOrWhiteSpace(input.Reference); var layers = new List { BuildSliceLayer(input.Slice, input.SliceQuery) }; if (input.DsseEnvelope is not null) { layers.Add(BuildDsseLayer(input.DsseEnvelope)); } var annotations = BuildAnnotations(input.SliceQuery, input.Slice); return new OciArtifactPushRequest { Reference = input.Reference, ArtifactType = OciMediaTypes.SliceArtifact, Layers = layers, SubjectDigest = input.SubjectImageDigest, Annotations = annotations }; } private OciLayerContent BuildSliceLayer(object slice, SliceQueryMetadata? query) { var sliceJson = JsonSerializer.SerializeToUtf8Bytes(slice, SerializerOptions); var annotations = new Dictionary(); if (query is not null) { if (!string.IsNullOrWhiteSpace(query.CveId)) annotations["org.stellaops.slice.cve"] = query.CveId; if (!string.IsNullOrWhiteSpace(query.Purl)) annotations["org.stellaops.slice.purl"] = query.Purl; if (!string.IsNullOrWhiteSpace(query.Verdict)) annotations["org.stellaops.slice.verdict"] = query.Verdict; } return new OciLayerContent { Content = sliceJson, MediaType = OciMediaTypes.ReachabilitySlice, Annotations = annotations }; } private OciLayerContent BuildDsseLayer(byte[] dsseEnvelope) { return new OciLayerContent { Content = dsseEnvelope, MediaType = OciMediaTypes.DsseEnvelope, Annotations = new Dictionary { ["org.stellaops.attestation.type"] = "in-toto/dsse" } }; } private Dictionary BuildAnnotations(SliceQueryMetadata? query, object slice) { var annotations = new Dictionary { ["org.opencontainers.image.vendor"] = "StellaOps", ["org.stellaops.artifact.type"] = "reachability-slice" }; if (query is not null) { if (!string.IsNullOrWhiteSpace(query.CveId)) annotations["org.stellaops.slice.query.cve"] = query.CveId; if (!string.IsNullOrWhiteSpace(query.Purl)) annotations["org.stellaops.slice.query.purl"] = query.Purl; if (!string.IsNullOrWhiteSpace(query.ScanId)) annotations["org.stellaops.slice.scan-id"] = query.ScanId; } return annotations; } } /// /// Input for building a slice OCI artifact. /// public sealed record SliceArtifactInput { public required string Reference { get; init; } public required object Slice { get; init; } public byte[]? DsseEnvelope { get; init; } public SliceQueryMetadata? SliceQuery { get; init; } public string? SubjectImageDigest { get; init; } } /// /// Query metadata for slice annotations. /// public sealed record SliceQueryMetadata { public string? CveId { get; init; } public string? Purl { get; init; } public string? Verdict { get; init; } public string? ScanId { get; init; } }