Files
git.stella-ops.org/src/Cli/StellaOps.Cli/Services/Models/PromotionModels.cs
master d1cbb905f8
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
AOC Guard CI / aoc-guard (push) Has been cancelled
AOC Guard CI / aoc-verify (push) Has been cancelled
Concelier Attestation Tests / attestation-tests (push) Has been cancelled
Policy Lint & Smoke / policy-lint (push) Has been cancelled
up
2025-11-28 18:21:46 +02:00

469 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace StellaOps.Cli.Services.Models;
// CLI-PROMO-70-001: Promotion attestation models
/// <summary>
/// Request for assembling a promotion attestation.
/// </summary>
internal sealed class PromotionAssembleRequest
{
[JsonPropertyName("tenant")]
public string Tenant { get; init; } = string.Empty;
[JsonPropertyName("image")]
public string Image { get; init; } = string.Empty;
[JsonPropertyName("sbomPath")]
public string? SbomPath { get; init; }
[JsonPropertyName("vexPath")]
public string? VexPath { get; init; }
[JsonPropertyName("fromEnvironment")]
public string FromEnvironment { get; init; } = "staging";
[JsonPropertyName("toEnvironment")]
public string ToEnvironment { get; init; } = "prod";
[JsonPropertyName("actor")]
public string? Actor { get; init; }
[JsonPropertyName("pipeline")]
public string? Pipeline { get; init; }
[JsonPropertyName("ticket")]
public string? Ticket { get; init; }
[JsonPropertyName("notes")]
public string? Notes { get; init; }
[JsonPropertyName("skipRekor")]
public bool SkipRekor { get; init; }
[JsonPropertyName("outputPath")]
public string? OutputPath { get; init; }
}
/// <summary>
/// Promotion attestation predicate following stella.ops/promotion@v1 schema.
/// </summary>
internal sealed class PromotionPredicate
{
[JsonPropertyName("_type")]
public string Type { get; init; } = "stella.ops/promotion@v1";
[JsonPropertyName("subject")]
public IReadOnlyList<PromotionSubject> Subject { get; init; } = Array.Empty<PromotionSubject>();
[JsonPropertyName("materials")]
public IReadOnlyList<PromotionMaterial> Materials { get; init; } = Array.Empty<PromotionMaterial>();
[JsonPropertyName("promotion")]
public PromotionMetadata Promotion { get; init; } = new();
[JsonPropertyName("rekor")]
public PromotionRekorEntry? Rekor { get; init; }
[JsonPropertyName("attestation")]
public PromotionAttestationMetadata? Attestation { get; init; }
}
/// <summary>
/// Subject in promotion attestation (image reference).
/// </summary>
internal sealed class PromotionSubject
{
[JsonPropertyName("name")]
public string Name { get; init; } = string.Empty;
[JsonPropertyName("digest")]
public IReadOnlyDictionary<string, string> Digest { get; init; } = new Dictionary<string, string>();
}
/// <summary>
/// Material in promotion attestation (SBOM, VEX, etc.).
/// </summary>
internal sealed class PromotionMaterial
{
[JsonPropertyName("role")]
public string Role { get; init; } = string.Empty;
[JsonPropertyName("algo")]
public string Algo { get; init; } = "sha256";
[JsonPropertyName("digest")]
public string Digest { get; init; } = string.Empty;
[JsonPropertyName("format")]
public string? Format { get; init; }
[JsonPropertyName("uri")]
public string? Uri { get; init; }
}
/// <summary>
/// Promotion metadata.
/// </summary>
internal sealed class PromotionMetadata
{
[JsonPropertyName("from")]
public string From { get; init; } = "staging";
[JsonPropertyName("to")]
public string To { get; init; } = "prod";
[JsonPropertyName("actor")]
public string? Actor { get; init; }
[JsonPropertyName("timestamp")]
public DateTimeOffset Timestamp { get; init; } = DateTimeOffset.UtcNow;
[JsonPropertyName("pipeline")]
public string? Pipeline { get; init; }
[JsonPropertyName("ticket")]
public string? Ticket { get; init; }
[JsonPropertyName("notes")]
public string? Notes { get; init; }
}
/// <summary>
/// Rekor entry in promotion attestation.
/// </summary>
internal sealed class PromotionRekorEntry
{
[JsonPropertyName("uuid")]
public string Uuid { get; init; } = string.Empty;
[JsonPropertyName("logIndex")]
public long LogIndex { get; init; }
[JsonPropertyName("inclusionProof")]
public PromotionInclusionProof? InclusionProof { get; init; }
}
/// <summary>
/// Merkle inclusion proof.
/// </summary>
internal sealed class PromotionInclusionProof
{
[JsonPropertyName("rootHash")]
public string RootHash { get; init; } = string.Empty;
[JsonPropertyName("hashes")]
public IReadOnlyList<string> Hashes { get; init; } = Array.Empty<string>();
[JsonPropertyName("treeSize")]
public long TreeSize { get; init; }
[JsonPropertyName("checkpoint")]
public PromotionCheckpoint? Checkpoint { get; init; }
}
/// <summary>
/// Rekor checkpoint.
/// </summary>
internal sealed class PromotionCheckpoint
{
[JsonPropertyName("origin")]
public string Origin { get; init; } = string.Empty;
[JsonPropertyName("size")]
public long Size { get; init; }
[JsonPropertyName("hash")]
public string Hash { get; init; } = string.Empty;
[JsonPropertyName("signedNote")]
public string? SignedNote { get; init; }
}
/// <summary>
/// Attestation metadata.
/// </summary>
internal sealed class PromotionAttestationMetadata
{
[JsonPropertyName("bundle_sha256")]
public string BundleSha256 { get; init; } = string.Empty;
[JsonPropertyName("witness")]
public string? Witness { get; init; }
}
/// <summary>
/// Result of promotion assemble operation.
/// </summary>
internal sealed class PromotionAssembleResult
{
[JsonPropertyName("success")]
public bool Success { get; init; }
[JsonPropertyName("predicate")]
public PromotionPredicate? Predicate { get; init; }
[JsonPropertyName("outputPath")]
public string? OutputPath { get; init; }
[JsonPropertyName("imageDigest")]
public string ImageDigest { get; init; } = string.Empty;
[JsonPropertyName("materials")]
public IReadOnlyList<PromotionMaterial> Materials { get; init; } = Array.Empty<PromotionMaterial>();
[JsonPropertyName("rekorEntry")]
public PromotionRekorEntry? RekorEntry { get; init; }
[JsonPropertyName("errors")]
public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
[JsonPropertyName("warnings")]
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
}
// CLI-PROMO-70-002: Promotion attest/verify models
/// <summary>
/// Request for attesting a promotion predicate.
/// </summary>
internal sealed class PromotionAttestRequest
{
[JsonPropertyName("tenant")]
public string Tenant { get; init; } = string.Empty;
[JsonPropertyName("predicatePath")]
public string? PredicatePath { get; init; }
[JsonPropertyName("predicate")]
public PromotionPredicate? Predicate { get; init; }
[JsonPropertyName("keyId")]
public string? KeyId { get; init; }
[JsonPropertyName("useKeyless")]
public bool UseKeyless { get; init; }
[JsonPropertyName("outputPath")]
public string? OutputPath { get; init; }
[JsonPropertyName("uploadToRekor")]
public bool UploadToRekor { get; init; } = true;
}
/// <summary>
/// Result of promotion attest operation.
/// </summary>
internal sealed class PromotionAttestResult
{
[JsonPropertyName("success")]
public bool Success { get; init; }
[JsonPropertyName("bundlePath")]
public string? BundlePath { get; init; }
[JsonPropertyName("dsseEnvelope")]
public DsseEnvelope? DsseEnvelope { get; init; }
[JsonPropertyName("rekorEntry")]
public PromotionRekorEntry? RekorEntry { get; init; }
[JsonPropertyName("auditId")]
public string? AuditId { get; init; }
[JsonPropertyName("signerKeyId")]
public string? SignerKeyId { get; init; }
[JsonPropertyName("signedAt")]
public DateTimeOffset? SignedAt { get; init; }
[JsonPropertyName("errors")]
public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
[JsonPropertyName("warnings")]
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
}
/// <summary>
/// DSSE envelope for promotion attestation.
/// </summary>
internal sealed class DsseEnvelope
{
[JsonPropertyName("payloadType")]
public string PayloadType { get; init; } = "application/vnd.in-toto+json";
[JsonPropertyName("payload")]
public string Payload { get; init; } = string.Empty;
[JsonPropertyName("signatures")]
public IReadOnlyList<DsseSignature> Signatures { get; init; } = Array.Empty<DsseSignature>();
}
/// <summary>
/// DSSE signature.
/// </summary>
internal sealed class DsseSignature
{
[JsonPropertyName("keyid")]
public string KeyId { get; init; } = string.Empty;
[JsonPropertyName("sig")]
public string Sig { get; init; } = string.Empty;
[JsonPropertyName("cert")]
public string? Cert { get; init; }
}
/// <summary>
/// Request for verifying a promotion attestation.
/// </summary>
internal sealed class PromotionVerifyRequest
{
[JsonPropertyName("tenant")]
public string Tenant { get; init; } = string.Empty;
[JsonPropertyName("bundlePath")]
public string? BundlePath { get; init; }
[JsonPropertyName("predicatePath")]
public string? PredicatePath { get; init; }
[JsonPropertyName("sbomPath")]
public string? SbomPath { get; init; }
[JsonPropertyName("vexPath")]
public string? VexPath { get; init; }
[JsonPropertyName("trustRootPath")]
public string? TrustRootPath { get; init; }
[JsonPropertyName("checkpointPath")]
public string? CheckpointPath { get; init; }
[JsonPropertyName("skipRekorVerification")]
public bool SkipRekorVerification { get; init; }
[JsonPropertyName("skipSignatureVerification")]
public bool SkipSignatureVerification { get; init; }
}
/// <summary>
/// Result of promotion verify operation.
/// </summary>
internal sealed class PromotionVerifyResult
{
[JsonPropertyName("success")]
public bool Success { get; init; }
[JsonPropertyName("verified")]
public bool Verified { get; init; }
[JsonPropertyName("signatureVerification")]
public PromotionSignatureVerification? SignatureVerification { get; init; }
[JsonPropertyName("materialVerification")]
public PromotionMaterialVerification? MaterialVerification { get; init; }
[JsonPropertyName("rekorVerification")]
public PromotionRekorVerification? RekorVerification { get; init; }
[JsonPropertyName("predicate")]
public PromotionPredicate? Predicate { get; init; }
[JsonPropertyName("errors")]
public IReadOnlyList<string> Errors { get; init; } = Array.Empty<string>();
[JsonPropertyName("warnings")]
public IReadOnlyList<string> Warnings { get; init; } = Array.Empty<string>();
}
/// <summary>
/// Signature verification result.
/// </summary>
internal sealed class PromotionSignatureVerification
{
[JsonPropertyName("verified")]
public bool Verified { get; init; }
[JsonPropertyName("keyId")]
public string? KeyId { get; init; }
[JsonPropertyName("algorithm")]
public string? Algorithm { get; init; }
[JsonPropertyName("certSubject")]
public string? CertSubject { get; init; }
[JsonPropertyName("certIssuer")]
public string? CertIssuer { get; init; }
[JsonPropertyName("validFrom")]
public DateTimeOffset? ValidFrom { get; init; }
[JsonPropertyName("validTo")]
public DateTimeOffset? ValidTo { get; init; }
[JsonPropertyName("error")]
public string? Error { get; init; }
}
/// <summary>
/// Material verification result.
/// </summary>
internal sealed class PromotionMaterialVerification
{
[JsonPropertyName("verified")]
public bool Verified { get; init; }
[JsonPropertyName("materials")]
public IReadOnlyList<PromotionMaterialVerificationEntry> Materials { get; init; } = Array.Empty<PromotionMaterialVerificationEntry>();
}
/// <summary>
/// Individual material verification entry.
/// </summary>
internal sealed class PromotionMaterialVerificationEntry
{
[JsonPropertyName("role")]
public string Role { get; init; } = string.Empty;
[JsonPropertyName("verified")]
public bool Verified { get; init; }
[JsonPropertyName("expectedDigest")]
public string ExpectedDigest { get; init; } = string.Empty;
[JsonPropertyName("actualDigest")]
public string? ActualDigest { get; init; }
[JsonPropertyName("error")]
public string? Error { get; init; }
}
/// <summary>
/// Rekor verification result.
/// </summary>
internal sealed class PromotionRekorVerification
{
[JsonPropertyName("verified")]
public bool Verified { get; init; }
[JsonPropertyName("uuid")]
public string? Uuid { get; init; }
[JsonPropertyName("logIndex")]
public long? LogIndex { get; init; }
[JsonPropertyName("inclusionProofVerified")]
public bool InclusionProofVerified { get; init; }
[JsonPropertyName("checkpointVerified")]
public bool CheckpointVerified { get; init; }
[JsonPropertyName("error")]
public string? Error { get; init; }
}