Files
git.stella-ops.org/src/Policy/__Libraries/StellaOps.Policy.Explainability/VerdictRationale.cs
2026-01-07 09:43:12 +02:00

198 lines
6.0 KiB
C#

namespace StellaOps.Policy.Explainability;
/// <summary>
/// Structured verdict rationale following the 4-line template.
/// Line 1: Evidence summary
/// Line 2: Policy clause that triggered the decision
/// Line 3: Attestations and proofs supporting the verdict
/// Line 4: Final decision with score and recommendation
/// </summary>
public sealed record VerdictRationale
{
/// <summary>Schema version for forward compatibility.</summary>
[JsonPropertyName("schema_version")]
public string SchemaVersion { get; init; } = "1.0";
/// <summary>Unique rationale ID (content-addressed).</summary>
[JsonPropertyName("rationale_id")]
public required string RationaleId { get; init; }
/// <summary>Reference to the verdict being explained.</summary>
[JsonPropertyName("verdict_ref")]
public required VerdictReference VerdictRef { get; init; }
/// <summary>Line 1: Evidence summary.</summary>
[JsonPropertyName("evidence")]
public required RationaleEvidence Evidence { get; init; }
/// <summary>Line 2: Policy clause that triggered the decision.</summary>
[JsonPropertyName("policy_clause")]
public required RationalePolicyClause PolicyClause { get; init; }
/// <summary>Line 3: Attestations and proofs supporting the verdict.</summary>
[JsonPropertyName("attestations")]
public required RationaleAttestations Attestations { get; init; }
/// <summary>Line 4: Final decision with score and recommendation.</summary>
[JsonPropertyName("decision")]
public required RationaleDecision Decision { get; init; }
/// <summary>Generation timestamp (UTC).</summary>
[JsonPropertyName("generated_at")]
public required DateTimeOffset GeneratedAt { get; init; }
/// <summary>Input digests for reproducibility.</summary>
[JsonPropertyName("input_digests")]
public required RationaleInputDigests InputDigests { get; init; }
}
/// <summary>Reference to the verdict being explained.</summary>
public sealed record VerdictReference
{
[JsonPropertyName("attestation_id")]
public required string AttestationId { get; init; }
[JsonPropertyName("artifact_digest")]
public required string ArtifactDigest { get; init; }
[JsonPropertyName("policy_id")]
public required string PolicyId { get; init; }
[JsonPropertyName("cve")]
public string? Cve { get; init; }
[JsonPropertyName("component_purl")]
public string? ComponentPurl { get; init; }
}
/// <summary>Line 1: Evidence summary.</summary>
public sealed record RationaleEvidence
{
[JsonPropertyName("cve")]
public required string Cve { get; init; }
[JsonPropertyName("component")]
public required ComponentIdentity Component { get; init; }
[JsonPropertyName("reachability")]
public ReachabilityDetail? Reachability { get; init; }
[JsonPropertyName("formatted_text")]
public required string FormattedText { get; init; }
}
public sealed record ComponentIdentity
{
[JsonPropertyName("purl")]
public required string Purl { get; init; }
[JsonPropertyName("name")]
public string? Name { get; init; }
[JsonPropertyName("version")]
public string? Version { get; init; }
[JsonPropertyName("ecosystem")]
public string? Ecosystem { get; init; }
}
public sealed record ReachabilityDetail
{
[JsonPropertyName("vulnerable_function")]
public string? VulnerableFunction { get; init; }
[JsonPropertyName("entry_point")]
public string? EntryPoint { get; init; }
[JsonPropertyName("path_summary")]
public string? PathSummary { get; init; }
}
/// <summary>Line 2: Policy clause reference.</summary>
public sealed record RationalePolicyClause
{
[JsonPropertyName("clause_id")]
public required string ClauseId { get; init; }
[JsonPropertyName("rule_description")]
public required string RuleDescription { get; init; }
[JsonPropertyName("conditions")]
public required IReadOnlyList<string> Conditions { get; init; }
[JsonPropertyName("formatted_text")]
public required string FormattedText { get; init; }
}
/// <summary>Line 3: Attestations and proofs.</summary>
public sealed record RationaleAttestations
{
[JsonPropertyName("path_witness")]
public AttestationReference? PathWitness { get; init; }
[JsonPropertyName("vex_statements")]
public IReadOnlyList<AttestationReference>? VexStatements { get; init; }
[JsonPropertyName("provenance")]
public AttestationReference? Provenance { get; init; }
[JsonPropertyName("formatted_text")]
public required string FormattedText { get; init; }
}
public sealed record AttestationReference
{
[JsonPropertyName("id")]
public required string Id { get; init; }
[JsonPropertyName("type")]
public required string Type { get; init; }
[JsonPropertyName("digest")]
public string? Digest { get; init; }
[JsonPropertyName("summary")]
public string? Summary { get; init; }
}
/// <summary>Line 4: Final decision.</summary>
public sealed record RationaleDecision
{
[JsonPropertyName("verdict")]
public required string Verdict { get; init; }
[JsonPropertyName("score")]
public double? Score { get; init; }
[JsonPropertyName("recommendation")]
public required string Recommendation { get; init; }
[JsonPropertyName("mitigation")]
public MitigationGuidance? Mitigation { get; init; }
[JsonPropertyName("formatted_text")]
public required string FormattedText { get; init; }
}
public sealed record MitigationGuidance
{
[JsonPropertyName("action")]
public required string Action { get; init; }
[JsonPropertyName("details")]
public string? Details { get; init; }
}
/// <summary>Input digests for reproducibility.</summary>
public sealed record RationaleInputDigests
{
[JsonPropertyName("verdict_digest")]
public required string VerdictDigest { get; init; }
[JsonPropertyName("policy_digest")]
public string? PolicyDigest { get; init; }
[JsonPropertyName("evidence_digest")]
public string? EvidenceDigest { get; init; }
}