using StellaOps.Policy.Gates.Opa; using StellaOps.Policy.TrustLattice; using System.Collections.Immutable; namespace StellaOps.Policy.Gates; public record PolicyGateContext { public string Environment { get; init; } = "production"; public int UnknownCount { get; init; } public IReadOnlyList UnknownClaimScores { get; init; } = Array.Empty(); public IReadOnlyDictionary SourceInfluence { get; init; } = new Dictionary(StringComparer.Ordinal); public bool HasReachabilityProof { get; init; } public string? Severity { get; init; } public IReadOnlyCollection ReasonCodes { get; init; } = Array.Empty(); /// /// Subgraph slice for reachability proof. /// Required for high-severity findings when RequireSubgraphProofForHighSeverity is enabled. /// public SubgraphSlice? SubgraphSlice { get; init; } /// /// Subject key for Signals lookup. /// public string? SubjectKey { get; init; } /// /// CVE ID if applicable. /// public string? CveId { get; init; } /// /// Mutable metadata for audit trail. /// Gates can add metadata here for later inspection. /// public Dictionary? Metadata { get; init; } /// /// Optional supply chain evidence bundle for OPA policy evaluation. /// When provided, OPA policies can access artifact metadata, SBOMs, /// attestations, Rekor receipts, and VEX merge decisions. /// public OpaSupplyChainEvidence? SupplyChainEvidence { get; init; } } public sealed record GateResult { public required string GateName { get; init; } public required bool Passed { get; init; } public required string? Reason { get; init; } public required ImmutableDictionary Details { get; init; } /// /// Creates a passing gate result. /// public static GateResult Pass(string gateName, string reason, IEnumerable? warnings = null) { var details = ImmutableDictionary.Empty; if (warnings != null) { var warningList = warnings.ToList(); if (warningList.Count > 0) { details = details.Add("warnings", warningList); } } return new GateResult { GateName = gateName, Passed = true, Reason = reason, Details = details }; } /// /// Creates a passing gate result with child gate results. /// public static GateResult Pass(string gateName, string reason, IReadOnlyList? childResults) { var details = childResults != null && childResults.Count > 0 ? ImmutableDictionary.Empty.Add("childResults", childResults) : ImmutableDictionary.Empty; return new GateResult { GateName = gateName, Passed = true, Reason = reason, Details = details }; } /// /// Creates a failing gate result. /// public static GateResult Fail(string gateName, string reason, ImmutableDictionary? details = null) { return new GateResult { GateName = gateName, Passed = false, Reason = reason, Details = details ?? ImmutableDictionary.Empty }; } /// /// Creates a failing gate result with child gate results. /// public static GateResult Fail(string gateName, string reason, IReadOnlyList? childResults) { var details = childResults != null && childResults.Count > 0 ? ImmutableDictionary.Empty.Add("childResults", childResults) : ImmutableDictionary.Empty; return new GateResult { GateName = gateName, Passed = false, Reason = reason, Details = details }; } } public sealed record GateEvaluationResult { public required bool AllPassed { get; init; } public required ImmutableArray Results { get; init; } public GateResult? FirstFailure => Results.FirstOrDefault(r => !r.Passed); } /// /// Policy gate interface for gates that require MergeResult. /// public interface IPolicyGate { Task EvaluateAsync( MergeResult mergeResult, PolicyGateContext context, CancellationToken ct = default); } /// /// Simplified policy gate interface for context-only evaluation. /// Used by attestation, runtime witness, and CVE gates that don't require MergeResult. /// public interface IContextPolicyGate { /// /// Gate identifier. /// string Id { get; } /// /// Display name for the gate. /// string DisplayName { get; } /// /// Description of what the gate checks. /// string Description { get; } /// /// Evaluates the gate against the given context. /// Task EvaluateAsync(PolicyGateContext context, CancellationToken ct = default); } public sealed record PolicyGateRegistryOptions { public bool StopOnFirstFailure { get; init; } = true; } public interface IPolicyGateRegistry { void Register(string name) where TGate : IPolicyGate; Task EvaluateAsync( MergeResult mergeResult, PolicyGateContext context, CancellationToken ct = default); }