sprints work
This commit is contained in:
358
src/AdvisoryAI/StellaOps.AdvisoryAI/Actions/IActionPolicyGate.cs
Normal file
358
src/AdvisoryAI/StellaOps.AdvisoryAI/Actions/IActionPolicyGate.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
// <copyright file="IActionPolicyGate.cs" company="StellaOps">
|
||||
// Copyright (c) StellaOps. Licensed under the AGPL-3.0-or-later.
|
||||
// </copyright>
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.AdvisoryAI.Actions;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates whether AI-proposed actions are allowed by policy.
|
||||
/// Integrates with K4 lattice for VEX-aware decisions and approval workflows.
|
||||
/// Sprint: SPRINT_20260109_011_004_BE Task PACT-001
|
||||
/// </summary>
|
||||
public interface IActionPolicyGate
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates whether an action is allowed by policy.
|
||||
/// </summary>
|
||||
/// <param name="proposal">The action proposal from the AI.</param>
|
||||
/// <param name="context">The execution context including tenant, user, roles, environment.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>The policy decision with any required approvals.</returns>
|
||||
Task<ActionPolicyDecision> EvaluateAsync(
|
||||
ActionProposal proposal,
|
||||
ActionContext context,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a human-readable explanation for a policy decision.
|
||||
/// </summary>
|
||||
/// <param name="decision">The decision to explain.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>Human-readable explanation with policy references.</returns>
|
||||
Task<PolicyExplanation> ExplainAsync(
|
||||
ActionPolicyDecision decision,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an action has already been executed (idempotency check).
|
||||
/// </summary>
|
||||
/// <param name="proposal">The action proposal.</param>
|
||||
/// <param name="context">The execution context.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>True if the action was already executed with the same parameters.</returns>
|
||||
Task<IdempotencyCheckResult> CheckIdempotencyAsync(
|
||||
ActionProposal proposal,
|
||||
ActionContext context,
|
||||
CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Context for action policy evaluation.
|
||||
/// </summary>
|
||||
public sealed record ActionContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Tenant identifier for multi-tenancy.
|
||||
/// </summary>
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// User identifier who initiated the action.
|
||||
/// </summary>
|
||||
public required string UserId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// User's roles/permissions.
|
||||
/// </summary>
|
||||
public required ImmutableArray<string> UserRoles { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Target environment (production, staging, development, etc.).
|
||||
/// </summary>
|
||||
public required string Environment { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Associated AI run ID, if any.
|
||||
/// </summary>
|
||||
public string? RunId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Associated finding ID for remediation actions.
|
||||
/// </summary>
|
||||
public string? FindingId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// CVE ID if this is a vulnerability-related action.
|
||||
/// </summary>
|
||||
public string? CveId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Image digest if this is a container-related action.
|
||||
/// </summary>
|
||||
public string? ImageDigest { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Optional correlation ID for tracing.
|
||||
/// </summary>
|
||||
public string? CorrelationId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional metadata for policy evaluation.
|
||||
/// </summary>
|
||||
public ImmutableDictionary<string, string> Metadata { get; init; } =
|
||||
ImmutableDictionary<string, string>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An action proposed by the AI system.
|
||||
/// </summary>
|
||||
public sealed record ActionProposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Unique identifier for this proposal.
|
||||
/// </summary>
|
||||
public required string ProposalId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Type of action (e.g., "approve", "quarantine", "create_vex").
|
||||
/// </summary>
|
||||
public required string ActionType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable label for the action.
|
||||
/// </summary>
|
||||
public required string Label { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Action parameters.
|
||||
/// </summary>
|
||||
public required ImmutableDictionary<string, string> Parameters { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the proposal was created.
|
||||
/// </summary>
|
||||
public required DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the proposal expires (null = never).
|
||||
/// </summary>
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Idempotency key for deduplication.
|
||||
/// </summary>
|
||||
public string? IdempotencyKey { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of policy gate evaluation.
|
||||
/// </summary>
|
||||
public sealed record ActionPolicyDecision
|
||||
{
|
||||
/// <summary>
|
||||
/// The decision outcome.
|
||||
/// </summary>
|
||||
public required PolicyDecisionKind Decision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Reference to the policy that made this decision.
|
||||
/// </summary>
|
||||
public string? PolicyId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Brief reason for the decision.
|
||||
/// </summary>
|
||||
public string? Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Required approvers if decision is AllowWithApproval.
|
||||
/// </summary>
|
||||
public ImmutableArray<RequiredApprover> RequiredApprovers { get; init; } =
|
||||
ImmutableArray<RequiredApprover>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Approval workflow ID if approval is required.
|
||||
/// </summary>
|
||||
public string? ApprovalWorkflowId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// K4 lattice position used in the decision.
|
||||
/// </summary>
|
||||
public string? K4Position { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// VEX status that influenced the decision, if any.
|
||||
/// </summary>
|
||||
public string? VexStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Severity level assigned by policy.
|
||||
/// </summary>
|
||||
public int? SeverityLevel { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When this decision expires.
|
||||
/// </summary>
|
||||
public DateTimeOffset? ExpiresAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional decision metadata.
|
||||
/// </summary>
|
||||
public ImmutableDictionary<string, string> Metadata { get; init; } =
|
||||
ImmutableDictionary<string, string>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kinds of policy decisions.
|
||||
/// </summary>
|
||||
public enum PolicyDecisionKind
|
||||
{
|
||||
/// <summary>
|
||||
/// Action is allowed and can execute immediately.
|
||||
/// </summary>
|
||||
Allow,
|
||||
|
||||
/// <summary>
|
||||
/// Action is allowed but requires approval workflow.
|
||||
/// </summary>
|
||||
AllowWithApproval,
|
||||
|
||||
/// <summary>
|
||||
/// Action is denied by policy.
|
||||
/// </summary>
|
||||
Deny,
|
||||
|
||||
/// <summary>
|
||||
/// Action is denied but admin can override.
|
||||
/// </summary>
|
||||
DenyWithOverride,
|
||||
|
||||
/// <summary>
|
||||
/// Decision could not be made (missing context).
|
||||
/// </summary>
|
||||
Indeterminate
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Describes a required approver for AllowWithApproval decisions.
|
||||
/// </summary>
|
||||
public sealed record RequiredApprover
|
||||
{
|
||||
/// <summary>
|
||||
/// Type of approver requirement.
|
||||
/// </summary>
|
||||
public required ApproverType Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier (user ID, role name, or group name).
|
||||
/// </summary>
|
||||
public required string Identifier { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable description.
|
||||
/// </summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of approval requirements.
|
||||
/// </summary>
|
||||
public enum ApproverType
|
||||
{
|
||||
/// <summary>
|
||||
/// Specific user must approve.
|
||||
/// </summary>
|
||||
User,
|
||||
|
||||
/// <summary>
|
||||
/// Any user with this role can approve.
|
||||
/// </summary>
|
||||
Role,
|
||||
|
||||
/// <summary>
|
||||
/// Any member of this group can approve.
|
||||
/// </summary>
|
||||
Group
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable explanation of a policy decision.
|
||||
/// </summary>
|
||||
public sealed record PolicyExplanation
|
||||
{
|
||||
/// <summary>
|
||||
/// Natural language summary of the decision.
|
||||
/// </summary>
|
||||
public required string Summary { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Detailed explanation points.
|
||||
/// </summary>
|
||||
public required ImmutableArray<string> Details { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// References to policies that were evaluated.
|
||||
/// </summary>
|
||||
public ImmutableArray<PolicyReference> PolicyReferences { get; init; } =
|
||||
ImmutableArray<PolicyReference>.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Suggested next steps for the user.
|
||||
/// </summary>
|
||||
public ImmutableArray<string> SuggestedActions { get; init; } =
|
||||
ImmutableArray<string>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reference to a specific policy.
|
||||
/// </summary>
|
||||
public sealed record PolicyReference
|
||||
{
|
||||
/// <summary>
|
||||
/// Policy identifier.
|
||||
/// </summary>
|
||||
public required string PolicyId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Policy name.
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Rule within the policy that matched.
|
||||
/// </summary>
|
||||
public string? RuleId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Link to policy documentation.
|
||||
/// </summary>
|
||||
public string? DocumentationUrl { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of idempotency check.
|
||||
/// </summary>
|
||||
public sealed record IdempotencyCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the action was previously executed.
|
||||
/// </summary>
|
||||
public required bool WasExecuted { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Previous execution ID if executed.
|
||||
/// </summary>
|
||||
public string? PreviousExecutionId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// When the action was previously executed.
|
||||
/// </summary>
|
||||
public DateTimeOffset? ExecutedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Result of the previous execution.
|
||||
/// </summary>
|
||||
public string? PreviousResult { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user