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; }
}